From a4a9b7ca2021b5b6948245ac69a4397964b7bb49 Mon Sep 17 00:00:00 2001 From: Jake Day Date: Thu, 6 Jun 2019 13:44:35 -0400 Subject: [PATCH] updating patches --- configs/4.19/config | 13 +- configs/{5.0 => 5.1}/config | 177 +- patches/4.19/0001-surface-acpi.patch | 4 +- patches/4.19/0002-suspend.patch | 6 +- patches/4.19/0003-buttons.patch | 4 +- patches/4.19/0004-cameras.patch | 4 +- patches/4.19/0005-ipts.patch | 4 +- patches/4.19/0006-hid.patch | 6 +- patches/4.19/0007-sdcard-reader.patch | 8 +- patches/4.19/0008-wifi.patch | 6 +- patches/4.19/0009-surface3-power.patch | 4 +- patches/4.19/0010-surface-dock.patch | 4 +- patches/4.19/0011-mwlwifi.patch | 4 +- patches/4.19/0012-surface-lte.patch | 4 +- patches/5.0/0001-surface-acpi.patch | 4139 ---- patches/5.0/0002-suspend.patch | 145 - patches/5.0/0003-buttons.patch | 257 - patches/5.0/0004-cameras.patch | 2753 --- patches/5.0/0005-ipts.patch | 6175 ----- patches/5.0/0006-hid.patch | 151 - patches/5.0/0007-sdcard-reader.patch | 26 - patches/5.0/0008-wifi.patch | 270 - patches/5.0/0009-surface-power.patch | 655 - patches/5.0/0010-surface-dock.patch | 25 - patches/5.0/0011-mwlwifi.patch | 19755 ---------------- patches/5.0/0012-surface-lte.patch | 24 - patches/5.1/0001-surface-acpi.patch | 8 +- patches/5.1/0002-suspend.patch | 10 +- patches/5.1/0003-buttons.patch | 8 +- patches/5.1/0004-cameras.patch | 8 +- patches/5.1/0005-ipts.patch | 10 +- patches/5.1/0006-hid.patch | 8 +- patches/5.1/0007-sdcard-reader.patch | 10 +- patches/5.1/0008-wifi.patch | 10 +- ...-power.patch => 0009-surface3-power.patch} | 10 +- patches/5.1/0010-surface-dock.patch | 8 +- patches/5.1/0011-mwlwifi.patch | 8 +- patches/5.1/0012-surface-lte.patch | 8 +- 38 files changed, 189 insertions(+), 34540 deletions(-) rename configs/{5.0 => 5.1}/config (98%) delete mode 100644 patches/5.0/0001-surface-acpi.patch delete mode 100644 patches/5.0/0002-suspend.patch delete mode 100644 patches/5.0/0003-buttons.patch delete mode 100644 patches/5.0/0004-cameras.patch delete mode 100644 patches/5.0/0005-ipts.patch delete mode 100644 patches/5.0/0006-hid.patch delete mode 100644 patches/5.0/0007-sdcard-reader.patch delete mode 100644 patches/5.0/0008-wifi.patch delete mode 100644 patches/5.0/0009-surface-power.patch delete mode 100644 patches/5.0/0010-surface-dock.patch delete mode 100644 patches/5.0/0011-mwlwifi.patch delete mode 100644 patches/5.0/0012-surface-lte.patch rename patches/5.1/{0009-surface-power.patch => 0009-surface3-power.patch} (98%) diff --git a/configs/4.19/config b/configs/4.19/config index ca8a5a41a..47b4c5887 100644 --- a/configs/4.19/config +++ b/configs/4.19/config @@ -1,14 +1,15 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86 4.19.27 Kernel Configuration +# Linux/x86 4.19.48 Kernel Configuration # # -# Compiler: gcc (Ubuntu 8.2.0-7ubuntu1) 8.2.0 +# Compiler: gcc (Ubuntu 8.3.0-6ubuntu1~18.10) 8.3.0 # CONFIG_CC_IS_GCC=y -CONFIG_GCC_VERSION=80200 +CONFIG_GCC_VERSION=80300 CONFIG_CLANG_VERSION=0 +CONFIG_CC_HAS_ASM_GOTO=y CONFIG_IRQ_WORK=y CONFIG_BUILDTIME_EXTABLE_SORT=y CONFIG_THREAD_INFO_IN_TASK=y @@ -4006,6 +4007,7 @@ CONFIG_N_HDLC=m CONFIG_N_GSM=m CONFIG_TRACE_ROUTER=m CONFIG_TRACE_SINK=m +CONFIG_LDISC_AUTOLOAD=y CONFIG_DEVMEM=y # CONFIG_DEVKMEM is not set @@ -4091,7 +4093,6 @@ CONFIG_HW_RANDOM_AMD=m CONFIG_HW_RANDOM_VIA=m CONFIG_HW_RANDOM_VIRTIO=m CONFIG_NVRAM=m -CONFIG_R3964=m CONFIG_APPLICOM=m # @@ -7733,7 +7734,6 @@ CONFIG_GREYBUS_USB=m # Gasket devices # # CONFIG_STAGING_GASKET_FRAMEWORK is not set -# CONFIG_XIL_AXIS_FIFO is not set # CONFIG_EROFS_FS is not set CONFIG_X86_PLATFORM_DEVICES=y CONFIG_ACER_WMI=m @@ -7774,7 +7774,7 @@ CONFIG_THINKPAD_ACPI_VIDEO=y CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y CONFIG_SURFACE_ACPI=m CONFIG_SURFACE_ACPI_SSH=y -CONFIG_SURFACE_ACPI_SSH_DEBUG_DEVICE=n +# CONFIG_SURFACE_ACPI_SSH_DEBUG_DEVICE is not set CONFIG_SURFACE_ACPI_SAN=y CONFIG_SURFACE_ACPI_VHF=y CONFIG_SURFACE_ACPI_DTX=y @@ -7823,6 +7823,7 @@ CONFIG_MLX_PLATFORM=m CONFIG_INTEL_TURBO_MAX_3=y # CONFIG_TOUCHSCREEN_DMI is not set # CONFIG_I2C_MULTI_INSTANTIATE is not set +# CONFIG_INTEL_ATOMISP2_PM is not set CONFIG_PMC_ATOM=y CONFIG_CHROME_PLATFORMS=y CONFIG_CHROMEOS_LAPTOP=m diff --git a/configs/5.0/config b/configs/5.1/config similarity index 98% rename from configs/5.0/config rename to configs/5.1/config index 9c0b3dea7..d568fd83d 100644 --- a/configs/5.0/config +++ b/configs/5.1/config @@ -1,15 +1,16 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86 5.0.1 Kernel Configuration +# Linux/x86 5.1.7 Kernel Configuration # # -# Compiler: gcc (Ubuntu 8.2.0-7ubuntu1) 8.2.0 +# Compiler: gcc (Ubuntu 8.3.0-6ubuntu1~18.10) 8.3.0 # CONFIG_CC_IS_GCC=y -CONFIG_GCC_VERSION=80200 +CONFIG_GCC_VERSION=80300 CONFIG_CLANG_VERSION=0 CONFIG_CC_HAS_ASM_GOTO=y +CONFIG_CC_HAS_WARN_MAYBE_UNINITIALIZED=y CONFIG_IRQ_WORK=y CONFIG_BUILDTIME_EXTABLE_SORT=y CONFIG_THREAD_INFO_IN_TASK=y @@ -198,6 +199,7 @@ CONFIG_TIMERFD=y CONFIG_EVENTFD=y CONFIG_SHMEM=y CONFIG_AIO=y +CONFIG_IO_URING=y CONFIG_ADVISE_SYSCALLS=y CONFIG_MEMBARRIER=y CONFIG_KALLSYMS=y @@ -586,6 +588,7 @@ CONFIG_X86_SPEEDSTEP_LIB=m CONFIG_CPU_IDLE=y CONFIG_CPU_IDLE_GOV_LADDER=y CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_CPU_IDLE_GOV_TEO is not set CONFIG_INTEL_IDLE=y # @@ -605,7 +608,6 @@ CONFIG_AMD_NB=y # Binary Emulations # CONFIG_IA32_EMULATION=y -# CONFIG_IA32_AOUT is not set CONFIG_X86_X32=y CONFIG_COMPAT_32=y CONFIG_COMPAT=y @@ -647,6 +649,7 @@ CONFIG_APPLE_PROPERTIES=y CONFIG_UEFI_CPER=y CONFIG_UEFI_CPER_X86=y CONFIG_EFI_DEV_PATH_PARSER=y +CONFIG_EFI_EARLYCON=y # # Tegra firmware driver @@ -763,6 +766,7 @@ CONFIG_HAVE_RELIABLE_STACKTRACE=y CONFIG_ISA_BUS_API=y CONFIG_OLD_SIGSUSPEND3=y CONFIG_COMPAT_OLD_SIGACTION=y +CONFIG_64BIT_TIME=y CONFIG_COMPAT_32BIT_TIME=y CONFIG_HAVE_ARCH_VMAP_STACK=y CONFIG_VMAP_STACK=y @@ -773,6 +777,7 @@ CONFIG_STRICT_MODULE_RWX=y CONFIG_ARCH_HAS_REFCOUNT=y # CONFIG_REFCOUNT_FULL is not set CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y +CONFIG_ARCH_USE_MEMREMAP_PROT=y # # GCOV-based kernel profiling @@ -965,6 +970,7 @@ CONFIG_SKB_EXTENSIONS=y CONFIG_PACKET=y CONFIG_PACKET_DIAG=m CONFIG_UNIX=y +CONFIG_UNIX_SCM=y CONFIG_UNIX_DIAG=m CONFIG_TLS=m # CONFIG_TLS_DEVICE is not set @@ -1105,7 +1111,7 @@ CONFIG_NF_CONNTRACK_TIMEOUT=y CONFIG_NF_CONNTRACK_TIMESTAMP=y CONFIG_NF_CONNTRACK_LABELS=y CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_CT_PROTO_GRE=y CONFIG_NF_CT_PROTO_SCTP=y CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m @@ -1131,6 +1137,7 @@ CONFIG_NF_NAT_IRC=m CONFIG_NF_NAT_SIP=m CONFIG_NF_NAT_TFTP=m CONFIG_NF_NAT_REDIRECT=y +CONFIG_NF_NAT_MASQUERADE=y CONFIG_NETFILTER_SYNPROXY=m CONFIG_NF_TABLES=m # CONFIG_NF_TABLES_SET is not set @@ -1329,11 +1336,6 @@ CONFIG_NF_DUP_IPV4=m CONFIG_NF_LOG_ARP=m CONFIG_NF_LOG_IPV4=m CONFIG_NF_REJECT_IPV4=m -CONFIG_NF_NAT_IPV4=m -CONFIG_NF_NAT_MASQUERADE_IPV4=y -CONFIG_NFT_CHAIN_NAT_IPV4=m -CONFIG_NFT_MASQ_IPV4=m -CONFIG_NFT_REDIR_IPV4=m CONFIG_NF_NAT_SNMP_BASIC=m CONFIG_NF_NAT_PPTP=m CONFIG_NF_NAT_H323=m @@ -1366,17 +1368,12 @@ CONFIG_NF_SOCKET_IPV6=m CONFIG_NF_TPROXY_IPV6=m CONFIG_NF_TABLES_IPV6=y CONFIG_NFT_CHAIN_ROUTE_IPV6=m -CONFIG_NFT_CHAIN_NAT_IPV6=m -CONFIG_NFT_MASQ_IPV6=m -CONFIG_NFT_REDIR_IPV6=m CONFIG_NFT_REJECT_IPV6=m CONFIG_NFT_DUP_IPV6=m CONFIG_NFT_FIB_IPV6=m CONFIG_NF_DUP_IPV6=m CONFIG_NF_REJECT_IPV6=m CONFIG_NF_LOG_IPV6=m -CONFIG_NF_NAT_IPV6=m -CONFIG_NF_NAT_MASQUERADE_IPV6=y CONFIG_IP6_NF_IPTABLES=m CONFIG_IP6_NF_MATCH_AH=m CONFIG_IP6_NF_MATCH_EUI64=m @@ -1888,8 +1885,7 @@ CONFIG_LWTUNNEL_BPF=y CONFIG_DST_CACHE=y CONFIG_GRO_CELLS=y CONFIG_NET_SOCK_MSG=y -CONFIG_NET_DEVLINK=m -CONFIG_MAY_USE_DEVLINK=m +# CONFIG_NET_DEVLINK is not set CONFIG_PAGE_POOL=y CONFIG_FAILOVER=m CONFIG_HAVE_EBPF_JIT=y @@ -2024,7 +2020,6 @@ CONFIG_REGMAP_MMIO=y CONFIG_REGMAP_IRQ=y CONFIG_DMA_SHARED_BUFFER=y # CONFIG_DMA_FENCE_TRACE is not set -# CONFIG_DMA_CMA is not set # # Bus devices @@ -2158,8 +2153,8 @@ CONFIG_MTD_NAND_PLATFORM=m CONFIG_MTD_LPDDR=m CONFIG_MTD_QINFO_PROBE=m CONFIG_MTD_SPI_NOR=m -CONFIG_MTD_MT81xx_NOR=m CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y +# CONFIG_SPI_MTK_QUADSPI is not set # CONFIG_SPI_INTEL_SPI_PCI is not set # CONFIG_SPI_INTEL_SPI_PLATFORM is not set CONFIG_MTD_UBI=m @@ -2321,6 +2316,7 @@ CONFIG_ALTERA_STAPL=m CONFIG_INTEL_MEI=m CONFIG_INTEL_MEI_ME=m CONFIG_INTEL_MEI_TXE=m +# CONFIG_INTEL_MEI_HDCP is not set CONFIG_INTEL_IPTS=m CONFIG_VMWARE_VMCI=m @@ -2374,6 +2370,7 @@ CONFIG_ECHO=m # CONFIG_MISC_ALCOR_PCI is not set CONFIG_MISC_RTSX_PCI=m # CONFIG_MISC_RTSX_USB is not set +# CONFIG_HABANA_AI is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -2526,10 +2523,6 @@ CONFIG_SCSI_DH_RDAC=m CONFIG_SCSI_DH_HP_SW=m CONFIG_SCSI_DH_EMC=m CONFIG_SCSI_DH_ALUA=m -CONFIG_SCSI_OSD_INITIATOR=m -CONFIG_SCSI_OSD_ULD=m -CONFIG_SCSI_OSD_DPRINT_SENSE=1 -# CONFIG_SCSI_OSD_DEBUG is not set CONFIG_ATA=y CONFIG_ATA_VERBOSE_ERROR=y CONFIG_ATA_ACPI=y @@ -2664,6 +2657,7 @@ CONFIG_DM_MULTIPATH=m CONFIG_DM_MULTIPATH_QL=m CONFIG_DM_MULTIPATH_ST=m CONFIG_DM_DELAY=m +# CONFIG_DM_INIT is not set CONFIG_DM_UEVENT=y CONFIG_DM_FLAKEY=m CONFIG_DM_VERITY=m @@ -2717,6 +2711,7 @@ CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m CONFIG_NET_TEAM_MODE_LOADBALANCE=m CONFIG_MACVLAN=m CONFIG_MACVTAP=m +CONFIG_IPVLAN_L3S=y CONFIG_IPVLAN=m CONFIG_IPVTAP=m CONFIG_VXLAN=m @@ -3101,6 +3096,7 @@ CONFIG_DWC_XLGMAC_PCI=m CONFIG_NET_VENDOR_TEHUTI=y CONFIG_TEHUTI=m CONFIG_NET_VENDOR_TI=y +# CONFIG_TI_CPSW_PHY_SEL is not set CONFIG_TI_CPSW_ALE=m CONFIG_TLAN=m CONFIG_NET_VENDOR_VIA=y @@ -3423,6 +3419,7 @@ CONFIG_MT7601U=m # CONFIG_MT76x0E is not set # CONFIG_MT76x2E is not set # CONFIG_MT76x2U is not set +# CONFIG_MT7603E is not set CONFIG_WLAN_VENDOR_RALINK=y CONFIG_RT2X00=m CONFIG_RT2400PCI=m @@ -3911,6 +3908,7 @@ CONFIG_INPUT_AD714X_SPI=m CONFIG_INPUT_ARIZONA_HAPTICS=m CONFIG_INPUT_BMA150=m CONFIG_INPUT_E3X0_BUTTON=m +# CONFIG_INPUT_MSM_VIBRATOR is not set CONFIG_INPUT_PCSPKR=m CONFIG_INPUT_MAX77693_HAPTIC=m CONFIG_INPUT_MAX8925_ONKEY=m @@ -4026,6 +4024,7 @@ CONFIG_N_HDLC=m CONFIG_N_GSM=m CONFIG_TRACE_ROUTER=m CONFIG_TRACE_SINK=m +CONFIG_LDISC_AUTOLOAD=y CONFIG_DEVMEM=y # CONFIG_DEVKMEM is not set @@ -4099,6 +4098,7 @@ CONFIG_HVC_XEN_FRONTEND=y CONFIG_VIRTIO_CONSOLE=y CONFIG_IPMI_HANDLER=m CONFIG_IPMI_DMI_DECODE=y +CONFIG_IPMI_PLAT_DATA=y # CONFIG_IPMI_PANIC_EVENT is not set CONFIG_IPMI_DEVICE_INTERFACE=m CONFIG_IPMI_SI=m @@ -4112,7 +4112,6 @@ CONFIG_HW_RANDOM_AMD=m CONFIG_HW_RANDOM_VIA=m CONFIG_HW_RANDOM_VIRTIO=m CONFIG_NVRAM=m -CONFIG_R3964=m CONFIG_APPLICOM=m # @@ -4267,6 +4266,7 @@ CONFIG_SPI_DW_PCI=m CONFIG_SPI_DW_MID_DMA=y CONFIG_SPI_DW_MMIO=m CONFIG_SPI_DLN2=m +# CONFIG_SPI_NXP_FLEXSPI is not set CONFIG_SPI_GPIO=m CONFIG_SPI_LM70_LLP=m CONFIG_SPI_OC_TINY=m @@ -4274,6 +4274,7 @@ CONFIG_SPI_PXA2XX=m CONFIG_SPI_PXA2XX_PCI=m # CONFIG_SPI_ROCKCHIP is not set CONFIG_SPI_SC18IS602=m +# CONFIG_SPI_SIFIVE is not set # CONFIG_SPI_MXIC is not set CONFIG_SPI_XCOMM=m # CONFIG_SPI_XILINX is not set @@ -4365,6 +4366,7 @@ CONFIG_GPIO_LYNXPOINT=y CONFIG_GPIO_MENZ127=m CONFIG_GPIO_MOCKUP=m CONFIG_GPIO_VX855=m +# CONFIG_GPIO_AMD_FCH is not set # # Port-mapped I/O GPIO drivers @@ -4962,6 +4964,7 @@ CONFIG_MFD_TWL4030_AUDIO=y CONFIG_TWL6040_CORE=y CONFIG_MFD_WL1273_CORE=m CONFIG_MFD_LM3533=m +# CONFIG_MFD_TQMX86 is not set CONFIG_MFD_VX855=m CONFIG_MFD_ARIZONA=y CONFIG_MFD_ARIZONA_I2C=m @@ -5080,6 +5083,7 @@ CONFIG_IR_SHARP_DECODER=m CONFIG_IR_MCE_KBD_DECODER=m CONFIG_IR_XMP_DECODER=m # CONFIG_IR_IMON_DECODER is not set +# CONFIG_IR_RCMM_DECODER is not set CONFIG_RC_DEVICES=y CONFIG_RC_ATI_REMOTE=m CONFIG_IR_ENE=m @@ -5325,6 +5329,7 @@ CONFIG_VIDEO_IVTV=m # CONFIG_VIDEO_IVTV_DEPRECATED_IOCTLS is not set CONFIG_VIDEO_IVTV_ALSA=m CONFIG_VIDEO_FB_IVTV=m +# CONFIG_VIDEO_FB_IVTV_FORCE_PAT is not set CONFIG_VIDEO_HEXIUM_GEMINI=m CONFIG_VIDEO_HEXIUM_ORION=m CONFIG_VIDEO_MXB=m @@ -5387,8 +5392,6 @@ CONFIG_VIDEO_CAFE_CCIC=m CONFIG_VIDEO_VIA_CAMERA=m # CONFIG_VIDEO_CADENCE is not set # CONFIG_VIDEO_ASPEED is not set -CONFIG_SOC_CAMERA=m -CONFIG_SOC_CAMERA_PLATFORM=m CONFIG_V4L_MEM2MEM_DRIVERS=y CONFIG_VIDEO_MEM2MEM_DEINTERLACE=m CONFIG_VIDEO_SH_VEU=m @@ -5533,7 +5536,6 @@ CONFIG_VIDEO_ADV7511=m CONFIG_VIDEO_OV2640=m CONFIG_VIDEO_OV7640=m CONFIG_VIDEO_OV7670=m -CONFIG_VIDEO_MT9M111=m CONFIG_VIDEO_MT9V011=m # @@ -5560,24 +5562,6 @@ CONFIG_VIDEO_SAA6752HS=m # CONFIG_VIDEO_M52790=m -# -# Sensors used on soc_camera driver -# - -# -# soc_camera sensor drivers -# -CONFIG_SOC_CAMERA_MT9M001=m -CONFIG_SOC_CAMERA_MT9M111=m -CONFIG_SOC_CAMERA_MT9T112=m -CONFIG_SOC_CAMERA_MT9V022=m -CONFIG_SOC_CAMERA_OV5642=m -CONFIG_SOC_CAMERA_OV772X=m -CONFIG_SOC_CAMERA_OV9640=m -CONFIG_SOC_CAMERA_OV9740=m -CONFIG_SOC_CAMERA_RJ54N1=m -CONFIG_SOC_CAMERA_TW9910=m - # # Media SPI Adapters # @@ -5816,6 +5800,10 @@ CONFIG_DRM_I2C_CH7006=m CONFIG_DRM_I2C_SIL164=m CONFIG_DRM_I2C_NXP_TDA998X=m # CONFIG_DRM_I2C_NXP_TDA9950 is not set + +# +# ARM devices +# CONFIG_DRM_RADEON=m # CONFIG_DRM_RADEON_USERPTR is not set CONFIG_DRM_AMDGPU=m @@ -5849,6 +5837,7 @@ CONFIG_NOUVEAU_DEBUG=5 CONFIG_NOUVEAU_DEBUG_DEFAULT=3 # CONFIG_NOUVEAU_DEBUG_MMU is not set CONFIG_DRM_NOUVEAU_BACKLIGHT=y +# CONFIG_DRM_NOUVEAU_SVM is not set CONFIG_DRM_I915=m CONFIG_DRM_I915_ALPHA_SUPPORT=y CONFIG_DRM_I915_CAPTURE_ERROR=y @@ -5896,6 +5885,7 @@ CONFIG_DRM_PANEL_BRIDGE=y # Display Interface Bridges # CONFIG_DRM_ANALOGIX_ANX78XX=m +# CONFIG_DRM_ETNAVIV is not set CONFIG_DRM_HISI_HIBMC=m CONFIG_DRM_TINYDRM=m CONFIG_TINYDRM_MIPI_DBI=m @@ -6293,6 +6283,7 @@ CONFIG_SND_SOC_FSL_SAI=m CONFIG_SND_SOC_FSL_SSI=m CONFIG_SND_SOC_FSL_SPDIF=m CONFIG_SND_SOC_FSL_ESAI=m +# CONFIG_SND_SOC_FSL_MICFIL is not set CONFIG_SND_SOC_IMX_AUDMUX=m CONFIG_SND_I2S_HI6210_I2S=m CONFIG_SND_SOC_IMG=y @@ -6327,11 +6318,14 @@ CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH=m # CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set # CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set # CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set +# CONFIG_SND_SOC_MTK_BTCVSD is not set # # STMicroelectronics STM32 SOC audio support # # CONFIG_SND_SOC_XILINX_I2S is not set +# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set +# CONFIG_SND_SOC_XILINX_SPDIF is not set CONFIG_SND_SOC_XTFPGA_I2S=m CONFIG_ZX_TDM=m CONFIG_SND_SOC_I2C_AND_SPI=m @@ -6358,10 +6352,12 @@ CONFIG_SND_SOC_AK5386=m CONFIG_SND_SOC_ALC5623=m # CONFIG_SND_SOC_BD28623 is not set CONFIG_SND_SOC_BT_SCO=m +# CONFIG_SND_SOC_CROS_EC_CODEC is not set CONFIG_SND_SOC_CS35L32=m CONFIG_SND_SOC_CS35L33=m CONFIG_SND_SOC_CS35L34=m CONFIG_SND_SOC_CS35L35=m +# CONFIG_SND_SOC_CS35L36 is not set CONFIG_SND_SOC_CS42L42=m CONFIG_SND_SOC_CS42L51=m CONFIG_SND_SOC_CS42L51_I2C=m @@ -6376,6 +6372,7 @@ CONFIG_SND_SOC_CS4271_SPI=m CONFIG_SND_SOC_CS42XX8=m CONFIG_SND_SOC_CS42XX8_I2C=m # CONFIG_SND_SOC_CS43130 is not set +# CONFIG_SND_SOC_CS4341 is not set CONFIG_SND_SOC_CS4349=m CONFIG_SND_SOC_CS53L30=m # CONFIG_SND_SOC_DMIC is not set @@ -6411,6 +6408,7 @@ CONFIG_SND_SOC_PCM3168A_SPI=m CONFIG_SND_SOC_PCM512x=m CONFIG_SND_SOC_PCM512x_I2C=m CONFIG_SND_SOC_PCM512x_SPI=m +# CONFIG_SND_SOC_RK3328 is not set CONFIG_SND_SOC_RL6231=m CONFIG_SND_SOC_RT5616=m CONFIG_SND_SOC_RT5631=m @@ -6466,6 +6464,7 @@ CONFIG_SND_SOC_WM8804=m CONFIG_SND_SOC_WM8804_I2C=m CONFIG_SND_SOC_WM8804_SPI=m CONFIG_SND_SOC_WM8903=m +# CONFIG_SND_SOC_WM8904 is not set CONFIG_SND_SOC_WM8960=m CONFIG_SND_SOC_WM8962=m CONFIG_SND_SOC_WM8974=m @@ -6474,6 +6473,7 @@ CONFIG_SND_SOC_WM8985=m CONFIG_SND_SOC_ZX_AUD96P22=m # CONFIG_SND_SOC_MAX9759 is not set # CONFIG_SND_SOC_MT6351 is not set +# CONFIG_SND_SOC_MT6358 is not set CONFIG_SND_SOC_NAU8540=m CONFIG_SND_SOC_NAU8810=m # CONFIG_SND_SOC_NAU8822 is not set @@ -6535,6 +6535,7 @@ CONFIG_HID_KEYTOUCH=m CONFIG_HID_KYE=m CONFIG_HID_UCLOGIC=m CONFIG_HID_WALTOP=m +# CONFIG_HID_VIEWSONIC is not set CONFIG_HID_GYRATION=m CONFIG_HID_ICADE=m CONFIG_HID_ITE=m @@ -6552,6 +6553,7 @@ CONFIG_LOGIRUMBLEPAD2_FF=y CONFIG_LOGIG940_FF=y CONFIG_LOGIWHEELS_FF=y CONFIG_HID_MAGICMOUSE=m +# CONFIG_HID_MALTRON is not set CONFIG_HID_MAYFLASH=m # CONFIG_HID_REDRAGON is not set CONFIG_HID_MICROSOFT=m @@ -6643,6 +6645,7 @@ CONFIG_USB_DYNAMIC_MINORS=y # CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set CONFIG_USB_LEDS_TRIGGER_USBPORT=m +CONFIG_USB_AUTOSUSPEND_DELAY=2 CONFIG_USB_MON=m CONFIG_USB_WUSB=m CONFIG_USB_WUSB_CBAF=m @@ -6660,6 +6663,7 @@ CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_ROOT_HUB_TT=y CONFIG_USB_EHCI_TT_NEWSCHED=y CONFIG_USB_EHCI_PCI=y +# CONFIG_USB_EHCI_FSL is not set CONFIG_USB_EHCI_HCD_PLATFORM=y CONFIG_USB_OXU210HP_HCD=m CONFIG_USB_ISP116X_HCD=m @@ -7158,6 +7162,13 @@ CONFIG_INFINIBAND_NES=m CONFIG_INFINIBAND_OCRDMA=m CONFIG_INFINIBAND_VMWARE_PVRDMA=m CONFIG_INFINIBAND_USNIC=m +CONFIG_INFINIBAND_BNXT_RE=m +CONFIG_INFINIBAND_HFI1=m +# CONFIG_HFI1_DEBUG_SDMA_ORDER is not set +# CONFIG_SDMA_VERBOSITY is not set +CONFIG_INFINIBAND_QEDR=m +CONFIG_INFINIBAND_RDMAVT=m +CONFIG_RDMA_RXE=m CONFIG_INFINIBAND_IPOIB=m CONFIG_INFINIBAND_IPOIB_CM=y # CONFIG_INFINIBAND_IPOIB_DEBUG is not set @@ -7166,13 +7177,6 @@ CONFIG_INFINIBAND_SRPT=m CONFIG_INFINIBAND_ISER=m CONFIG_INFINIBAND_ISERT=m CONFIG_INFINIBAND_OPA_VNIC=m -CONFIG_INFINIBAND_RDMAVT=m -CONFIG_RDMA_RXE=m -CONFIG_INFINIBAND_HFI1=m -# CONFIG_HFI1_DEBUG_SDMA_ORDER is not set -# CONFIG_SDMA_VERBOSITY is not set -CONFIG_INFINIBAND_QEDR=m -CONFIG_INFINIBAND_BNXT_RE=m CONFIG_EDAC_ATOMIC_SCRUB=y CONFIG_EDAC_SUPPORT=y CONFIG_EDAC=y @@ -7195,6 +7199,7 @@ CONFIG_EDAC_I5100=m CONFIG_EDAC_I7300=m CONFIG_EDAC_SBRIDGE=m CONFIG_EDAC_SKX=m +# CONFIG_EDAC_I10NM is not set CONFIG_EDAC_PND2=m CONFIG_RTC_LIB=y CONFIG_RTC_MC146818_LIB=y @@ -7221,6 +7226,7 @@ CONFIG_RTC_INTF_DEV=y CONFIG_RTC_DRV_88PM860X=m CONFIG_RTC_DRV_88PM80X=m CONFIG_RTC_DRV_ABB5ZES3=m +# CONFIG_RTC_DRV_ABEOZ9 is not set CONFIG_RTC_DRV_ABX80X=m CONFIG_RTC_DRV_DS1307=m CONFIG_RTC_DRV_DS1307_CENTURY=y @@ -7256,8 +7262,10 @@ CONFIG_RTC_DRV_RX8010=m CONFIG_RTC_DRV_RX8581=m CONFIG_RTC_DRV_RX8025=m CONFIG_RTC_DRV_EM3027=m +# CONFIG_RTC_DRV_RV3028 is not set CONFIG_RTC_DRV_RV8803=m CONFIG_RTC_DRV_S5M=m +# CONFIG_RTC_DRV_SD3078 is not set # # SPI RTC drivers @@ -7374,10 +7382,14 @@ CONFIG_KS0108_DELAY=2 CONFIG_CFAG12864B=m CONFIG_CFAG12864B_RATE=20 CONFIG_IMG_ASCII_LCD=m -CONFIG_PANEL=m +CONFIG_PARPORT_PANEL=m CONFIG_PANEL_PARPORT=0 CONFIG_PANEL_PROFILE=5 # CONFIG_PANEL_CHANGE_MESSAGE is not set +# CONFIG_CHARLCD_BL_OFF is not set +# CONFIG_CHARLCD_BL_ON is not set +CONFIG_CHARLCD_BL_FLASH=y +CONFIG_PANEL=m CONFIG_CHARLCD=m CONFIG_UIO=m CONFIG_UIO_CIF=m @@ -7621,9 +7633,6 @@ CONFIG_ADIS16240=m # # Analog to digital converters # -CONFIG_AD7606=m -CONFIG_AD7606_IFACE_PARALLEL=m -CONFIG_AD7606_IFACE_SPI=m CONFIG_AD7780=m CONFIG_AD7816=m CONFIG_AD7192=m @@ -7640,7 +7649,6 @@ CONFIG_ADT7316_I2C=m # Capacitance to digital converters # CONFIG_AD7150=m -CONFIG_AD7152=m CONFIG_AD7746=m # @@ -7666,7 +7674,6 @@ CONFIG_ADE7854_SPI=m # CONFIG_AD2S1210=m CONFIG_FB_SM750=m -CONFIG_FB_XGI=m # # Speakup console speech @@ -7685,8 +7692,6 @@ CONFIG_SPEAKUP_SYNTH_TXPRT=m CONFIG_SPEAKUP_SYNTH_DUMMY=m CONFIG_STAGING_MEDIA=y CONFIG_I2C_BCM2048=m -CONFIG_SOC_CAMERA_IMX074=m -CONFIG_SOC_CAMERA_MT9T031=m CONFIG_VIDEO_ZORAN=m CONFIG_VIDEO_ZORAN_DC30=m CONFIG_VIDEO_ZORAN_ZR36060=m @@ -7697,6 +7702,10 @@ CONFIG_VIDEO_ZORAN_LML33R10=m CONFIG_VIDEO_ZORAN_AVS6EYES=m # CONFIG_VIDEO_IPU3_IMGU is not set +# +# soc_camera sensor drivers +# + # # Android # @@ -7777,13 +7786,11 @@ CONFIG_GREYBUS_UART=m CONFIG_GREYBUS_USB=m # CONFIG_DRM_VBOXVIDEO is not set # CONFIG_PI433 is not set -# CONFIG_MTK_MMC is not set # # Gasket devices # # CONFIG_STAGING_GASKET_FRAMEWORK is not set -# CONFIG_XIL_AXIS_FIFO is not set # CONFIG_EROFS_FS is not set CONFIG_X86_PLATFORM_DEVICES=y CONFIG_ACER_WMI=m @@ -7827,7 +7834,7 @@ CONFIG_THINKPAD_ACPI_VIDEO=y CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y CONFIG_SURFACE_ACPI=m CONFIG_SURFACE_ACPI_SSH=y -CONFIG_SURFACE_ACPI_SSH_DEBUG_DEVICE=n +# CONFIG_SURFACE_ACPI_SSH_DEBUG_DEVICE is not set CONFIG_SURFACE_ACPI_SAN=y CONFIG_SURFACE_ACPI_VHF=y CONFIG_SURFACE_ACPI_DTX=y @@ -7876,6 +7883,7 @@ CONFIG_INTEL_TURBO_MAX_3=y # CONFIG_I2C_MULTI_INSTANTIATE is not set # CONFIG_INTEL_ATOMISP2_PM is not set # CONFIG_HUAWEI_WMI is not set +# CONFIG_PCENGINES_APU2 is not set CONFIG_PMC_ATOM=y CONFIG_CHROME_PLATFORMS=y CONFIG_CHROMEOS_LAPTOP=m @@ -7887,6 +7895,7 @@ CONFIG_CROS_EC_LPC=m CONFIG_CROS_EC_LPC_MEC=y CONFIG_CROS_EC_PROTO=y CONFIG_CROS_KBD_LED_BACKLIGHT=m +# CONFIG_WILCO_EC is not set # CONFIG_MELLANOX_PLATFORM is not set CONFIG_CLKDEV_LOOKUP=y CONFIG_HAVE_CLK_PREPARE=y @@ -7916,6 +7925,7 @@ CONFIG_CLKBLD_I8253=y CONFIG_MAILBOX=y CONFIG_PCC=y CONFIG_ALTERA_MBOX=m +CONFIG_IOMMU_IOVA=y CONFIG_IOMMU_API=y CONFIG_IOMMU_SUPPORT=y @@ -7924,7 +7934,6 @@ CONFIG_IOMMU_SUPPORT=y # # CONFIG_IOMMU_DEBUGFS is not set # CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set -CONFIG_IOMMU_IOVA=y CONFIG_AMD_IOMMU=y CONFIG_AMD_IOMMU_V2=m CONFIG_DMAR_TABLE=y @@ -7933,6 +7942,7 @@ CONFIG_INTEL_IOMMU_SVM=y # CONFIG_INTEL_IOMMU_DEFAULT_ON is not set CONFIG_INTEL_IOMMU_FLOPPY_WA=y CONFIG_IRQ_REMAP=y +CONFIG_HYPERV_IOMMU=y # # Remoteproc drivers @@ -8009,6 +8019,7 @@ CONFIG_EXTCON_MAX77693=m CONFIG_EXTCON_MAX77843=m CONFIG_EXTCON_MAX8997=m CONFIG_EXTCON_PALMAS=m +# CONFIG_EXTCON_PTN5150 is not set CONFIG_EXTCON_RT8973A=m CONFIG_EXTCON_SM5502=m CONFIG_EXTCON_USB_GPIO=m @@ -8076,7 +8087,11 @@ CONFIG_AD7266=m CONFIG_AD7291=m CONFIG_AD7298=m CONFIG_AD7476=m +CONFIG_AD7606=m +CONFIG_AD7606_IFACE_PARALLEL=m +CONFIG_AD7606_IFACE_SPI=m CONFIG_AD7766=m +# CONFIG_AD7768_1 is not set CONFIG_AD7791=m CONFIG_AD7793=m CONFIG_AD7887=m @@ -8142,6 +8157,9 @@ CONFIG_ATLAS_PH_SENSOR=m # CONFIG_BME680 is not set # CONFIG_CCS811 is not set CONFIG_IAQCORE=m +# CONFIG_PMS7003 is not set +# CONFIG_SENSIRION_SGP30 is not set +# CONFIG_SPS30 is not set CONFIG_VZ89X=m CONFIG_IIO_CROS_EC_SENSORS_CORE=m CONFIG_IIO_CROS_EC_SENSORS=m @@ -8201,6 +8219,7 @@ CONFIG_MCP4922=m # CONFIG_TI_DAC082S085 is not set # CONFIG_TI_DAC5571 is not set # CONFIG_TI_DAC7311 is not set +# CONFIG_TI_DAC7612 is not set # # IIO dummy driver @@ -8313,6 +8332,7 @@ CONFIG_SENSORS_LM3533=m CONFIG_LTR501=m # CONFIG_LV0104CS is not set CONFIG_MAX44000=m +# CONFIG_MAX44009 is not set CONFIG_OPT3001=m CONFIG_PA12203001=m # CONFIG_SI1133 is not set @@ -8546,6 +8566,8 @@ CONFIG_DAX_DRIVER=y CONFIG_DAX=y CONFIG_DEV_DAX=m CONFIG_DEV_DAX_PMEM=m +CONFIG_DEV_DAX_KMEM=m +CONFIG_DEV_DAX_PMEM_COMPAT=m CONFIG_NVMEM=y # @@ -8578,11 +8600,13 @@ CONFIG_PM_OPP=y CONFIG_UNISYS_VISORBUS=m # CONFIG_SIOX is not set # CONFIG_SLIMBUS is not set +# CONFIG_INTERCONNECT is not set # # File systems # CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_VALIDATE_FS_PARSER=y CONFIG_FS_IOMAP=y # CONFIG_EXT2_FS is not set # CONFIG_EXT3_FS is not set @@ -8590,8 +8614,6 @@ CONFIG_EXT4_FS=y CONFIG_EXT4_USE_FOR_EXT2=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y -CONFIG_EXT4_ENCRYPTION=y -CONFIG_EXT4_FS_ENCRYPTION=y # CONFIG_EXT4_DEBUG is not set CONFIG_JBD2=y # CONFIG_JBD2_DEBUG is not set @@ -8636,7 +8658,6 @@ CONFIG_F2FS_FS_XATTR=y CONFIG_F2FS_FS_POSIX_ACL=y CONFIG_F2FS_FS_SECURITY=y # CONFIG_F2FS_CHECK_FS is not set -CONFIG_F2FS_FS_ENCRYPTION=y # CONFIG_F2FS_IO_TRACE is not set # CONFIG_F2FS_FAULT_INJECTION is not set CONFIG_FS_DAX=y @@ -8762,7 +8783,6 @@ CONFIG_UBIFS_FS_LZO=y CONFIG_UBIFS_FS_ZLIB=y # CONFIG_UBIFS_ATIME_SUPPORT is not set CONFIG_UBIFS_FS_XATTR=y -CONFIG_UBIFS_FS_ENCRYPTION=y CONFIG_UBIFS_FS_SECURITY=y # CONFIG_UBIFS_FS_AUTHENTICATION is not set CONFIG_CRAMFS=m @@ -8813,9 +8833,6 @@ CONFIG_SYSV_FS=m CONFIG_UFS_FS=m # CONFIG_UFS_FS_WRITE is not set # CONFIG_UFS_DEBUG is not set -CONFIG_EXOFS_FS=m -# CONFIG_EXOFS_DEBUG is not set -CONFIG_ORE=m CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V2=m @@ -8856,6 +8873,7 @@ CONFIG_SUNRPC_GSS=m CONFIG_SUNRPC_BACKCHANNEL=y CONFIG_SUNRPC_SWAP=y CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES is not set CONFIG_SUNRPC_DEBUG=y CONFIG_SUNRPC_XPRT_RDMA=m CONFIG_CEPH_FS=m @@ -8966,7 +8984,6 @@ CONFIG_FORTIFY_SOURCE=y # CONFIG_STATIC_USERMODEHELPER is not set CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y -CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=0 # CONFIG_SECURITY_SELINUX_DISABLE is not set CONFIG_SECURITY_SELINUX_DEVELOP=y CONFIG_SECURITY_SELINUX_AVC_STATS=y @@ -8982,12 +8999,12 @@ CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG=1024 CONFIG_SECURITY_TOMOYO_POLICY_LOADER="/sbin/tomoyo-init" CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER="/sbin/init" CONFIG_SECURITY_APPARMOR=y -CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 CONFIG_SECURITY_APPARMOR_HASH=y CONFIG_SECURITY_APPARMOR_HASH_DEFAULT=y # CONFIG_SECURITY_APPARMOR_DEBUG is not set # CONFIG_SECURITY_LOADPIN is not set CONFIG_SECURITY_YAMA=y +# CONFIG_SECURITY_SAFESETID is not set CONFIG_INTEGRITY=y CONFIG_INTEGRITY_SIGNATURE=y CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y @@ -9025,7 +9042,7 @@ CONFIG_DEFAULT_SECURITY_SELINUX=y # CONFIG_DEFAULT_SECURITY_TOMOYO is not set # CONFIG_DEFAULT_SECURITY_APPARMOR is not set # CONFIG_DEFAULT_SECURITY_DAC is not set -CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" CONFIG_XOR_BLOCKS=m CONFIG_ASYNC_CORE=m CONFIG_ASYNC_MEMCPY=m @@ -9340,8 +9357,11 @@ CONFIG_HAS_DMA=y CONFIG_NEED_SG_DMA_LENGTH=y CONFIG_NEED_DMA_MAP_STATE=y CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_DMA_DECLARE_COHERENT=y CONFIG_DMA_VIRT_OPS=y CONFIG_SWIOTLB=y +# CONFIG_DMA_CMA is not set +# CONFIG_DMA_API_DEBUG is not set CONFIG_SGL_ALLOC=y CONFIG_IOMMU_HELPER=y CONFIG_CHECK_SIGNATURE=y @@ -9381,6 +9401,7 @@ CONFIG_OBJAGG=m # printk and dmesg options # CONFIG_PRINTK_TIME=y +# CONFIG_PRINTK_CALLER is not set CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 CONFIG_CONSOLE_LOGLEVEL_QUIET=4 CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 @@ -9396,7 +9417,6 @@ CONFIG_FRAME_WARN=1024 # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_READABLE_ASM is not set CONFIG_UNUSED_SYMBOLS=y -# CONFIG_PAGE_OWNER is not set CONFIG_DEBUG_FS=y # CONFIG_HEADERS_CHECK is not set # CONFIG_DEBUG_SECTION_MISMATCH is not set @@ -9414,6 +9434,7 @@ CONFIG_DEBUG_KERNEL=y # # CONFIG_PAGE_EXTENSION is not set # CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_PAGE_OWNER is not set # CONFIG_PAGE_POISONING is not set # CONFIG_DEBUG_PAGE_REF is not set # CONFIG_DEBUG_RODATA_TEST is not set @@ -9566,7 +9587,6 @@ CONFIG_HIST_TRIGGERS=y # CONFIG_TRACE_EVAL_MAP_FILE is not set CONFIG_TRACING_EVENTS_GPIO=y # CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set -# CONFIG_DMA_API_DEBUG is not set CONFIG_RUNTIME_TESTING_MENU=y # CONFIG_LKDTM is not set # CONFIG_TEST_LIST_SORT is not set @@ -9592,6 +9612,7 @@ CONFIG_RUNTIME_TESTING_MENU=y # CONFIG_TEST_IDA is not set # CONFIG_TEST_PARMAN is not set CONFIG_TEST_LKM=m +# CONFIG_TEST_VMALLOC is not set CONFIG_TEST_USER_COPY=m CONFIG_TEST_BPF=m # CONFIG_FIND_BIT_BENCHMARK is not set @@ -9601,7 +9622,9 @@ CONFIG_TEST_UDELAY=m CONFIG_TEST_STATIC_KEYS=m # CONFIG_TEST_KMOD is not set # CONFIG_TEST_MEMCAT_P is not set +# CONFIG_TEST_LIVEPATCH is not set # CONFIG_TEST_OBJAGG is not set +# CONFIG_TEST_STACKINIT is not set CONFIG_MEMTEST=y # CONFIG_BUG_ON_DATA_CORRUPTION is not set # CONFIG_SAMPLES is not set @@ -9616,6 +9639,7 @@ CONFIG_KDB_KEYBOARD=y CONFIG_KDB_CONTINUE_CATASTROPHIC=0 CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y # CONFIG_UBSAN is not set +CONFIG_UBSAN_ALIGNMENT=y CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y CONFIG_STRICT_DEVMEM=y # CONFIG_IO_STRICT_DEVMEM is not set @@ -9624,7 +9648,6 @@ CONFIG_EARLY_PRINTK_USB=y # CONFIG_X86_VERBOSE_BOOTUP is not set CONFIG_EARLY_PRINTK=y CONFIG_EARLY_PRINTK_DBGP=y -CONFIG_EARLY_PRINTK_EFI=y # CONFIG_EARLY_PRINTK_USB_XDBC is not set CONFIG_X86_PTDUMP_CORE=y # CONFIG_X86_PTDUMP is not set diff --git a/patches/4.19/0001-surface-acpi.patch b/patches/4.19/0001-surface-acpi.patch index 677091555..f6bf55c1c 100644 --- a/patches/4.19/0001-surface-acpi.patch +++ b/patches/4.19/0001-surface-acpi.patch @@ -1,6 +1,6 @@ -From c92cf7a3c3c798ea4f4e2b94e1b1b439bc590136 Mon Sep 17 00:00:00 2001 +From 10039747e796d3b91ce5d32ccaef5f61c0001abb Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:48:48 -0400 +Date: Thu, 6 Jun 2019 13:33:54 -0400 Subject: [PATCH 01/12] surface-acpi --- diff --git a/patches/4.19/0002-suspend.patch b/patches/4.19/0002-suspend.patch index c88dad89f..04d271b47 100644 --- a/patches/4.19/0002-suspend.patch +++ b/patches/4.19/0002-suspend.patch @@ -1,6 +1,6 @@ -From 0a0ec97aa74e28a9fdea0649bad95ba959172ded Mon Sep 17 00:00:00 2001 +From 6184c48acb21fac3f0100abb4954c6d1c838b1f0 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:48:59 -0400 +Date: Thu, 6 Jun 2019 13:34:08 -0400 Subject: [PATCH 02/12] suspend --- @@ -65,7 +65,7 @@ index 7b9ef8e734e7..b0e3ae90f5b7 100644 { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index 37d897bc4cf1..7587fbff6ea5 100644 +index 28c64f84bfe7..30c013926470 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1332,6 +1332,10 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, diff --git a/patches/4.19/0003-buttons.patch b/patches/4.19/0003-buttons.patch index f3c1236fa..87c7af8cb 100644 --- a/patches/4.19/0003-buttons.patch +++ b/patches/4.19/0003-buttons.patch @@ -1,6 +1,6 @@ -From 764ba5cae99743ada66637b4317eb0cefaca1145 Mon Sep 17 00:00:00 2001 +From 9b8c7a4d6c572a4c5745c329361479f01579f5e5 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:49:09 -0400 +Date: Thu, 6 Jun 2019 13:34:21 -0400 Subject: [PATCH 03/12] buttons --- diff --git a/patches/4.19/0004-cameras.patch b/patches/4.19/0004-cameras.patch index 0550bba48..0b06138c0 100644 --- a/patches/4.19/0004-cameras.patch +++ b/patches/4.19/0004-cameras.patch @@ -1,6 +1,6 @@ -From e135879f02f66d62dcaffc9aca7a72c8daf6ce1e Mon Sep 17 00:00:00 2001 +From 82174bf56d6de5b1e3110451a8098a81a23aeee2 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:49:19 -0400 +Date: Thu, 6 Jun 2019 13:34:32 -0400 Subject: [PATCH 04/12] cameras --- diff --git a/patches/4.19/0005-ipts.patch b/patches/4.19/0005-ipts.patch index fbe6455f8..142d8e48f 100644 --- a/patches/4.19/0005-ipts.patch +++ b/patches/4.19/0005-ipts.patch @@ -1,6 +1,6 @@ -From 867c13ecf97ac50d5d4f9c7d87271ce0d47ad0d8 Mon Sep 17 00:00:00 2001 +From 63a1bd5157c965fdbd8ea5d873e2cdf0972e9e7b Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:49:33 -0400 +Date: Thu, 6 Jun 2019 13:34:44 -0400 Subject: [PATCH 05/12] ipts --- diff --git a/patches/4.19/0006-hid.patch b/patches/4.19/0006-hid.patch index 23636bb5b..cadc2e059 100644 --- a/patches/4.19/0006-hid.patch +++ b/patches/4.19/0006-hid.patch @@ -1,6 +1,6 @@ -From 1d1ba68b6186ed4ee456334335f1f2e4fed52d22 Mon Sep 17 00:00:00 2001 +From 5b2a1f69cb5cde820daefefd099ca3f2a5798e56 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:49:42 -0400 +Date: Thu, 6 Jun 2019 13:34:54 -0400 Subject: [PATCH 06/12] hid --- @@ -125,7 +125,7 @@ index 831617c386e6..21337070068a 100644 { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, MT_USB_DEVICE(USB_VENDOR_ID_ASUS, diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c -index 94088c0ed68a..391b882fd6e5 100644 +index e24790c988c0..70f75d60c8c9 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -112,6 +112,17 @@ static const struct hid_device_id hid_quirks[] = { diff --git a/patches/4.19/0007-sdcard-reader.patch b/patches/4.19/0007-sdcard-reader.patch index b54197036..645e42d7c 100644 --- a/patches/4.19/0007-sdcard-reader.patch +++ b/patches/4.19/0007-sdcard-reader.patch @@ -1,6 +1,6 @@ -From 3624ea2106d56ecb0b0413028e059576d35ae2e1 Mon Sep 17 00:00:00 2001 +From 1e11b04bcc92b87388927282f8801e0b6f14ec91 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:49:52 -0400 +Date: Thu, 6 Jun 2019 13:35:09 -0400 Subject: [PATCH 07/12] sdcard-reader --- @@ -8,10 +8,10 @@ Subject: [PATCH 07/12] sdcard-reader 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index 3adff4da2ee1..7980accfe9df 100644 +index eb24ec0e160d..8327ac3880c8 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c -@@ -4160,7 +4160,8 @@ void usb_enable_lpm(struct usb_device *udev) +@@ -4157,7 +4157,8 @@ void usb_enable_lpm(struct usb_device *udev) if (!udev || !udev->parent || udev->speed < USB_SPEED_SUPER || !udev->lpm_capable || diff --git a/patches/4.19/0008-wifi.patch b/patches/4.19/0008-wifi.patch index 3d84df5a4..f40e0644f 100644 --- a/patches/4.19/0008-wifi.patch +++ b/patches/4.19/0008-wifi.patch @@ -1,6 +1,6 @@ -From ba3a53fc48453d746fe492c7a068edab0f9fbf90 Mon Sep 17 00:00:00 2001 +From cf6e06464c190de8ed1fa0c42d0feb305c87a677 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:50:02 -0400 +Date: Thu, 6 Jun 2019 13:35:21 -0400 Subject: [PATCH 08/12] wifi --- @@ -33,7 +33,7 @@ index 042a1d07f686..fc9041f58e9f 100644 skb_src = skb_dequeue(&pra_list->skb_head); diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -index 2d87ebbfa4da..8e95553a3344 100644 +index 47ec5293c045..435c346116a2 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -428,7 +428,10 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, diff --git a/patches/4.19/0009-surface3-power.patch b/patches/4.19/0009-surface3-power.patch index 354cfee03..5a7098d5d 100644 --- a/patches/4.19/0009-surface3-power.patch +++ b/patches/4.19/0009-surface3-power.patch @@ -1,6 +1,6 @@ -From 2de834be2c64eee61ec15bc91c87a67bd554ab72 Mon Sep 17 00:00:00 2001 +From d67736bea842f4b73d35b0c857c6046c2d7ab968 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:50:14 -0400 +Date: Thu, 6 Jun 2019 13:35:37 -0400 Subject: [PATCH 09/12] surface3-power --- diff --git a/patches/4.19/0010-surface-dock.patch b/patches/4.19/0010-surface-dock.patch index fbde5c765..a4a71ad51 100644 --- a/patches/4.19/0010-surface-dock.patch +++ b/patches/4.19/0010-surface-dock.patch @@ -1,6 +1,6 @@ -From 63653d197c3ea1c536fd95eebc7ff8c81cf974e8 Mon Sep 17 00:00:00 2001 +From cb9a440ae30c94e3cfab2a080c29d48da688d890 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:50:29 -0400 +Date: Thu, 6 Jun 2019 13:35:49 -0400 Subject: [PATCH 10/12] surface-dock --- diff --git a/patches/4.19/0011-mwlwifi.patch b/patches/4.19/0011-mwlwifi.patch index 3424e71f9..141fb416f 100644 --- a/patches/4.19/0011-mwlwifi.patch +++ b/patches/4.19/0011-mwlwifi.patch @@ -1,6 +1,6 @@ -From 6f4922a7175ff9814c89f7d3e78eabf0e6500753 Mon Sep 17 00:00:00 2001 +From 2c987ce456bb32320347a791cbdc146f1a5258d2 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:50:47 -0400 +Date: Thu, 6 Jun 2019 13:36:02 -0400 Subject: [PATCH 11/12] mwlwifi --- diff --git a/patches/4.19/0012-surface-lte.patch b/patches/4.19/0012-surface-lte.patch index 47ade6ba8..dbef8ae73 100644 --- a/patches/4.19/0012-surface-lte.patch +++ b/patches/4.19/0012-surface-lte.patch @@ -1,6 +1,6 @@ -From 971c1032f810c0653470a4871fbdcdf519f01a6d Mon Sep 17 00:00:00 2001 +From 5f3af7b383eb3af16bf63e11d16a83856d8ee677 Mon Sep 17 00:00:00 2001 From: Jake Day -Date: Tue, 30 Apr 2019 20:51:04 -0400 +Date: Thu, 6 Jun 2019 13:36:16 -0400 Subject: [PATCH 12/12] surface-lte --- diff --git a/patches/5.0/0001-surface-acpi.patch b/patches/5.0/0001-surface-acpi.patch deleted file mode 100644 index bceac4fd4..000000000 --- a/patches/5.0/0001-surface-acpi.patch +++ /dev/null @@ -1,4139 +0,0 @@ -From 65b2e85e725ef3280ea555710e0c3a5c2afbbbe9 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:40:13 -0400 -Subject: [PATCH 01/12] surface-acpi - ---- - drivers/acpi/acpica/dsopcode.c | 2 +- - drivers/acpi/acpica/exfield.c | 12 +- - drivers/platform/x86/Kconfig | 97 + - drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surface_acpi.c | 3828 +++++++++++++++++++++++++++ - drivers/tty/serdev/core.c | 90 +- - 6 files changed, 4024 insertions(+), 6 deletions(-) - create mode 100644 drivers/platform/x86/surface_acpi.c - -diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c -index 78f9de260d5f..0cd858520f5b 100644 ---- a/drivers/acpi/acpica/dsopcode.c -+++ b/drivers/acpi/acpica/dsopcode.c -@@ -123,7 +123,7 @@ acpi_ds_init_buffer_field(u16 aml_opcode, - - /* Offset is in bits, count is in bits */ - -- field_flags = AML_FIELD_ACCESS_BYTE; -+ field_flags = AML_FIELD_ACCESS_BUFFER; - bit_offset = offset; - bit_count = (u32) length_desc->integer.value; - -diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c -index e5798f15793a..55abd9e035a0 100644 ---- a/drivers/acpi/acpica/exfield.c -+++ b/drivers/acpi/acpica/exfield.c -@@ -98,6 +98,7 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, - union acpi_operand_object *buffer_desc; - void *buffer; - u32 buffer_length; -+ u8 field_flags; - - ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc); - -@@ -146,11 +147,16 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, - * Note: Field.length is in bits. - */ - buffer_length = -- (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length); -+ (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length); -+ field_flags = obj_desc->common_field.field_flags; - -- if (buffer_length > acpi_gbl_integer_byte_width) { -+ if (buffer_length > acpi_gbl_integer_byte_width || -+ (field_flags & AML_FIELD_ACCESS_TYPE_MASK) == AML_FIELD_ACCESS_BUFFER) { - -- /* Field is too large for an Integer, create a Buffer instead */ -+ /* -+ * Field is either too large for an Integer, or a actually of type -+ * buffer, so create a Buffer. -+ */ - - buffer_desc = acpi_ut_create_buffer_object(buffer_length); - if (!buffer_desc) { -diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index b5e9db85e881..a00b5f6b23ce 100644 ---- a/drivers/platform/x86/Kconfig -+++ b/drivers/platform/x86/Kconfig -@@ -622,6 +622,103 @@ config THINKPAD_ACPI_HOTKEY_POLL - If you are not sure, say Y here. The driver enables polling only if - it is strictly necessary to do so. - -+config SURFACE_ACPI -+ depends on ACPI -+ tristate "Microsoft Surface ACPI/Platform Drivers" -+ ---help--- -+ ACPI and platform drivers for Microsoft Surface devices. -+ -+config SURFACE_ACPI_SSH -+ bool "Surface Serial Hub Driver" -+ depends on SURFACE_ACPI -+ depends on X86_INTEL_LPSS -+ depends on SERIAL_8250_DW -+ depends on SERIAL_8250_DMA -+ depends on SERIAL_DEV_CTRL_TTYPORT -+ select CRC_CCITT -+ default y -+ ---help--- -+ Surface Serial Hub driver for 5th generation (or later) Microsoft -+ Surface devices. -+ -+ This is the base driver for the embedded serial controller found on -+ 5th generation (and later) Microsoft Surface devices (e.g. Book 2, -+ Laptop, Laptop 2, Pro 2017, Pro 6, ...). This driver itself only -+ provides access to the embedded controller and subsequent drivers are -+ required for the respective functionalities. -+ -+ If you have a 5th generation (or later) Microsoft Surface device, say -+ Y or M here. -+ -+config SURFACE_ACPI_SSH_DEBUG_DEVICE -+ bool "Surface Serial Hub Debug Device" -+ depends on SURFACE_ACPI_SSH -+ default n -+ ---help--- -+ Debug device for direct communication with the embedded controller -+ found on 5th generation (and later) Microsoft Surface devices (e.g. -+ Book 2, Laptop, Laptop 2, Pro 2017, Pro 6, ...) via sysfs. -+ -+ If you are not sure, say N here. -+ -+config SURFACE_ACPI_SAN -+ bool "Surface ACPI Notify Driver" -+ depends on SURFACE_ACPI_SSH -+ default y -+ ---help--- -+ Surface ACPI Notify driver for 5th generation (or later) Microsoft -+ Surface devices. -+ -+ This driver enables basic ACPI events and requests, such as battery -+ status requests/events, thermal events, lid status, and possibly more, -+ which would otherwise not work on these devices. -+ -+ If you are not sure, say Y here. -+ -+config SURFACE_ACPI_VHF -+ bool "Surface Virtual HID Framework Driver" -+ depends on SURFACE_ACPI_SSH -+ depends on HID -+ default y -+ ---help--- -+ Surface Virtual HID Framework driver for 5th generation (or later) -+ Microsoft Surface devices. -+ -+ This driver provides support for the Microsoft Virtual HID framework, -+ which is required for the Surface Laptop (1 and newer) keyboard. -+ -+ If you are not sure, say Y here. -+ -+config SURFACE_ACPI_DTX -+ bool "Surface Detachment System (DTX) Driver" -+ depends on SURFACE_ACPI_SSH -+ depends on INPUT -+ default y -+ ---help--- -+ Surface Detachment System (DTX) driver for the Microsoft Surface Book -+ 2. This driver provides support for proper detachment handling in -+ user-space, status-events relating to the base and support for -+ the safe-guard keeping the base attached when the discrete GPU -+ contained in it is running via the special /dev/surface-dtx device. -+ -+ Also provides a standard input device to provide SW_TABLET_MODE events -+ upon device mode change. -+ -+ If you are not sure, say Y here. -+ -+config SURFACE_ACPI_SID -+ bool "Surface Platform Integration Driver" -+ depends on SURFACE_ACPI_SSH -+ default y -+ ---help--- -+ Surface Platform Integration Driver for the Microsoft Surface Devices. -+ Currently only supports the Surface Book 2. This driver provides suport -+ for setting performance-modes via the perf_mode sysfs attribute. -+ Performance-modes directly influence the fan-profile of the device, -+ allowing to choose between higher performance or quieter operation. -+ -+ If you are not sure, say Y here. -+ - config SENSORS_HDAPS - tristate "Thinkpad Hard Drive Active Protection System (hdaps)" - depends on INPUT -diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index ce8da260c223..8412fe7a169d 100644 ---- a/drivers/platform/x86/Makefile -+++ b/drivers/platform/x86/Makefile -@@ -39,6 +39,7 @@ obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o - obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o - obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o - obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o -+obj-$(CONFIG_SURFACE_ACPI) += surface_acpi.o - obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o - obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o - obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o -diff --git a/drivers/platform/x86/surface_acpi.c b/drivers/platform/x86/surface_acpi.c -new file mode 100644 -index 000000000000..ab793f6774a0 ---- /dev/null -+++ b/drivers/platform/x86/surface_acpi.c -@@ -0,0 +1,3828 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+#define USB_VENDOR_ID_MICROSOFT 0x045e -+#define USB_DEVICE_ID_MS_VHF 0xf001 -+#define USB_DEVICE_ID_MS_SURFACE_BASE_2_INTEGRATION 0x0922 -+ -+#define SG5_PARAM_PERM (S_IRUGO | S_IWUSR) -+ -+ -+/************************************************************************* -+ * Surface Serial Hub driver (cross-driver interface) -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_SSH -+ -+/* -+ * Maximum request payload size in bytes. -+ * Value based on ACPI (255 bytes minus header/status bytes). -+ */ -+#define SURFACEGEN5_MAX_RQST_PAYLOAD (255 - 10) -+ -+/* -+ * Maximum response payload size in bytes. -+ * Value based on ACPI (255 bytes minus header/status bytes). -+ */ -+#define SURFACEGEN5_MAX_RQST_RESPONSE (255 - 4) -+ -+#define SURFACEGEN5_RQID_EVENT_BITS 5 -+ -+#define SURFACEGEN5_EVENT_IMMEDIATE ((unsigned long) -1) -+ -+ -+struct surfacegen5_buf { -+ u8 cap; -+ u8 len; -+ u8 *data; -+}; -+ -+struct surfacegen5_rqst { -+ u8 tc; -+ u8 iid; -+ u8 cid; -+ u8 snc; -+ u8 cdl; -+ u8 *pld; -+}; -+ -+struct surfacegen5_event { -+ u16 rqid; -+ u8 tc; -+ u8 iid; -+ u8 cid; -+ u8 len; -+ u8 *pld; -+}; -+ -+ -+typedef int (*surfacegen5_ec_event_handler_fn)(struct surfacegen5_event *event, void *data); -+typedef unsigned long (*surfacegen5_ec_event_handler_delay)(struct surfacegen5_event *event, void *data); -+ -+struct device_link *surfacegen5_ec_consumer_add(struct device *consumer, u32 flags); -+int surfacegen5_ec_consumer_remove(struct device_link *link); -+ -+int surfacegen5_ec_rqst(const struct surfacegen5_rqst *rqst, struct surfacegen5_buf *result); -+ -+int surfacegen5_ec_enable_event_source(u8 tc, u8 unknown, u16 rqid); -+int surfacegen5_ec_disable_event_source(u8 tc, u8 unknown, u16 rqid); -+int surfacegen5_ec_remove_event_handler(u16 rqid); -+int surfacegen5_ec_set_event_handler(u16 rqid, surfacegen5_ec_event_handler_fn fn, void *data); -+int surfacegen5_ec_set_delayed_event_handler(u16 rqid, -+ surfacegen5_ec_event_handler_fn fn, -+ surfacegen5_ec_event_handler_delay delay, void *data); -+ -+#endif /* CONFIG_SURFACE_ACPI_SSH */ -+ -+ -+/************************************************************************* -+ * Surface Serial Hub Debug Device (cross-driver interface) -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_SSH -+ -+int surfacegen5_ssh_sysfs_register(struct device *dev); -+void surfacegen5_ssh_sysfs_unregister(struct device *dev); -+ -+#endif /* CONFIG_SURFACE_ACPI_SSH */ -+ -+ -+/************************************************************************* -+ * Surface Serial Hub driver (private implementation) -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_SSH -+ -+#define SG5_RQST_TAG_FULL "surfacegen5_ec_rqst: " -+#define SG5_RQST_TAG "rqst: " -+#define SG5_EVENT_TAG "event: " -+#define SG5_RECV_TAG "recv: " -+ -+#define SG5_SUPPORTED_FLOW_CONTROL_MASK (~((u8) ACPI_UART_FLOW_CONTROL_HW)) -+ -+#define SG5_BYTELEN_SYNC 2 -+#define SG5_BYTELEN_TERM 2 -+#define SG5_BYTELEN_CRC 2 -+#define SG5_BYTELEN_CTRL 4 // command-header, ACK, or RETRY -+#define SG5_BYTELEN_CMDFRAME 8 // without payload -+ -+#define SG5_MAX_WRITE ( \ -+ SG5_BYTELEN_SYNC \ -+ + SG5_BYTELEN_CTRL \ -+ + SG5_BYTELEN_CRC \ -+ + SG5_BYTELEN_CMDFRAME \ -+ + SURFACEGEN5_MAX_RQST_PAYLOAD \ -+ + SG5_BYTELEN_CRC \ -+) -+ -+#define SG5_MSG_LEN_CTRL ( \ -+ SG5_BYTELEN_SYNC \ -+ + SG5_BYTELEN_CTRL \ -+ + SG5_BYTELEN_CRC \ -+ + SG5_BYTELEN_TERM \ -+) -+ -+#define SG5_MSG_LEN_CMD_BASE ( \ -+ SG5_BYTELEN_SYNC \ -+ + SG5_BYTELEN_CTRL \ -+ + SG5_BYTELEN_CRC \ -+ + SG5_BYTELEN_CRC \ -+) // without payload and command-frame -+ -+#define SG5_WRITE_TIMEOUT msecs_to_jiffies(1000) -+#define SG5_READ_TIMEOUT msecs_to_jiffies(1000) -+#define SG5_NUM_RETRY 3 -+ -+#define SG5_WRITE_BUF_LEN SG5_MAX_WRITE -+#define SG5_READ_BUF_LEN 512 // must be power of 2 -+#define SG5_EVAL_BUF_LEN SG5_MAX_WRITE // also works for reading -+ -+#define SG5_FRAME_TYPE_CMD 0x80 -+#define SG5_FRAME_TYPE_ACK 0x40 -+#define SG5_FRAME_TYPE_RETRY 0x04 -+ -+#define SG5_FRAME_OFFS_CTRL SG5_BYTELEN_SYNC -+#define SG5_FRAME_OFFS_CTRL_CRC (SG5_FRAME_OFFS_CTRL + SG5_BYTELEN_CTRL) -+#define SG5_FRAME_OFFS_TERM (SG5_FRAME_OFFS_CTRL_CRC + SG5_BYTELEN_CRC) -+#define SG5_FRAME_OFFS_CMD SG5_FRAME_OFFS_TERM // either TERM or CMD -+#define SG5_FRAME_OFFS_CMD_PLD (SG5_FRAME_OFFS_CMD + SG5_BYTELEN_CMDFRAME) -+ -+/* -+ * A note on Request IDs (RQIDs): -+ * 0x0000 is not a valid RQID -+ * 0x0001 is valid, but reserved for Surface Laptop keyboard events -+ */ -+#define SG5_NUM_EVENT_TYPES ((1 << SURFACEGEN5_RQID_EVENT_BITS) - 1) -+ -+/* -+ * Sync: aa 55 -+ * Terminate: ff ff -+ * -+ * Request Message: sync cmd-hdr crc(cmd-hdr) cmd-rqst-frame crc(cmd-rqst-frame) -+ * Ack Message: sync ack crc(ack) terminate -+ * Retry Message: sync retry crc(retry) terminate -+ * Response Message: sync cmd-hdr crc(cmd-hdr) cmd-resp-frame crc(cmd-resp-frame) -+ * -+ * Command Header: 80 LEN 00 SEQ -+ * Ack: 40 00 00 SEQ -+ * Retry: 04 00 00 00 -+ * Command Request Frame: 80 RTC 01 00 RIID RQID RCID PLD -+ * Command Response Frame: 80 RTC 00 01 RIID RQID RCID PLD -+ */ -+ -+struct surfacegen5_frame_ctrl { -+ u8 type; -+ u8 len; // without crc -+ u8 pad; -+ u8 seq; -+} __packed; -+ -+struct surfacegen5_frame_cmd { -+ u8 type; -+ u8 tc; -+ u8 unknown1; -+ u8 unknown2; -+ u8 iid; -+ u8 rqid_lo; // id for request/response matching (low byte) -+ u8 rqid_hi; // id for request/response matching (high byte) -+ u8 cid; -+} __packed; -+ -+ -+enum surfacegen5_ec_state { -+ SG5_EC_UNINITIALIZED, -+ SG5_EC_INITIALIZED, -+ SG5_EC_SUSPENDED, -+}; -+ -+struct surfacegen5_ec_counters { -+ u8 seq; // control sequence id -+ u16 rqid; // id for request/response matching -+}; -+ -+struct surfacegen5_ec_writer { -+ u8 *data; -+ u8 *ptr; -+} __packed; -+ -+enum surfacegen5_ec_receiver_state { -+ SG5_RCV_DISCARD, -+ SG5_RCV_CONTROL, -+ SG5_RCV_COMMAND, -+}; -+ -+struct surfacegen5_ec_receiver { -+ spinlock_t lock; -+ enum surfacegen5_ec_receiver_state state; -+ struct completion signal; -+ struct kfifo fifo; -+ struct { -+ bool pld; -+ u8 seq; -+ u16 rqid; -+ } expect; -+ struct { -+ u16 cap; -+ u16 len; -+ u8 *ptr; -+ } eval_buf; -+}; -+ -+struct surfacegen5_ec_event_handler { -+ surfacegen5_ec_event_handler_fn handler; -+ surfacegen5_ec_event_handler_delay delay; -+ void *data; -+}; -+ -+struct surfacegen5_ec_events { -+ spinlock_t lock; -+ struct workqueue_struct *queue_ack; -+ struct workqueue_struct *queue_evt; -+ struct surfacegen5_ec_event_handler handler[SG5_NUM_EVENT_TYPES]; -+}; -+ -+struct surfacegen5_ec { -+ struct mutex lock; -+ enum surfacegen5_ec_state state; -+ struct serdev_device *serdev; -+ struct surfacegen5_ec_counters counter; -+ struct surfacegen5_ec_writer writer; -+ struct surfacegen5_ec_receiver receiver; -+ struct surfacegen5_ec_events events; -+}; -+ -+struct surfacegen5_fifo_packet { -+ u8 type; // packet type (ACK/RETRY/CMD) -+ u8 seq; -+ u8 len; -+}; -+ -+struct surfacegen5_event_work { -+ refcount_t refcount; -+ struct surfacegen5_ec *ec; -+ struct work_struct work_ack; -+ struct delayed_work work_evt; -+ struct surfacegen5_event event; -+ u8 seq; -+}; -+ -+ -+static struct surfacegen5_ec surfacegen5_ec = { -+ .lock = __MUTEX_INITIALIZER(surfacegen5_ec.lock), -+ .state = SG5_EC_UNINITIALIZED, -+ .serdev = NULL, -+ .counter = { -+ .seq = 0, -+ .rqid = 0, -+ }, -+ .writer = { -+ .data = NULL, -+ .ptr = NULL, -+ }, -+ .receiver = { -+ .lock = __SPIN_LOCK_UNLOCKED(), -+ .state = SG5_RCV_DISCARD, -+ .expect = {}, -+ }, -+ .events = { -+ .lock = __SPIN_LOCK_UNLOCKED(), -+ .handler = {}, -+ } -+}; -+ -+ -+static int surfacegen5_ec_rqst_unlocked(struct surfacegen5_ec *ec, -+ const struct surfacegen5_rqst *rqst, -+ struct surfacegen5_buf *result); -+ -+ -+inline static struct surfacegen5_ec *surfacegen5_ec_acquire(void) -+{ -+ struct surfacegen5_ec *ec = &surfacegen5_ec; -+ -+ mutex_lock(&ec->lock); -+ return ec; -+} -+ -+inline static void surfacegen5_ec_release(struct surfacegen5_ec *ec) -+{ -+ mutex_unlock(&ec->lock); -+} -+ -+inline static struct surfacegen5_ec *surfacegen5_ec_acquire_init(void) -+{ -+ struct surfacegen5_ec *ec = surfacegen5_ec_acquire(); -+ -+ if (ec->state == SG5_EC_UNINITIALIZED) { -+ surfacegen5_ec_release(ec); -+ return NULL; -+ } -+ -+ return ec; -+} -+ -+struct device_link *surfacegen5_ec_consumer_add(struct device *consumer, u32 flags) -+{ -+ struct surfacegen5_ec *ec; -+ struct device_link *link; -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ return ERR_PTR(-ENXIO); -+ } -+ -+ link = device_link_add(consumer, &ec->serdev->dev, flags); -+ -+ surfacegen5_ec_release(ec); -+ return link; -+} -+ -+int surfacegen5_ec_consumer_remove(struct device_link *link) -+{ -+ struct surfacegen5_ec *ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ return -ENXIO; -+ } -+ -+ device_link_del(link); -+ -+ surfacegen5_ec_release(ec); -+ return 0; -+} -+ -+ -+inline static u16 surfacegen5_rqid_to_rqst(u16 rqid) { -+ return rqid << SURFACEGEN5_RQID_EVENT_BITS; -+} -+ -+inline static bool surfacegen5_rqid_is_event(u16 rqid) { -+ const u16 mask = (1 << SURFACEGEN5_RQID_EVENT_BITS) - 1; -+ return rqid != 0 && (rqid | mask) == mask; -+} -+ -+int surfacegen5_ec_enable_event_source(u8 tc, u8 unknown, u16 rqid) -+{ -+ struct surfacegen5_ec *ec; -+ -+ u8 pld[4] = { tc, unknown, rqid & 0xff, rqid >> 8 }; -+ u8 buf[1] = { 0x00 }; -+ -+ struct surfacegen5_rqst rqst = { -+ .tc = 0x01, -+ .iid = 0x00, -+ .cid = 0x0b, -+ .snc = 0x01, -+ .cdl = 0x04, -+ .pld = pld, -+ }; -+ -+ struct surfacegen5_buf result = { -+ result.cap = ARRAY_SIZE(buf), -+ result.len = 0, -+ result.data = buf, -+ }; -+ -+ int status; -+ -+ // only allow RQIDs that lie within event spectrum -+ if (!surfacegen5_rqid_is_event(rqid)) { -+ return -EINVAL; -+ } -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n"); -+ return -ENXIO; -+ } -+ -+ if (ec->state == SG5_EC_SUSPENDED) { -+ dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n"); -+ -+ surfacegen5_ec_release(ec); -+ return -EPERM; -+ } -+ -+ status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); -+ -+ if (buf[0] != 0x00) { -+ dev_warn(&ec->serdev->dev, -+ "unexpected result while enabling event source: 0x%02x\n", -+ buf[0]); -+ } -+ -+ surfacegen5_ec_release(ec); -+ return status; -+ -+} -+ -+int surfacegen5_ec_disable_event_source(u8 tc, u8 unknown, u16 rqid) -+{ -+ struct surfacegen5_ec *ec; -+ -+ u8 pld[4] = { tc, unknown, rqid & 0xff, rqid >> 8 }; -+ u8 buf[1] = { 0x00 }; -+ -+ struct surfacegen5_rqst rqst = { -+ .tc = 0x01, -+ .iid = 0x00, -+ .cid = 0x0c, -+ .snc = 0x01, -+ .cdl = 0x04, -+ .pld = pld, -+ }; -+ -+ struct surfacegen5_buf result = { -+ result.cap = ARRAY_SIZE(buf), -+ result.len = 0, -+ result.data = buf, -+ }; -+ -+ int status; -+ -+ // only allow RQIDs that lie within event spectrum -+ if (!surfacegen5_rqid_is_event(rqid)) { -+ return -EINVAL; -+ } -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n"); -+ return -ENXIO; -+ } -+ -+ if (ec->state == SG5_EC_SUSPENDED) { -+ dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n"); -+ -+ surfacegen5_ec_release(ec); -+ return -EPERM; -+ } -+ -+ status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); -+ -+ if (buf[0] != 0x00) { -+ dev_warn(&ec->serdev->dev, -+ "unexpected result while disabling event source: 0x%02x\n", -+ buf[0]); -+ } -+ -+ surfacegen5_ec_release(ec); -+ return status; -+} -+ -+int surfacegen5_ec_set_delayed_event_handler( -+ u16 rqid, surfacegen5_ec_event_handler_fn fn, -+ surfacegen5_ec_event_handler_delay delay, -+ void *data) -+{ -+ struct surfacegen5_ec *ec; -+ unsigned long flags; -+ -+ if (!surfacegen5_rqid_is_event(rqid)) { -+ return -EINVAL; -+ } -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ return -ENXIO; -+ } -+ -+ spin_lock_irqsave(&ec->events.lock, flags); -+ -+ // 0 is not a valid event RQID -+ ec->events.handler[rqid - 1].handler = fn; -+ ec->events.handler[rqid - 1].delay = delay; -+ ec->events.handler[rqid - 1].data = data; -+ -+ spin_unlock_irqrestore(&ec->events.lock, flags); -+ surfacegen5_ec_release(ec); -+ -+ return 0; -+} -+ -+int surfacegen5_ec_set_event_handler( -+ u16 rqid, surfacegen5_ec_event_handler_fn fn, void *data) -+{ -+ return surfacegen5_ec_set_delayed_event_handler(rqid, fn, NULL, data); -+} -+ -+int surfacegen5_ec_remove_event_handler(u16 rqid) -+{ -+ struct surfacegen5_ec *ec; -+ unsigned long flags; -+ -+ if (!surfacegen5_rqid_is_event(rqid)) { -+ return -EINVAL; -+ } -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ return -ENXIO; -+ } -+ -+ spin_lock_irqsave(&ec->events.lock, flags); -+ -+ // 0 is not a valid event RQID -+ ec->events.handler[rqid - 1].handler = NULL; -+ ec->events.handler[rqid - 1].delay = NULL; -+ ec->events.handler[rqid - 1].data = NULL; -+ -+ spin_unlock_irqrestore(&ec->events.lock, flags); -+ surfacegen5_ec_release(ec); -+ -+ /* -+ * Make sure that the handler is not in use any more after we've -+ * removed it. -+ */ -+ flush_workqueue(ec->events.queue_evt); -+ -+ return 0; -+} -+ -+ -+inline static u16 surfacegen5_ssh_crc(const u8 *buf, size_t size) -+{ -+ return crc_ccitt_false(0xffff, buf, size); -+} -+ -+inline static void surfacegen5_ssh_write_u16(struct surfacegen5_ec_writer *writer, u16 in) -+{ -+ put_unaligned_le16(in, writer->ptr); -+ writer->ptr += 2; -+} -+ -+inline static void surfacegen5_ssh_write_crc(struct surfacegen5_ec_writer *writer, -+ const u8 *buf, size_t size) -+{ -+ surfacegen5_ssh_write_u16(writer, surfacegen5_ssh_crc(buf, size)); -+} -+ -+inline static void surfacegen5_ssh_write_syn(struct surfacegen5_ec_writer *writer) -+{ -+ u8 *w = writer->ptr; -+ -+ *w++ = 0xaa; -+ *w++ = 0x55; -+ -+ writer->ptr = w; -+} -+ -+inline static void surfacegen5_ssh_write_ter(struct surfacegen5_ec_writer *writer) -+{ -+ u8 *w = writer->ptr; -+ -+ *w++ = 0xff; -+ *w++ = 0xff; -+ -+ writer->ptr = w; -+} -+ -+inline static void surfacegen5_ssh_write_buf(struct surfacegen5_ec_writer *writer, -+ u8 *in, size_t len) -+{ -+ writer->ptr = memcpy(writer->ptr, in, len) + len; -+} -+ -+inline static void surfacegen5_ssh_write_hdr(struct surfacegen5_ec_writer *writer, -+ const struct surfacegen5_rqst *rqst, -+ struct surfacegen5_ec *ec) -+{ -+ struct surfacegen5_frame_ctrl *hdr = (struct surfacegen5_frame_ctrl *)writer->ptr; -+ u8 *begin = writer->ptr; -+ -+ hdr->type = SG5_FRAME_TYPE_CMD; -+ hdr->len = SG5_BYTELEN_CMDFRAME + rqst->cdl; // without CRC -+ hdr->pad = 0x00; -+ hdr->seq = ec->counter.seq; -+ -+ writer->ptr += sizeof(*hdr); -+ -+ surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin); -+} -+ -+inline static void surfacegen5_ssh_write_cmd(struct surfacegen5_ec_writer *writer, -+ const struct surfacegen5_rqst *rqst, -+ struct surfacegen5_ec *ec) -+{ -+ struct surfacegen5_frame_cmd *cmd = (struct surfacegen5_frame_cmd *)writer->ptr; -+ u8 *begin = writer->ptr; -+ -+ u16 rqid = surfacegen5_rqid_to_rqst(ec->counter.rqid); -+ u8 rqid_lo = rqid & 0xFF; -+ u8 rqid_hi = rqid >> 8; -+ -+ cmd->type = SG5_FRAME_TYPE_CMD; -+ cmd->tc = rqst->tc; -+ cmd->unknown1 = 0x01; -+ cmd->unknown2 = 0x00; -+ cmd->iid = rqst->iid; -+ cmd->rqid_lo = rqid_lo; -+ cmd->rqid_hi = rqid_hi; -+ cmd->cid = rqst->cid; -+ -+ writer->ptr += sizeof(*cmd); -+ -+ surfacegen5_ssh_write_buf(writer, rqst->pld, rqst->cdl); -+ surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin); -+} -+ -+inline static void surfacegen5_ssh_write_ack(struct surfacegen5_ec_writer *writer, u8 seq) -+{ -+ struct surfacegen5_frame_ctrl *ack = (struct surfacegen5_frame_ctrl *)writer->ptr; -+ u8 *begin = writer->ptr; -+ -+ ack->type = SG5_FRAME_TYPE_ACK; -+ ack->len = 0x00; -+ ack->pad = 0x00; -+ ack->seq = seq; -+ -+ writer->ptr += sizeof(*ack); -+ -+ surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin); -+} -+ -+inline static void surfacegen5_ssh_writer_reset(struct surfacegen5_ec_writer *writer) -+{ -+ writer->ptr = writer->data; -+} -+ -+inline static int surfacegen5_ssh_writer_flush(struct surfacegen5_ec *ec) -+{ -+ struct surfacegen5_ec_writer *writer = &ec->writer; -+ struct serdev_device *serdev = ec->serdev; -+ int status; -+ -+ size_t len = writer->ptr - writer->data; -+ -+ dev_dbg(&ec->serdev->dev, "sending message\n"); -+ print_hex_dump_debug("send: ", DUMP_PREFIX_OFFSET, 16, 1, -+ writer->data, writer->ptr - writer->data, false); -+ -+ status = serdev_device_write(serdev, writer->data, len, SG5_WRITE_TIMEOUT); -+ return status >= 0 ? 0 : status; -+} -+ -+inline static void surfacegen5_ssh_write_msg_cmd(struct surfacegen5_ec *ec, -+ const struct surfacegen5_rqst *rqst) -+{ -+ surfacegen5_ssh_writer_reset(&ec->writer); -+ surfacegen5_ssh_write_syn(&ec->writer); -+ surfacegen5_ssh_write_hdr(&ec->writer, rqst, ec); -+ surfacegen5_ssh_write_cmd(&ec->writer, rqst, ec); -+} -+ -+inline static void surfacegen5_ssh_write_msg_ack(struct surfacegen5_ec *ec, u8 seq) -+{ -+ surfacegen5_ssh_writer_reset(&ec->writer); -+ surfacegen5_ssh_write_syn(&ec->writer); -+ surfacegen5_ssh_write_ack(&ec->writer, seq); -+ surfacegen5_ssh_write_ter(&ec->writer); -+} -+ -+inline static void surfacegen5_ssh_receiver_restart(struct surfacegen5_ec *ec, -+ const struct surfacegen5_rqst *rqst) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ec->receiver.lock, flags); -+ reinit_completion(&ec->receiver.signal); -+ ec->receiver.state = SG5_RCV_CONTROL; -+ ec->receiver.expect.pld = rqst->snc; -+ ec->receiver.expect.seq = ec->counter.seq; -+ ec->receiver.expect.rqid = surfacegen5_rqid_to_rqst(ec->counter.rqid); -+ ec->receiver.eval_buf.len = 0; -+ spin_unlock_irqrestore(&ec->receiver.lock, flags); -+} -+ -+inline static void surfacegen5_ssh_receiver_discard(struct surfacegen5_ec *ec) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ec->receiver.lock, flags); -+ ec->receiver.state = SG5_RCV_DISCARD; -+ ec->receiver.eval_buf.len = 0; -+ kfifo_reset(&ec->receiver.fifo); -+ spin_unlock_irqrestore(&ec->receiver.lock, flags); -+} -+ -+static int surfacegen5_ec_rqst_unlocked(struct surfacegen5_ec *ec, -+ const struct surfacegen5_rqst *rqst, -+ struct surfacegen5_buf *result) -+{ -+ struct device *dev = &ec->serdev->dev; -+ struct surfacegen5_fifo_packet packet = {}; -+ int status; -+ int try; -+ unsigned int rem; -+ -+ if (rqst->cdl > SURFACEGEN5_MAX_RQST_PAYLOAD) { -+ dev_err(dev, SG5_RQST_TAG "request payload too large\n"); -+ return -EINVAL; -+ } -+ -+ // write command in buffer, we may need it multiple times -+ surfacegen5_ssh_write_msg_cmd(ec, rqst); -+ surfacegen5_ssh_receiver_restart(ec, rqst); -+ -+ // send command, try to get an ack response -+ for (try = 0; try < SG5_NUM_RETRY; try++) { -+ status = surfacegen5_ssh_writer_flush(ec); -+ if (status) { -+ goto ec_rqst_out; -+ } -+ -+ rem = wait_for_completion_timeout(&ec->receiver.signal, SG5_READ_TIMEOUT); -+ if (rem) { -+ // completion assures valid packet, thus ignore returned length -+ (void) !kfifo_out(&ec->receiver.fifo, &packet, sizeof(packet)); -+ -+ if (packet.type == SG5_FRAME_TYPE_ACK) { -+ break; -+ } -+ } -+ } -+ -+ // check if we ran out of tries? -+ if (try >= SG5_NUM_RETRY) { -+ dev_err(dev, SG5_RQST_TAG "communication failed %d times, giving up\n", try); -+ status = -EIO; -+ goto ec_rqst_out; -+ } -+ -+ ec->counter.seq += 1; -+ ec->counter.rqid += 1; -+ -+ // get command response/payload -+ if (rqst->snc && result) { -+ rem = wait_for_completion_timeout(&ec->receiver.signal, SG5_READ_TIMEOUT); -+ if (rem) { -+ // completion assures valid packet, thus ignore returned length -+ (void) !kfifo_out(&ec->receiver.fifo, &packet, sizeof(packet)); -+ -+ if (result->cap < packet.len) { -+ status = -EINVAL; -+ goto ec_rqst_out; -+ } -+ -+ // completion assures valid packet, thus ignore returned length -+ (void) !kfifo_out(&ec->receiver.fifo, result->data, packet.len); -+ result->len = packet.len; -+ } else { -+ dev_err(dev, SG5_RQST_TAG "communication timed out\n"); -+ status = -EIO; -+ goto ec_rqst_out; -+ } -+ -+ // send ACK -+ surfacegen5_ssh_write_msg_ack(ec, packet.seq); -+ status = surfacegen5_ssh_writer_flush(ec); -+ if (status) { -+ goto ec_rqst_out; -+ } -+ } -+ -+ec_rqst_out: -+ surfacegen5_ssh_receiver_discard(ec); -+ return status; -+} -+ -+int surfacegen5_ec_rqst(const struct surfacegen5_rqst *rqst, struct surfacegen5_buf *result) -+{ -+ struct surfacegen5_ec *ec; -+ int status; -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n"); -+ return -ENXIO; -+ } -+ -+ if (ec->state == SG5_EC_SUSPENDED) { -+ dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n"); -+ -+ surfacegen5_ec_release(ec); -+ return -EPERM; -+ } -+ -+ status = surfacegen5_ec_rqst_unlocked(ec, rqst, result); -+ -+ surfacegen5_ec_release(ec); -+ return status; -+} -+ -+ -+static int surfacegen5_ssh_ec_resume(struct surfacegen5_ec *ec) -+{ -+ u8 buf[1] = { 0x00 }; -+ -+ struct surfacegen5_rqst rqst = { -+ .tc = 0x01, -+ .iid = 0x00, -+ .cid = 0x16, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surfacegen5_buf result = { -+ result.cap = ARRAY_SIZE(buf), -+ result.len = 0, -+ result.data = buf, -+ }; -+ -+ int status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); -+ if (status) { -+ return status; -+ } -+ -+ if (buf[0] != 0x00) { -+ dev_warn(&ec->serdev->dev, -+ "unexpected result while trying to resume EC: 0x%02x\n", -+ buf[0]); -+ } -+ -+ return 0; -+} -+ -+static int surfacegen5_ssh_ec_suspend(struct surfacegen5_ec *ec) -+{ -+ u8 buf[1] = { 0x00 }; -+ -+ struct surfacegen5_rqst rqst = { -+ .tc = 0x01, -+ .iid = 0x00, -+ .cid = 0x15, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surfacegen5_buf result = { -+ result.cap = ARRAY_SIZE(buf), -+ result.len = 0, -+ result.data = buf, -+ }; -+ -+ int status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result); -+ if (status) { -+ return status; -+ } -+ -+ if (buf[0] != 0x00) { -+ dev_warn(&ec->serdev->dev, -+ "unexpected result while trying to suspend EC: 0x%02x\n", -+ buf[0]); -+ } -+ -+ return 0; -+} -+ -+ -+inline static bool surfacegen5_ssh_is_valid_syn(const u8 *ptr) -+{ -+ return ptr[0] == 0xaa && ptr[1] == 0x55; -+} -+ -+inline static bool surfacegen5_ssh_is_valid_ter(const u8 *ptr) -+{ -+ return ptr[0] == 0xff && ptr[1] == 0xff; -+} -+ -+inline static bool surfacegen5_ssh_is_valid_crc(const u8 *begin, const u8 *end) -+{ -+ u16 crc = surfacegen5_ssh_crc(begin, end - begin); -+ return (end[0] == (crc & 0xff)) && (end[1] == (crc >> 8)); -+} -+ -+ -+static int surfacegen5_ssh_send_ack(struct surfacegen5_ec *ec, u8 seq) -+{ -+ int status; -+ u8 buf[SG5_MSG_LEN_CTRL]; -+ u16 crc; -+ -+ buf[0] = 0xaa; -+ buf[1] = 0x55; -+ buf[2] = 0x40; -+ buf[3] = 0x00; -+ buf[4] = 0x00; -+ buf[5] = seq; -+ -+ crc = surfacegen5_ssh_crc(buf + SG5_FRAME_OFFS_CTRL, SG5_BYTELEN_CTRL); -+ buf[6] = crc & 0xff; -+ buf[7] = crc >> 8; -+ -+ buf[8] = 0xff; -+ buf[9] = 0xff; -+ -+ dev_dbg(&ec->serdev->dev, "sending message\n"); -+ print_hex_dump_debug("send: ", DUMP_PREFIX_OFFSET, 16, 1, -+ buf, SG5_MSG_LEN_CTRL, false); -+ -+ status = serdev_device_write(ec->serdev, buf, SG5_MSG_LEN_CTRL, SG5_WRITE_TIMEOUT); -+ return status >= 0 ? 0 : status; -+} -+ -+static void surfacegen5_event_work_ack_handler(struct work_struct *_work) -+{ -+ struct surfacegen5_event_work *work; -+ struct surfacegen5_event *event; -+ struct surfacegen5_ec *ec; -+ struct device *dev; -+ int status; -+ -+ work = container_of(_work, struct surfacegen5_event_work, work_ack); -+ event = &work->event; -+ ec = work->ec; -+ dev = &ec->serdev->dev; -+ -+ // make sure we load a fresh ec state -+ smp_mb(); -+ -+ if (ec->state == SG5_EC_INITIALIZED) { -+ status = surfacegen5_ssh_send_ack(ec, work->seq); -+ if (status) { -+ dev_err(dev, SG5_EVENT_TAG "failed to send ACK: %d\n", status); -+ } -+ } -+ -+ if (refcount_dec_and_test(&work->refcount)) { -+ kfree(work); -+ } -+} -+ -+static void surfacegen5_event_work_evt_handler(struct work_struct *_work) -+{ -+ struct delayed_work *dwork = (struct delayed_work *)_work; -+ struct surfacegen5_event_work *work; -+ struct surfacegen5_event *event; -+ struct surfacegen5_ec *ec; -+ struct device *dev; -+ unsigned long flags; -+ -+ surfacegen5_ec_event_handler_fn handler; -+ void *handler_data; -+ -+ int status = 0; -+ -+ work = container_of(dwork, struct surfacegen5_event_work, work_evt); -+ event = &work->event; -+ ec = work->ec; -+ dev = &ec->serdev->dev; -+ -+ spin_lock_irqsave(&ec->events.lock, flags); -+ handler = ec->events.handler[event->rqid - 1].handler; -+ handler_data = ec->events.handler[event->rqid - 1].data; -+ spin_unlock_irqrestore(&ec->events.lock, flags); -+ -+ /* -+ * During handler removal or driver release, we ensure every event gets -+ * handled before return of that function. Thus a handler obtained here is -+ * guaranteed to be valid at least until this function returns. -+ */ -+ -+ if (handler) { -+ status = handler(event, handler_data); -+ } else { -+ dev_warn(dev, SG5_EVENT_TAG "unhandled event (rqid: %04x)\n", event->rqid); -+ } -+ -+ if (status) { -+ dev_err(dev, SG5_EVENT_TAG "error handling event: %d\n", status); -+ } -+ -+ if (refcount_dec_and_test(&work->refcount)) { -+ kfree(work); -+ } -+} -+ -+static void surfacegen5_ssh_handle_event(struct surfacegen5_ec *ec, const u8 *buf) -+{ -+ struct device *dev = &ec->serdev->dev; -+ const struct surfacegen5_frame_ctrl *ctrl; -+ const struct surfacegen5_frame_cmd *cmd; -+ struct surfacegen5_event_work *work; -+ unsigned long flags; -+ u16 pld_len; -+ -+ surfacegen5_ec_event_handler_delay delay_fn; -+ void *handler_data; -+ unsigned long delay = 0; -+ -+ ctrl = (const struct surfacegen5_frame_ctrl *)(buf + SG5_FRAME_OFFS_CTRL); -+ cmd = (const struct surfacegen5_frame_cmd *)(buf + SG5_FRAME_OFFS_CMD); -+ -+ pld_len = ctrl->len - SG5_BYTELEN_CMDFRAME; -+ -+ work = kzalloc(sizeof(struct surfacegen5_event_work) + pld_len, GFP_ATOMIC); -+ if (!work) { -+ dev_warn(dev, SG5_EVENT_TAG "failed to allocate memory, dropping event\n"); -+ return; -+ } -+ -+ refcount_set(&work->refcount, 2); -+ work->ec = ec; -+ work->seq = ctrl->seq; -+ work->event.rqid = (cmd->rqid_hi << 8) | cmd->rqid_lo; -+ work->event.tc = cmd->tc; -+ work->event.iid = cmd->iid; -+ work->event.cid = cmd->cid; -+ work->event.len = pld_len; -+ work->event.pld = ((u8*) work) + sizeof(struct surfacegen5_event_work); -+ -+ memcpy(work->event.pld, buf + SG5_FRAME_OFFS_CMD_PLD, pld_len); -+ -+ INIT_WORK(&work->work_ack, surfacegen5_event_work_ack_handler); -+ queue_work(ec->events.queue_ack, &work->work_ack); -+ -+ spin_lock_irqsave(&ec->events.lock, flags); -+ handler_data = ec->events.handler[work->event.rqid - 1].data; -+ delay_fn = ec->events.handler[work->event.rqid - 1].delay; -+ if (delay_fn) { -+ delay = delay_fn(&work->event, handler_data); -+ } -+ spin_unlock_irqrestore(&ec->events.lock, flags); -+ -+ // immediate execution for high priority events (e.g. keyboard) -+ if (delay == SURFACEGEN5_EVENT_IMMEDIATE) { -+ surfacegen5_event_work_evt_handler(&work->work_evt.work); -+ } else { -+ INIT_DELAYED_WORK(&work->work_evt, surfacegen5_event_work_evt_handler); -+ queue_delayed_work(ec->events.queue_evt, &work->work_evt, delay); -+ } -+} -+ -+static int surfacegen5_ssh_receive_msg_ctrl(struct surfacegen5_ec *ec, -+ const u8 *buf, size_t size) -+{ -+ struct device *dev = &ec->serdev->dev; -+ struct surfacegen5_ec_receiver *rcv = &ec->receiver; -+ const struct surfacegen5_frame_ctrl *ctrl; -+ struct surfacegen5_fifo_packet packet; -+ -+ const u8 *ctrl_begin = buf + SG5_FRAME_OFFS_CTRL; -+ const u8 *ctrl_end = buf + SG5_FRAME_OFFS_CTRL_CRC; -+ -+ ctrl = (const struct surfacegen5_frame_ctrl *)(ctrl_begin); -+ -+ // actual length check -+ if (size < SG5_MSG_LEN_CTRL) { -+ return 0; // need more bytes -+ } -+ -+ // validate TERM -+ if (!surfacegen5_ssh_is_valid_ter(buf + SG5_FRAME_OFFS_TERM)) { -+ dev_err(dev, SG5_RECV_TAG "invalid end of message\n"); -+ return size; // discard everything -+ } -+ -+ // validate CRC -+ if (!surfacegen5_ssh_is_valid_crc(ctrl_begin, ctrl_end)) { -+ dev_err(dev, SG5_RECV_TAG "invalid checksum (ctrl)\n"); -+ return SG5_MSG_LEN_CTRL; // only discard message -+ } -+ -+ // check if we expect the message -+ if (rcv->state != SG5_RCV_CONTROL) { -+ dev_err(dev, SG5_RECV_TAG "discarding message: ctrl not expected\n"); -+ return SG5_MSG_LEN_CTRL; // discard message -+ } -+ -+ // check if it is for our request -+ if (ctrl->type == SG5_FRAME_TYPE_ACK && ctrl->seq != rcv->expect.seq) { -+ dev_err(dev, SG5_RECV_TAG "discarding message: ack does not match\n"); -+ return SG5_MSG_LEN_CTRL; // discard message -+ } -+ -+ // we now have a valid & expected ACK/RETRY message -+ dev_dbg(dev, SG5_RECV_TAG "valid control message received (type: 0x%02x)\n", ctrl->type); -+ -+ packet.type = ctrl->type; -+ packet.seq = ctrl->seq; -+ packet.len = 0; -+ -+ if (kfifo_avail(&rcv->fifo) >= sizeof(packet)) { -+ kfifo_in(&rcv->fifo, (u8 *) &packet, sizeof(packet)); -+ -+ } else { -+ dev_warn(dev, SG5_RECV_TAG -+ "dropping frame: not enough space in fifo (type = %d)\n", -+ SG5_FRAME_TYPE_CMD); -+ -+ return SG5_MSG_LEN_CTRL; // discard message -+ } -+ -+ // update decoder state -+ if (ctrl->type == SG5_FRAME_TYPE_ACK) { -+ rcv->state = rcv->expect.pld -+ ? SG5_RCV_COMMAND -+ : SG5_RCV_DISCARD; -+ } -+ -+ complete(&rcv->signal); -+ return SG5_MSG_LEN_CTRL; // handled message -+} -+ -+static int surfacegen5_ssh_receive_msg_cmd(struct surfacegen5_ec *ec, -+ const u8 *buf, size_t size) -+{ -+ struct device *dev = &ec->serdev->dev; -+ struct surfacegen5_ec_receiver *rcv = &ec->receiver; -+ const struct surfacegen5_frame_ctrl *ctrl; -+ const struct surfacegen5_frame_cmd *cmd; -+ struct surfacegen5_fifo_packet packet; -+ -+ const u8 *ctrl_begin = buf + SG5_FRAME_OFFS_CTRL; -+ const u8 *ctrl_end = buf + SG5_FRAME_OFFS_CTRL_CRC; -+ const u8 *cmd_begin = buf + SG5_FRAME_OFFS_CMD; -+ const u8 *cmd_begin_pld = buf + SG5_FRAME_OFFS_CMD_PLD; -+ const u8 *cmd_end; -+ -+ size_t msg_len; -+ -+ ctrl = (const struct surfacegen5_frame_ctrl *)(ctrl_begin); -+ cmd = (const struct surfacegen5_frame_cmd *)(cmd_begin); -+ -+ // we need at least a full control frame -+ if (size < (SG5_BYTELEN_SYNC + SG5_BYTELEN_CTRL + SG5_BYTELEN_CRC)) { -+ return 0; // need more bytes -+ } -+ -+ // validate control-frame CRC -+ if (!surfacegen5_ssh_is_valid_crc(ctrl_begin, ctrl_end)) { -+ dev_err(dev, SG5_RECV_TAG "invalid checksum (cmd-ctrl)\n"); -+ /* -+ * We can't be sure here if length is valid, thus -+ * discard everything. -+ */ -+ return size; -+ } -+ -+ // actual length check (ctrl->len contains command-frame but not crc) -+ msg_len = SG5_MSG_LEN_CMD_BASE + ctrl->len; -+ if (size < msg_len) { -+ return 0; // need more bytes -+ } -+ -+ cmd_end = cmd_begin + ctrl->len; -+ -+ // validate command-frame type -+ if (cmd->type != SG5_FRAME_TYPE_CMD) { -+ dev_err(dev, SG5_RECV_TAG "expected command frame type but got 0x%02x\n", cmd->type); -+ return size; // discard everything -+ } -+ -+ // validate command-frame CRC -+ if (!surfacegen5_ssh_is_valid_crc(cmd_begin, cmd_end)) { -+ dev_err(dev, SG5_RECV_TAG "invalid checksum (cmd-pld)\n"); -+ -+ /* -+ * The message length is provided in the control frame. As we -+ * already validated that, we can be sure here that it's -+ * correct, so we only need to discard the message. -+ */ -+ return msg_len; -+ } -+ -+ // check if we received an event notification -+ if (surfacegen5_rqid_is_event((cmd->rqid_hi << 8) | cmd->rqid_lo)) { -+ surfacegen5_ssh_handle_event(ec, buf); -+ return msg_len; // handled message -+ } -+ -+ // check if we expect the message -+ if (rcv->state != SG5_RCV_COMMAND) { -+ dev_dbg(dev, SG5_RECV_TAG "discarding message: command not expected\n"); -+ return msg_len; // discard message -+ } -+ -+ // check if response is for our request -+ if (rcv->expect.rqid != (cmd->rqid_lo | (cmd->rqid_hi << 8))) { -+ dev_dbg(dev, SG5_RECV_TAG "discarding message: command not a match\n"); -+ return msg_len; // discard message -+ } -+ -+ // we now have a valid & expected command message -+ dev_dbg(dev, SG5_RECV_TAG "valid command message received\n"); -+ -+ packet.type = ctrl->type; -+ packet.seq = ctrl->seq; -+ packet.len = cmd_end - cmd_begin_pld; -+ -+ if (kfifo_avail(&rcv->fifo) >= sizeof(packet) + packet.len) { -+ kfifo_in(&rcv->fifo, &packet, sizeof(packet)); -+ kfifo_in(&rcv->fifo, cmd_begin_pld, packet.len); -+ -+ } else { -+ dev_warn(dev, SG5_RECV_TAG -+ "dropping frame: not enough space in fifo (type = %d)\n", -+ SG5_FRAME_TYPE_CMD); -+ -+ return SG5_MSG_LEN_CTRL; // discard message -+ } -+ -+ rcv->state = SG5_RCV_DISCARD; -+ -+ complete(&rcv->signal); -+ return msg_len; // handled message -+} -+ -+static int surfacegen5_ssh_eval_buf(struct surfacegen5_ec *ec, -+ const u8 *buf, size_t size) -+{ -+ struct device *dev = &ec->serdev->dev; -+ struct surfacegen5_frame_ctrl *ctrl; -+ -+ // we need at least a control frame to check what to do -+ if (size < (SG5_BYTELEN_SYNC + SG5_BYTELEN_CTRL)) { -+ return 0; // need more bytes -+ } -+ -+ // make sure we're actually at the start of a new message -+ if (!surfacegen5_ssh_is_valid_syn(buf)) { -+ dev_err(dev, SG5_RECV_TAG "invalid start of message\n"); -+ return size; // discard everything -+ } -+ -+ // handle individual message types seperately -+ ctrl = (struct surfacegen5_frame_ctrl *)(buf + SG5_FRAME_OFFS_CTRL); -+ -+ switch (ctrl->type) { -+ case SG5_FRAME_TYPE_ACK: -+ case SG5_FRAME_TYPE_RETRY: -+ return surfacegen5_ssh_receive_msg_ctrl(ec, buf, size); -+ -+ case SG5_FRAME_TYPE_CMD: -+ return surfacegen5_ssh_receive_msg_cmd(ec, buf, size); -+ -+ default: -+ dev_err(dev, SG5_RECV_TAG "unknown frame type 0x%02x\n", ctrl->type); -+ return size; // discard everything -+ } -+} -+ -+static int surfacegen5_ssh_receive_buf(struct serdev_device *serdev, -+ const unsigned char *buf, size_t size) -+{ -+ struct surfacegen5_ec *ec = serdev_device_get_drvdata(serdev); -+ struct surfacegen5_ec_receiver *rcv = &ec->receiver; -+ unsigned long flags; -+ int offs = 0; -+ int used, n; -+ -+ dev_dbg(&serdev->dev, SG5_RECV_TAG "received buffer (size: %zu)\n", size); -+ print_hex_dump_debug(SG5_RECV_TAG, DUMP_PREFIX_OFFSET, 16, 1, buf, size, false); -+ -+ /* -+ * The battery _BIX message gets a bit long, thus we have to add some -+ * additional buffering here. -+ */ -+ -+ spin_lock_irqsave(&rcv->lock, flags); -+ -+ // copy to eval-buffer -+ used = min(size, (size_t)(rcv->eval_buf.cap - rcv->eval_buf.len)); -+ memcpy(rcv->eval_buf.ptr + rcv->eval_buf.len, buf, used); -+ rcv->eval_buf.len += used; -+ -+ // evaluate buffer until we need more bytes or eval-buf is empty -+ while (offs < rcv->eval_buf.len) { -+ n = rcv->eval_buf.len - offs; -+ n = surfacegen5_ssh_eval_buf(ec, rcv->eval_buf.ptr + offs, n); -+ if (n <= 0) break; // need more bytes -+ -+ offs += n; -+ } -+ -+ // throw away the evaluated parts -+ rcv->eval_buf.len -= offs; -+ memmove(rcv->eval_buf.ptr, rcv->eval_buf.ptr + offs, rcv->eval_buf.len); -+ -+ spin_unlock_irqrestore(&rcv->lock, flags); -+ -+ return used; -+} -+ -+ -+static acpi_status -+surfacegen5_ssh_setup_from_resource(struct acpi_resource *resource, void *context) -+{ -+ struct serdev_device *serdev = context; -+ struct acpi_resource_common_serialbus *serial; -+ struct acpi_resource_uart_serialbus *uart; -+ int status = 0; -+ -+ if (resource->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { -+ return AE_OK; -+ } -+ -+ serial = &resource->data.common_serial_bus; -+ if (serial->type != ACPI_RESOURCE_SERIAL_TYPE_UART) { -+ return AE_OK; -+ } -+ -+ uart = &resource->data.uart_serial_bus; -+ -+ // set up serdev device -+ serdev_device_set_baudrate(serdev, uart->default_baud_rate); -+ -+ // serdev currently only supports RTSCTS flow control -+ if (uart->flow_control & SG5_SUPPORTED_FLOW_CONTROL_MASK) { -+ dev_warn(&serdev->dev, "unsupported flow control (value: 0x%02x)\n", uart->flow_control); -+ } -+ -+ // set RTSCTS flow control -+ serdev_device_set_flow_control(serdev, uart->flow_control & ACPI_UART_FLOW_CONTROL_HW); -+ -+ // serdev currently only supports EVEN/ODD parity -+ switch (uart->parity) { -+ case ACPI_UART_PARITY_NONE: -+ status = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); -+ break; -+ case ACPI_UART_PARITY_EVEN: -+ status = serdev_device_set_parity(serdev, SERDEV_PARITY_EVEN); -+ break; -+ case ACPI_UART_PARITY_ODD: -+ status = serdev_device_set_parity(serdev, SERDEV_PARITY_ODD); -+ break; -+ default: -+ dev_warn(&serdev->dev, "unsupported parity (value: 0x%02x)\n", uart->parity); -+ break; -+ } -+ -+ if (status) { -+ dev_err(&serdev->dev, "failed to set parity (value: 0x%02x)\n", uart->parity); -+ return status; -+ } -+ -+ return AE_CTRL_TERMINATE; // we've found the resource and are done -+} -+ -+ -+static bool surfacegen5_idma_filter(struct dma_chan *chan, void *param) -+{ -+ // see dw8250_idma_filter -+ return param == chan->device->dev->parent; -+} -+ -+static int surfacegen5_ssh_check_dma(struct serdev_device *serdev) -+{ -+ struct device *dev = serdev->ctrl->dev.parent; -+ struct dma_chan *rx, *tx; -+ dma_cap_mask_t mask; -+ int status = 0; -+ -+ /* -+ * The EC UART requires DMA for proper communication. If we don't use DMA, -+ * we'll drop bytes when the system has high load, e.g. during boot. This -+ * causes some ugly behaviour, i.e. battery information (_BIX) messages -+ * failing frequently. We're making sure the required DMA channels are -+ * available here so serial8250_do_startup is able to grab them later -+ * instead of silently falling back to a non-DMA approach. -+ */ -+ -+ dma_cap_zero(mask); -+ dma_cap_set(DMA_SLAVE, mask); -+ -+ rx = dma_request_slave_channel_compat(mask, surfacegen5_idma_filter, dev->parent, dev, "rx"); -+ if (IS_ERR_OR_NULL(rx)) { -+ status = rx ? PTR_ERR(rx) : -EPROBE_DEFER; -+ if (status != -EPROBE_DEFER) { -+ dev_err(&serdev->dev, "sg5_dma: error requesting rx channel: %d\n", status); -+ } else { -+ dev_dbg(&serdev->dev, "sg5_dma: rx channel not found, deferring probe\n"); -+ } -+ goto check_dma_out; -+ } -+ -+ tx = dma_request_slave_channel_compat(mask, surfacegen5_idma_filter, dev->parent, dev, "tx"); -+ if (IS_ERR_OR_NULL(tx)) { -+ status = tx ? PTR_ERR(tx) : -EPROBE_DEFER; -+ if (status != -EPROBE_DEFER) { -+ dev_err(&serdev->dev, "sg5_dma: error requesting tx channel: %d\n", status); -+ } else { -+ dev_dbg(&serdev->dev, "sg5_dma: tx channel not found, deferring probe\n"); -+ } -+ goto check_dma_release_rx; -+ } -+ -+ dma_release_channel(tx); -+check_dma_release_rx: -+ dma_release_channel(rx); -+check_dma_out: -+ return status; -+} -+ -+ -+static int surfacegen5_ssh_suspend(struct device *dev) -+{ -+ struct surfacegen5_ec *ec; -+ int status = 0; -+ -+ dev_dbg(dev, "suspending\n"); -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (ec) { -+ status = surfacegen5_ssh_ec_suspend(ec); -+ if (status) { -+ dev_err(dev, "failed to suspend EC: %d\n", status); -+ } -+ -+ ec->state = SG5_EC_SUSPENDED; -+ surfacegen5_ec_release(ec); -+ } -+ -+ return status; -+} -+ -+static int surfacegen5_ssh_resume(struct device *dev) -+{ -+ struct surfacegen5_ec *ec; -+ int status = 0; -+ -+ dev_dbg(dev, "resuming\n"); -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (ec) { -+ ec->state = SG5_EC_INITIALIZED; -+ -+ status = surfacegen5_ssh_ec_resume(ec); -+ if (status) { -+ dev_err(dev, "failed to resume EC: %d\n", status); -+ } -+ -+ surfacegen5_ec_release(ec); -+ } -+ -+ return status; -+} -+ -+static SIMPLE_DEV_PM_OPS(surfacegen5_ssh_pm_ops, surfacegen5_ssh_suspend, surfacegen5_ssh_resume); -+ -+ -+static const struct serdev_device_ops surfacegen5_ssh_device_ops = { -+ .receive_buf = surfacegen5_ssh_receive_buf, -+ .write_wakeup = serdev_device_write_wakeup, -+}; -+ -+static int surfacegen5_acpi_ssh_probe(struct serdev_device *serdev) -+{ -+ struct surfacegen5_ec *ec; -+ struct workqueue_struct *event_queue_ack; -+ struct workqueue_struct *event_queue_evt; -+ u8 *write_buf; -+ u8 *read_buf; -+ u8 *eval_buf; -+ acpi_handle *ssh = ACPI_HANDLE(&serdev->dev); -+ acpi_status status; -+ -+ dev_dbg(&serdev->dev, "probing\n"); -+ -+ // ensure DMA is ready before we set up the device -+ status = surfacegen5_ssh_check_dma(serdev); -+ if (status) { -+ return status; -+ } -+ -+ // allocate buffers -+ write_buf = kzalloc(SG5_WRITE_BUF_LEN, GFP_KERNEL); -+ if (!write_buf) { -+ status = -ENOMEM; -+ goto err_probe_write_buf; -+ } -+ -+ read_buf = kzalloc(SG5_READ_BUF_LEN, GFP_KERNEL); -+ if (!read_buf) { -+ status = -ENOMEM; -+ goto err_probe_read_buf; -+ } -+ -+ eval_buf = kzalloc(SG5_EVAL_BUF_LEN, GFP_KERNEL); -+ if (!eval_buf) { -+ status = -ENOMEM; -+ goto err_probe_eval_buf; -+ } -+ -+ event_queue_ack = create_singlethread_workqueue("sg5_ackq"); -+ if (!event_queue_ack) { -+ status = -ENOMEM; -+ goto err_probe_ackq; -+ } -+ -+ event_queue_evt = create_workqueue("sg5_evtq"); -+ if (!event_queue_evt) { -+ status = -ENOMEM; -+ goto err_probe_evtq; -+ } -+ -+ // set up EC -+ ec = surfacegen5_ec_acquire(); -+ if (ec->state != SG5_EC_UNINITIALIZED) { -+ dev_err(&serdev->dev, "embedded controller already initialized\n"); -+ surfacegen5_ec_release(ec); -+ -+ status = -EBUSY; -+ goto err_probe_busy; -+ } -+ -+ ec->serdev = serdev; -+ ec->writer.data = write_buf; -+ ec->writer.ptr = write_buf; -+ -+ // initialize receiver -+ init_completion(&ec->receiver.signal); -+ kfifo_init(&ec->receiver.fifo, read_buf, SG5_READ_BUF_LEN); -+ ec->receiver.eval_buf.ptr = eval_buf; -+ ec->receiver.eval_buf.cap = SG5_EVAL_BUF_LEN; -+ ec->receiver.eval_buf.len = 0; -+ -+ // initialize event handling -+ ec->events.queue_ack = event_queue_ack; -+ ec->events.queue_evt = event_queue_evt; -+ -+ ec->state = SG5_EC_INITIALIZED; -+ -+ serdev_device_set_drvdata(serdev, ec); -+ -+ // ensure everything is properly set-up before we open the device -+ smp_mb(); -+ -+ serdev_device_set_client_ops(serdev, &surfacegen5_ssh_device_ops); -+ status = serdev_device_open(serdev); -+ if (status) { -+ goto err_probe_open; -+ } -+ -+ status = acpi_walk_resources(ssh, METHOD_NAME__CRS, -+ surfacegen5_ssh_setup_from_resource, serdev); -+ if (ACPI_FAILURE(status)) { -+ goto err_probe_devinit; -+ } -+ -+ status = surfacegen5_ssh_ec_resume(ec); -+ if (status) { -+ goto err_probe_devinit; -+ } -+ -+ status = surfacegen5_ssh_sysfs_register(&serdev->dev); -+ if (status) { -+ goto err_probe_devinit; -+ } -+ -+ surfacegen5_ec_release(ec); -+ -+ acpi_walk_dep_device_list(ssh); -+ -+ return 0; -+ -+err_probe_devinit: -+ serdev_device_close(serdev); -+err_probe_open: -+ ec->state = SG5_EC_UNINITIALIZED; -+ serdev_device_set_drvdata(serdev, NULL); -+ surfacegen5_ec_release(ec); -+err_probe_busy: -+ destroy_workqueue(event_queue_evt); -+err_probe_evtq: -+ destroy_workqueue(event_queue_ack); -+err_probe_ackq: -+ kfree(eval_buf); -+err_probe_eval_buf: -+ kfree(read_buf); -+err_probe_read_buf: -+ kfree(write_buf); -+err_probe_write_buf: -+ return status; -+} -+ -+static void surfacegen5_acpi_ssh_remove(struct serdev_device *serdev) -+{ -+ struct surfacegen5_ec *ec; -+ unsigned long flags; -+ int status; -+ -+ ec = surfacegen5_ec_acquire_init(); -+ if (!ec) { -+ return; -+ } -+ -+ surfacegen5_ssh_sysfs_unregister(&serdev->dev); -+ -+ // suspend EC and disable events -+ status = surfacegen5_ssh_ec_suspend(ec); -+ if (status) { -+ dev_err(&serdev->dev, "failed to suspend EC: %d\n", status); -+ } -+ -+ // make sure all events (received up to now) have been properly handled -+ flush_workqueue(ec->events.queue_ack); -+ flush_workqueue(ec->events.queue_evt); -+ -+ // remove event handlers -+ spin_lock_irqsave(&ec->events.lock, flags); -+ memset(ec->events.handler, 0, -+ sizeof(struct surfacegen5_ec_event_handler) -+ * SG5_NUM_EVENT_TYPES); -+ spin_unlock_irqrestore(&ec->events.lock, flags); -+ -+ // set device to deinitialized state -+ ec->state = SG5_EC_UNINITIALIZED; -+ ec->serdev = NULL; -+ -+ // ensure state and serdev get set before continuing -+ smp_mb(); -+ -+ /* -+ * Flush any event that has not been processed yet to ensure we're not going to -+ * use the serial device any more (e.g. for ACKing). -+ */ -+ flush_workqueue(ec->events.queue_ack); -+ flush_workqueue(ec->events.queue_evt); -+ -+ serdev_device_close(serdev); -+ -+ /* -+ * Only at this point, no new events can be received. Destroying the -+ * workqueue here flushes all remaining events. Those events will be -+ * silently ignored and neither ACKed nor any handler gets called. -+ */ -+ destroy_workqueue(ec->events.queue_ack); -+ destroy_workqueue(ec->events.queue_evt); -+ -+ // free writer -+ kfree(ec->writer.data); -+ ec->writer.data = NULL; -+ ec->writer.ptr = NULL; -+ -+ // free receiver -+ spin_lock_irqsave(&ec->receiver.lock, flags); -+ ec->receiver.state = SG5_RCV_DISCARD; -+ kfifo_free(&ec->receiver.fifo); -+ -+ kfree(ec->receiver.eval_buf.ptr); -+ ec->receiver.eval_buf.ptr = NULL; -+ ec->receiver.eval_buf.cap = 0; -+ ec->receiver.eval_buf.len = 0; -+ spin_unlock_irqrestore(&ec->receiver.lock, flags); -+ -+ serdev_device_set_drvdata(serdev, NULL); -+ surfacegen5_ec_release(ec); -+} -+ -+ -+static const struct acpi_device_id surfacegen5_acpi_ssh_match[] = { -+ { "MSHW0084", 0 }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_ssh_match); -+ -+struct serdev_device_driver surfacegen5_acpi_ssh = { -+ .probe = surfacegen5_acpi_ssh_probe, -+ .remove = surfacegen5_acpi_ssh_remove, -+ .driver = { -+ .name = "surfacegen5_acpi_ssh", -+ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_ssh_match), -+ .pm = &surfacegen5_ssh_pm_ops, -+ }, -+}; -+ -+inline int surfacegen5_acpi_ssh_register(void) -+{ -+ return serdev_device_driver_register(&surfacegen5_acpi_ssh); -+} -+ -+inline void surfacegen5_acpi_ssh_unregister(void) -+{ -+ serdev_device_driver_unregister(&surfacegen5_acpi_ssh); -+} -+ -+#else /* CONFIG_SURFACE_ACPI_SSH */ -+ -+inline int surfacegen5_acpi_ssh_register(void) -+{ -+ return 0; -+} -+ -+inline void surfacegen5_acpi_ssh_unregister(void) -+{ -+} -+ -+ -+#endif /* CONFIG_SURFACE_ACPI_SSH */ -+ -+ -+/************************************************************************* -+ * Surface Serial Hub Debug Device (private implementation) -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_SSH_DEBUG_DEVICE -+ -+static char sg5_ssh_debug_rqst_buf_sysfs[SURFACEGEN5_MAX_RQST_RESPONSE + 1] = { 0 }; -+static char sg5_ssh_debug_rqst_buf_pld[SURFACEGEN5_MAX_RQST_PAYLOAD] = { 0 }; -+static char sg5_ssh_debug_rqst_buf_res[SURFACEGEN5_MAX_RQST_RESPONSE] = { 0 }; -+ -+static ssize_t rqst_read(struct file *f, struct kobject *kobj, struct bin_attribute *attr, -+ char *buf, loff_t offs, size_t count) -+{ -+ if (offs < 0 || count + offs > SURFACEGEN5_MAX_RQST_RESPONSE) { -+ return -EINVAL; -+ } -+ -+ memcpy(buf, sg5_ssh_debug_rqst_buf_sysfs + offs, count); -+ return count; -+} -+ -+static ssize_t rqst_write(struct file *f, struct kobject *kobj, struct bin_attribute *attr, -+ char *buf, loff_t offs, size_t count) -+{ -+ struct surfacegen5_rqst rqst = {}; -+ struct surfacegen5_buf result = {}; -+ int status; -+ -+ // check basic write constriants -+ if (offs != 0 || count > SURFACEGEN5_MAX_RQST_PAYLOAD + 5) { -+ return -EINVAL; -+ } -+ -+ // payload length should be consistent with data provided -+ if (buf[4] + 5 != count) { -+ return -EINVAL; -+ } -+ -+ rqst.tc = buf[0]; -+ rqst.iid = buf[1]; -+ rqst.cid = buf[2]; -+ rqst.snc = buf[3]; -+ rqst.cdl = buf[4]; -+ rqst.pld = sg5_ssh_debug_rqst_buf_pld; -+ memcpy(sg5_ssh_debug_rqst_buf_pld, buf + 5, count - 5); -+ -+ result.cap = SURFACEGEN5_MAX_RQST_RESPONSE; -+ result.len = 0; -+ result.data = sg5_ssh_debug_rqst_buf_res; -+ -+ status = surfacegen5_ec_rqst(&rqst, &result); -+ if (status) { -+ return status; -+ } -+ -+ sg5_ssh_debug_rqst_buf_sysfs[0] = result.len; -+ memcpy(sg5_ssh_debug_rqst_buf_sysfs + 1, result.data, result.len); -+ memset(sg5_ssh_debug_rqst_buf_sysfs + result.len + 1, 0, -+ SURFACEGEN5_MAX_RQST_RESPONSE + 1 - result.len); -+ -+ return count; -+} -+ -+static const BIN_ATTR_RW(rqst, SURFACEGEN5_MAX_RQST_RESPONSE + 1); -+ -+ -+inline int surfacegen5_ssh_sysfs_register(struct device *dev) -+{ -+ return sysfs_create_bin_file(&dev->kobj, &bin_attr_rqst); -+} -+ -+inline void surfacegen5_ssh_sysfs_unregister(struct device *dev) -+{ -+ sysfs_remove_bin_file(&dev->kobj, &bin_attr_rqst); -+} -+ -+#elif defined(CONFIG_SURFACE_ACPI_SSH) -+ -+inline int surfacegen5_ssh_sysfs_register(struct device *dev) -+{ -+ return 0; -+} -+ -+inline void surfacegen5_ssh_sysfs_unregister(struct device *dev) -+{ -+} -+ -+#endif /* CONFIG_SURFACE_ACPI_SSH_DEBUG_DEVICE*/ -+ -+ -+/************************************************************************* -+ * Surface ACPI Notify driver -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_SAN -+ -+#define SG5_SAN_RQST_RETRY 5 -+ -+#define SG5_SAN_DSM_REVISION 0 -+#define SG5_SAN_DSM_FN_NOTIFY_SENSOR_TRIP_POINT 0x09 -+ -+static const guid_t SG5_SAN_DSM_UUID = -+ GUID_INIT(0x93b666c5, 0x70c6, 0x469f, 0xa2, 0x15, 0x3d, -+ 0x48, 0x7c, 0x91, 0xab, 0x3c); -+ -+#define SG5_EVENT_DELAY_POWER msecs_to_jiffies(5000) -+ -+#define SG5_EVENT_PWR_TC 0x02 -+#define SG5_EVENT_PWR_RQID 0x0002 -+#define SG5_EVENT_PWR_CID_HWCHANGE 0x15 -+#define SG5_EVENT_PWR_CID_CHARGING 0x16 -+#define SG5_EVENT_PWR_CID_ADAPTER 0x17 -+#define SG5_EVENT_PWR_CID_STATE 0x4f -+ -+#define SG5_EVENT_TEMP_TC 0x03 -+#define SG5_EVENT_TEMP_RQID 0x0003 -+#define SG5_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT 0x0b -+ -+#define SG5_SAN_RQST_TAG "surfacegen5_ec_rqst: " -+ -+#define SG5_QUIRK_BASE_STATE_DELAY 1000 -+ -+ -+struct surfacegen5_san_acpi_consumer { -+ char *path; -+ bool required; -+ u32 flags; -+}; -+ -+struct surfacegen5_san_opreg_context { -+ struct acpi_connection_info connection; -+ struct device *dev; -+}; -+ -+struct surfacegen5_san_consumers { -+ u32 num; -+ struct device_link **links; -+}; -+ -+struct surfacegen5_san_drvdata { -+ struct surfacegen5_san_opreg_context opreg_ctx; -+ struct surfacegen5_san_consumers consumers; -+ struct device_link *ec_link; -+}; -+ -+struct gsb_data_in { -+ u8 cv; -+} __packed; -+ -+struct gsb_data_rqsx { -+ u8 cv; // command value (should be 0x01 or 0x03) -+ u8 tc; // target controller -+ u8 tid; // expected to be 0x01, could be revision -+ u8 iid; // target sub-controller (e.g. primary vs. secondary battery) -+ u8 snc; // expect-response-flag -+ u8 cid; // command ID -+ u8 cdl; // payload length -+ u8 _pad; // padding -+ u8 pld[0]; // payload -+} __packed; -+ -+struct gsb_data_etwl { -+ u8 cv; // command value (should be 0x02) -+ u8 etw3; // ? -+ u8 etw4; // ? -+ u8 msg[0]; // error message (ASCIIZ) -+} __packed; -+ -+struct gsb_data_out { -+ u8 status; // _SSH communication status -+ u8 len; // _SSH payload length -+ u8 pld[0]; // _SSH payload -+} __packed; -+ -+union gsb_buffer_data { -+ struct gsb_data_in in; // common input -+ struct gsb_data_rqsx rqsx; // RQSX input -+ struct gsb_data_etwl etwl; // ETWL input -+ struct gsb_data_out out; // output -+}; -+ -+struct gsb_buffer { -+ u8 status; // GSB AttribRawProcess status -+ u8 len; // GSB AttribRawProcess length -+ union gsb_buffer_data data; -+} __packed; -+ -+ -+enum surfacegen5_pwr_event { -+ SURFACEGEN5_PWR_EVENT_BAT1_STAT = 0x03, -+ SURFACEGEN5_PWR_EVENT_BAT1_INFO = 0x04, -+ SURFACEGEN5_PWR_EVENT_ADP1_STAT = 0x05, -+ SURFACEGEN5_PWR_EVENT_ADP1_INFO = 0x06, -+ SURFACEGEN5_PWR_EVENT_BAT2_STAT = 0x07, -+ SURFACEGEN5_PWR_EVENT_BAT2_INFO = 0x08, -+}; -+ -+ -+static int surfacegen5_acpi_notify_power_event(struct device *dev, enum surfacegen5_pwr_event event) -+{ -+ acpi_handle san = ACPI_HANDLE(dev); -+ union acpi_object *obj; -+ -+ obj = acpi_evaluate_dsm_typed(san, &SG5_SAN_DSM_UUID, SG5_SAN_DSM_REVISION, -+ (u8) event, NULL, ACPI_TYPE_BUFFER); -+ -+ if (IS_ERR_OR_NULL(obj)) { -+ return obj ? PTR_ERR(obj) : -ENXIO; -+ } -+ -+ if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) { -+ dev_err(dev, "got unexpected result from _DSM\n"); -+ return -EFAULT; -+ } -+ -+ ACPI_FREE(obj); -+ return 0; -+} -+ -+static int surfacegen5_acpi_notify_sensor_trip_point(struct device *dev, u8 iid) -+{ -+ acpi_handle san = ACPI_HANDLE(dev); -+ union acpi_object *obj; -+ union acpi_object param; -+ -+ param.type = ACPI_TYPE_INTEGER; -+ param.integer.value = iid; -+ -+ obj = acpi_evaluate_dsm_typed(san, &SG5_SAN_DSM_UUID, SG5_SAN_DSM_REVISION, -+ SG5_SAN_DSM_FN_NOTIFY_SENSOR_TRIP_POINT, -+ ¶m, ACPI_TYPE_BUFFER); -+ -+ if (IS_ERR_OR_NULL(obj)) { -+ return obj ? PTR_ERR(obj) : -ENXIO; -+ } -+ -+ if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) { -+ dev_err(dev, "got unexpected result from _DSM\n"); -+ return -EFAULT; -+ } -+ -+ ACPI_FREE(obj); -+ return 0; -+} -+ -+ -+inline static int surfacegen5_evt_power_adapter(struct device *dev, struct surfacegen5_event *event) -+{ -+ int status; -+ -+ status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_ADP1_STAT); -+ if (status) { -+ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); -+ return status; -+ } -+ -+ return 0; -+} -+ -+inline static int surfacegen5_evt_power_hwchange(struct device *dev, struct surfacegen5_event *event) -+{ -+ enum surfacegen5_pwr_event evcode; -+ int status; -+ -+ if (event->iid == 0x02) { -+ evcode = SURFACEGEN5_PWR_EVENT_BAT2_INFO; -+ } else { -+ evcode = SURFACEGEN5_PWR_EVENT_BAT1_INFO; -+ } -+ -+ status = surfacegen5_acpi_notify_power_event(dev, evcode); -+ if (status) { -+ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); -+ return status; -+ } -+ -+ return 0; -+} -+ -+inline static int surfacegen5_evt_power_state(struct device *dev, struct surfacegen5_event *event) -+{ -+ int status; -+ -+ status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_BAT1_STAT); -+ if (status) { -+ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); -+ return status; -+ } -+ -+ status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_BAT2_STAT); -+ if (status) { -+ dev_err(dev, "error handling power event (cid = %x)\n", event->cid); -+ return status; -+ } -+ -+ return 0; -+} -+ -+static unsigned long surfacegen5_evt_power_delay(struct surfacegen5_event *event, void *data) -+{ -+ switch (event->cid) { -+ case SG5_EVENT_PWR_CID_CHARGING: -+ case SG5_EVENT_PWR_CID_STATE: -+ return SG5_EVENT_DELAY_POWER; -+ -+ case SG5_EVENT_PWR_CID_ADAPTER: -+ case SG5_EVENT_PWR_CID_HWCHANGE: -+ default: -+ return 0; -+ } -+} -+ -+static int surfacegen5_evt_power(struct surfacegen5_event *event, void *data) -+{ -+ struct device *dev = (struct device *)data; -+ -+ switch (event->cid) { -+ case SG5_EVENT_PWR_CID_HWCHANGE: -+ return surfacegen5_evt_power_hwchange(dev, event); -+ -+ case SG5_EVENT_PWR_CID_ADAPTER: -+ return surfacegen5_evt_power_adapter(dev, event); -+ -+ case SG5_EVENT_PWR_CID_CHARGING: -+ case SG5_EVENT_PWR_CID_STATE: -+ return surfacegen5_evt_power_state(dev, event); -+ -+ default: -+ dev_warn(dev, "unhandled power event (cid = %x)\n", event->cid); -+ } -+ -+ return 0; -+} -+ -+ -+inline static int surfacegen5_evt_thermal_notify(struct device *dev, struct surfacegen5_event *event) -+{ -+ int status; -+ -+ status = surfacegen5_acpi_notify_sensor_trip_point(dev, event->iid); -+ if (status) { -+ dev_err(dev, "error handling thermal event (cid = %x)\n", event->cid); -+ return status; -+ } -+ -+ return 0; -+} -+ -+static int surfacegen5_evt_thermal(struct surfacegen5_event *event, void *data) -+{ -+ struct device *dev = (struct device *)data; -+ -+ switch (event->cid) { -+ case SG5_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT: -+ return surfacegen5_evt_thermal_notify(dev, event); -+ -+ default: -+ dev_warn(dev, "unhandled thermal event (cid = %x)\n", event->cid); -+ } -+ -+ return 0; -+} -+ -+ -+static struct gsb_data_rqsx *surfacegen5_san_validate_rqsx( -+ struct device *dev, const char *type, struct gsb_buffer *buffer) -+{ -+ struct gsb_data_rqsx *rqsx = &buffer->data.rqsx; -+ -+ if (buffer->len < 8) { -+ dev_err(dev, "invalid %s package (len = %d)\n", -+ type, buffer->len); -+ return NULL; -+ } -+ -+ if (rqsx->cdl != buffer->len - 8) { -+ dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n", -+ type, buffer->len, rqsx->cdl); -+ return NULL; -+ } -+ -+ if (rqsx->tid != 0x01) { -+ dev_warn(dev, "unsupported %s package (tid = 0x%02x)\n", -+ type, rqsx->tid); -+ return NULL; -+ } -+ -+ return rqsx; -+} -+ -+static acpi_status -+surfacegen5_san_etwl(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer) -+{ -+ struct gsb_data_etwl *etwl = &buffer->data.etwl; -+ -+ if (buffer->len < 3) { -+ dev_err(ctx->dev, "invalid ETWL package (len = %d)\n", buffer->len); -+ return AE_OK; -+ } -+ -+ dev_err(ctx->dev, "ETWL(0x%02x, 0x%02x): %.*s\n", -+ etwl->etw3, etwl->etw4, -+ buffer->len - 3, (char *)etwl->msg); -+ -+ // indicate success -+ buffer->status = 0x00; -+ buffer->len = 0x00; -+ -+ return AE_OK; -+} -+ -+static acpi_status -+surfacegen5_san_rqst(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer) -+{ -+ struct gsb_data_rqsx *gsb_rqst = surfacegen5_san_validate_rqsx(ctx->dev, "RQST", buffer); -+ struct surfacegen5_rqst rqst = {}; -+ struct surfacegen5_buf result = {}; -+ int status = 0; -+ int try; -+ -+ if (!gsb_rqst) { -+ return AE_OK; -+ } -+ -+ rqst.tc = gsb_rqst->tc; -+ rqst.iid = gsb_rqst->iid; -+ rqst.cid = gsb_rqst->cid; -+ rqst.snc = gsb_rqst->snc; -+ rqst.cdl = gsb_rqst->cdl; -+ rqst.pld = &gsb_rqst->pld[0]; -+ -+ result.cap = SURFACEGEN5_MAX_RQST_RESPONSE; -+ result.len = 0; -+ result.data = kzalloc(result.cap, GFP_KERNEL); -+ -+ if (!result.data) { -+ return AE_NO_MEMORY; -+ } -+ -+ for (try = 0; try < SG5_SAN_RQST_RETRY; try++) { -+ if (try) { -+ dev_warn(ctx->dev, SG5_SAN_RQST_TAG "IO error occured, trying again\n"); -+ } -+ -+ status = surfacegen5_ec_rqst(&rqst, &result); -+ if (status != -EIO) break; -+ } -+ -+ if (rqst.tc == 0x11 && rqst.cid == 0x0D && status == -EPERM) { -+ /* Base state quirk: -+ * The base state may be queried from ACPI when the EC is -+ * still suspended. In this case it will return '-EPERM'. -+ * Returning 0xff (unknown base status) here will suppress -+ * error messages and cause an immediate re-query of the -+ * state. Delay return to avoid spinning. -+ */ -+ -+ buffer->status = 0x00; -+ buffer->len = 0x03; -+ buffer->data.out.status = 0x00; -+ buffer->data.out.len = 0x01; -+ buffer->data.out.pld[0] = 0xFF; -+ msleep(SG5_QUIRK_BASE_STATE_DELAY); -+ -+ } else if (!status) { // success -+ buffer->status = 0x00; -+ buffer->len = result.len + 2; -+ buffer->data.out.status = 0x00; -+ buffer->data.out.len = result.len; -+ memcpy(&buffer->data.out.pld[0], result.data, result.len); -+ -+ } else { // failure -+ dev_err(ctx->dev, SG5_SAN_RQST_TAG "failed with error %d\n", status); -+ buffer->status = 0x00; -+ buffer->len = 0x02; -+ buffer->data.out.status = 0x01; // indicate _SSH error -+ buffer->data.out.len = 0x00; -+ } -+ -+ kfree(result.data); -+ -+ return AE_OK; -+} -+ -+static acpi_status -+surfacegen5_san_rqsg(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer) -+{ -+ struct gsb_data_rqsx *rqsg = surfacegen5_san_validate_rqsx(ctx->dev, "RQSG", buffer); -+ -+ if (!rqsg) { -+ return AE_OK; -+ } -+ -+ // TODO: RQSG handler -+ -+ dev_warn(ctx->dev, "unsupported request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", -+ rqsg->tc, rqsg->cid, rqsg->iid); -+ -+ return AE_OK; -+} -+ -+ -+static acpi_status -+surfacegen5_san_opreg_handler(u32 function, acpi_physical_address command, -+ u32 bits, u64 *value64, -+ void *opreg_context, void *region_context) -+{ -+ struct surfacegen5_san_opreg_context *context = opreg_context; -+ struct gsb_buffer *buffer = (struct gsb_buffer *)value64; -+ int accessor_type = (0xFFFF0000 & function) >> 16; -+ -+ if (command != 0) { -+ dev_warn(context->dev, "unsupported command: 0x%02llx\n", command); -+ return AE_OK; -+ } -+ -+ if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) { -+ dev_err(context->dev, "invalid access type: 0x%02x\n", accessor_type); -+ return AE_OK; -+ } -+ -+ // buffer must have at least contain the command-value -+ if (buffer->len == 0) { -+ dev_err(context->dev, "request-package too small\n"); -+ return AE_OK; -+ } -+ -+ switch (buffer->data.in.cv) { -+ case 0x01: return surfacegen5_san_rqst(context, buffer); -+ case 0x02: return surfacegen5_san_etwl(context, buffer); -+ case 0x03: return surfacegen5_san_rqsg(context, buffer); -+ } -+ -+ dev_warn(context->dev, "unsupported SAN0 request (cv: 0x%02x)\n", buffer->data.in.cv); -+ return AE_OK; -+} -+ -+static int surfacegen5_san_enable_events(struct device *dev) -+{ -+ int status; -+ -+ status = surfacegen5_ec_set_delayed_event_handler( -+ SG5_EVENT_PWR_RQID, surfacegen5_evt_power, -+ surfacegen5_evt_power_delay, dev); -+ if (status) { -+ goto err_event_handler_power; -+ } -+ -+ status = surfacegen5_ec_set_event_handler( -+ SG5_EVENT_TEMP_RQID, surfacegen5_evt_thermal, -+ dev); -+ if (status) { -+ goto err_event_handler_thermal; -+ } -+ -+ status = surfacegen5_ec_enable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID); -+ if (status) { -+ goto err_event_source_power; -+ } -+ -+ status = surfacegen5_ec_enable_event_source(SG5_EVENT_TEMP_TC, 0x01, SG5_EVENT_TEMP_RQID); -+ if (status) { -+ goto err_event_source_thermal; -+ } -+ -+ return 0; -+ -+err_event_source_thermal: -+ surfacegen5_ec_disable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID); -+err_event_source_power: -+ surfacegen5_ec_remove_event_handler(SG5_EVENT_TEMP_RQID); -+err_event_handler_thermal: -+ surfacegen5_ec_remove_event_handler(SG5_EVENT_PWR_RQID); -+err_event_handler_power: -+ return status; -+} -+ -+static void surfacegen5_san_disable_events(void) -+{ -+ surfacegen5_ec_disable_event_source(SG5_EVENT_TEMP_TC, 0x01, SG5_EVENT_TEMP_RQID); -+ surfacegen5_ec_disable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID); -+ surfacegen5_ec_remove_event_handler(SG5_EVENT_TEMP_RQID); -+ surfacegen5_ec_remove_event_handler(SG5_EVENT_PWR_RQID); -+} -+ -+ -+static int surfacegen5_san_consumers_link(struct platform_device *pdev, -+ const struct surfacegen5_san_acpi_consumer *cons, -+ struct surfacegen5_san_consumers *out) -+{ -+ const struct surfacegen5_san_acpi_consumer *con; -+ struct device_link **links, **link; -+ struct acpi_device *adev; -+ acpi_handle handle; -+ u32 max_links = 0; -+ int status; -+ -+ if (!cons) { -+ return 0; -+ } -+ -+ // count links -+ for (con = cons; con->path; ++con) { -+ max_links += 1; -+ } -+ -+ // allocate -+ links = kzalloc(max_links * sizeof(struct device_link *), GFP_KERNEL); -+ link = &links[0]; -+ -+ if (!links) { -+ return -ENOMEM; -+ } -+ -+ // create links -+ for (con = cons; con->path; ++con) { -+ status = acpi_get_handle(NULL, con->path, &handle); -+ if (status) { -+ if (con->required || status != AE_NOT_FOUND) { -+ status = -ENXIO; -+ goto consumers_link_cleanup; -+ } else { -+ continue; -+ } -+ } -+ -+ status = acpi_bus_get_device(handle, &adev); -+ if (status) { -+ goto consumers_link_cleanup; -+ } -+ -+ *link = device_link_add(&adev->dev, &pdev->dev, con->flags); -+ if (!(*link)) { -+ status = -EFAULT; -+ goto consumers_link_cleanup; -+ } -+ -+ link += 1; -+ } -+ -+ out->num = link - links; -+ out->links = links; -+ -+ return 0; -+ -+consumers_link_cleanup: -+ for (link = link - 1; link >= links; --link) { -+ device_link_del(*link); -+ } -+ -+ return status; -+} -+ -+static void surfacegen5_san_consumers_unlink(struct surfacegen5_san_consumers *consumers) { -+ u32 i; -+ -+ if (!consumers) { -+ return; -+ } -+ -+ for (i = 0; i < consumers->num; ++i) { -+ device_link_del(consumers->links[i]); -+ } -+ -+ kfree(consumers->links); -+ -+ consumers->num = 0; -+ consumers->links = NULL; -+} -+ -+static int surfacegen5_acpi_san_probe(struct platform_device *pdev) -+{ -+ const struct surfacegen5_san_acpi_consumer *cons; -+ struct surfacegen5_san_drvdata *drvdata; -+ struct device_link *ec_link; -+ acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node -+ int status; -+ -+ drvdata = kzalloc(sizeof(struct surfacegen5_san_drvdata), GFP_KERNEL); -+ if (!drvdata) { -+ return -ENOMEM; -+ } -+ -+ /* -+ * Defer probe if the _SSH driver has not set up the controller yet. This -+ * makes sure we do not fail any initial requests (e.g. _STA request without -+ * which the battery does not get set up correctly). Otherwise register as -+ * consumer to set up a device_link. -+ */ -+ ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME); -+ if (IS_ERR_OR_NULL(ec_link)) { -+ if (PTR_ERR(ec_link) == -ENXIO) { -+ status = -EPROBE_DEFER; -+ } else { -+ status = -EFAULT; -+ } -+ -+ goto err_probe_ec_link; -+ } -+ -+ drvdata->ec_link = ec_link; -+ drvdata->opreg_ctx.dev = &pdev->dev; -+ -+ cons = acpi_device_get_match_data(&pdev->dev); -+ status = surfacegen5_san_consumers_link(pdev, cons, &drvdata->consumers); -+ if (status) { -+ goto err_probe_consumers; -+ } -+ -+ platform_set_drvdata(pdev, drvdata); -+ -+ status = acpi_install_address_space_handler(san, -+ ACPI_ADR_SPACE_GSBUS, -+ &surfacegen5_san_opreg_handler, -+ NULL, &drvdata->opreg_ctx); -+ -+ if (ACPI_FAILURE(status)) { -+ status = -ENODEV; -+ goto err_probe_install_handler; -+ } -+ -+ status = surfacegen5_san_enable_events(&pdev->dev); -+ if (status) { -+ goto err_probe_enable_events; -+ } -+ -+ acpi_walk_dep_device_list(san); -+ return 0; -+ -+err_probe_enable_events: -+ acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &surfacegen5_san_opreg_handler); -+err_probe_install_handler: -+ platform_set_drvdata(san, NULL); -+ surfacegen5_san_consumers_unlink(&drvdata->consumers); -+err_probe_consumers: -+ surfacegen5_ec_consumer_remove(drvdata->ec_link); -+err_probe_ec_link: -+ kfree(drvdata); -+ return status; -+} -+ -+static int surfacegen5_acpi_san_remove(struct platform_device *pdev) -+{ -+ struct surfacegen5_san_drvdata *drvdata = platform_get_drvdata(pdev); -+ acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node -+ acpi_status status = AE_OK; -+ -+ acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &surfacegen5_san_opreg_handler); -+ surfacegen5_san_disable_events(); -+ -+ surfacegen5_san_consumers_unlink(&drvdata->consumers); -+ surfacegen5_ec_consumer_remove(drvdata->ec_link); -+ kfree(drvdata); -+ -+ platform_set_drvdata(pdev, NULL); -+ return status; -+} -+ -+ -+static const struct surfacegen5_san_acpi_consumer surfacegen5_mshw0091_consumers[] = { -+ { "\\_SB.SRTC", true, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, -+ { "\\ADP1", true, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, -+ { "\\_SB.BAT1", true, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, -+ { "\\_SB.BAT2", false, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS }, -+ { }, -+}; -+ -+static const struct acpi_device_id surfacegen5_acpi_san_match[] = { -+ { "MSHW0091", (long unsigned int) surfacegen5_mshw0091_consumers }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_san_match); -+ -+struct platform_driver surfacegen5_acpi_san = { -+ .probe = surfacegen5_acpi_san_probe, -+ .remove = surfacegen5_acpi_san_remove, -+ .driver = { -+ .name = "surfacegen5_acpi_san", -+ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_san_match), -+ }, -+}; -+ -+ -+inline int surfacegen5_acpi_san_register(void) -+{ -+ return platform_driver_register(&surfacegen5_acpi_san); -+} -+ -+inline void surfacegen5_acpi_san_unregister(void) -+{ -+ platform_driver_unregister(&surfacegen5_acpi_san); -+} -+ -+#else /* CONFIG_SURFACE_ACPI_SAN */ -+ -+inline int surfacegen5_acpi_san_register(void) -+{ -+ return 0; -+} -+ -+inline void surfacegen5_acpi_san_unregister(void) -+{ -+} -+ -+#endif /* CONFIG_SURFACE_ACPI_SAN */ -+ -+ -+/************************************************************************* -+ * Virtual HID Framework driver -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_VHF -+ -+#define SG5_VHF_INPUT_NAME "Microsoft Virtual HID Framework Device" -+ -+/* -+ * Request ID for VHF events. This value is based on the output of the Surface -+ * EC and should not be changed. -+ */ -+#define SG5_VHF_RQID 0x0001 -+ -+ -+struct surfacegen5_vhf_evtctx { -+ struct device *dev; -+ struct hid_device *hid; -+}; -+ -+struct surfacegen5_vhf_drvdata { -+ struct device_link *ec_link; -+ struct surfacegen5_vhf_evtctx event_ctx; -+}; -+ -+ -+/* -+ * These report descriptors have been extracted from a Surface Book 2. -+ * They seems to be similar enough to be usable on the Surface Laptop. -+ */ -+static const u8 vhf_hid_desc[] = { -+ // keyboard descriptor (event command ID 0x03) -+ 0x05, 0x01, /* Usage Page (Desktop), */ -+ 0x09, 0x06, /* Usage (Keyboard), */ -+ 0xA1, 0x01, /* Collection (Application), */ -+ 0x85, 0x01, /* Report ID (1), */ -+ 0x15, 0x00, /* Logical Minimum (0), */ -+ 0x25, 0x01, /* Logical Maximum (1), */ -+ 0x75, 0x01, /* Report Size (1), */ -+ 0x95, 0x08, /* Report Count (8), */ -+ 0x05, 0x07, /* Usage Page (Keyboard), */ -+ 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ -+ 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ -+ 0x81, 0x02, /* Input (Variable), */ -+ 0x75, 0x08, /* Report Size (8), */ -+ 0x95, 0x0A, /* Report Count (10), */ -+ 0x19, 0x00, /* Usage Minimum (None), */ -+ 0x29, 0x91, /* Usage Maximum (KB LANG2), */ -+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ -+ 0x81, 0x00, /* Input, */ -+ 0x05, 0x0C, /* Usage Page (Consumer), */ -+ 0x0A, 0xC0, 0x02, /* Usage (02C0h), */ -+ 0xA1, 0x02, /* Collection (Logical), */ -+ 0x1A, 0xC1, 0x02, /* Usage Minimum (02C1h), */ -+ 0x2A, 0xC6, 0x02, /* Usage Maximum (02C6h), */ -+ 0x95, 0x06, /* Report Count (6), */ -+ 0xB1, 0x03, /* Feature (Constant, Variable), */ -+ 0xC0, /* End Collection, */ -+ 0x05, 0x08, /* Usage Page (LED), */ -+ 0x19, 0x01, /* Usage Minimum (01h), */ -+ 0x29, 0x03, /* Usage Maximum (03h), */ -+ 0x75, 0x01, /* Report Size (1), */ -+ 0x95, 0x03, /* Report Count (3), */ -+ 0x25, 0x01, /* Logical Maximum (1), */ -+ 0x91, 0x02, /* Output (Variable), */ -+ 0x95, 0x05, /* Report Count (5), */ -+ 0x91, 0x01, /* Output (Constant), */ -+ 0xC0, /* End Collection, */ -+ -+ // media key descriptor (event command ID 0x04) -+ 0x05, 0x0C, /* Usage Page (Consumer), */ -+ 0x09, 0x01, /* Usage (Consumer Control), */ -+ 0xA1, 0x01, /* Collection (Application), */ -+ 0x85, 0x03, /* Report ID (3), */ -+ 0x75, 0x10, /* Report Size (16), */ -+ 0x15, 0x00, /* Logical Minimum (0), */ -+ 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ -+ 0x19, 0x00, /* Usage Minimum (00h), */ -+ 0x2A, 0xFF, 0x03, /* Usage Maximum (03FFh), */ -+ 0x81, 0x00, /* Input, */ -+ 0xC0, /* End Collection, */ -+}; -+ -+ -+static int vhf_hid_start(struct hid_device *hid) -+{ -+ hid_dbg(hid, "%s\n", __func__); -+ return 0; -+} -+ -+static void vhf_hid_stop(struct hid_device *hid) -+{ -+ hid_dbg(hid, "%s\n", __func__); -+} -+ -+static int vhf_hid_open(struct hid_device *hid) -+{ -+ hid_dbg(hid, "%s\n", __func__); -+ return 0; -+} -+ -+static void vhf_hid_close(struct hid_device *hid) -+{ -+ hid_dbg(hid, "%s\n", __func__); -+} -+ -+static int vhf_hid_parse(struct hid_device *hid) -+{ -+ return hid_parse_report(hid, (u8 *)vhf_hid_desc, ARRAY_SIZE(vhf_hid_desc)); -+} -+ -+static int vhf_hid_raw_request(struct hid_device *hid, unsigned char reportnum, -+ u8 *buf, size_t len, unsigned char rtype, -+ int reqtype) -+{ -+ hid_dbg(hid, "%s\n", __func__); -+ return 0; -+} -+ -+static int vhf_hid_output_report(struct hid_device *hid, u8 *buf, size_t len) -+{ -+ hid_dbg(hid, "%s\n", __func__); -+ print_hex_dump_debug("report:", DUMP_PREFIX_OFFSET, 16, 1, buf, len, false); -+ -+ return len; -+} -+ -+static struct hid_ll_driver vhf_hid_ll_driver = { -+ .start = vhf_hid_start, -+ .stop = vhf_hid_stop, -+ .open = vhf_hid_open, -+ .close = vhf_hid_close, -+ .parse = vhf_hid_parse, -+ .raw_request = vhf_hid_raw_request, -+ .output_report = vhf_hid_output_report, -+}; -+ -+ -+static struct hid_device *surfacegen5_vhf_create_hid_device(struct platform_device *pdev) -+{ -+ struct hid_device *hid; -+ -+ hid = hid_allocate_device(); -+ if (IS_ERR(hid)) { -+ return hid; -+ } -+ -+ hid->dev.parent = &pdev->dev; -+ -+ hid->bus = BUS_VIRTUAL; -+ hid->vendor = USB_VENDOR_ID_MICROSOFT; -+ hid->product = USB_DEVICE_ID_MS_VHF; -+ -+ hid->ll_driver = &vhf_hid_ll_driver; -+ -+ sprintf(hid->name, "%s", SG5_VHF_INPUT_NAME); -+ -+ return hid; -+} -+ -+static int surfacegen5_vhf_event_handler(struct surfacegen5_event *event, void *data) -+{ -+ struct surfacegen5_vhf_evtctx *ctx = (struct surfacegen5_vhf_evtctx *)data; -+ -+ if (event->tc == 0x08 && (event->cid == 0x03 || event->cid == 0x04)) { -+ return hid_input_report(ctx->hid, HID_INPUT_REPORT, event->pld, event->len, 1); -+ } -+ -+ dev_warn(ctx->dev, "unsupported event (tc = %d, cid = %d)\n", event->tc, event->cid); -+ return 0; -+} -+ -+static unsigned long surfacegen5_vhf_event_delay(struct surfacegen5_event *event, void *data) -+{ -+ // high priority immediate execution for keyboard events -+ if (event->tc == 0x08 && (event->cid == 0x03 || event->cid == 0x04)) { -+ return SURFACEGEN5_EVENT_IMMEDIATE; -+ } -+ -+ return 0; -+} -+ -+static int surfacegen5_acpi_vhf_probe(struct platform_device *pdev) -+{ -+ struct surfacegen5_vhf_drvdata *drvdata; -+ struct device_link *ec_link; -+ struct hid_device *hid; -+ int status; -+ -+ drvdata = kzalloc(sizeof(struct surfacegen5_vhf_drvdata), GFP_KERNEL); -+ if (!drvdata) { -+ return -ENOMEM; -+ } -+ -+ // add device link to EC -+ ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME); -+ if (IS_ERR_OR_NULL(ec_link)) { -+ if (PTR_ERR(ec_link) == -ENXIO) { -+ status = -EPROBE_DEFER; -+ } else { -+ status = -EFAULT; -+ } -+ -+ goto err_probe_ec_link; -+ } -+ -+ hid = surfacegen5_vhf_create_hid_device(pdev); -+ if (IS_ERR(hid)) { -+ status = PTR_ERR(hid); -+ goto err_probe_hid; -+ } -+ -+ status = hid_add_device(hid); -+ if (status) { -+ goto err_add_hid; -+ } -+ -+ drvdata->ec_link = ec_link; -+ drvdata->event_ctx.dev = &pdev->dev; -+ drvdata->event_ctx.hid = hid; -+ -+ platform_set_drvdata(pdev, drvdata); -+ -+ /* -+ * Set event hanlder for VHF events. They seem to be enabled by -+ * default, thus there should be no need to explicitly enable them. -+ */ -+ status = surfacegen5_ec_set_delayed_event_handler( -+ SG5_VHF_RQID, -+ surfacegen5_vhf_event_handler, -+ surfacegen5_vhf_event_delay, -+ &drvdata->event_ctx); -+ if (status) { -+ goto err_add_hid; -+ } -+ -+ return 0; -+ -+err_add_hid: -+ hid_destroy_device(hid); -+ platform_set_drvdata(pdev, NULL); -+err_probe_hid: -+ surfacegen5_ec_consumer_remove(drvdata->ec_link); -+err_probe_ec_link: -+ kfree(drvdata); -+ return status; -+} -+ -+static int surfacegen5_acpi_vhf_remove(struct platform_device *pdev) -+{ -+ struct surfacegen5_vhf_drvdata *drvdata = platform_get_drvdata(pdev); -+ -+ surfacegen5_ec_remove_event_handler(SG5_VHF_RQID); -+ -+ hid_destroy_device(drvdata->event_ctx.hid); -+ surfacegen5_ec_consumer_remove(drvdata->ec_link); -+ kfree(drvdata); -+ -+ platform_set_drvdata(pdev, NULL); -+ return 0; -+} -+ -+ -+static const struct acpi_device_id surfacegen5_acpi_vhf_match[] = { -+ { "MSHW0096" }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_vhf_match); -+ -+struct platform_driver surfacegen5_acpi_vhf = { -+ .probe = surfacegen5_acpi_vhf_probe, -+ .remove = surfacegen5_acpi_vhf_remove, -+ .driver = { -+ .name = "surfacegen5_acpi_vhf", -+ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_vhf_match), -+ }, -+}; -+ -+ -+inline int surfacegen5_acpi_vhf_register(void) -+{ -+ return platform_driver_register(&surfacegen5_acpi_vhf); -+} -+ -+inline void surfacegen5_acpi_vhf_unregister(void) -+{ -+ platform_driver_unregister(&surfacegen5_acpi_vhf); -+} -+ -+#else /* CONFIG_SURFACE_ACPI_VHF */ -+ -+inline int surfacegen5_acpi_vhf_register(void) -+{ -+ return 0; -+} -+ -+inline void surfacegen5_acpi_vhf_unregister(void) -+{ -+} -+ -+#endif /* CONFIG_SURFACE_ACPI_VHF */ -+ -+ -+/************************************************************************* -+ * Detachment System Driver (DTX) -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_DTX -+ -+#define SG5_DTX_INPUT_NAME "Microsoft Surface Base 2 Integration Device" -+ -+#define DTX_CMD_LATCH_LOCK _IO(0x11, 0x01) -+#define DTX_CMD_LATCH_UNLOCK _IO(0x11, 0x02) -+#define DTX_CMD_LATCH_REQUEST _IO(0x11, 0x03) -+#define DTX_CMD_LATCH_OPEN _IO(0x11, 0x04) -+#define DTX_CMD_GET_OPMODE _IOR(0x11, 0x05, int) -+ -+#define SG5_RQST_DTX_TC 0x11 -+#define SG5_RQST_DTX_CID_LATCH_LOCK 0x06 -+#define SG5_RQST_DTX_CID_LATCH_UNLOCK 0x07 -+#define SG5_RQST_DTX_CID_LATCH_REQUEST 0x08 -+#define SG5_RQST_DTX_CID_LATCH_OPEN 0x09 -+#define SG5_RQST_DTX_CID_GET_OPMODE 0x0D -+ -+#define SG5_EVENT_DTX_TC 0x11 -+#define SG5_EVENT_DTX_RQID 0x0011 -+#define SG5_EVENT_DTX_CID_CONNECTION 0x0c -+#define SG5_EVENT_DTX_CID_BUTTON 0x0e -+#define SG5_EVENT_DTX_CID_ERROR 0x0f -+#define SG5_EVENT_DTX_CID_LATCH_STATUS 0x11 -+ -+#define DTX_OPMODE_TABLET 0x00 -+#define DTX_OPMODE_LAPTOP 0x01 -+#define DTX_OPMODE_STUDIO 0x02 -+ -+#define DTX_LATCH_CLOSED 0x00 -+#define DTX_LATCH_OPENED 0x01 -+ -+// Warning: This must always be a power of 2! -+#define SURFACE_DTX_CLIENT_BUF_SIZE 16 -+ -+#define SG5_DTX_CONNECT_OPMODE_DELAY 1000 -+ -+#define DTX_ERR KERN_ERR "surfacegen5_acpi_dtx: " -+#define DTX_WARN KERN_WARNING "surfacegen5_acpi_dtx: " -+ -+ -+struct surface_dtx_event { -+ u8 type; -+ u8 code; -+ u8 arg0; -+ u8 arg1; -+} __packed; -+ -+struct surface_dtx_dev { -+ wait_queue_head_t waitq; -+ struct miscdevice mdev; -+ spinlock_t client_lock; -+ struct list_head client_list; -+ struct mutex mutex; -+ bool active; -+ struct device_link *ec_link; -+ spinlock_t input_lock; -+ struct input_dev *input_dev; -+}; -+ -+struct surface_dtx_client { -+ struct list_head node; -+ struct surface_dtx_dev *ddev; -+ struct fasync_struct *fasync; -+ spinlock_t buffer_lock; -+ unsigned int buffer_head; -+ unsigned int buffer_tail; -+ struct surface_dtx_event buffer[SURFACE_DTX_CLIENT_BUF_SIZE]; -+}; -+ -+ -+static struct surface_dtx_dev surface_dtx_dev; -+ -+ -+static int sg5_ec_query_opmpde(void) -+{ -+ u8 result_buf[1]; -+ int status; -+ -+ struct surfacegen5_rqst rqst = { -+ .tc = SG5_RQST_DTX_TC, -+ .iid = 0, -+ .cid = SG5_RQST_DTX_CID_GET_OPMODE, -+ .snc = 1, -+ .cdl = 0, -+ .pld = NULL, -+ }; -+ -+ struct surfacegen5_buf result = { -+ .cap = 1, -+ .len = 0, -+ .data = result_buf, -+ }; -+ -+ status = surfacegen5_ec_rqst(&rqst, &result); -+ if (status) { -+ return status; -+ } -+ -+ if (result.len != 1) { -+ return -EFAULT; -+ } -+ -+ return result.data[0]; -+} -+ -+ -+static int dtx_cmd_simple(u8 cid) -+{ -+ struct surfacegen5_rqst rqst = { -+ .tc = SG5_RQST_DTX_TC, -+ .iid = 0, -+ .cid = cid, -+ .snc = 0, -+ .cdl = 0, -+ .pld = NULL, -+ }; -+ -+ return surfacegen5_ec_rqst(&rqst, NULL); -+} -+ -+static int dtx_cmd_get_opmode(int __user *buf) -+{ -+ int opmode = sg5_ec_query_opmpde(); -+ if (opmode < 0) { -+ return opmode; -+ } -+ -+ if (put_user(opmode, buf)) { -+ return -EACCES; -+ } -+ -+ return 0; -+} -+ -+ -+static int surface_dtx_open(struct inode *inode, struct file *file) -+{ -+ struct surface_dtx_dev *ddev = container_of(file->private_data, struct surface_dtx_dev, mdev); -+ struct surface_dtx_client *client; -+ -+ // initialize client -+ client = kzalloc(sizeof(struct surface_dtx_client), GFP_KERNEL); -+ if (!client) { -+ return -ENOMEM; -+ } -+ -+ spin_lock_init(&client->buffer_lock); -+ client->buffer_head = 0; -+ client->buffer_tail = 0; -+ client->ddev = ddev; -+ -+ // attach client -+ spin_lock(&ddev->client_lock); -+ list_add_tail_rcu(&client->node, &ddev->client_list); -+ spin_unlock(&ddev->client_lock); -+ -+ file->private_data = client; -+ nonseekable_open(inode, file); -+ -+ return 0; -+} -+ -+static int surface_dtx_release(struct inode *inode, struct file *file) -+{ -+ struct surface_dtx_client *client = file->private_data; -+ -+ // detach client -+ spin_lock(&client->ddev->client_lock); -+ list_del_rcu(&client->node); -+ spin_unlock(&client->ddev->client_lock); -+ synchronize_rcu(); -+ -+ kfree(client); -+ file->private_data = NULL; -+ -+ return 0; -+} -+ -+static ssize_t surface_dtx_read(struct file *file, char __user *buf, size_t count, loff_t *offs) -+{ -+ struct surface_dtx_client *client = file->private_data; -+ struct surface_dtx_dev *ddev = client->ddev; -+ struct surface_dtx_event event; -+ size_t read = 0; -+ int status = 0; -+ -+ if (count != 0 && count < sizeof(struct surface_dtx_event)) { -+ return -EINVAL; -+ } -+ -+ if (!ddev->active) { -+ return -ENODEV; -+ } -+ -+ // check availability -+ if (client->buffer_head == client->buffer_tail){ -+ if (file->f_flags & O_NONBLOCK) { -+ return -EAGAIN; -+ } -+ -+ status = wait_event_interruptible(ddev->waitq, -+ client->buffer_head != client->buffer_tail || -+ !ddev->active); -+ if (status) { -+ return status; -+ } -+ -+ if (!ddev->active) { -+ return -ENODEV; -+ } -+ } -+ -+ // copy events one by one -+ while (read + sizeof(struct surface_dtx_event) <= count) { -+ spin_lock_irq(&client->buffer_lock); -+ -+ if(client->buffer_head == client->buffer_tail) { -+ spin_unlock_irq(&client->buffer_lock); -+ break; -+ } -+ -+ // get one event -+ event = client->buffer[client->buffer_tail]; -+ client->buffer_tail = (client->buffer_tail + 1) & (SURFACE_DTX_CLIENT_BUF_SIZE - 1); -+ spin_unlock_irq(&client->buffer_lock); -+ -+ // copy to userspace -+ if(copy_to_user(buf, &event, sizeof(struct surface_dtx_event))) { -+ return -EFAULT; -+ } -+ -+ read += sizeof(struct surface_dtx_event); -+ } -+ -+ return read; -+} -+ -+static __poll_t surface_dtx_poll(struct file *file, struct poll_table_struct *pt) -+{ -+ struct surface_dtx_client *client = file->private_data; -+ int mask; -+ -+ poll_wait(file, &client->ddev->waitq, pt); -+ -+ if (client->ddev->active) { -+ mask = EPOLLOUT | EPOLLWRNORM; -+ } else { -+ mask = EPOLLHUP | EPOLLERR; -+ } -+ -+ if (client->buffer_head != client->buffer_tail) { -+ mask |= EPOLLIN | EPOLLRDNORM; -+ } -+ -+ return mask; -+} -+ -+static int surface_dtx_fasync(int fd, struct file *file, int on) -+{ -+ struct surface_dtx_client *client = file->private_data; -+ -+ return fasync_helper(fd, file, on, &client->fasync); -+} -+ -+static long surface_dtx_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ struct surface_dtx_client *client = file->private_data; -+ struct surface_dtx_dev *ddev = client->ddev; -+ int status; -+ -+ status = mutex_lock_interruptible(&ddev->mutex); -+ if (status) { -+ return status; -+ } -+ -+ if (!ddev->active) { -+ mutex_unlock(&ddev->mutex); -+ return -ENODEV; -+ } -+ -+ switch (cmd) { -+ case DTX_CMD_LATCH_LOCK: -+ status = dtx_cmd_simple(SG5_RQST_DTX_CID_LATCH_LOCK); -+ break; -+ -+ case DTX_CMD_LATCH_UNLOCK: -+ status = dtx_cmd_simple(SG5_RQST_DTX_CID_LATCH_UNLOCK); -+ break; -+ -+ case DTX_CMD_LATCH_REQUEST: -+ status = dtx_cmd_simple(SG5_RQST_DTX_CID_LATCH_REQUEST); -+ break; -+ -+ case DTX_CMD_LATCH_OPEN: -+ status = dtx_cmd_simple(SG5_RQST_DTX_CID_LATCH_OPEN); -+ break; -+ -+ case DTX_CMD_GET_OPMODE: -+ status = dtx_cmd_get_opmode((int __user *)arg); -+ break; -+ -+ default: -+ status = -EINVAL; -+ break; -+ } -+ -+ mutex_unlock(&ddev->mutex); -+ return status; -+} -+ -+static const struct file_operations surface_dtx_fops = { -+ .owner = THIS_MODULE, -+ .open = surface_dtx_open, -+ .release = surface_dtx_release, -+ .read = surface_dtx_read, -+ .poll = surface_dtx_poll, -+ .fasync = surface_dtx_fasync, -+ .unlocked_ioctl = surface_dtx_ioctl, -+ .llseek = no_llseek, -+}; -+ -+static struct surface_dtx_dev surface_dtx_dev = { -+ .mdev = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "surface_dtx", -+ .fops = &surface_dtx_fops, -+ }, -+ .client_lock = __SPIN_LOCK_UNLOCKED(), -+ .input_lock = __SPIN_LOCK_UNLOCKED(), -+ .mutex = __MUTEX_INITIALIZER(surface_dtx_dev.mutex), -+ .active = false, -+}; -+ -+ -+static void surface_dtx_push_event(struct surface_dtx_dev *ddev, struct surface_dtx_event *event) -+{ -+ struct surface_dtx_client *client; -+ -+ rcu_read_lock(); -+ list_for_each_entry_rcu(client, &ddev->client_list, node) { -+ spin_lock(&client->buffer_lock); -+ -+ client->buffer[client->buffer_head++] = *event; -+ client->buffer_head &= SURFACE_DTX_CLIENT_BUF_SIZE - 1; -+ -+ if (unlikely(client->buffer_head == client->buffer_tail)) { -+ printk(DTX_WARN "event buffer overrun\n"); -+ client->buffer_tail = (client->buffer_tail + 1) & (SURFACE_DTX_CLIENT_BUF_SIZE - 1); -+ } -+ -+ spin_unlock(&client->buffer_lock); -+ -+ kill_fasync(&client->fasync, SIGIO, POLL_IN); -+ } -+ rcu_read_unlock(); -+ -+ wake_up_interruptible(&ddev->waitq); -+} -+ -+ -+static void surface_dtx_update_opmpde(struct surface_dtx_dev *ddev) -+{ -+ struct surface_dtx_event event; -+ int opmode; -+ -+ // get operation mode -+ opmode = sg5_ec_query_opmpde(); -+ if (opmode < 0) { -+ printk(DTX_ERR "EC request failed with error %d\n", opmode); -+ } -+ -+ // send DTX event -+ event.type = 0x11; -+ event.code = 0x0D; -+ event.arg0 = opmode; -+ event.arg1 = 0x00; -+ -+ surface_dtx_push_event(ddev, &event); -+ -+ // send SW_TABLET_MODE event -+ spin_lock(&ddev->input_lock); -+ input_report_switch(ddev->input_dev, SW_TABLET_MODE, opmode == 0x00); -+ input_sync(ddev->input_dev); -+ spin_unlock(&ddev->input_lock); -+} -+ -+static int surface_dtx_evt_dtx(struct surfacegen5_event *in_event, void *data) -+{ -+ struct surface_dtx_dev *ddev = data; -+ struct surface_dtx_event event; -+ -+ switch (in_event->cid) { -+ case SG5_EVENT_DTX_CID_CONNECTION: -+ case SG5_EVENT_DTX_CID_BUTTON: -+ case SG5_EVENT_DTX_CID_ERROR: -+ case SG5_EVENT_DTX_CID_LATCH_STATUS: -+ if (in_event->len > 2) { -+ printk(DTX_ERR "unexpected payload size (cid: %x, len: %u)\n", -+ in_event->cid, in_event->len); -+ return 0; -+ } -+ -+ event.type = in_event->tc; -+ event.code = in_event->cid; -+ event.arg0 = in_event->len >= 1 ? in_event->pld[0] : 0x00; -+ event.arg1 = in_event->len >= 2 ? in_event->pld[1] : 0x00; -+ surface_dtx_push_event(ddev, &event); -+ break; -+ -+ default: -+ printk(DTX_WARN "unhandled dtx event (cid: %x)\n", in_event->cid); -+ } -+ -+ // update device mode -+ if (in_event->cid == SG5_EVENT_DTX_CID_CONNECTION) { -+ if (in_event->pld[0]) { -+ // Note: we're already in a workqueue task -+ msleep(SG5_DTX_CONNECT_OPMODE_DELAY); -+ } -+ -+ surface_dtx_update_opmpde(ddev); -+ } -+ -+ return 0; -+} -+ -+static int surface_dtx_events_setup(struct surface_dtx_dev *ddev) -+{ -+ int status; -+ -+ status = surfacegen5_ec_set_event_handler(SG5_EVENT_DTX_RQID, surface_dtx_evt_dtx, ddev); -+ if (status) { -+ goto err_event_handler; -+ } -+ -+ status = surfacegen5_ec_enable_event_source(SG5_EVENT_DTX_TC, 0x01, SG5_EVENT_DTX_RQID); -+ if (status) { -+ goto err_event_source; -+ } -+ -+ return 0; -+ -+err_event_source: -+ surfacegen5_ec_remove_event_handler(SG5_EVENT_DTX_RQID); -+err_event_handler: -+ return status; -+} -+ -+static void surface_dtx_events_disable(void) -+{ -+ surfacegen5_ec_disable_event_source(SG5_EVENT_DTX_TC, 0x01, SG5_EVENT_DTX_RQID); -+ surfacegen5_ec_remove_event_handler(SG5_EVENT_DTX_RQID); -+} -+ -+ -+static struct input_dev *surface_dtx_register_inputdev(struct platform_device *pdev) -+{ -+ struct input_dev *input_dev; -+ int status; -+ -+ input_dev = input_allocate_device(); -+ if (!input_dev) { -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ input_dev->name = SG5_DTX_INPUT_NAME; -+ input_dev->dev.parent = &pdev->dev; -+ input_dev->id.bustype = BUS_VIRTUAL; -+ input_dev->id.vendor = USB_VENDOR_ID_MICROSOFT; -+ input_dev->id.product = USB_DEVICE_ID_MS_SURFACE_BASE_2_INTEGRATION; -+ -+ input_set_capability(input_dev, EV_SW, SW_TABLET_MODE); -+ -+ status = sg5_ec_query_opmpde(); -+ if (status < 0) { -+ input_free_device(input_dev); -+ return ERR_PTR(status); -+ } -+ -+ input_report_switch(input_dev, SW_TABLET_MODE, status == 0x00); -+ -+ status = input_register_device(input_dev); -+ if (status) { -+ input_unregister_device(input_dev); -+ return ERR_PTR(status); -+ } -+ -+ return input_dev; -+} -+ -+ -+static int surfacegen5_acpi_dtx_probe(struct platform_device *pdev) -+{ -+ struct surface_dtx_dev *ddev = &surface_dtx_dev; -+ struct device_link *ec_link; -+ struct input_dev *input_dev; -+ int status; -+ -+ // link to ec -+ ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME); -+ if (IS_ERR_OR_NULL(ec_link)) { -+ if (PTR_ERR(ec_link) == -ENXIO) { -+ // Defer probe if the _SSH driver has not set up the controller yet. -+ status = -EPROBE_DEFER; -+ } else { -+ status = -EFAULT; -+ } -+ -+ goto err_probe_ec_link; -+ } -+ -+ input_dev = surface_dtx_register_inputdev(pdev); -+ if (IS_ERR(input_dev)) { -+ status = PTR_ERR(input_dev); -+ goto err_input_dev; -+ } -+ -+ // initialize device -+ mutex_lock(&ddev->mutex); -+ if (ddev->active) { -+ mutex_unlock(&ddev->mutex); -+ status = -ENODEV; -+ goto err_register; -+ } -+ -+ INIT_LIST_HEAD(&ddev->client_list); -+ init_waitqueue_head(&ddev->waitq); -+ ddev->active = true; -+ ddev->ec_link = ec_link; -+ ddev->input_dev = input_dev; -+ mutex_unlock(&ddev->mutex); -+ -+ status = misc_register(&ddev->mdev); -+ if (status) { -+ goto err_register; -+ } -+ -+ // enable events -+ status = surface_dtx_events_setup(ddev); -+ if (status) { -+ goto err_events_setup; -+ } -+ -+ return 0; -+ -+err_events_setup: -+ misc_deregister(&ddev->mdev); -+err_register: -+ input_unregister_device(ddev->input_dev); -+err_input_dev: -+ surfacegen5_ec_consumer_remove(ec_link); -+err_probe_ec_link: -+ return status; -+} -+ -+static int surfacegen5_acpi_dtx_remove(struct platform_device *pdev) -+{ -+ struct surface_dtx_dev *ddev = &surface_dtx_dev; -+ struct surface_dtx_client *client; -+ -+ mutex_lock(&ddev->mutex); -+ if (!ddev->active) { -+ mutex_unlock(&ddev->mutex); -+ return 0; -+ } -+ -+ // mark as inactive -+ ddev->active = false; -+ mutex_unlock(&ddev->mutex); -+ -+ // After this call we're guaranteed that no more input events will arive -+ surface_dtx_events_disable(); -+ -+ // wake up clients -+ spin_lock(&ddev->client_lock); -+ list_for_each_entry(client, &ddev->client_list, node) { -+ kill_fasync(&client->fasync, SIGIO, POLL_HUP); -+ } -+ spin_unlock(&ddev->client_lock); -+ -+ wake_up_interruptible(&ddev->waitq); -+ -+ // unregister user-space devices -+ input_unregister_device(ddev->input_dev); -+ misc_deregister(&ddev->mdev); -+ -+ // unlink -+ surfacegen5_ec_consumer_remove(ddev->ec_link); -+ -+ return 0; -+} -+ -+ -+static const struct acpi_device_id surfacegen5_acpi_dtx_match[] = { -+ { "MSHW0133", 0 }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_dtx_match); -+ -+struct platform_driver surfacegen5_acpi_dtx = { -+ .probe = surfacegen5_acpi_dtx_probe, -+ .remove = surfacegen5_acpi_dtx_remove, -+ .driver = { -+ .name = "surfacegen5_acpi_dtx", -+ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_dtx_match), -+ }, -+}; -+ -+ -+inline int surfacegen5_acpi_dtx_register(void) -+{ -+ return platform_driver_register(&surfacegen5_acpi_dtx); -+} -+ -+inline void surfacegen5_acpi_dtx_unregister(void) -+{ -+ platform_driver_unregister(&surfacegen5_acpi_dtx); -+} -+ -+#else /* CONFIG_SURFACE_ACPI_DTX */ -+ -+inline int surfacegen5_acpi_dtx_register(void) -+{ -+ return 0; -+} -+ -+inline void surfacegen5_acpi_dtx_unregister(void) -+{ -+} -+ -+#endif /* CONFIG_SURFACE_ACPI_DTX */ -+ -+ -+/************************************************************************* -+ * Surface Platform Integration Driver -+ */ -+ -+#ifdef CONFIG_SURFACE_ACPI_SID -+ -+enum sg5_perf_mode { -+ SG5_PERF_MODE_NORMAL = 1, -+ SG5_PERF_MODE_BATTERY = 2, -+ SG5_PERF_MODE_PERF1 = 3, -+ SG5_PERF_MODE_PERF2 = 4, -+ -+ __SG5_PERF_MODE__START = 1, -+ __SG5_PERF_MODE__END = 4, -+}; -+ -+enum sg5_param_perf_mode { -+ SG5_PARAM_PERF_MODE_AS_IS = 0, -+ SG5_PARAM_PERF_MODE_NORMAL = SG5_PERF_MODE_NORMAL, -+ SG5_PARAM_PERF_MODE_BATTERY = SG5_PERF_MODE_BATTERY, -+ SG5_PARAM_PERF_MODE_PERF1 = SG5_PERF_MODE_PERF1, -+ SG5_PARAM_PERF_MODE_PERF2 = SG5_PERF_MODE_PERF2, -+ -+ __SG5_PARAM_PERF_MODE__START = 0, -+ __SG5_PARAM_PERF_MODE__END = 4, -+}; -+ -+struct surface_sid_drvdata { -+ struct device_link *ec_link; -+}; -+ -+ -+static int sg5_ec_perf_mode_get(void) -+{ -+ u8 result_buf[8] = { 0 }; -+ int status; -+ -+ struct surfacegen5_rqst rqst = { -+ .tc = 0x03, -+ .iid = 0x00, -+ .cid = 0x02, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surfacegen5_buf result = { -+ .cap = ARRAY_SIZE(result_buf), -+ .len = 0, -+ .data = result_buf, -+ }; -+ -+ status = surfacegen5_ec_rqst(&rqst, &result); -+ if (status) { -+ return status; -+ } -+ -+ if (result.len != 8) { -+ return -EFAULT; -+ } -+ -+ return get_unaligned_le32(&result.data[0]); -+} -+ -+static int sg5_ec_perf_mode_set(int perf_mode) -+{ -+ u8 payload[4] = { 0 }; -+ -+ struct surfacegen5_rqst rqst = { -+ .tc = 0x03, -+ .iid = 0x00, -+ .cid = 0x03, -+ .snc = 0x00, -+ .cdl = ARRAY_SIZE(payload), -+ .pld = payload, -+ }; -+ -+ if (perf_mode < __SG5_PERF_MODE__START || perf_mode > __SG5_PERF_MODE__END) { -+ return -EINVAL; -+ } -+ -+ put_unaligned_le32(perf_mode, &rqst.pld[0]); -+ return surfacegen5_ec_rqst(&rqst, NULL); -+} -+ -+ -+static int param_perf_mode_set(const char *val, const struct kernel_param *kp) -+{ -+ int perf_mode; -+ int status; -+ -+ status = kstrtoint(val, 0, &perf_mode); -+ if (status) { -+ return status; -+ } -+ -+ if (perf_mode < __SG5_PARAM_PERF_MODE__START || perf_mode > __SG5_PARAM_PERF_MODE__END) { -+ return -EINVAL; -+ } -+ -+ return param_set_int(val, kp); -+} -+ -+static const struct kernel_param_ops param_perf_mode_ops = { -+ .set = param_perf_mode_set, -+ .get = param_get_int, -+}; -+ -+static int param_perf_mode_init = SG5_PARAM_PERF_MODE_AS_IS; -+static int param_perf_mode_exit = SG5_PARAM_PERF_MODE_AS_IS; -+ -+module_param_cb(perf_mode_init, ¶m_perf_mode_ops, ¶m_perf_mode_init, SG5_PARAM_PERM); -+module_param_cb(perf_mode_exit, ¶m_perf_mode_ops, ¶m_perf_mode_exit, SG5_PARAM_PERM); -+ -+MODULE_PARM_DESC(perf_mode_init, "Performance-mode to be set on module initialization"); -+MODULE_PARM_DESC(perf_mode_exit, "Performance-mode to be set on module exit"); -+ -+ -+static ssize_t perf_mode_show(struct device *dev, struct device_attribute *attr, char *data) -+{ -+ int perf_mode; -+ -+ perf_mode = sg5_ec_perf_mode_get(); -+ if (perf_mode < 0) { -+ dev_err(dev, "failed to get current performance mode: %d", perf_mode); -+ return -EIO; -+ } -+ -+ return sprintf(data, "%d\n", perf_mode); -+} -+ -+static ssize_t perf_mode_store(struct device *dev, struct device_attribute *attr, -+ const char *data, size_t count) -+{ -+ int perf_mode; -+ int status; -+ -+ status = kstrtoint(data, 0, &perf_mode); -+ if (status) { -+ return status; -+ } -+ -+ status = sg5_ec_perf_mode_set(perf_mode); -+ if (status) { -+ return status; -+ } -+ -+ // TODO: Should we notify ACPI here? -+ // -+ // There is a _DSM call described as -+ // WSID._DSM: Notify DPTF on Slider State change -+ // which calls -+ // ODV3 = ToInteger (Arg3) -+ // Notify(IETM, 0x88) -+ // IETM is an INT3400 Intel Dynamic Power Performance Management -+ // device, part of the DPTF framework. From the corresponding -+ // kernel driver, it looks like event 0x88 is being ignored. Also -+ // it is currently unknown what the consequecnes of setting ODV3 -+ // are. -+ -+ return count; -+} -+ -+const static DEVICE_ATTR_RW(perf_mode); -+ -+ -+static int surfacegen5_acpi_sid_probe(struct platform_device *pdev) -+{ -+ struct surface_sid_drvdata *drvdata; -+ struct device_link *ec_link; -+ int status; -+ -+ // link to ec -+ ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME); -+ if (IS_ERR_OR_NULL(ec_link)) { -+ if (PTR_ERR(ec_link) == -ENXIO) { -+ // Defer probe if the _SSH driver has not set up the controller yet. -+ status = -EPROBE_DEFER; -+ } else { -+ status = -EFAULT; -+ } -+ -+ goto err_probe_ec_link; -+ } -+ -+ // set up driver data -+ drvdata = kzalloc(sizeof(struct surface_sid_drvdata), GFP_KERNEL); -+ if (!drvdata) { -+ status = -ENOMEM; -+ goto err_drvdata; -+ } -+ drvdata->ec_link = ec_link; -+ platform_set_drvdata(pdev, drvdata); -+ -+ // set initial perf_mode -+ if (param_perf_mode_init != SG5_PARAM_PERF_MODE_AS_IS) { -+ status = sg5_ec_perf_mode_set(param_perf_mode_init); -+ if (status) { -+ goto err_set_perf; -+ } -+ } -+ -+ // register perf_mode attribute -+ status = sysfs_create_file(&pdev->dev.kobj, &dev_attr_perf_mode.attr); -+ if (status) { -+ goto err_sysfs; -+ } -+ -+ return 0; -+ -+err_sysfs: -+ sg5_ec_perf_mode_set(param_perf_mode_exit); -+err_set_perf: -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+err_drvdata: -+ surfacegen5_ec_consumer_remove(ec_link); -+err_probe_ec_link: -+ return status; -+} -+ -+static int surfacegen5_acpi_sid_remove(struct platform_device *pdev) -+{ -+ struct surface_sid_drvdata *drvdata = platform_get_drvdata(pdev); -+ -+ // remove perf_mode attribute -+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_perf_mode.attr); -+ -+ // set exit perf_mode -+ sg5_ec_perf_mode_set(param_perf_mode_exit); -+ -+ // remove consumer and clean up -+ surfacegen5_ec_consumer_remove(drvdata->ec_link); -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+ -+ return 0; -+} -+ -+ -+static const struct acpi_device_id surfacegen5_acpi_sid_match[] = { -+ { "MSHW0107", 0 }, /* Surface Book 2 */ -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_sid_match); -+ -+struct platform_driver surfacegen5_acpi_sid = { -+ .probe = surfacegen5_acpi_sid_probe, -+ .remove = surfacegen5_acpi_sid_remove, -+ .driver = { -+ .name = "surfacegen5_acpi_sid", -+ .acpi_match_table = ACPI_PTR(surfacegen5_acpi_sid_match), -+ }, -+}; -+ -+ -+inline int surfacegen5_acpi_sid_register(void) -+{ -+ return platform_driver_register(&surfacegen5_acpi_sid); -+} -+ -+inline void surfacegen5_acpi_sid_unregister(void) -+{ -+ platform_driver_unregister(&surfacegen5_acpi_sid); -+} -+ -+#else /* CONFIG_SURFACE_ACPI_SID */ -+ -+inline int surfacegen5_acpi_sid_register(void) -+{ -+ return 0; -+} -+ -+inline void surfacegen5_acpi_sid_unregister(void) -+{ -+} -+ -+#endif /* CONFIG_SURFACE_ACPI_SID */ -+ -+ -+/************************************************************************* -+ * Module initialization -+ */ -+ -+int __init surface_acpi_init(void) -+{ -+ int status; -+ -+ status = surfacegen5_acpi_ssh_register(); -+ if (status) { -+ goto err_ssh; -+ } -+ -+ status = surfacegen5_acpi_san_register(); -+ if (status) { -+ goto err_san; -+ } -+ -+ status = surfacegen5_acpi_vhf_register(); -+ if (status) { -+ goto err_vhf; -+ } -+ -+ status = surfacegen5_acpi_dtx_register(); -+ if (status) { -+ goto err_dtx; -+ } -+ -+ status = surfacegen5_acpi_sid_register(); -+ if (status) { -+ goto err_sid; -+ } -+ -+ return 0; -+ -+err_sid: -+ surfacegen5_acpi_sid_unregister(); -+err_dtx: -+ surfacegen5_acpi_vhf_unregister(); -+err_vhf: -+ surfacegen5_acpi_san_unregister(); -+err_san: -+ surfacegen5_acpi_ssh_unregister(); -+err_ssh: -+ return status; -+} -+ -+void __exit surface_acpi_exit(void) -+{ -+ surfacegen5_acpi_sid_unregister(); -+ surfacegen5_acpi_dtx_unregister(); -+ surfacegen5_acpi_vhf_unregister(); -+ surfacegen5_acpi_san_unregister(); -+ surfacegen5_acpi_ssh_unregister(); -+} -+ -+module_init(surface_acpi_init) -+module_exit(surface_acpi_exit) -+ -+MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("ACPI/Platform Drivers for Microsoft Surface Devices"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c -index a0ac16ee6575..0dd242ff24d1 100644 ---- a/drivers/tty/serdev/core.c -+++ b/drivers/tty/serdev/core.c -@@ -558,8 +558,7 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl, - struct serdev_device *serdev = NULL; - int err; - -- if (acpi_bus_get_status(adev) || !adev->status.present || -- acpi_device_enumerated(adev)) -+ if (acpi_bus_get_status(adev) || !adev->status.present) - return AE_OK; - - serdev = serdev_device_alloc(ctrl); -@@ -582,6 +581,81 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl, - return AE_OK; - } - -+struct acpi_serdev_add_device_from_resource_ctx { -+ acpi_handle ctrl_handle; -+ acpi_handle device_handle; -+ struct serdev_controller *ctrl; -+ struct acpi_device *device; -+}; -+ -+static acpi_status -+acpi_serdev_add_device_from_resource(struct acpi_resource *resource, void *data) -+{ -+ struct acpi_serdev_add_device_from_resource_ctx* ctx = data; -+ struct acpi_resource_source *ctrl_name; -+ acpi_handle ctrl_handle; -+ acpi_status status; -+ -+ if (resource->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) -+ return AE_OK; -+ -+ if (resource->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART) -+ return AE_OK; -+ -+ ctrl_name = &resource->data.common_serial_bus.resource_source; -+ if (ctrl_name->string_length == 0 || !ctrl_name->string_ptr) { -+ return AE_OK; -+ } -+ -+ status = acpi_get_handle(ctx->device_handle, ctrl_name->string_ptr, -+ &ctrl_handle); -+ if (ACPI_FAILURE(status)) { -+ return AE_OK; -+ } -+ -+ if (ctrl_handle == ctx->ctrl_handle) { -+ return acpi_serdev_register_device(ctx->ctrl, ctx->device); -+ } -+ -+ return AE_OK; -+} -+ -+static acpi_status -+acpi_serdev_add_devices_from_resources(acpi_handle handle, u32 level, -+ void *data, void **return_value) -+{ -+ struct acpi_serdev_add_device_from_resource_ctx *ctx = data; -+ acpi_status status; -+ -+ ctx->device_handle = handle; -+ -+ status = acpi_bus_get_device(handle, &ctx->device); -+ if (status) return AE_OK; -+ -+ status = acpi_walk_resources(handle, METHOD_NAME__CRS, -+ acpi_serdev_add_device_from_resource, -+ ctx); -+ -+ if (status == AE_NOT_FOUND) -+ return AE_OK; // not finding _CRS is not an error -+ else -+ return status; -+} -+ -+static int -+acpi_serdev_register_devices_from_resources(acpi_handle handle, -+ struct serdev_controller *ctrl) -+{ -+ struct acpi_serdev_add_device_from_resource_ctx ctx = { -+ .ctrl = ctrl, -+ .ctrl_handle = handle, -+ }; -+ -+ return acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 128, -+ acpi_serdev_add_devices_from_resources, -+ NULL, &ctx, NULL); -+} -+ - static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, - void *data, void **return_value) - { -@@ -591,6 +665,9 @@ static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - -+ if (acpi_device_enumerated(adev)) -+ return AE_OK; -+ - return acpi_serdev_register_device(ctrl, adev); - } - -@@ -608,6 +685,15 @@ static int acpi_serdev_register_devices(struct serdev_controller *ctrl) - if (ACPI_FAILURE(status)) - dev_dbg(&ctrl->dev, "failed to enumerate serdev slaves\n"); - -+ if (!ctrl->serdev) { -+ status = acpi_serdev_register_devices_from_resources(handle, ctrl); -+ if (ACPI_FAILURE(status)) { -+ dev_dbg(&ctrl->dev, -+ "failed to register serdev slaves from resources: %x\n", -+ status); -+ } -+ } -+ - if (!ctrl->serdev) - return -ENODEV; - --- -2.19.1 - diff --git a/patches/5.0/0002-suspend.patch b/patches/5.0/0002-suspend.patch deleted file mode 100644 index 58649c840..000000000 --- a/patches/5.0/0002-suspend.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 689cde2d569ede2c591c49faaf5917e709033fad Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:40:30 -0400 -Subject: [PATCH 02/12] suspend - ---- - drivers/nvme/host/nvme.h | 5 +++++ - drivers/nvme/host/pci.c | 10 +++++++++- - drivers/pci/quirks.c | 4 ++++ - include/linux/pci_ids.h | 2 ++ - kernel/power/suspend.c | 11 +++++++++++ - kernel/sysctl.c | 9 +++++++++ - 6 files changed, 40 insertions(+), 1 deletion(-) - -diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h -index c4a1bb41abf0..5537c35ab677 100644 ---- a/drivers/nvme/host/nvme.h -+++ b/drivers/nvme/host/nvme.h -@@ -95,6 +95,11 @@ enum nvme_quirks { - * Ignore device provided subnqn. - */ - NVME_QUIRK_IGNORE_DEV_SUBNQN = (1 << 8), -+ -+ /* -+ * Do not disable nvme when suspending (s2idle) -+ */ -+ NVME_QUIRK_NO_DISABLE = (1 << 9), - }; - - /* -diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c -index 7fee665ec45e..ebf615570787 100644 ---- a/drivers/nvme/host/pci.c -+++ b/drivers/nvme/host/pci.c -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - - #include "trace.h" - #include "nvme.h" -@@ -2896,8 +2897,11 @@ static int nvme_suspend(struct device *dev) - { - struct pci_dev *pdev = to_pci_dev(dev); - struct nvme_dev *ndev = pci_get_drvdata(pdev); -+ struct nvme_ctrl *ctrl = &ndev->ctrl; -+ -+ if (!(pm_suspend_via_s2idle() && (ctrl->quirks & NVME_QUIRK_NO_DISABLE))) -+ nvme_dev_disable(ndev, true); - -- nvme_dev_disable(ndev, true); - return 0; - } - -@@ -3002,6 +3006,10 @@ static const struct pci_device_id nvme_id_table[] = { - .driver_data = NVME_QUIRK_LIGHTNVM, }, - { PCI_DEVICE(0x1d1d, 0x2601), /* CNEX Granby */ - .driver_data = NVME_QUIRK_LIGHTNVM, }, -+ { PCI_VDEVICE(SK_HYNIX, 0x1527), /* Sk Hynix */ -+ .driver_data = NVME_QUIRK_NO_DISABLE, }, -+ { PCI_VDEVICE(TOSHIBA, 0x010f), /* TOSHIBA NVMe found on Surface Book with Performance Base */ -+ .driver_data = NVME_QUIRK_NO_DISABLE, }, - { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) }, - { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, - { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, -diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index fba03a7d5c7f..84e9fed12b55 100644 ---- a/drivers/pci/quirks.c -+++ b/drivers/pci/quirks.c -@@ -1357,6 +1357,10 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, - occur when mode detecting */ - DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID, - PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3); -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SK_HYNIX, 0x1527, quirk_no_ata_d3); -+/* TOSHIBA NVMe disk found on Surface Book with Performance Base -+ cannot wakeup from D3 state after s2idle */ -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_TOSHIBA, 0x010f, quirk_no_ata_d3); - - /* - * This was originally an Alpha-specific thing, but it really fits here. -diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h -index 5eaf39dbc388..20f4e40cf04a 100644 ---- a/include/linux/pci_ids.h -+++ b/include/linux/pci_ids.h -@@ -3098,4 +3098,6 @@ - - #define PCI_VENDOR_ID_NCUBE 0x10ff - -+#define PCI_VENDOR_ID_SK_HYNIX 0x1c5c -+ - #endif /* _LINUX_PCI_IDS_H */ -diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c -index 0bd595a0b610..a8385e8894a5 100644 ---- a/kernel/power/suspend.c -+++ b/kernel/power/suspend.c -@@ -526,6 +526,8 @@ int suspend_devices_and_enter(suspend_state_t state) - goto Resume_devices; - } - -+unsigned int resume_delay = 3000; -+ - /** - * suspend_finish - Clean up before finishing the suspend sequence. - * -@@ -534,6 +536,15 @@ int suspend_devices_and_enter(suspend_state_t state) - */ - static void suspend_finish(void) - { -+ if (resume_delay) { -+ /* Give kernel threads a head start, such that usb-storage -+ * can detect devices before syslog attempts to write log -+ * messages from the suspend code. -+ */ -+ thaw_kernel_threads(); -+ pr_debug("PM: Sleeping for %d milliseconds.\n", resume_delay); -+ msleep(resume_delay); -+ } - suspend_thaw_processes(); - pm_notifier_call_chain(PM_POST_SUSPEND); - pm_restore_console(); -diff --git a/kernel/sysctl.c b/kernel/sysctl.c -index f50f1471c119..eabfe9059ff6 100644 ---- a/kernel/sysctl.c -+++ b/kernel/sysctl.c -@@ -314,7 +314,16 @@ static int min_extfrag_threshold; - static int max_extfrag_threshold = 1000; - #endif - -+extern unsigned int resume_delay; -+ - static struct ctl_table kern_table[] = { -+ { -+ .procname = "resume_delay", -+ .data = &resume_delay, -+ .maxlen = sizeof(unsigned int), -+ .mode = 0644, -+ .proc_handler = proc_dointvec, -+ }, - { - .procname = "sched_child_runs_first", - .data = &sysctl_sched_child_runs_first, --- -2.19.1 - diff --git a/patches/5.0/0003-buttons.patch b/patches/5.0/0003-buttons.patch deleted file mode 100644 index 8d4051501..000000000 --- a/patches/5.0/0003-buttons.patch +++ /dev/null @@ -1,257 +0,0 @@ -From 18655a338f67c8c5bcb96f7f48802537a08dac94 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:40:42 -0400 -Subject: [PATCH 03/12] buttons - ---- - drivers/input/misc/soc_button_array.c | 133 ++++++++++++++++++++-- - drivers/platform/x86/surfacepro3_button.c | 38 +++++++ - 2 files changed, 159 insertions(+), 12 deletions(-) - -diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c -index 55cd6e0b409c..ad4d591cf179 100644 ---- a/drivers/input/misc/soc_button_array.c -+++ b/drivers/input/misc/soc_button_array.c -@@ -29,6 +29,17 @@ struct soc_button_info { - bool wakeup; - }; - -+/** -+ * struct soc_device_data - driver data for different device types -+ * @button_info: specifications of buttons, if NULL specification is assumed to -+ * be present in _DSD -+ * @check: device-specific check (NULL means all will be accepted) -+ */ -+struct soc_device_data { -+ struct soc_button_info *button_info; -+ int (*check)(struct device *dev); -+}; -+ - /* - * Some of the buttons like volume up/down are auto repeat, while others - * are not. To support both, we register two platform devices, and put -@@ -310,6 +321,7 @@ static int soc_button_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; - const struct acpi_device_id *id; -+ struct soc_device_data *device_data; - struct soc_button_info *button_info; - struct soc_button_data *priv; - struct platform_device *pd; -@@ -320,18 +332,19 @@ static int soc_button_probe(struct platform_device *pdev) - if (!id) - return -ENODEV; - -- if (!id->driver_data) { -+ device_data = (struct soc_device_data *)id->driver_data; -+ if (device_data && device_data->check) { -+ error = device_data->check(dev); -+ if (error) -+ return error; -+ } -+ -+ if (device_data && device_data->button_info) { -+ button_info = (struct soc_button_info *)device_data->button_info; -+ } else { - button_info = soc_button_get_button_info(dev); - if (IS_ERR(button_info)) - return PTR_ERR(button_info); -- } else { -- button_info = (struct soc_button_info *)id->driver_data; -- } -- -- error = gpiod_count(dev, NULL); -- if (error < 0) { -- dev_dbg(dev, "no GPIO attached, ignoring...\n"); -- return -ENODEV; - } - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -@@ -357,12 +370,32 @@ static int soc_button_probe(struct platform_device *pdev) - if (!priv->children[0] && !priv->children[1]) - return -ENODEV; - -- if (!id->driver_data) -+ if (!device_data || !device_data->button_info) - devm_kfree(dev, button_info); - - return 0; - } - -+ -+static int soc_device_check_generic(struct device *dev) -+{ -+ int gpios; -+ -+ gpios = gpiod_count(dev, NULL); -+ if (gpios < 0) { -+ dev_dbg(dev, "no GPIO attached, ignoring...\n"); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static struct soc_device_data soc_device_ACPI0011 = { -+ .button_info = NULL, -+ .check = soc_device_check_generic, -+}; -+ -+ - /* - * Definition of buttons on the tablet. The ACPI index of each button - * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC -@@ -377,9 +410,85 @@ static struct soc_button_info soc_button_PNP0C40[] = { - { } - }; - -+static struct soc_device_data soc_device_PNP0C40 = { -+ .button_info = soc_button_PNP0C40, -+ .check = soc_device_check_generic, -+}; -+ -+ -+/* -+ * Special device check for Surface Book 2 and Surface Pro (2017). -+ * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned -+ * devices use MSHW0040 for power and volume buttons, however the way they -+ * have to be addressed differs. Make sure that we only load this drivers -+ * for the correct devices by checking the OEM Platform Revision provided by -+ * the _DSM method. -+ */ -+#define MSHW0040_DSM_REVISION 0x01 -+#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision -+static const guid_t MSHW0040_DSM_UUID = -+ GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, -+ 0x49, 0x80, 0x35); -+ -+static int soc_device_check_MSHW0040(struct device *dev) -+{ -+ acpi_handle handle = ACPI_HANDLE(dev); -+ union acpi_object *result; -+ u64 oem_platform_rev = 0; -+ int gpios; -+ -+ // get OEM platform revision -+ result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, -+ MSHW0040_DSM_REVISION, -+ MSHW0040_DSM_GET_OMPR, NULL, -+ ACPI_TYPE_INTEGER); -+ -+ if (result) { -+ oem_platform_rev = result->integer.value; -+ ACPI_FREE(result); -+ } -+ -+ if (oem_platform_rev == 0) -+ return -ENODEV; -+ -+ dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); -+ -+ /* -+ * We are _really_ expecting GPIOs here. If we do not get any, this -+ * means the GPIO driver has not been loaded yet (which can happen). -+ * Try again later. -+ */ -+ gpios = gpiod_count(dev, NULL); -+ if (gpios < 0) -+ return -EAGAIN; -+ -+ return 0; -+} -+ -+/* -+ * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). -+ * Obtained from DSDT/testing. -+ */ -+static struct soc_button_info soc_button_MSHW0040[] = { -+ { "power", 0, EV_KEY, KEY_POWER, false, true }, -+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, -+ { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false }, -+ { } -+}; -+ -+static struct soc_device_data soc_device_MSHW0040 = { -+ .button_info = soc_button_MSHW0040, -+ .check = soc_device_check_MSHW0040, -+}; -+ -+ - static const struct acpi_device_id soc_button_acpi_match[] = { -- { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, -- { "ACPI0011", 0 }, -+ { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, -+ { "ACPI0011", (unsigned long)&soc_device_ACPI0011 }, -+ -+ /* Microsoft Surface Devices (5th and 6th generation) */ -+ { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, -+ - { } - }; - -diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c -index 1b491690ce07..eaec30380b11 100644 ---- a/drivers/platform/x86/surfacepro3_button.c -+++ b/drivers/platform/x86/surfacepro3_button.c -@@ -24,6 +24,12 @@ - #define SURFACE_BUTTON_OBJ_NAME "VGBI" - #define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" - -+#define MSHW0040_DSM_REVISION 0x01 -+#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision -+static const guid_t MSHW0040_DSM_UUID = -+ GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, -+ 0x49, 0x80, 0x35); -+ - #define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 - - #define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6 -@@ -146,6 +152,34 @@ static int surface_button_resume(struct device *dev) - } - #endif - -+/* -+ * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device -+ * ID (MSHW0040) for the power/volume buttons. Make sure this is the right -+ * device by checking for the _DSM method and OEM Platform Revision. -+ */ -+static int surface_button_check_MSHW0040(struct acpi_device *dev) -+{ -+ acpi_handle handle = dev->handle; -+ union acpi_object *result; -+ u64 oem_platform_rev = 0; -+ -+ // get OEM platform revision -+ result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, -+ MSHW0040_DSM_REVISION, -+ MSHW0040_DSM_GET_OMPR, -+ NULL, ACPI_TYPE_INTEGER); -+ -+ if (result) { -+ oem_platform_rev = result->integer.value; -+ ACPI_FREE(result); -+ } -+ -+ dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); -+ -+ return oem_platform_rev == 0 ? 0 : -ENODEV; -+} -+ -+ - static int surface_button_add(struct acpi_device *device) - { - struct surface_button *button; -@@ -158,6 +192,10 @@ static int surface_button_add(struct acpi_device *device) - strlen(SURFACE_BUTTON_OBJ_NAME))) - return -ENODEV; - -+ error = surface_button_check_MSHW0040(device); -+ if (error) -+ return error; -+ - button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); - if (!button) - return -ENOMEM; --- -2.19.1 - diff --git a/patches/5.0/0004-cameras.patch b/patches/5.0/0004-cameras.patch deleted file mode 100644 index bff2e0d8e..000000000 --- a/patches/5.0/0004-cameras.patch +++ /dev/null @@ -1,2753 +0,0 @@ -From 517e9664fe7944c90418bc7f32a677d45e87e0ee Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:40:53 -0400 -Subject: [PATCH 04/12] cameras - ---- - drivers/media/usb/uvc/uvc_driver.c | 40 + - drivers/staging/Makefile | 1 + - drivers/staging/ov5693/Kconfig | 10 + - drivers/staging/ov5693/Makefile | 5 + - drivers/staging/ov5693/ad5823.c | 218 +++++ - drivers/staging/ov5693/ad5823.h | 90 ++ - drivers/staging/ov5693/ov5693.c | 1461 ++++++++++++++++++++++++++++ - drivers/staging/ov5693/ov5693.h | 848 ++++++++++++++++ - 8 files changed, 2673 insertions(+) - create mode 100644 drivers/staging/ov5693/Kconfig - create mode 100644 drivers/staging/ov5693/Makefile - create mode 100644 drivers/staging/ov5693/ad5823.c - create mode 100644 drivers/staging/ov5693/ad5823.h - create mode 100644 drivers/staging/ov5693/ov5693.c - create mode 100644 drivers/staging/ov5693/ov5693.h - -diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c -index 33a22c016456..ce0a47362b2f 100644 ---- a/drivers/media/usb/uvc/uvc_driver.c -+++ b/drivers/media/usb/uvc/uvc_driver.c -@@ -2399,6 +2399,46 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { - * though they are compliant. - */ - static const struct usb_device_id uvc_ids[] = { -+ /* Microsoft Surface Pro 3 Front */ -+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE -+ | USB_DEVICE_ID_MATCH_INT_INFO, -+ .idVendor = 0x045e, -+ .idProduct = 0x07be, -+ .bInterfaceClass = USB_CLASS_VIDEO, -+ .bInterfaceSubClass = 1, -+ .bInterfaceProtocol = 1 }, -+ /* Microsoft Surface Pro 3 Rear */ -+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE -+ | USB_DEVICE_ID_MATCH_INT_INFO, -+ .idVendor = 0x045e, -+ .idProduct = 0x07bf, -+ .bInterfaceClass = USB_CLASS_VIDEO, -+ .bInterfaceSubClass = 1, -+ .bInterfaceProtocol = 1 }, -+ /* Microsoft Surface Pro 4 Cam */ -+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE -+ | USB_DEVICE_ID_MATCH_INT_INFO, -+ .idVendor = 0x045e, -+ .idProduct = 0x090c, -+ .bInterfaceClass = USB_CLASS_VIDEO, -+ .bInterfaceSubClass = 1, -+ .bInterfaceProtocol = 1 }, -+ /* Microsoft Surface Book Cam 1 */ -+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE -+ | USB_DEVICE_ID_MATCH_INT_INFO, -+ .idVendor = 0x045e, -+ .idProduct = 0x090b, -+ .bInterfaceClass = USB_CLASS_VIDEO, -+ .bInterfaceSubClass = 1, -+ .bInterfaceProtocol = 1 }, -+ /* Microsoft Surface Book Cam 2 */ -+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE -+ | USB_DEVICE_ID_MATCH_INT_INFO, -+ .idVendor = 0x045e, -+ .idProduct = 0x091a, -+ .bInterfaceClass = USB_CLASS_VIDEO, -+ .bInterfaceSubClass = 1, -+ .bInterfaceProtocol = 1 }, - /* LogiLink Wireless Webcam */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, -diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile -index 5868631e8f1b..c6414b3858f2 100644 ---- a/drivers/staging/Makefile -+++ b/drivers/staging/Makefile -@@ -51,3 +51,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ - obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ - obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ - obj-$(CONFIG_EROFS_FS) += erofs/ -+obj-$(CONFIG_VIDEO_OV5693) += ov5693/ -diff --git a/drivers/staging/ov5693/Kconfig b/drivers/staging/ov5693/Kconfig -new file mode 100644 -index 000000000000..96000f112c4d ---- /dev/null -+++ b/drivers/staging/ov5693/Kconfig -@@ -0,0 +1,10 @@ -+config VIDEO_OV5693 -+ tristate "Omnivision ov5693 sensor support" -+ depends on I2C && VIDEO_V4L2 -+ ---help--- -+ This is a Video4Linux2 sensor-level driver for the Micron -+ ov5693 5 Mpixel camera. -+ -+ ov5693 is video camera sensor. -+ -+ It currently only works with the atomisp driver. -diff --git a/drivers/staging/ov5693/Makefile b/drivers/staging/ov5693/Makefile -new file mode 100644 -index 000000000000..d8a63faa591f ---- /dev/null -+++ b/drivers/staging/ov5693/Makefile -@@ -0,0 +1,5 @@ -+obj-$(CONFIG_VIDEO_OV5693) += ov569x.o -+ -+ov569x-objs := ov5693.o ad5823.o -+ -+ccflags-y += -Werror -diff --git a/drivers/staging/ov5693/ad5823.c b/drivers/staging/ov5693/ad5823.c -new file mode 100644 -index 000000000000..7c34c36e77e5 ---- /dev/null -+++ b/drivers/staging/ov5693/ad5823.c -@@ -0,0 +1,218 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ad5823.h" -+ -+static struct ad5823_device ad5823_dev; -+static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val) -+{ -+ struct i2c_msg msg; -+ u8 buf[2]; -+ buf[0] = reg; -+ buf[1] = val; -+ msg.addr = AD5823_VCM_ADDR; -+ msg.flags = 0; -+ msg.len = AD5823_8BIT; -+ msg.buf = &buf[0]; -+ -+ if (i2c_transfer(client->adapter, &msg, 1) != 1) -+ return -EIO; -+ return 0; -+} -+ -+static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val) -+{ -+ struct i2c_msg msg[2]; -+ u8 buf[2]; -+ buf[0] = reg; -+ buf[1] = 0; -+ -+ msg[0].addr = AD5823_VCM_ADDR; -+ msg[0].flags = 0; -+ msg[0].len = AD5823_8BIT; -+ msg[0].buf = &buf[0]; -+ -+ msg[1].addr = AD5823_VCM_ADDR; -+ msg[1].flags = I2C_M_RD; -+ msg[1].len = AD5823_8BIT; -+ msg[1].buf = &buf[1]; -+ *val = 0; -+ if (i2c_transfer(client->adapter, msg, 2) != 2) -+ return -EIO; -+ *val = buf[1]; -+ return 0; -+} -+ -+int ad5823_vcm_power_up(struct v4l2_subdev *sd) -+{ -+ int ret = -ENODEV; -+ -+ /* Enable power */ -+ if (ad5823_dev.platform_data) -+ ret = ad5823_dev.platform_data->power_ctrl(sd, 1); -+ /* -+ * waiting time requested by AD5823(vcm) -+ */ -+ usleep_range(1000, 2000); -+ return ret; -+} -+ -+int ad5823_vcm_power_down(struct v4l2_subdev *sd) -+{ -+ int ret = -ENODEV; -+ -+ if (ad5823_dev.platform_data) -+ ret = ad5823_dev.platform_data->power_ctrl(sd, 0); -+ -+ return ret; -+} -+ -+ -+int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret = -EINVAL; -+ u8 vcm_code; -+ u8 vcm_mode_reg_val[4] = { -+ AD5823_ARC_RES0, -+ AD5823_ARC_RES1, -+ AD5823_ARC_RES2, -+ AD5823_ESRC -+ }; -+ -+ if (ad5823_dev.vcm_mode != AD5823_DIRECT) { -+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, -+ AD5823_RING_CTRL_ENABLE); -+ if (ret) -+ return ret; -+ -+ ret = ad5823_i2c_write(client, AD5823_REG_MODE, -+ vcm_mode_reg_val[ad5823_dev.vcm_mode]); -+ if (ret) -+ return ret; -+ } else { -+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, -+ AD5823_RING_CTRL_DISABLE); -+ if (ret) -+ return ret; -+ } -+ -+ ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code); -+ if (ret) -+ return ret; -+ -+ /* set reg VCM_CODE_MSB Bit[1:0] */ -+ vcm_code = (vcm_code & VCM_CODE_MSB_MASK) | ((val >> 8) & ~VCM_CODE_MSB_MASK); -+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, vcm_code); -+ if (ret) -+ return ret; -+ -+ /* set reg VCM_CODE_LSB Bit[7:0] */ -+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_LSB, -+ (val & 0x0f)); -+ if (ret) -+ return ret; -+ -+ /* set required vcm move time */ -+ vcm_code = AD5823_RESONANCE_PERIOD / AD5823_RESONANCE_COEF -+ - AD5823_HIGH_FREQ_RANGE; -+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_MOVE_TIME, vcm_code); -+ -+ return ret; -+} -+ -+int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value) -+{ -+ int ret; -+ -+ value = min(value, AD5823_MAX_FOCUS_POS); -+ ret = ad5823_t_focus_vcm(sd, AD5823_MAX_FOCUS_POS - value); -+ if (ret == 0) { -+ ad5823_dev.number_of_steps = value - ad5823_dev.focus; -+ ad5823_dev.focus = value; -+ ktime_get_ts(&ad5823_dev.timestamp_t_focus_abs); -+ } -+ -+ return ret; -+} -+ -+int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value) -+{ -+ return ad5823_t_focus_abs(sd, ad5823_dev.focus + value); -+} -+ -+int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value) -+{ -+ u32 status = 0; -+ struct timespec temptime; -+ const struct timespec timedelay = { -+ 0, -+ min_t(u32, abs(ad5823_dev.number_of_steps)*DELAY_PER_STEP_NS, -+ DELAY_MAX_PER_STEP_NS), -+ }; -+ -+ ktime_get_ts(&temptime); -+ -+ temptime = timespec_sub(temptime, (ad5823_dev.timestamp_t_focus_abs)); -+ -+ if (timespec_compare(&temptime, &timedelay) <= 0) -+ status = ATOMISP_FOCUS_STATUS_MOVING -+ | ATOMISP_FOCUS_HP_IN_PROGRESS; -+ else -+ status = ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE -+ | ATOMISP_FOCUS_HP_COMPLETE; -+ -+ *value = status; -+ -+ return 0; -+} -+ -+int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value) -+{ -+ s32 val; -+ -+ ad5823_q_focus_status(sd, &val); -+ -+ if (val & ATOMISP_FOCUS_STATUS_MOVING) -+ *value = ad5823_dev.focus - ad5823_dev.number_of_steps; -+ else -+ *value = ad5823_dev.focus ; -+ -+ return 0; -+} -+ -+int ad5823_t_vcm_slew(struct v4l2_subdev *sd, s32 value) -+{ -+ return 0; -+} -+ -+int ad5823_t_vcm_timing(struct v4l2_subdev *sd, s32 value) -+{ -+ return 0; -+} -+ -+int ad5823_vcm_init(struct v4l2_subdev *sd) -+{ -+ /* set vcm mode to ARC RES0.5 */ -+ ad5823_dev.vcm_mode = AD5823_ARC_RES1; -+ ad5823_dev.platform_data = camera_get_af_platform_data(); -+ return ad5823_dev.platform_data ? 0 : -ENODEV; -+} -diff --git a/drivers/staging/ov5693/ad5823.h b/drivers/staging/ov5693/ad5823.h -new file mode 100644 -index 000000000000..8b046c31f3af ---- /dev/null -+++ b/drivers/staging/ov5693/ad5823.h -@@ -0,0 +1,90 @@ -+/* -+ * Support for AD5823 VCM. -+ * -+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License 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 __AD5823_H__ -+#define __AD5823_H__ -+ -+#include -+#include -+ -+ -+#define AD5823_VCM_ADDR 0x0c -+ -+#define AD5823_REG_RESET 0x01 -+#define AD5823_REG_MODE 0x02 -+#define AD5823_REG_VCM_MOVE_TIME 0x03 -+#define AD5823_REG_VCM_CODE_MSB 0x04 -+#define AD5823_REG_VCM_CODE_LSB 0x05 -+#define AD5823_REG_VCM_THRESHOLD_MSB 0x06 -+#define AD5823_REG_VCM_THRESHOLD_LSB 0x07 -+ -+#define AD5823_RING_CTRL_ENABLE 0x04 -+#define AD5823_RING_CTRL_DISABLE 0x00 -+ -+#define AD5823_RESONANCE_PERIOD 100000 -+#define AD5823_RESONANCE_COEF 512 -+#define AD5823_HIGH_FREQ_RANGE 0x80 -+ -+#define VCM_CODE_MSB_MASK 0xfc -+ -+enum ad5823_tok_type { -+ AD5823_8BIT = 0x0001, -+ AD5823_16BIT = 0x0002, -+}; -+ -+enum ad5823_vcm_mode { -+ AD5823_ARC_RES0 = 0x0, /* Actuator response control RES1 */ -+ AD5823_ARC_RES1 = 0x1, /* Actuator response control RES0.5 */ -+ AD5823_ARC_RES2 = 0x2, /* Actuator response control RES2 */ -+ AD5823_ESRC = 0x3, /* Enhanced slew rate control */ -+ AD5823_DIRECT = 0x4, /* Direct control */ -+}; -+ -+/* ad5823 device structure */ -+struct ad5823_device { -+ struct timespec timestamp_t_focus_abs; -+ enum ad5823_vcm_mode vcm_mode; -+ s16 number_of_steps; -+ bool initialized; /* true if ad5823 is detected */ -+ s32 focus; /* Current focus value */ -+ struct timespec focus_time; /* Time when focus was last time set */ -+ __u8 buffer[4]; /* Used for i2c transactions */ -+ const struct camera_af_platform_data *platform_data; -+}; -+ -+#define AD5823_INVALID_CONFIG 0xffffffff -+#define AD5823_MAX_FOCUS_POS 1023 -+ -+ -+#define DELAY_PER_STEP_NS 1000000 -+#define DELAY_MAX_PER_STEP_NS (1000000 * 1023) -+ -+int ad5823_vcm_power_up(struct v4l2_subdev *sd); -+int ad5823_vcm_power_down(struct v4l2_subdev *sd); -+int ad5823_vcm_init(struct v4l2_subdev *sd); -+ -+int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val); -+int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value); -+int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value); -+int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value); -+int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value); -+ -+#endif -diff --git a/drivers/staging/ov5693/ov5693.c b/drivers/staging/ov5693/ov5693.c -new file mode 100644 -index 000000000000..51d218da3722 ---- /dev/null -+++ b/drivers/staging/ov5693/ov5693.c -@@ -0,0 +1,1461 @@ -+/* -+ * Support for OmniVision OV5693 5M HD camera sensor. -+ * -+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ov5693.h" -+ -+/* i2c read/write stuff */ -+static int ov5693_read_reg(struct i2c_client *client, -+ u16 data_length, u16 reg, u16 *val) -+{ -+ int err; -+ struct i2c_msg msg[2]; -+ unsigned char data[6]; -+ -+ if (!client->adapter) { -+ dev_err(&client->dev, "%s error, no client->adapter\n", -+ __func__); -+ return -ENODEV; -+ } -+ -+ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT -+ && data_length != OV5693_32BIT) { -+ dev_err(&client->dev, "%s error, invalid data length\n", -+ __func__); -+ return -EINVAL; -+ } -+ -+ memset(msg, 0 , sizeof(msg)); -+ -+ msg[0].addr = client->addr; -+ msg[0].flags = 0; -+ msg[0].len = I2C_MSG_LENGTH; -+ msg[0].buf = data; -+ -+ /* high byte goes out first */ -+ data[0] = (u8)(reg >> 8); -+ data[1] = (u8)(reg & 0xff); -+ -+ msg[1].addr = client->addr; -+ msg[1].len = data_length; -+ msg[1].flags = I2C_M_RD; -+ msg[1].buf = data; -+ -+ err = i2c_transfer(client->adapter, msg, 2); -+ if (err != 2) { -+ if (err >= 0) -+ err = -EIO; -+ dev_err(&client->dev, -+ "read from offset 0x%x error %d", reg, err); -+ return err; -+ } -+ -+ *val = 0; -+ /* high byte comes first */ -+ if (data_length == OV5693_8BIT) -+ *val = (u8)data[0]; -+ else if (data_length == OV5693_16BIT) -+ *val = be16_to_cpu(*(u16 *)&data[0]); -+ else -+ *val = be32_to_cpu(*(u32 *)&data[0]); -+ -+ return 0; -+} -+ -+static int ov5693_i2c_write(struct i2c_client *client, u16 len, u8 *data) -+{ -+ struct i2c_msg msg; -+ const int num_msg = 1; -+ int ret; -+ -+ msg.addr = client->addr; -+ msg.flags = 0; -+ msg.len = len; -+ msg.buf = data; -+ ret = i2c_transfer(client->adapter, &msg, 1); -+ -+ return ret == num_msg ? 0 : -EIO; -+} -+ -+static int ov5693_write_reg(struct i2c_client *client, u16 data_length, -+ u16 reg, u16 val) -+{ -+ int ret; -+ unsigned char data[4] = {0}; -+ u16 *wreg = (u16 *)data; -+ const u16 len = data_length + sizeof(u16); /* 16-bit address + data */ -+ -+ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT) { -+ dev_err(&client->dev, -+ "%s error, invalid data_length\n", __func__); -+ return -EINVAL; -+ } -+ -+ /* high byte goes out first */ -+ *wreg = cpu_to_be16(reg); -+ -+ if (data_length == OV5693_8BIT) { -+ data[2] = (u8)(val); -+ } else { -+ /* OV5693_16BIT */ -+ u16 *wdata = (u16 *)&data[2]; -+ *wdata = cpu_to_be16(val); -+ } -+ -+ ret = ov5693_i2c_write(client, len, data); -+ if (ret) -+ dev_err(&client->dev, -+ "write error: wrote 0x%x to offset 0x%x error %d", -+ val, reg, ret); -+ -+ return ret; -+} -+ -+/* -+ * ov5693_write_reg_array - Initializes a list of OV5693 registers -+ * @client: i2c driver client structure -+ * @reglist: list of registers to be written -+ * -+ * This function initializes a list of registers. When consecutive addresses -+ * are found in a row on the list, this function creates a buffer and sends -+ * consecutive data in a single i2c_transfer(). -+ * -+ * __ov5693_flush_reg_array, __ov5693_buf_reg_array() and -+ * __ov5693_write_reg_is_consecutive() are internal functions to -+ * ov5693_write_reg_array_fast() and should be not used anywhere else. -+ * -+ */ -+static int __ov5693_flush_reg_array(struct i2c_client *client, -+ struct ov5693_write_ctrl *ctrl) -+{ -+ u16 size; -+ -+ if (ctrl->index == 0) -+ return 0; -+ -+ size = sizeof(u16) + ctrl->index; /* 16-bit address + data */ -+ ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr); -+ ctrl->index = 0; -+ -+ return ov5693_i2c_write(client, size, (u8 *)&ctrl->buffer); -+} -+ -+static int __ov5693_buf_reg_array(struct i2c_client *client, -+ struct ov5693_write_ctrl *ctrl, -+ const struct ov5693_reg *next) -+{ -+ int size; -+ u16 *data16; -+ -+ switch (next->type) { -+ case OV5693_8BIT: -+ size = 1; -+ ctrl->buffer.data[ctrl->index] = (u8)next->val; -+ break; -+ case OV5693_16BIT: -+ size = 2; -+ data16 = (u16 *)&ctrl->buffer.data[ctrl->index]; -+ *data16 = cpu_to_be16((u16)next->val); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* When first item is added, we need to store its starting address */ -+ if (ctrl->index == 0) -+ ctrl->buffer.addr = next->reg; -+ -+ ctrl->index += size; -+ -+ /* -+ * Buffer cannot guarantee free space for u32? Better flush it to avoid -+ * possible lack of memory for next item. -+ */ -+ if (ctrl->index + sizeof(u16) >= OV5693_MAX_WRITE_BUF_SIZE) -+ return __ov5693_flush_reg_array(client, ctrl); -+ -+ return 0; -+} -+ -+static int __ov5693_write_reg_is_consecutive(struct i2c_client *client, -+ struct ov5693_write_ctrl *ctrl, -+ const struct ov5693_reg *next) -+{ -+ if (ctrl->index == 0) -+ return 1; -+ -+ return ctrl->buffer.addr + ctrl->index == next->reg; -+} -+ -+static int ov5693_write_reg_array(struct i2c_client *client, -+ const struct ov5693_reg *reglist) -+{ -+ const struct ov5693_reg *next = reglist; -+ struct ov5693_write_ctrl ctrl; -+ int err; -+ -+ ctrl.index = 0; -+ for (; next->type != OV5693_TOK_TERM; next++) { -+ switch (next->type & OV5693_TOK_MASK) { -+ case OV5693_TOK_DELAY: -+ err = __ov5693_flush_reg_array(client, &ctrl); -+ if (err) -+ return err; -+ usleep_range(next->val * 1000, (next->val + 1) * 1000); -+ break; -+ default: -+ /* -+ * If next address is not consecutive, data needs to be -+ * flushed before proceed. -+ */ -+ if (!__ov5693_write_reg_is_consecutive(client, &ctrl, -+ next)) { -+ err = __ov5693_flush_reg_array(client, &ctrl); -+ if (err) -+ return err; -+ } -+ err = __ov5693_buf_reg_array(client, &ctrl, next); -+ if (err) { -+ dev_err(&client->dev, "%s: write error, aborted\n", -+ __func__); -+ return err; -+ } -+ break; -+ } -+ } -+ -+ return __ov5693_flush_reg_array(client, &ctrl); -+} -+static int ov5693_g_focal(struct v4l2_subdev *sd, s32 *val) -+{ -+ *val = (OV5693_FOCAL_LENGTH_NUM << 16) | OV5693_FOCAL_LENGTH_DEM; -+ return 0; -+} -+ -+static int ov5693_g_fnumber(struct v4l2_subdev *sd, s32 *val) -+{ -+ /*const f number for ov5693*/ -+ *val = (OV5693_F_NUMBER_DEFAULT_NUM << 16) | OV5693_F_NUMBER_DEM; -+ return 0; -+} -+ -+static int ov5693_g_fnumber_range(struct v4l2_subdev *sd, s32 *val) -+{ -+ *val = (OV5693_F_NUMBER_DEFAULT_NUM << 24) | -+ (OV5693_F_NUMBER_DEM << 16) | -+ (OV5693_F_NUMBER_DEFAULT_NUM << 8) | OV5693_F_NUMBER_DEM; -+ return 0; -+} -+ -+ -+static int ov5693_get_intg_factor(struct i2c_client *client, -+ struct camera_mipi_info *info, -+ const struct ov5693_resolution *res) -+{ -+ struct atomisp_sensor_mode_data *buf = &info->data; -+ unsigned int pix_clk_freq_hz; -+ u16 reg_val; -+ int ret; -+ -+ if (info == NULL) -+ return -EINVAL; -+ -+ /* pixel clock calculattion */ -+ pix_clk_freq_hz = res->pix_clk_freq * 1000000; -+ -+ buf->vt_pix_clk_freq_mhz = pix_clk_freq_hz; -+ -+ /* get integration time */ -+ buf->coarse_integration_time_min = OV5693_COARSE_INTG_TIME_MIN; -+ buf->coarse_integration_time_max_margin = -+ OV5693_COARSE_INTG_TIME_MAX_MARGIN; -+ -+ buf->fine_integration_time_min = OV5693_FINE_INTG_TIME_MIN; -+ buf->fine_integration_time_max_margin = -+ OV5693_FINE_INTG_TIME_MAX_MARGIN; -+ -+ buf->fine_integration_time_def = OV5693_FINE_INTG_TIME_MIN; -+ buf->frame_length_lines = res->lines_per_frame; -+ buf->line_length_pck = res->pixels_per_line; -+ buf->read_mode = res->bin_mode; -+ -+ /* get the cropping and output resolution to ISP for this mode. */ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_H_CROP_START_H, ®_val); -+ if (ret) -+ return ret; -+ buf->crop_horizontal_start = reg_val; -+ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_V_CROP_START_H, ®_val); -+ if (ret) -+ return ret; -+ buf->crop_vertical_start = reg_val; -+ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_H_CROP_END_H, ®_val); -+ if (ret) -+ return ret; -+ buf->crop_horizontal_end = reg_val; -+ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_V_CROP_END_H, ®_val); -+ if (ret) -+ return ret; -+ buf->crop_vertical_end = reg_val; -+ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_H_OUTSIZE_H, ®_val); -+ if (ret) -+ return ret; -+ buf->output_width = reg_val; -+ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_V_OUTSIZE_H, ®_val); -+ if (ret) -+ return ret; -+ buf->output_height = reg_val; -+ -+ /* -+ * we can't return 0 for bin_factor, this is because camera -+ * HAL will use them as denominator, bin_factor = 0 will -+ * cause camera HAL crash. So we return bin_factor as this -+ * rules: -+ * [1]. res->bin_factor = 0, return 1 for bin_factor. -+ * [2]. res->bin_factor > 0, return res->bin_factor. -+ */ -+ buf->binning_factor_x = res->bin_factor_x ? -+ res->bin_factor_x : 1; -+ buf->binning_factor_y = res->bin_factor_y ? -+ res->bin_factor_y : 1; -+ return 0; -+} -+ -+static long __ov5693_set_exposure(struct v4l2_subdev *sd, int coarse_itg, -+ int gain, int digitgain) -+ -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ u16 vts; -+ int ret; -+ -+ /* -+ * According to spec, the low 4 bits of exposure/gain reg are -+ * fraction bits, so need to take 4 bits left shift to align -+ * reg integer bits. -+ */ -+ coarse_itg <<= 4; -+ gain <<= 4; -+ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_VTS_H, &vts); -+ if (ret) -+ return ret; -+ -+ if (coarse_itg + OV5693_INTEGRATION_TIME_MARGIN >= vts) -+ vts = coarse_itg + OV5693_INTEGRATION_TIME_MARGIN; -+ -+ ret = ov5693_write_reg(client, OV5693_16BIT, OV5693_VTS_H, vts); -+ if (ret) -+ return ret; -+ -+ /* group hold start */ -+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_GROUP_ACCESS, 0); -+ if (ret) -+ return ret; -+ -+ /* set exposure */ -+ ret = ov5693_write_reg(client, OV5693_8BIT, -+ OV5693_AEC_PK_EXPO_L, -+ coarse_itg & 0xff); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_write_reg(client, OV5693_16BIT, -+ OV5693_AEC_PK_EXPO_H, -+ (coarse_itg >> 8) & 0xfff); -+ if (ret) -+ return ret; -+ -+ /* set analog gain */ -+ ret = ov5693_write_reg(client, OV5693_16BIT, -+ OV5693_AGC_ADJ_H, gain); -+ if (ret) -+ return ret; -+ -+ /* set digital gain */ -+ ret = ov5693_write_reg(client, OV5693_16BIT, -+ OV5693_MWB_GAIN_R_H, digitgain); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_write_reg(client, OV5693_16BIT, -+ OV5693_MWB_GAIN_G_H, digitgain); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_write_reg(client, OV5693_16BIT, -+ OV5693_MWB_GAIN_B_H, digitgain); -+ if (ret) -+ return ret; -+ -+ /* group hold end */ -+ ret = ov5693_write_reg(client, OV5693_8BIT, -+ OV5693_GROUP_ACCESS, 0x10); -+ if (ret) -+ return ret; -+ -+ /* group hold launch */ -+ ret = ov5693_write_reg(client, OV5693_8BIT, -+ OV5693_GROUP_ACCESS, 0xa0); -+ -+ return ret; -+} -+ -+static int ov5693_set_exposure(struct v4l2_subdev *sd, int exposure, -+ int gain, int digitgain) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int ret; -+ -+ mutex_lock(&dev->input_lock); -+ ret = __ov5693_set_exposure(sd, exposure, gain, digitgain); -+ mutex_unlock(&dev->input_lock); -+ -+ return ret; -+} -+ -+static long ov5693_s_exposure(struct v4l2_subdev *sd, -+ struct atomisp_exposure *exposure) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int exp = exposure->integration_time[0]; -+ int gain = exposure->gain[0]; -+ int digitgain = exposure->gain[1]; -+ -+ /* we should not accept the invalid value below. */ -+ if (gain == 0) { -+ dev_err(&client->dev, "%s: invalid value\n", __func__); -+ return -EINVAL; -+ } -+ -+ return ov5693_set_exposure(sd, exp, gain, digitgain); -+} -+ -+static long ov5693_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -+{ -+ -+ switch (cmd) { -+ case ATOMISP_IOC_S_EXPOSURE: -+ return ov5693_s_exposure(sd, arg); -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+/* This returns the exposure time being used. This should only be used -+ for filling in EXIF data, not for actual image processing. */ -+static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ u16 reg_v, reg_v2; -+ int ret; -+ -+ /* get exposure */ -+ ret = ov5693_read_reg(client, OV5693_8BIT, -+ OV5693_AEC_PK_EXPO_L, -+ ®_v); -+ if (ret) -+ goto err; -+ -+ ret = ov5693_read_reg(client, OV5693_8BIT, -+ OV5693_AEC_PK_EXPO_M, -+ ®_v2); -+ if (ret) -+ goto err; -+ -+ reg_v += reg_v2 << 8; -+ ret = ov5693_read_reg(client, OV5693_8BIT, -+ OV5693_AEC_PK_EXPO_H, -+ ®_v2); -+ if (ret) -+ goto err; -+ -+ *value = (reg_v + (((u32)reg_v2 << 16))) >> 4; -+err: -+ return ret; -+} -+ -+/* -+ * This below focus func don't need input_lock mutex_lock -+ * since they are just called in v4l2 s_ctrl/g_ctrl framework -+ * where mutex input_lock have been done. -+ */ -+int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int ret = 0; -+ -+ if (dev->vcm_driver && dev->vcm_driver->t_focus_abs) -+ ret = dev->vcm_driver->t_focus_abs(sd, value); -+ -+ return ret; -+} -+ -+int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int ret = 0; -+ -+ if (dev->vcm_driver && dev->vcm_driver->t_focus_rel) -+ ret = dev->vcm_driver->t_focus_rel(sd, value); -+ -+ return ret; -+} -+ -+int ov5693_q_focus_status(struct v4l2_subdev *sd, s32 *value) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int ret = 0; -+ -+ if (dev->vcm_driver && dev->vcm_driver->q_focus_status) -+ ret = dev->vcm_driver->q_focus_status(sd, value); -+ -+ return ret; -+} -+ -+int ov5693_q_focus_abs(struct v4l2_subdev *sd, s32 *value) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int ret = 0; -+ -+ if (dev->vcm_driver && dev->vcm_driver->q_focus_abs) -+ ret = dev->vcm_driver->q_focus_abs(sd, value); -+ -+ return ret; -+} -+ -+/* ov5693 control set/get */ -+static int ov5693_g_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ov5693_device *dev = container_of( -+ ctrl->handler, struct ov5693_device, ctrl_handler); -+ int ret = 0; -+ -+ switch (ctrl->id) { -+ case V4L2_CID_EXPOSURE_ABSOLUTE: -+ ret = ov5693_q_exposure(&dev->sd, &ctrl->val); -+ break; -+ case V4L2_CID_FOCUS_ABSOLUTE: -+ ret = ov5693_q_focus_abs(&dev->sd, &ctrl->val); -+ break; -+ case V4L2_CID_FOCUS_STATUS: -+ ret = ov5693_q_focus_status(&dev->sd, &ctrl->val); -+ break; -+ case V4L2_CID_FOCAL_ABSOLUTE: -+ ret = ov5693_g_focal(&dev->sd, &ctrl->val); -+ break; -+ case V4L2_CID_FNUMBER_ABSOLUTE: -+ ret = ov5693_g_fnumber(&dev->sd, &ctrl->val); -+ break; -+ case V4L2_CID_FNUMBER_RANGE: -+ ret = ov5693_g_fnumber_range(&dev->sd, &ctrl->val); -+ break; -+ case V4L2_CID_BIN_FACTOR_HORZ: -+ ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_x; -+ break; -+ case V4L2_CID_BIN_FACTOR_VERT: -+ ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_y; -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ov5693_device *dev = container_of( -+ ctrl->handler, struct ov5693_device, ctrl_handler); -+ int ret = 0; -+ -+ if (!ctrl) -+ return -EINVAL; -+ -+ switch (ctrl->id) { -+ case V4L2_CID_RUN_MODE: -+ switch (ctrl->val) { -+ case ATOMISP_RUN_MODE_VIDEO: -+ dev->ov5693_res = ov5693_res_video; -+ dev->curr_res_num = N_RES_VIDEO; -+ break; -+ case ATOMISP_RUN_MODE_STILL_CAPTURE: -+ dev->ov5693_res = ov5693_res_still; -+ dev->curr_res_num = N_RES_STILL; -+ break; -+ default: -+ dev->ov5693_res = ov5693_res_preview; -+ dev->curr_res_num = N_RES_PREVIEW; -+ } -+ break; -+ case V4L2_CID_FOCUS_ABSOLUTE: -+ ret = ov5693_t_focus_abs(&dev->sd, ctrl->val); -+ break; -+ case V4L2_CID_FOCUS_RELATIVE: -+ ret = ov5693_t_focus_rel(&dev->sd, ctrl->val); -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static int ov5693_init(struct v4l2_subdev *sd) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int ret = 0; -+ -+ /* restore settings */ -+ dev->ov5693_res = ov5693_res_preview; -+ dev->curr_res_num = N_RES_PREVIEW; -+ -+ ret = ov5693_write_reg_array(client, ov5693_init_setting); -+ if (ret) -+ dev_err(&client->dev, "ov5693 write init setting reg err.\n"); -+ -+ return ret; -+} -+ -+ -+static int power_up(struct v4l2_subdev *sd) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret; -+ -+ if (NULL == dev->platform_data) { -+ dev_err(&client->dev, -+ "no camera_sensor_platform_data"); -+ return -ENODEV; -+ } -+ -+ /* power control */ -+ ret = dev->platform_data->power_ctrl(sd, 1); -+ if (ret) -+ goto fail_power; -+ -+ /* gpio ctrl */ -+ ret = dev->platform_data->gpio_ctrl(sd, 1); -+ if (ret) -+ goto fail_power; -+ -+ /* flis clock control */ -+ ret = dev->platform_data->flisclk_ctrl(sd, 1); -+ if (ret) -+ goto fail_clk; -+ -+ /* according to DS, 20ms is needed between PWDN and i2c access */ -+ msleep(20); -+ -+ return 0; -+ -+fail_clk: -+ dev->platform_data->gpio_ctrl(sd, 0); -+fail_power: -+ dev->platform_data->power_ctrl(sd, 0); -+ dev_err(&client->dev, "sensor power-up failed\n"); -+ -+ return ret; -+} -+ -+static int power_down(struct v4l2_subdev *sd) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret = 0; -+ -+ if (NULL == dev->platform_data) { -+ dev_err(&client->dev, -+ "no camera_sensor_platform_data"); -+ return -ENODEV; -+ } -+ -+ ret = dev->platform_data->flisclk_ctrl(sd, 0); -+ if (ret) -+ dev_err(&client->dev, "flisclk failed\n"); -+ -+ /* gpio ctrl */ -+ ret = dev->platform_data->gpio_ctrl(sd, 0); -+ if (ret) -+ dev_err(&client->dev, "gpio failed.\n"); -+ -+ /* power control */ -+ ret = dev->platform_data->power_ctrl(sd, 0); -+ if (ret) -+ dev_err(&client->dev, "vprog failed.\n"); -+ -+ return ret; -+} -+ -+static int ov5693_s_power(struct v4l2_subdev *sd, int on) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret = 0; -+ -+ mutex_lock(&dev->input_lock); -+ if (on == 0) { -+ if (dev->vcm_driver && dev->vcm_driver->power_down) -+ ret = dev->vcm_driver->power_down(sd); -+ if (ret) -+ dev_err(&client->dev, "vcm power-down failed.\n"); -+ -+ ret = power_down(sd); -+ } else { -+ if (dev->vcm_driver && dev->vcm_driver->power_up) -+ ret = dev->vcm_driver->power_up(sd); -+ if (ret) -+ dev_err(&client->dev, "vcm power-up failed.\n"); -+ -+ ret = power_up(sd); -+ if (!ret) -+ ret = ov5693_init(sd); -+ } -+ mutex_unlock(&dev->input_lock); -+ return ret; -+} -+ -+/* -+ * distance - calculate the distance -+ * @res: resolution -+ * @w: width -+ * @h: height -+ * -+ * Get the gap between resolution and w/h. -+ * res->width/height smaller than w/h wouldn't be considered. -+ * Returns the value of gap or -1 if fail. -+ */ -+static int distance(struct ov5693_resolution *res, u32 w, u32 h) -+{ -+ unsigned int w_ratio = ((res->width << RATIO_SHIFT_BITS)/w); -+ unsigned int h_ratio; -+ int match; -+ -+ if (h == 0) -+ return -1; -+ h_ratio = ((res->height << RATIO_SHIFT_BITS) / h); -+ if (h_ratio == 0) -+ return -1; -+ match = abs(((w_ratio << RATIO_SHIFT_BITS) / h_ratio) -+ - ((int)(1 << RATIO_SHIFT_BITS))); -+ -+ if ((w_ratio < (int)(1 << RATIO_SHIFT_BITS)) -+ || (h_ratio < (int)(1 << RATIO_SHIFT_BITS)) || -+ (match > LARGEST_ALLOWED_RATIO_MISMATCH)) -+ return -1; -+ -+ return w_ratio + h_ratio; -+} -+ -+/* Return the nearest higher resolution index */ -+static int nearest_resolution_index(struct v4l2_subdev *sd, -+ int w, int h) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int i; -+ int idx = dev->curr_res_num-1; -+ int dist; -+ int min_dist = INT_MAX; -+ struct ov5693_resolution *tmp_res = NULL; -+ -+ for (i = 0; i < dev->curr_res_num; i++) { -+ tmp_res = &dev->ov5693_res[i]; -+ dist = distance(tmp_res, w, h); -+ if (dist == -1) -+ continue; -+ if (dist < min_dist) { -+ min_dist = dist; -+ idx = i; -+ } -+ } -+ -+ return idx; -+} -+ -+static int get_resolution_index(struct v4l2_subdev *sd, -+ int w, int h) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int i; -+ -+ for (i = 0; i < dev->curr_res_num; i++) { -+ if (w != dev->ov5693_res[i].width) -+ continue; -+ if (h != dev->ov5693_res[i].height) -+ continue; -+ -+ return i; -+ } -+ -+ return -1; -+} -+ -+static int __ov5693_try_mbus_fmt(struct v4l2_subdev *sd, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int idx; -+ -+ if (!fmt) -+ return -EINVAL; -+ -+ idx = nearest_resolution_index(sd, fmt->width, fmt->height); -+ fmt->width = dev->ov5693_res[idx].width; -+ fmt->height = dev->ov5693_res[idx].height; -+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; -+ -+ return 0; -+} -+ -+static int ov5693_try_mbus_fmt(struct v4l2_subdev *sd, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int ret; -+ -+ mutex_lock(&dev->input_lock); -+ ret = __ov5693_try_mbus_fmt(sd, fmt); -+ mutex_unlock(&dev->input_lock); -+ -+ return ret; -+} -+ -+static int ov5693_s_mbus_fmt(struct v4l2_subdev *sd, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct camera_mipi_info *ov5693_info = NULL; -+ int ret = 0; -+ -+ ov5693_info = v4l2_get_subdev_hostdata(sd); -+ if (ov5693_info == NULL) -+ return -EINVAL; -+ -+ mutex_lock(&dev->input_lock); -+ ret = __ov5693_try_mbus_fmt(sd, fmt); -+ if (ret == -1) { -+ dev_err(&client->dev, "try fmt fail\n"); -+ goto done; -+ } -+ -+ dev->fmt_idx = get_resolution_index(sd, fmt->width, fmt->height); -+ if (dev->fmt_idx == -1) { -+ dev_err(&client->dev, "get resolution fail\n"); -+ goto done; -+ } -+ -+ ret = ov5693_write_reg_array(client, dev->ov5693_res[dev->fmt_idx].regs); -+ if (ret) { -+ dev_err(&client->dev, "ov5693 write fmt register err.\n"); -+ goto done; -+ } -+ -+ ret = ov5693_get_intg_factor(client, ov5693_info, -+ &dev->ov5693_res[dev->fmt_idx]); -+ if (ret) -+ dev_err(&client->dev, "failed to get integration_factor\n"); -+ -+done: -+ mutex_unlock(&dev->input_lock); -+ return ret; -+} -+static int ov5693_g_mbus_fmt(struct v4l2_subdev *sd, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ -+ mutex_lock(&dev->input_lock); -+ fmt->width = dev->ov5693_res[dev->fmt_idx].width; -+ fmt->height = dev->ov5693_res[dev->fmt_idx].height; -+ fmt->code = V4L2_MBUS_FMT_SBGGR10_1X10; -+ mutex_unlock(&dev->input_lock); -+ -+ return 0; -+} -+ -+static int ov5693_detect(struct i2c_client *client) -+{ -+ struct i2c_adapter *adapter = client->adapter; -+ int ret = 0; -+ u16 id; -+ u8 revision; -+ -+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) -+ return -ENODEV; -+ -+ ret = ov5693_read_reg(client, OV5693_16BIT, -+ OV5693_SC_CMMN_CHIP_ID, &id); -+ if (ret) { -+ dev_err(&client->dev, "read sensor_id err.\n"); -+ return -ENODEV; -+ } -+ -+ if (id != OV5693_ID) { -+ dev_err(&client->dev, "sensor ID error\n"); -+ return -ENODEV; -+ } -+ -+ ret = ov5693_read_reg(client, OV5693_8BIT, -+ OV5693_SC_CMMN_SUB_ID, &id); -+ revision = (u8)id & 0x0f; -+ -+ dev_dbg(&client->dev, "sensor_revision = 0x%x\n", revision); -+ dev_dbg(&client->dev, "detect ov5693 success\n"); -+ return ret; -+} -+ -+static int ov5693_s_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret; -+ -+ mutex_lock(&dev->input_lock); -+ -+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM, -+ enable ? OV5693_START_STREAMING : -+ OV5693_STOP_STREAMING); -+ -+ mutex_unlock(&dev->input_lock); -+ return ret; -+} -+ -+/* ov5693 enum frame size, frame intervals */ -+static int ov5693_enum_framesizes(struct v4l2_subdev *sd, -+ struct v4l2_frmsizeenum *fsize) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ unsigned int index = fsize->index; -+ -+ mutex_lock(&dev->input_lock); -+ -+ if (index >= dev->curr_res_num) { -+ mutex_unlock(&dev->input_lock); -+ return -EINVAL; -+ } -+ -+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; -+ fsize->discrete.width = dev->ov5693_res[index].width; -+ fsize->discrete.height = dev->ov5693_res[index].height; -+ -+ mutex_unlock(&dev->input_lock); -+ return 0; -+} -+ -+static int ov5693_enum_frameintervals(struct v4l2_subdev *sd, -+ struct v4l2_frmivalenum *fival) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ unsigned int index = fival->index; -+ -+ mutex_lock(&dev->input_lock); -+ -+ if (index >= dev->curr_res_num) { -+ mutex_unlock(&dev->input_lock); -+ return -EINVAL; -+ } -+ -+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; -+ fival->width = dev->ov5693_res[index].width; -+ fival->height = dev->ov5693_res[index].height; -+ fival->discrete.numerator = 1; -+ fival->discrete.denominator = dev->ov5693_res[index].fps; -+ -+ mutex_unlock(&dev->input_lock); -+ -+ return 0; -+} -+ -+static int ov5693_enum_mbus_fmt(struct v4l2_subdev *sd, -+ unsigned int index, -+ enum v4l2_mbus_pixelcode *code) -+{ -+ *code = V4L2_MBUS_FMT_SBGGR10_1X10; -+ -+ return 0; -+} -+ -+static int ov5693_s_config(struct v4l2_subdev *sd, -+ int irq, void *platform_data) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret = 0; -+ -+ if (platform_data == NULL) -+ return -ENODEV; -+ -+ mutex_lock(&dev->input_lock); -+ -+ dev->platform_data = platform_data; -+ -+ ret = power_up(sd); -+ if (ret) { -+ dev_err(&client->dev, "ov5693 power-up err.\n"); -+ goto fail_power_on; -+ } -+ -+ ret = dev->platform_data->csi_cfg(sd, 1); -+ if (ret) -+ goto fail_csi_cfg; -+ -+ /* config & detect sensor */ -+ ret = ov5693_detect(client); -+ if (ret) { -+ dev_err(&client->dev, "ov5693_detect err s_config.\n"); -+ goto fail_csi_cfg; -+ } -+ -+ /* turn off sensor, after probed */ -+ ret = power_down(sd); -+ if (ret) { -+ dev_err(&client->dev, "ov5693 power-off err.\n"); -+ goto fail_csi_cfg; -+ } -+ mutex_unlock(&dev->input_lock); -+ -+ return 0; -+ -+fail_csi_cfg: -+ dev->platform_data->csi_cfg(sd, 0); -+fail_power_on: -+ power_down(sd); -+ dev_err(&client->dev, "sensor power-gating failed\n"); -+ mutex_unlock(&dev->input_lock); -+ return ret; -+} -+ -+static int ov5693_g_parm(struct v4l2_subdev *sd, -+ struct v4l2_streamparm *param) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ -+ if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { -+ dev_err(&client->dev, "unsupported buffer type.\n"); -+ return -EINVAL; -+ } -+ -+ memset(param, 0, sizeof(*param)); -+ param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ -+ mutex_lock(&dev->input_lock); -+ if (dev->fmt_idx >= 0 && dev->fmt_idx < dev->curr_res_num) { -+ param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; -+ param->parm.capture.timeperframe.numerator = 1; -+ param->parm.capture.capturemode = dev->run_mode->val; -+ param->parm.capture.timeperframe.denominator = -+ dev->ov5693_res[dev->fmt_idx].fps; -+ } -+ mutex_unlock(&dev->input_lock); -+ return 0; -+} -+ -+static int ov5693_g_frame_interval(struct v4l2_subdev *sd, -+ struct v4l2_subdev_frame_interval *interval) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ -+ mutex_lock(&dev->input_lock); -+ interval->interval.numerator = 1; -+ interval->interval.denominator = dev->ov5693_res[dev->fmt_idx].fps; -+ mutex_unlock(&dev->input_lock); -+ -+ return 0; -+} -+ -+static int ov5693_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_fh *fh, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ code->code = V4L2_MBUS_FMT_SBGGR10_1X10; -+ return 0; -+} -+ -+static int ov5693_enum_frame_size(struct v4l2_subdev *sd, -+ struct v4l2_subdev_fh *fh, -+ struct v4l2_subdev_frame_size_enum *fse) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ int index = fse->index; -+ -+ mutex_lock(&dev->input_lock); -+ -+ if (index >= dev->curr_res_num) { -+ mutex_unlock(&dev->input_lock); -+ return -EINVAL; -+ } -+ -+ fse->min_width = dev->ov5693_res[index].width; -+ fse->min_height = dev->ov5693_res[index].height; -+ fse->max_width = dev->ov5693_res[index].width; -+ fse->max_height = dev->ov5693_res[index].height; -+ -+ mutex_unlock(&dev->input_lock); -+ return 0; -+} -+ -+static int ov5693_get_pad_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_fh *fh, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ struct v4l2_mbus_framefmt *format; -+ -+ mutex_lock(&dev->input_lock); -+ -+ switch (fmt->which) { -+ case V4L2_SUBDEV_FORMAT_TRY: -+ format = v4l2_subdev_get_try_format(fh, fmt->pad); -+ break; -+ case V4L2_SUBDEV_FORMAT_ACTIVE: -+ format = &dev->format; -+ break; -+ default: -+ format = NULL; -+ } -+ -+ mutex_unlock(&dev->input_lock); -+ -+ if (!format) -+ return -EINVAL; -+ -+ fmt->format = *format; -+ return 0; -+} -+ -+static int ov5693_set_pad_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_fh *fh, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ -+ mutex_lock(&dev->input_lock); -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) -+ dev->format = fmt->format; -+ -+ mutex_unlock(&dev->input_lock); -+ return 0; -+} -+ -+static int ov5693_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) -+{ -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ -+ mutex_lock(&dev->input_lock); -+ *frames = dev->ov5693_res[dev->fmt_idx].skip_frames; -+ mutex_unlock(&dev->input_lock); -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops ctrl_ops = { -+ .s_ctrl = ov5693_s_ctrl, -+ .g_volatile_ctrl = ov5693_g_ctrl, -+}; -+ -+static const char * const ctrl_run_mode_menu[] = { -+ NULL, -+ "Video", -+ "Still capture", -+ "Continuous capture", -+ "Preview", -+}; -+ -+static const struct v4l2_ctrl_config ctrl_run_mode = { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_RUN_MODE, -+ .name = "run mode", -+ .type = V4L2_CTRL_TYPE_MENU, -+ .min = 1, -+ .def = 4, -+ .max = 4, -+ .qmenu = ctrl_run_mode_menu, -+}; -+ -+static const struct v4l2_ctrl_config ctrls[] = { -+ { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_EXPOSURE_ABSOLUTE, -+ .name = "absolute exposure", -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .min = 0x0, -+ .max = 0xffff, -+ .step = 0x01, -+ .def = 0x00, -+ .flags = 0, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_FOCUS_ABSOLUTE, -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .name = "focus move absolute", -+ .min = 0, -+ .max = OV5693_MAX_FOCUS_POS, -+ .step = 1, -+ .def = 0, -+ .flags = 0, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_FOCUS_RELATIVE, -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .name = "focus move relative", -+ .min = OV5693_MAX_FOCUS_NEG, -+ .max = OV5693_MAX_FOCUS_POS, -+ .step = 1, -+ .def = 0, -+ .flags = 0, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_FOCUS_STATUS, -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .name = "focus status", -+ .min = 0, -+ .max = 100, -+ .step = 1, -+ .def = 0, -+ .flags = 0, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_FOCAL_ABSOLUTE, -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .name = "focal length", -+ .min = OV5693_FOCAL_LENGTH_DEFAULT, -+ .max = OV5693_FOCAL_LENGTH_DEFAULT, -+ .step = 0x01, -+ .def = OV5693_FOCAL_LENGTH_DEFAULT, -+ .flags = 0, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_FNUMBER_ABSOLUTE, -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .name = "f-number", -+ .min = OV5693_F_NUMBER_DEFAULT, -+ .max = OV5693_F_NUMBER_DEFAULT, -+ .step = 0x01, -+ .def = OV5693_F_NUMBER_DEFAULT, -+ .flags = 0, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_FNUMBER_RANGE, -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .name = "f-number range", -+ .min = OV5693_F_NUMBER_RANGE, -+ .max = OV5693_F_NUMBER_RANGE, -+ .step = 0x01, -+ .def = OV5693_F_NUMBER_RANGE, -+ .flags = 0, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_BIN_FACTOR_HORZ, -+ .name = "horizontal binning factor", -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .max = OV5693_BIN_FACTOR_MAX, -+ .step = 2, -+ .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, -+ }, { -+ .ops = &ctrl_ops, -+ .id = V4L2_CID_BIN_FACTOR_VERT, -+ .name = "vertical binning factor", -+ .type = V4L2_CTRL_TYPE_INTEGER, -+ .max = OV5693_BIN_FACTOR_MAX, -+ .step = 2, -+ .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, -+ } -+}; -+ -+static const struct v4l2_subdev_sensor_ops ov5693_sensor_ops = { -+ .g_skip_frames = ov5693_g_skip_frames, -+}; -+ -+static const struct v4l2_subdev_video_ops ov5693_video_ops = { -+ .s_stream = ov5693_s_stream, -+ .g_parm = ov5693_g_parm, -+ .enum_framesizes = ov5693_enum_framesizes, -+ .enum_frameintervals = ov5693_enum_frameintervals, -+ .enum_mbus_fmt = ov5693_enum_mbus_fmt, -+ .try_mbus_fmt = ov5693_try_mbus_fmt, -+ .g_mbus_fmt = ov5693_g_mbus_fmt, -+ .s_mbus_fmt = ov5693_s_mbus_fmt, -+ .g_frame_interval = ov5693_g_frame_interval, -+}; -+ -+static const struct v4l2_subdev_core_ops ov5693_core_ops = { -+ .s_power = ov5693_s_power, -+ .queryctrl = v4l2_subdev_queryctrl, -+ .g_ctrl = v4l2_subdev_g_ctrl, -+ .s_ctrl = v4l2_subdev_s_ctrl, -+ .ioctl = ov5693_ioctl, -+}; -+ -+static const struct v4l2_subdev_pad_ops ov5693_pad_ops = { -+ .enum_mbus_code = ov5693_enum_mbus_code, -+ .enum_frame_size = ov5693_enum_frame_size, -+ .get_fmt = ov5693_get_pad_format, -+ .set_fmt = ov5693_set_pad_format, -+}; -+ -+static const struct v4l2_subdev_ops ov5693_ops = { -+ .core = &ov5693_core_ops, -+ .video = &ov5693_video_ops, -+ .pad = &ov5693_pad_ops, -+ .sensor = &ov5693_sensor_ops, -+}; -+ -+static int ov5693_remove(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct ov5693_device *dev = to_ov5693_sensor(sd); -+ dev_dbg(&client->dev, "ov5693_remove...\n"); -+ -+ dev->platform_data->csi_cfg(sd, 0); -+ -+ v4l2_device_unregister_subdev(sd); -+ media_entity_cleanup(&dev->sd.entity); -+ devm_kfree(&client->dev, dev); -+ -+ return 0; -+} -+ -+static int ov5693_probe(struct i2c_client *client, -+ const struct i2c_device_id *id) -+{ -+ struct ov5693_device *dev; -+ int i; -+ int ret; -+ -+ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); -+ if (!dev) { -+ dev_err(&client->dev, "out of memory\n"); -+ return -ENOMEM; -+ } -+ -+ mutex_init(&dev->input_lock); -+ -+ /* -+ * Initialize related res members of dev. -+ */ -+ dev->fmt_idx = 0; -+ dev->ov5693_res = ov5693_res_preview; -+ dev->curr_res_num = N_RES_PREVIEW; -+ -+ v4l2_i2c_subdev_init(&(dev->sd), client, &ov5693_ops); -+ -+ if (client->dev.platform_data) { -+ ret = ov5693_s_config(&dev->sd, client->irq, -+ client->dev.platform_data); -+ if (ret) -+ goto out_free; -+ } -+ -+ dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -+ dev->pad.flags = MEDIA_PAD_FL_SOURCE; -+ dev->format.code = V4L2_MBUS_FMT_SBGGR10_1X10; -+ dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; -+ dev->vcm_driver = &ov5693_vcm_ops; -+ -+ ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls) + 1); -+ if (ret) { -+ ov5693_remove(client); -+ return ret; -+ } -+ -+ dev->run_mode = v4l2_ctrl_new_custom(&dev->ctrl_handler, -+ &ctrl_run_mode, NULL); -+ -+ for (i = 0; i < ARRAY_SIZE(ctrls); i++) -+ v4l2_ctrl_new_custom(&dev->ctrl_handler, &ctrls[i], NULL); -+ -+ if (dev->ctrl_handler.error) { -+ ov5693_remove(client); -+ return dev->ctrl_handler.error; -+ } -+ -+ dev->ctrl_handler.lock = &dev->input_lock; -+ dev->sd.ctrl_handler = &dev->ctrl_handler; -+ v4l2_ctrl_handler_setup(&dev->ctrl_handler); -+ -+ ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0); -+ if (ret) -+ ov5693_remove(client); -+ -+ /* vcm initialization */ -+ if (dev->vcm_driver && dev->vcm_driver->init) -+ ret = dev->vcm_driver->init(&dev->sd); -+ if (ret) { -+ dev_err(&client->dev, "vcm init failed.\n"); -+ ov5693_remove(client); -+ } -+ -+ return ret; -+out_free: -+ v4l2_device_unregister_subdev(&dev->sd); -+ devm_kfree(&client->dev, dev); -+ return ret; -+} -+ -+MODULE_DEVICE_TABLE(i2c, ov5693_id); -+static struct i2c_driver ov5693_driver = { -+ .driver = { -+ .owner = THIS_MODULE, -+ .name = OV5693_NAME, -+ }, -+ .probe = ov5693_probe, -+ .remove = ov5693_remove, -+ .id_table = ov5693_id, -+}; -+ -+module_i2c_driver(ov5693_driver); -+ -+MODULE_DESCRIPTION("A low-level driver for OmniVision 5693 sensors"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/staging/ov5693/ov5693.h b/drivers/staging/ov5693/ov5693.h -new file mode 100644 -index 000000000000..79aef69666e8 ---- /dev/null -+++ b/drivers/staging/ov5693/ov5693.h -@@ -0,0 +1,848 @@ -+/* -+ * Support for OmniVision OV5693 5M HD camera sensor. -+ * -+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License 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 __OV5693_H__ -+#define __OV5693_H__ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include "ad5823.h" -+ -+#define OV5693_NAME "ov5693" -+ -+/* Defines for register writes and register array processing */ -+#define I2C_MSG_LENGTH 0x2 -+ -+#define OV5693_FOCAL_LENGTH_NUM 278 /*2.78mm*/ -+#define OV5693_FOCAL_LENGTH_DEM 100 -+#define OV5693_F_NUMBER_DEFAULT_NUM 26 -+#define OV5693_F_NUMBER_DEM 10 -+ -+#define OV5693_MAX_FOCUS_POS 1023 -+#define OV5693_MAX_FOCUS_POS 1023 -+#define OV5693_MAX_FOCUS_NEG (-1023) -+ -+#define LARGEST_ALLOWED_RATIO_MISMATCH 800 -+#define RATIO_SHIFT_BITS 13 -+ -+/* -+ * focal length bits definition: -+ * bits 31-16: numerator, bits 15-0: denominator -+ */ -+#define OV5693_FOCAL_LENGTH_DEFAULT 0x1160064 -+ -+/* -+ * current f-number bits definition: -+ * bits 31-16: numerator, bits 15-0: denominator -+ */ -+#define OV5693_F_NUMBER_DEFAULT 0x1a000a -+ -+/* -+ * f-number range bits definition: -+ * bits 31-24: max f-number numerator -+ * bits 23-16: max f-number denominator -+ * bits 15-8: min f-number numerator -+ * bits 7-0: min f-number denominator -+ */ -+#define OV5693_F_NUMBER_RANGE 0x1a0a1a0a -+#define OV5693_ID 0x5690 -+ -+#define OV5693_FINE_INTG_TIME_MIN 0 -+#define OV5693_FINE_INTG_TIME_MAX_MARGIN 0 -+#define OV5693_COARSE_INTG_TIME_MIN 1 -+#define OV5693_COARSE_INTG_TIME_MAX_MARGIN (0xffff - 6) -+#define OV5693_INTEGRATION_TIME_MARGIN 8 -+ -+#define OV5693_BIN_FACTOR_MAX 2 -+ -+/* -+ * OV5693 System control registers -+ */ -+#define OV5693_SW_RESET 0x0103 -+#define OV5693_SW_STREAM 0x0100 -+ -+#define OV5693_SC_CMMN_CHIP_ID 0x300a -+#define OV5693_SC_CMMN_SUB_ID 0x302a /* process, version*/ -+ -+#define OV5693_AEC_PK_EXPO_H 0x3500 -+#define OV5693_AEC_PK_EXPO_M 0x3501 -+#define OV5693_AEC_PK_EXPO_L 0x3502 -+#define OV5693_AGC_ADJ_H 0x350a -+#define OV5693_VTS_H 0x380e -+#define OV5693_GROUP_ACCESS 0x3208 -+ -+#define OV5693_MWB_GAIN_R_H 0x3400 -+#define OV5693_MWB_GAIN_G_H 0x3402 -+#define OV5693_MWB_GAIN_B_H 0x3404 -+ -+#define OV5693_H_CROP_START_H 0x3800 -+#define OV5693_V_CROP_START_H 0x3802 -+#define OV5693_H_CROP_END_H 0x3804 -+#define OV5693_V_CROP_END_H 0x3806 -+#define OV5693_H_OUTSIZE_H 0x3808 -+#define OV5693_V_OUTSIZE_H 0x380a -+ -+#define OV5693_START_STREAMING 0x01 -+#define OV5693_STOP_STREAMING 0x00 -+ -+struct ov5693_vcm { -+ int (*power_up)(struct v4l2_subdev *sd); -+ int (*power_down)(struct v4l2_subdev *sd); -+ int (*init)(struct v4l2_subdev *sd); -+ int (*t_focus_vcm)(struct v4l2_subdev *sd, u16 val); -+ int (*t_focus_abs)(struct v4l2_subdev *sd, s32 value); -+ int (*t_focus_rel)(struct v4l2_subdev *sd, s32 value); -+ int (*q_focus_status)(struct v4l2_subdev *sd, s32 *value); -+ int (*q_focus_abs)(struct v4l2_subdev *sd, s32 *value); -+}; -+ -+struct ov5693_resolution { -+ u8 *desc; -+ const struct ov5693_reg *regs; -+ int res; -+ int width; -+ int height; -+ int fps; -+ int pix_clk_freq; -+ u16 skip_frames; -+ u16 pixels_per_line; -+ u16 lines_per_frame; -+ u8 bin_factor_x; -+ u8 bin_factor_y; -+ u8 bin_mode; -+ bool used; -+}; -+ -+struct ov5693_control { -+ struct v4l2_queryctrl qc; -+ int (*query)(struct v4l2_subdev *sd, s32 *value); -+ int (*tweak)(struct v4l2_subdev *sd, s32 value); -+}; -+ -+/* -+ * ov5693 device structure. -+ */ -+struct ov5693_device { -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ struct v4l2_mbus_framefmt format; -+ struct mutex input_lock; -+ -+ struct camera_sensor_platform_data *platform_data; -+ struct ov5693_vcm *vcm_driver; -+ int fmt_idx; -+ u8 res; -+ u8 type; -+ -+ struct ov5693_resolution *ov5693_res; -+ int curr_res_num; -+ -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct v4l2_ctrl *run_mode; -+}; -+ -+enum ov5693_tok_type { -+ OV5693_8BIT = 0x0001, -+ OV5693_16BIT = 0x0002, -+ OV5693_32BIT = 0x0004, -+ OV5693_TOK_TERM = 0xf000, /* terminating token for reg list */ -+ OV5693_TOK_DELAY = 0xfe00, /* delay token for reg list */ -+ OV5693_TOK_MASK = 0xfff0 -+}; -+ -+/** -+ * struct ov5693_reg - MI sensor register format -+ * @type: type of the register -+ * @reg: 16-bit offset to register -+ * @val: 8/16/32-bit register value -+ * -+ * Define a structure for sensor register initialization values -+ */ -+struct ov5693_reg { -+ enum ov5693_tok_type type; -+ u16 reg; -+ u32 val; /* @set value for read/mod/write, @mask */ -+}; -+ -+#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd) -+ -+#define OV5693_MAX_WRITE_BUF_SIZE 30 -+ -+struct ov5693_write_buffer { -+ u16 addr; -+ u8 data[OV5693_MAX_WRITE_BUF_SIZE]; -+}; -+ -+struct ov5693_write_ctrl { -+ int index; -+ struct ov5693_write_buffer buffer; -+}; -+ -+static const struct i2c_device_id ov5693_id[] = { -+ {OV5693_NAME, 0}, -+ {} -+}; -+ -+/* ov5693 sensor initialization setting */ -+static struct ov5693_reg const ov5693_init_setting[] = { -+ {OV5693_8BIT, 0x0103, 0x01}, -+ {OV5693_8BIT, 0x3001, 0x0a}, -+ {OV5693_8BIT, 0x3002, 0x80}, -+ {OV5693_8BIT, 0x3006, 0x00}, -+ {OV5693_8BIT, 0x3011, 0x21}, -+ {OV5693_8BIT, 0x3012, 0x09}, -+ {OV5693_8BIT, 0x3013, 0x10}, -+ {OV5693_8BIT, 0x3014, 0x00}, -+ {OV5693_8BIT, 0x3015, 0x08}, -+ {OV5693_8BIT, 0x3016, 0xf0}, -+ {OV5693_8BIT, 0x3017, 0xf0}, -+ {OV5693_8BIT, 0x3018, 0xf0}, -+ {OV5693_8BIT, 0x301b, 0xb4}, -+ {OV5693_8BIT, 0x301d, 0x02}, -+ {OV5693_8BIT, 0x3021, 0x00}, -+ {OV5693_8BIT, 0x3022, 0x01}, -+ {OV5693_8BIT, 0x3028, 0x44}, -+ {OV5693_8BIT, 0x3098, 0x02}, -+ {OV5693_8BIT, 0x3099, 0x19}, -+ {OV5693_8BIT, 0x309a, 0x02}, -+ {OV5693_8BIT, 0x309b, 0x01}, -+ {OV5693_8BIT, 0x309c, 0x00}, -+ {OV5693_8BIT, 0x30a0, 0xd2}, -+ {OV5693_8BIT, 0x30a2, 0x01}, -+ {OV5693_8BIT, 0x30b2, 0x00}, -+ {OV5693_8BIT, 0x30b3, 0x7d}, -+ {OV5693_8BIT, 0x30b4, 0x03}, -+ {OV5693_8BIT, 0x30b5, 0x04}, -+ {OV5693_8BIT, 0x30b6, 0x01}, -+ {OV5693_8BIT, 0x3104, 0x21}, -+ {OV5693_8BIT, 0x3106, 0x00}, -+ -+ /* Manual white balance */ -+ {OV5693_8BIT, 0x3400, 0x04}, -+ {OV5693_8BIT, 0x3401, 0x00}, -+ {OV5693_8BIT, 0x3402, 0x04}, -+ {OV5693_8BIT, 0x3403, 0x00}, -+ {OV5693_8BIT, 0x3404, 0x04}, -+ {OV5693_8BIT, 0x3405, 0x00}, -+ {OV5693_8BIT, 0x3406, 0x01}, -+ -+ /* Manual exposure control */ -+ {OV5693_8BIT, 0x3500, 0x00}, -+ {OV5693_8BIT, 0x3503, 0x07}, -+ {OV5693_8BIT, 0x3504, 0x00}, -+ {OV5693_8BIT, 0x3505, 0x00}, -+ {OV5693_8BIT, 0x3506, 0x00}, -+ {OV5693_8BIT, 0x3507, 0x02}, -+ {OV5693_8BIT, 0x3508, 0x00}, -+ -+ /* Manual gain control */ -+ {OV5693_8BIT, 0x3509, 0x10}, -+ {OV5693_8BIT, 0x350a, 0x00}, -+ {OV5693_8BIT, 0x350b, 0x40}, -+ -+ {OV5693_8BIT, 0x3601, 0x0a}, -+ {OV5693_8BIT, 0x3602, 0x38}, -+ {OV5693_8BIT, 0x3612, 0x80}, -+ {OV5693_8BIT, 0x3620, 0x54}, -+ {OV5693_8BIT, 0x3621, 0xc7}, -+ {OV5693_8BIT, 0x3622, 0x0f}, -+ {OV5693_8BIT, 0x3625, 0x10}, -+ {OV5693_8BIT, 0x3630, 0x55}, -+ {OV5693_8BIT, 0x3631, 0xf4}, -+ {OV5693_8BIT, 0x3632, 0x00}, -+ {OV5693_8BIT, 0x3633, 0x34}, -+ {OV5693_8BIT, 0x3634, 0x02}, -+ {OV5693_8BIT, 0x364d, 0x0d}, -+ {OV5693_8BIT, 0x364f, 0xdd}, -+ {OV5693_8BIT, 0x3660, 0x04}, -+ {OV5693_8BIT, 0x3662, 0x10}, -+ {OV5693_8BIT, 0x3663, 0xf1}, -+ {OV5693_8BIT, 0x3665, 0x00}, -+ {OV5693_8BIT, 0x3666, 0x20}, -+ {OV5693_8BIT, 0x3667, 0x00}, -+ {OV5693_8BIT, 0x366a, 0x80}, -+ {OV5693_8BIT, 0x3680, 0xe0}, -+ {OV5693_8BIT, 0x3681, 0x00}, -+ {OV5693_8BIT, 0x3700, 0x42}, -+ {OV5693_8BIT, 0x3701, 0x14}, -+ {OV5693_8BIT, 0x3702, 0xa0}, -+ {OV5693_8BIT, 0x3703, 0xd8}, -+ {OV5693_8BIT, 0x3704, 0x78}, -+ {OV5693_8BIT, 0x3705, 0x02}, -+ {OV5693_8BIT, 0x370a, 0x00}, -+ {OV5693_8BIT, 0x370b, 0x20}, -+ {OV5693_8BIT, 0x370c, 0x0c}, -+ {OV5693_8BIT, 0x370d, 0x11}, -+ {OV5693_8BIT, 0x370e, 0x00}, -+ {OV5693_8BIT, 0x370f, 0x40}, -+ {OV5693_8BIT, 0x3710, 0x00}, -+ {OV5693_8BIT, 0x371a, 0x1c}, -+ {OV5693_8BIT, 0x371b, 0x05}, -+ {OV5693_8BIT, 0x371c, 0x01}, -+ {OV5693_8BIT, 0x371e, 0xa1}, -+ {OV5693_8BIT, 0x371f, 0x0c}, -+ {OV5693_8BIT, 0x3721, 0x00}, -+ {OV5693_8BIT, 0x3724, 0x10}, -+ {OV5693_8BIT, 0x3726, 0x00}, -+ {OV5693_8BIT, 0x372a, 0x01}, -+ {OV5693_8BIT, 0x3730, 0x10}, -+ {OV5693_8BIT, 0x3738, 0x22}, -+ {OV5693_8BIT, 0x3739, 0xe5}, -+ {OV5693_8BIT, 0x373a, 0x50}, -+ {OV5693_8BIT, 0x373b, 0x02}, -+ {OV5693_8BIT, 0x373c, 0x41}, -+ {OV5693_8BIT, 0x373f, 0x02}, -+ {OV5693_8BIT, 0x3740, 0x42}, -+ {OV5693_8BIT, 0x3741, 0x02}, -+ {OV5693_8BIT, 0x3742, 0x18}, -+ {OV5693_8BIT, 0x3743, 0x01}, -+ {OV5693_8BIT, 0x3744, 0x02}, -+ {OV5693_8BIT, 0x3747, 0x10}, -+ {OV5693_8BIT, 0x374c, 0x04}, -+ {OV5693_8BIT, 0x3751, 0xf0}, -+ {OV5693_8BIT, 0x3752, 0x00}, -+ {OV5693_8BIT, 0x3753, 0x00}, -+ {OV5693_8BIT, 0x3754, 0xc0}, -+ {OV5693_8BIT, 0x3755, 0x00}, -+ {OV5693_8BIT, 0x3756, 0x1a}, -+ {OV5693_8BIT, 0x3758, 0x00}, -+ {OV5693_8BIT, 0x3759, 0x0f}, -+ {OV5693_8BIT, 0x376b, 0x44}, -+ {OV5693_8BIT, 0x375c, 0x04}, -+ {OV5693_8BIT, 0x3774, 0x10}, -+ {OV5693_8BIT, 0x3776, 0x00}, -+ {OV5693_8BIT, 0x377f, 0x08}, -+ {OV5693_8BIT, 0x3780, 0x22}, -+ {OV5693_8BIT, 0x3781, 0x0c}, -+ {OV5693_8BIT, 0x3784, 0x2c}, -+ {OV5693_8BIT, 0x3785, 0x1e}, -+ {OV5693_8BIT, 0x378f, 0xf5}, -+ {OV5693_8BIT, 0x3791, 0xb0}, -+ {OV5693_8BIT, 0x3795, 0x00}, -+ {OV5693_8BIT, 0x3796, 0x64}, -+ {OV5693_8BIT, 0x3797, 0x11}, -+ {OV5693_8BIT, 0x3798, 0x30}, -+ {OV5693_8BIT, 0x3799, 0x41}, -+ {OV5693_8BIT, 0x379a, 0x07}, -+ {OV5693_8BIT, 0x379b, 0xb0}, -+ {OV5693_8BIT, 0x379c, 0x0c}, -+ {OV5693_8BIT, 0x37c5, 0x00}, -+ {OV5693_8BIT, 0x37c6, 0x00}, -+ {OV5693_8BIT, 0x37c7, 0x00}, -+ {OV5693_8BIT, 0x37c9, 0x00}, -+ {OV5693_8BIT, 0x37ca, 0x00}, -+ {OV5693_8BIT, 0x37cb, 0x00}, -+ {OV5693_8BIT, 0x37de, 0x00}, -+ {OV5693_8BIT, 0x37df, 0x00}, -+ {OV5693_8BIT, 0x3800, 0x00}, -+ {OV5693_8BIT, 0x3801, 0x00}, -+ {OV5693_8BIT, 0x3802, 0x00}, -+ {OV5693_8BIT, 0x3804, 0x0a}, -+ {OV5693_8BIT, 0x3805, 0x3f}, -+ {OV5693_8BIT, 0x3810, 0x00}, -+ {OV5693_8BIT, 0x3812, 0x00}, -+ {OV5693_8BIT, 0x3823, 0x00}, -+ {OV5693_8BIT, 0x3824, 0x00}, -+ {OV5693_8BIT, 0x3825, 0x00}, -+ {OV5693_8BIT, 0x3826, 0x00}, -+ {OV5693_8BIT, 0x3827, 0x00}, -+ {OV5693_8BIT, 0x382a, 0x04}, -+ {OV5693_8BIT, 0x3a04, 0x06}, -+ {OV5693_8BIT, 0x3a05, 0x14}, -+ {OV5693_8BIT, 0x3a06, 0x00}, -+ {OV5693_8BIT, 0x3a07, 0xfe}, -+ {OV5693_8BIT, 0x3b00, 0x00}, -+ {OV5693_8BIT, 0x3b02, 0x00}, -+ {OV5693_8BIT, 0x3b03, 0x00}, -+ {OV5693_8BIT, 0x3b04, 0x00}, -+ {OV5693_8BIT, 0x3b05, 0x00}, -+ {OV5693_8BIT, 0x3e07, 0x20}, -+ {OV5693_8BIT, 0x4000, 0x08}, -+ {OV5693_8BIT, 0x4001, 0x04}, -+ {OV5693_8BIT, 0x4002, 0x45}, -+ {OV5693_8BIT, 0x4004, 0x08}, -+ {OV5693_8BIT, 0x4005, 0x18}, -+ {OV5693_8BIT, 0x4006, 0x20}, -+ {OV5693_8BIT, 0x4008, 0x24}, -+ {OV5693_8BIT, 0x4009, 0x10}, -+ {OV5693_8BIT, 0x400c, 0x00}, -+ {OV5693_8BIT, 0x400d, 0x00}, -+ {OV5693_8BIT, 0x4058, 0x00}, -+ {OV5693_8BIT, 0x404e, 0x37}, -+ {OV5693_8BIT, 0x404f, 0x8f}, -+ {OV5693_8BIT, 0x4058, 0x00}, -+ {OV5693_8BIT, 0x4101, 0xb2}, -+ {OV5693_8BIT, 0x4303, 0x00}, -+ {OV5693_8BIT, 0x4304, 0x08}, -+ {OV5693_8BIT, 0x4307, 0x30}, -+ {OV5693_8BIT, 0x4311, 0x04}, -+ {OV5693_8BIT, 0x4315, 0x01}, -+ {OV5693_8BIT, 0x4511, 0x05}, -+ {OV5693_8BIT, 0x4512, 0x01}, -+ {OV5693_8BIT, 0x4806, 0x00}, -+ {OV5693_8BIT, 0x4816, 0x52}, -+ {OV5693_8BIT, 0x481f, 0x30}, -+ {OV5693_8BIT, 0x4826, 0x2c}, -+ {OV5693_8BIT, 0x4831, 0x64}, -+ {OV5693_8BIT, 0x4d00, 0x04}, -+ {OV5693_8BIT, 0x4d01, 0x71}, -+ {OV5693_8BIT, 0x4d02, 0xfd}, -+ {OV5693_8BIT, 0x4d03, 0xf5}, -+ {OV5693_8BIT, 0x4d04, 0x0c}, -+ {OV5693_8BIT, 0x4d05, 0xcc}, -+ {OV5693_8BIT, 0x4837, 0x0a}, -+ {OV5693_8BIT, 0x5000, 0x06}, -+ {OV5693_8BIT, 0x5001, 0x01}, -+ {OV5693_8BIT, 0x5003, 0x20}, -+ {OV5693_8BIT, 0x5046, 0x0a}, -+ {OV5693_8BIT, 0x5013, 0x00}, -+ {OV5693_8BIT, 0x5046, 0x0a}, -+ {OV5693_8BIT, 0x5780, 0x1c}, -+ {OV5693_8BIT, 0x5786, 0x20}, -+ {OV5693_8BIT, 0x5787, 0x10}, -+ {OV5693_8BIT, 0x5788, 0x18}, -+ {OV5693_8BIT, 0x578a, 0x04}, -+ {OV5693_8BIT, 0x578b, 0x02}, -+ {OV5693_8BIT, 0x578c, 0x02}, -+ {OV5693_8BIT, 0x578e, 0x06}, -+ {OV5693_8BIT, 0x578f, 0x02}, -+ {OV5693_8BIT, 0x5790, 0x02}, -+ {OV5693_8BIT, 0x5791, 0xff}, -+ {OV5693_8BIT, 0x5842, 0x01}, -+ {OV5693_8BIT, 0x5843, 0x2b}, -+ {OV5693_8BIT, 0x5844, 0x01}, -+ {OV5693_8BIT, 0x5845, 0x92}, -+ {OV5693_8BIT, 0x5846, 0x01}, -+ {OV5693_8BIT, 0x5847, 0x8f}, -+ {OV5693_8BIT, 0x5848, 0x01}, -+ {OV5693_8BIT, 0x5849, 0x0c}, -+ {OV5693_8BIT, 0x5e00, 0x00}, -+ {OV5693_8BIT, 0x5e10, 0x0c}, -+ {OV5693_8BIT, 0x0100, 0x00}, -+ {OV5693_TOK_TERM, 0, 0} -+}; -+ -+/* -+ * Register settings for various resolution -+ */ -+ -+/* -+------------------------------------ -+@@ FULL QSXGA (2592x1944) 15fps 33.33ms VBlank 2lane 10Bit -+100 99 2592 1944 -+100 98 1 0 -+102 3601 bb8 ;Pather tool use only -+c8 1 f2 ; New FPGA Board -+c8 20 22 ; New FPGA Board -+c8 10 42 ; MIPI DFGA CYCY3 Board use only -+; -+c8 f 32 ; input clock to 19.2MHz -+; -+; OV5690 setting version History -+; -+; -+; V18b -+*/ -+static struct ov5693_reg const ov5693_5M_15fps[] = { -+ {OV5693_8BIT, 0x3501, 0x7b}, -+ {OV5693_8BIT, 0x3502, 0x00}, -+ {OV5693_8BIT, 0x3708, 0xe2}, -+ {OV5693_8BIT, 0x3709, 0xc3}, -+ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ -+ {OV5693_8BIT, 0x3801, 0x00}, -+ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ -+ {OV5693_8BIT, 0x3803, 0x00}, -+ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ -+ {OV5693_8BIT, 0x3805, 0x3f}, -+ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ -+ {OV5693_8BIT, 0x3807, 0xa3}, -+ {OV5693_8BIT, 0x3808, 0x0a}, /* x output size: 2592 */ -+ {OV5693_8BIT, 0x3809, 0x20}, -+ {OV5693_8BIT, 0x380a, 0x07}, /* y output size: 1944 */ -+ {OV5693_8BIT, 0x380b, 0x98}, -+ {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ -+ {OV5693_8BIT, 0x380d, 0x68}, -+ {OV5693_8BIT, 0x380e, 0x0f}, /* total y output size: 3968 */ -+ {OV5693_8BIT, 0x380f, 0x80}, -+ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 16 */ -+ {OV5693_8BIT, 0x3811, 0x10}, -+ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 6 */ -+ {OV5693_8BIT, 0x3813, 0x06}, -+ {OV5693_8BIT, 0x3814, 0x11}, -+ {OV5693_8BIT, 0x3815, 0x11}, -+ {OV5693_8BIT, 0x3820, 0x00}, -+ {OV5693_8BIT, 0x3821, 0x1e}, -+ {OV5693_8BIT, 0x5002, 0x00}, -+ {OV5693_TOK_TERM, 0, 0} -+}; -+ -+/* -+@@ OV5693 1940x1096 30fps 8.8ms VBlanking 2lane 10Bit(Scaling) -+100 99 1940 1096 -+100 98 1 0 -+102 3601 bb8 ;Pather tool use only -+102 40 0 ; HDR Mode off -+c8 1 f2 ; New FPGA Board -+c8 20 22 ; New FPGA Board -+c8 10 42 ; MIPI DFGA CYCY3 Board use only -+; -+c8 f 32 ; input clock to 19.2MHz -+; -+; OV5690 setting version History -+; -+; -+; V18b -+*/ -+static struct ov5693_reg const ov5693_1080p_30fps[] = { -+ {OV5693_8BIT, 0x3501, 0x7b}, -+ {OV5693_8BIT, 0x3502, 0x00}, -+ {OV5693_8BIT, 0x3708, 0xe2}, -+ {OV5693_8BIT, 0x3709, 0xc3}, -+ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ -+ {OV5693_8BIT, 0x3801, 0x00}, -+ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 240 */ -+ {OV5693_8BIT, 0x3803, 0xf0}, -+ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2591 */ -+ {OV5693_8BIT, 0x3805, 0x1f}, -+ {OV5693_8BIT, 0x3806, 0x06}, /* y_addr_end: 1703 */ -+ {OV5693_8BIT, 0x3807, 0xa7}, -+ {OV5693_8BIT, 0x3808, 0x07}, /* x output size: 1940 */ -+ {OV5693_8BIT, 0x3809, 0x94}, -+ {OV5693_8BIT, 0x380a, 0x04}, /* y output size: 1096 */ -+ {OV5693_8BIT, 0x380b, 0x48}, -+ {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ -+ {OV5693_8BIT, 0x380d, 0x68}, -+ {OV5693_8BIT, 0x380e, 0x0b}, /* total y output size: 2984 */ -+ {OV5693_8BIT, 0x380f, 0xa8}, -+ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 2 */ -+ {OV5693_8BIT, 0x3811, 0x02}, -+ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ -+ {OV5693_8BIT, 0x3813, 0x02}, -+ {OV5693_8BIT, 0x3814, 0x11}, -+ {OV5693_8BIT, 0x3815, 0x11}, -+ {OV5693_8BIT, 0x3820, 0x00}, -+ {OV5693_8BIT, 0x3821, 0x1e}, -+ {OV5693_8BIT, 0x5002, 0x80}, -+ {OV5693_TOK_TERM, 0, 0} -+}; -+ -+/* -+ * 1296x736 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) -+ */ -+static struct ov5693_reg const ov5693_720p_30fps[] = { -+ {OV5693_8BIT, 0x3501, 0x3d}, -+ {OV5693_8BIT, 0x3502, 0x00}, -+ {OV5693_8BIT, 0x3708, 0xe6}, -+ {OV5693_8BIT, 0x3709, 0xc7}, -+ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ -+ {OV5693_8BIT, 0x3801, 0x00}, -+ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ -+ {OV5693_8BIT, 0x3803, 0x00}, -+ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ -+ {OV5693_8BIT, 0x3805, 0x3f}, -+ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ -+ {OV5693_8BIT, 0x3807, 0xa3}, -+ {OV5693_8BIT, 0x3808, 0x05}, /* x output size: 1296 */ -+ {OV5693_8BIT, 0x3809, 0x10}, -+ {OV5693_8BIT, 0x380a, 0x02}, /* y output size: 736 */ -+ {OV5693_8BIT, 0x380b, 0xe0}, -+ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ -+ {OV5693_8BIT, 0x380d, 0x80}, -+ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ -+ {OV5693_8BIT, 0x380f, 0xc0}, -+ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ -+ {OV5693_8BIT, 0x3811, 0x08}, -+ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ -+ {OV5693_8BIT, 0x3813, 0x02}, -+ {OV5693_8BIT, 0x3814, 0x31}, -+ {OV5693_8BIT, 0x3815, 0x31}, -+ {OV5693_8BIT, 0x3820, 0x04}, -+ {OV5693_8BIT, 0x3821, 0x1f}, -+ {OV5693_8BIT, 0x5002, 0x80}, -+ {OV5693_TOK_TERM, 0, 0} -+}; -+ -+/* -+ * 736x496 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) -+ */ -+static struct ov5693_reg const ov5693_480p_30fps[] = { -+ {OV5693_8BIT, 0x3501, 0x3d}, -+ {OV5693_8BIT, 0x3502, 0x00}, -+ {OV5693_8BIT, 0x3708, 0xe6}, -+ {OV5693_8BIT, 0x3709, 0xc7}, -+ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ -+ {OV5693_8BIT, 0x3801, 0x00}, -+ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 7 */ -+ {OV5693_8BIT, 0x3803, 0x07}, -+ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ -+ {OV5693_8BIT, 0x3805, 0x3f}, -+ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ -+ {OV5693_8BIT, 0x3807, 0xa3}, -+ {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 736 */ -+ {OV5693_8BIT, 0x3809, 0xe0}, -+ {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ -+ {OV5693_8BIT, 0x380b, 0xf0}, -+ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ -+ {OV5693_8BIT, 0x380d, 0x80}, -+ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ -+ {OV5693_8BIT, 0x380f, 0xc0}, -+ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ -+ {OV5693_8BIT, 0x3811, 0x08}, -+ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ -+ {OV5693_8BIT, 0x3813, 0x02}, -+ {OV5693_8BIT, 0x3814, 0x31}, -+ {OV5693_8BIT, 0x3815, 0x31}, -+ {OV5693_8BIT, 0x3820, 0x04}, -+ {OV5693_8BIT, 0x3821, 0x1f}, -+ {OV5693_8BIT, 0x5002, 0x80}, -+ {OV5693_TOK_TERM, 0, 0} -+}; -+ -+/* -+@@ OV5693 656x496 30fps 17ms VBlanking 2lane 10Bit(Scaling) -+100 99 656 496 -+100 98 1 0 -+102 3601 BB8 ;Pather tool use only -+c8 1 f2 ; New FPGA Board -+c8 20 22 ; New FPGA Board -+; OV5690 setting version History -+; -+c8 f 32 ; input clock to 19.2MHz -+; -+; V18b -+*/ -+static struct ov5693_reg const ov5693_VGA_30fps[] = { -+ {OV5693_8BIT, 0x3501, 0x3d}, -+ {OV5693_8BIT, 0x3502, 0x00}, -+ {OV5693_8BIT, 0x3708, 0xe6}, -+ {OV5693_8BIT, 0x3709, 0xc7}, -+ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ -+ {OV5693_8BIT, 0x3801, 0x00}, -+ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ -+ {OV5693_8BIT, 0x3803, 0x00}, -+ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ -+ {OV5693_8BIT, 0x3805, 0x3f}, -+ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ -+ {OV5693_8BIT, 0x3807, 0xa3}, -+ {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 656 */ -+ {OV5693_8BIT, 0x3809, 0x90}, -+ {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ -+ {OV5693_8BIT, 0x380b, 0xf0}, -+ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ -+ {OV5693_8BIT, 0x380d, 0x80}, -+ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ -+ {OV5693_8BIT, 0x380f, 0xc0}, -+ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 13 */ -+ {OV5693_8BIT, 0x3811, 0x0d}, -+ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 3 */ -+ {OV5693_8BIT, 0x3813, 0x03}, -+ {OV5693_8BIT, 0x3814, 0x31}, -+ {OV5693_8BIT, 0x3815, 0x31}, -+ {OV5693_8BIT, 0x3820, 0x04}, -+ {OV5693_8BIT, 0x3821, 0x1f}, -+ {OV5693_8BIT, 0x5002, 0x80}, -+ {OV5693_TOK_TERM, 0, 0} -+}; -+ -+struct ov5693_resolution ov5693_res_preview[] = { -+ { -+ .desc = "ov5693_VGA_30fps", -+ .width = 656, -+ .height = 496, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 2688, -+ .lines_per_frame = 1984, -+ .bin_factor_x = 2, -+ .bin_factor_y = 2, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_VGA_30fps, -+ }, -+ { -+ .desc = "ov5693_1080P_30fps", -+ .width = 1940, -+ .height = 1096, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 3688, -+ .lines_per_frame = 2984, -+ .bin_factor_x = 0, -+ .bin_factor_y = 0, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_1080p_30fps, -+ }, -+ { -+ .desc = "ov5693_5M_15fps", -+ .width = 2592, -+ .height = 1944, -+ .fps = 15, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 3688, -+ .lines_per_frame = 3968, -+ .bin_factor_x = 0, -+ .bin_factor_y = 0, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_5M_15fps, -+ }, -+}; -+#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview)) -+ -+struct ov5693_resolution ov5693_res_still[] = { -+ { -+ .desc = "ov5693_VGA_30fps", -+ .width = 656, -+ .height = 496, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 2688, -+ .lines_per_frame = 1984, -+ .bin_factor_x = 2, -+ .bin_factor_y = 2, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_VGA_30fps, -+ }, -+ { -+ .desc = "ov5693_1080P_30fps", -+ .width = 1940, -+ .height = 1096, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 3688, -+ .lines_per_frame = 2984, -+ .bin_factor_x = 0, -+ .bin_factor_y = 0, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_1080p_30fps, -+ }, -+ { -+ .desc = "ov5693_5M_15fps", -+ .width = 2592, -+ .height = 1944, -+ .fps = 15, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 3688, -+ .lines_per_frame = 3968, -+ .bin_factor_x = 0, -+ .bin_factor_y = 0, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_5M_15fps, -+ }, -+}; -+#define N_RES_STILL (ARRAY_SIZE(ov5693_res_still)) -+ -+struct ov5693_resolution ov5693_res_video[] = { -+ { -+ .desc = "ov5693_VGA_30fps", -+ .width = 656, -+ .height = 496, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 2688, -+ .lines_per_frame = 1984, -+ .bin_factor_x = 2, -+ .bin_factor_y = 2, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_VGA_30fps, -+ }, -+ { -+ .desc = "ov5693_480P_30fps", -+ .width = 736, -+ .height = 496, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 2688, -+ .lines_per_frame = 1984, -+ .bin_factor_x = 2, -+ .bin_factor_y = 2, -+ .bin_mode = 0, -+ .skip_frames = 1, -+ .regs = ov5693_480p_30fps, -+ }, -+ { -+ .desc = "ov5693_720p_30fps", -+ .width = 1296, -+ .height = 736, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 2688, -+ .lines_per_frame = 1984, -+ .bin_factor_x = 2, -+ .bin_factor_y = 2, -+ .bin_mode = 0, -+ .skip_frames = 1, -+ .regs = ov5693_720p_30fps, -+ }, -+ { -+ .desc = "ov5693_1080P_30fps", -+ .width = 1940, -+ .height = 1096, -+ .fps = 30, -+ .pix_clk_freq = 81, -+ .used = 0, -+ .pixels_per_line = 3688, -+ .lines_per_frame = 2984, -+ .bin_factor_x = 0, -+ .bin_factor_y = 0, -+ .bin_mode = 0, -+ .skip_frames = 3, -+ .regs = ov5693_1080p_30fps, -+ }, -+}; -+#define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video)) -+ -+struct ov5693_vcm ov5693_vcm_ops = { -+ .power_up = ad5823_vcm_power_up, -+ .power_down = ad5823_vcm_power_down, -+ .init = ad5823_vcm_init, -+ .t_focus_vcm = ad5823_t_focus_vcm, -+ .t_focus_abs = ad5823_t_focus_abs, -+ .t_focus_rel = ad5823_t_focus_rel, -+ .q_focus_status = ad5823_q_focus_status, -+ .q_focus_abs = ad5823_q_focus_abs, -+}; -+#endif --- -2.19.1 - diff --git a/patches/5.0/0005-ipts.patch b/patches/5.0/0005-ipts.patch deleted file mode 100644 index a3235e39d..000000000 --- a/patches/5.0/0005-ipts.patch +++ /dev/null @@ -1,6175 +0,0 @@ -From 070310d2187dd943218746633d3f037fe04e1d2d Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:41:03 -0400 -Subject: [PATCH 05/12] ipts - ---- - drivers/gpu/drm/i915/Makefile | 3 + - drivers/gpu/drm/i915/i915_drv.c | 13 + - drivers/gpu/drm/i915/i915_drv.h | 3 + - drivers/gpu/drm/i915/i915_gem_context.c | 12 + - drivers/gpu/drm/i915/i915_irq.c | 7 +- - drivers/gpu/drm/i915/i915_params.c | 5 +- - drivers/gpu/drm/i915/i915_params.h | 5 +- - drivers/gpu/drm/i915/intel_dp.c | 4 +- - drivers/gpu/drm/i915/intel_guc.h | 1 + - drivers/gpu/drm/i915/intel_guc_submission.c | 90 +- - drivers/gpu/drm/i915/intel_guc_submission.h | 4 + - drivers/gpu/drm/i915/intel_ipts.c | 657 ++++++++++++ - drivers/gpu/drm/i915/intel_ipts.h | 36 + - drivers/gpu/drm/i915/intel_lrc.c | 12 +- - drivers/gpu/drm/i915/intel_lrc.h | 8 + - drivers/gpu/drm/i915/intel_panel.c | 7 + - drivers/hid/hid-multitouch.c | 22 +- - drivers/misc/Kconfig | 1 + - drivers/misc/Makefile | 1 + - drivers/misc/ipts/Kconfig | 9 + - drivers/misc/ipts/Makefile | 13 + - drivers/misc/ipts/ipts-binary-spec.h | 118 +++ - drivers/misc/ipts/ipts-dbgfs.c | 152 +++ - drivers/misc/ipts/ipts-gfx.c | 184 ++++ - drivers/misc/ipts/ipts-gfx.h | 24 + - drivers/misc/ipts/ipts-hid.c | 456 ++++++++ - drivers/misc/ipts/ipts-hid.h | 34 + - drivers/misc/ipts/ipts-kernel.c | 1050 +++++++++++++++++++ - drivers/misc/ipts/ipts-kernel.h | 23 + - drivers/misc/ipts/ipts-mei-msgs.h | 585 +++++++++++ - drivers/misc/ipts/ipts-mei.c | 282 +++++ - drivers/misc/ipts/ipts-msg-handler.c | 431 ++++++++ - drivers/misc/ipts/ipts-msg-handler.h | 32 + - drivers/misc/ipts/ipts-resource.c | 277 +++++ - drivers/misc/ipts/ipts-resource.h | 30 + - drivers/misc/ipts/ipts-sensor-regs.h | 700 +++++++++++++ - drivers/misc/ipts/ipts-state.h | 29 + - drivers/misc/ipts/ipts.h | 200 ++++ - drivers/misc/mei/hw-me-regs.h | 1 + - drivers/misc/mei/pci-me.c | 1 + - include/linux/intel_ipts_if.h | 75 ++ - 41 files changed, 5572 insertions(+), 25 deletions(-) - create mode 100644 drivers/gpu/drm/i915/intel_ipts.c - create mode 100644 drivers/gpu/drm/i915/intel_ipts.h - create mode 100644 drivers/misc/ipts/Kconfig - create mode 100644 drivers/misc/ipts/Makefile - create mode 100644 drivers/misc/ipts/ipts-binary-spec.h - create mode 100644 drivers/misc/ipts/ipts-dbgfs.c - create mode 100644 drivers/misc/ipts/ipts-gfx.c - create mode 100644 drivers/misc/ipts/ipts-gfx.h - create mode 100644 drivers/misc/ipts/ipts-hid.c - create mode 100644 drivers/misc/ipts/ipts-hid.h - create mode 100644 drivers/misc/ipts/ipts-kernel.c - create mode 100644 drivers/misc/ipts/ipts-kernel.h - create mode 100644 drivers/misc/ipts/ipts-mei-msgs.h - create mode 100644 drivers/misc/ipts/ipts-mei.c - create mode 100644 drivers/misc/ipts/ipts-msg-handler.c - create mode 100644 drivers/misc/ipts/ipts-msg-handler.h - create mode 100644 drivers/misc/ipts/ipts-resource.c - create mode 100644 drivers/misc/ipts/ipts-resource.h - create mode 100644 drivers/misc/ipts/ipts-sensor-regs.h - create mode 100644 drivers/misc/ipts/ipts-state.h - create mode 100644 drivers/misc/ipts/ipts.h - create mode 100644 include/linux/intel_ipts_if.h - -diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile -index 19b5fe5016bf..402e5b15173b 100644 ---- a/drivers/gpu/drm/i915/Makefile -+++ b/drivers/gpu/drm/i915/Makefile -@@ -160,6 +160,9 @@ i915-y += dvo_ch7017.o \ - vlv_dsi_pll.o \ - intel_vdsc.o - -+# intel precise touch & stylus -+i915-y += intel_ipts.o -+ - # Post-mortem debug and GPU hang state capture - i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o - i915-$(CONFIG_DRM_I915_SELFTEST) += \ -diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c -index b310a897a4ad..cfad40f65513 100644 ---- a/drivers/gpu/drm/i915/i915_drv.c -+++ b/drivers/gpu/drm/i915/i915_drv.c -@@ -54,6 +54,7 @@ - #include "intel_drv.h" - #include "intel_uc.h" - #include "intel_workarounds.h" -+#include "intel_ipts.h" - - static struct drm_driver driver; - -@@ -709,6 +710,9 @@ static int i915_load_modeset_init(struct drm_device *dev) - - intel_init_ipc(dev_priv); - -+ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) -+ intel_ipts_init(dev); -+ - return 0; - - cleanup_gem: -@@ -1772,6 +1776,9 @@ void i915_driver_unload(struct drm_device *dev) - - disable_rpm_wakeref_asserts(dev_priv); - -+ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) -+ intel_ipts_cleanup(dev); -+ - i915_driver_unregister(dev_priv); - - if (i915_gem_suspend(dev_priv)) -@@ -1906,6 +1913,9 @@ static int i915_drm_suspend(struct drm_device *dev) - struct pci_dev *pdev = dev_priv->drm.pdev; - pci_power_t opregion_target_state; - -+ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) -+ intel_ipts_suspend(dev); -+ - disable_rpm_wakeref_asserts(dev_priv); - - /* We do a lot of poking in a lot of registers, make sure they work -@@ -2100,6 +2110,9 @@ static int i915_drm_resume(struct drm_device *dev) - - enable_rpm_wakeref_asserts(dev_priv); - -+ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) -+ intel_ipts_resume(dev); -+ - return 0; - } - -diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h -index 489c1e656ff6..de6e9c7aaf57 100644 ---- a/drivers/gpu/drm/i915/i915_drv.h -+++ b/drivers/gpu/drm/i915/i915_drv.h -@@ -3115,6 +3115,9 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj, - void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj, - struct sg_table *pages); - -+struct i915_gem_context * -+i915_gem_context_create_ipts(struct drm_device *dev); -+ - static inline struct i915_gem_context * - __i915_gem_context_lookup_rcu(struct drm_i915_file_private *file_priv, u32 id) - { -diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c -index 371c07087095..ece9ba353a1b 100644 ---- a/drivers/gpu/drm/i915/i915_gem_context.c -+++ b/drivers/gpu/drm/i915/i915_gem_context.c -@@ -532,6 +532,18 @@ static bool needs_preempt_context(struct drm_i915_private *i915) - return HAS_LOGICAL_RING_PREEMPTION(i915); - } - -+struct i915_gem_context *i915_gem_context_create_ipts(struct drm_device *dev) -+{ -+ struct drm_i915_private *dev_priv = to_i915(dev); -+ struct i915_gem_context *ctx; -+ -+ BUG_ON(!mutex_is_locked(&dev->struct_mutex)); -+ -+ ctx = i915_gem_create_context(dev_priv, NULL); -+ -+ return ctx; -+} -+ - int i915_gem_contexts_init(struct drm_i915_private *dev_priv) - { - struct i915_gem_context *ctx; -diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c -index d447d7d508f4..9d2d88cf2340 100644 ---- a/drivers/gpu/drm/i915/i915_irq.c -+++ b/drivers/gpu/drm/i915/i915_irq.c -@@ -36,6 +36,7 @@ - #include "i915_drv.h" - #include "i915_trace.h" - #include "intel_drv.h" -+#include "intel_ipts.h" - - /** - * DOC: interrupt handling -@@ -1503,6 +1504,9 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir) - tasklet |= USES_GUC_SUBMISSION(engine->i915); - } - -+ if (iir & GT_RENDER_PIPECTL_NOTIFY_INTERRUPT && i915_modparams.enable_ipts) -+ intel_ipts_notify_complete(); -+ - if (tasklet) - tasklet_hi_schedule(&engine->execlists.tasklet); - } -@@ -4170,7 +4174,8 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) - { - /* These are interrupts we'll toggle with the ring mask register */ - uint32_t gt_interrupts[] = { -- GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | -+ GT_RENDER_PIPECTL_NOTIFY_INTERRUPT << GEN8_RCS_IRQ_SHIFT | -+ GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | - GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT | - GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT | - GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT, -diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c -index 2e0356561839..ff3a9924d719 100644 ---- a/drivers/gpu/drm/i915/i915_params.c -+++ b/drivers/gpu/drm/i915/i915_params.c -@@ -141,7 +141,10 @@ i915_param_named_unsafe(edp_vswing, int, 0400, - i915_param_named_unsafe(enable_guc, int, 0400, - "Enable GuC load for GuC submission and/or HuC load. " - "Required functionality can be selected using bitmask values. " -- "(-1=auto, 0=disable [default], 1=GuC submission, 2=HuC load)"); -+ "(-1=auto [default], 0=disable, 1=GuC submission, 2=HuC load)"); -+ -+i915_param_named_unsafe(enable_ipts, int, 0400, -+ "Enable IPTS Touchscreen and Pen support (default: 1)"); - - i915_param_named(guc_log_level, int, 0400, - "GuC firmware logging level. Requires GuC to be loaded. " -diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h -index 7e56c516c815..208f0c056529 100644 ---- a/drivers/gpu/drm/i915/i915_params.h -+++ b/drivers/gpu/drm/i915/i915_params.h -@@ -45,7 +45,7 @@ struct drm_printer; - param(int, disable_power_well, -1) \ - param(int, enable_ips, 1) \ - param(int, invert_brightness, 0) \ -- param(int, enable_guc, 0) \ -+ param(int, enable_guc, -1) \ - param(int, guc_log_level, -1) \ - param(char *, guc_firmware_path, NULL) \ - param(char *, huc_firmware_path, NULL) \ -@@ -67,7 +67,8 @@ struct drm_printer; - param(bool, nuclear_pageflip, false) \ - param(bool, enable_dp_mst, true) \ - param(bool, enable_dpcd_backlight, false) \ -- param(bool, enable_gvt, false) -+ param(bool, enable_gvt, false) \ -+ param(int, enable_ipts, 1) - - #define MEMBER(T, member, ...) T member; - struct i915_params { -diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c -index dcd1df5322e8..4c2c1a59a02e 100644 ---- a/drivers/gpu/drm/i915/intel_dp.c -+++ b/drivers/gpu/drm/i915/intel_dp.c -@@ -2854,8 +2854,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) - return; - - if (mode != DRM_MODE_DPMS_ON) { -- if (downstream_hpd_needs_d0(intel_dp)) -- return; -+ //if (downstream_hpd_needs_d0(intel_dp)) -+ // return; - - ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, - DP_SET_POWER_D3); -diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h -index 0f1c4f9ebfd8..b99cbafe91f3 100644 ---- a/drivers/gpu/drm/i915/intel_guc.h -+++ b/drivers/gpu/drm/i915/intel_guc.h -@@ -66,6 +66,7 @@ struct intel_guc { - - struct intel_guc_client *execbuf_client; - struct intel_guc_client *preempt_client; -+ struct intel_guc_client *ipts_client; - - struct guc_preempt_work preempt_work[I915_NUM_ENGINES]; - struct workqueue_struct *preempt_wq; -diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c -index 1570dcbe249c..d75c997cdd8a 100644 ---- a/drivers/gpu/drm/i915/intel_guc_submission.c -+++ b/drivers/gpu/drm/i915/intel_guc_submission.c -@@ -88,12 +88,17 @@ static inline struct i915_priolist *to_priolist(struct rb_node *rb) - - static inline bool is_high_priority(struct intel_guc_client *client) - { -- return (client->priority == GUC_CLIENT_PRIORITY_KMD_HIGH || -- client->priority == GUC_CLIENT_PRIORITY_HIGH); -+ return (client->priority == GUC_CLIENT_PRIORITY_HIGH); -+} -+ -+static inline bool is_high_priority_kmd(struct intel_guc_client *client) -+{ -+ return (client->priority == GUC_CLIENT_PRIORITY_KMD_HIGH); - } - - static int reserve_doorbell(struct intel_guc_client *client) - { -+ struct drm_i915_private *dev_priv = guc_to_i915(client->guc); - unsigned long offset; - unsigned long end; - u16 id; -@@ -106,10 +111,15 @@ static int reserve_doorbell(struct intel_guc_client *client) - * priority contexts, the second half for high-priority ones. - */ - offset = 0; -- end = GUC_NUM_DOORBELLS / 2; -- if (is_high_priority(client)) { -- offset = end; -- end += offset; -+ if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { -+ end = GUC_NUM_DOORBELLS; -+ } -+ else { -+ end = GUC_NUM_DOORBELLS/2; -+ if (is_high_priority(client)) { -+ offset = end; -+ end += offset; -+ } - } - - id = find_next_zero_bit(client->guc->doorbell_bitmap, end, offset); -@@ -367,9 +377,15 @@ static void guc_stage_desc_init(struct intel_guc_client *client) - desc = __get_stage_desc(client); - memset(desc, 0, sizeof(*desc)); - -- desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE | -- GUC_STAGE_DESC_ATTR_KERNEL; -- if (is_high_priority(client)) -+ desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE; -+ if ((client->priority == GUC_CLIENT_PRIORITY_KMD_NORMAL) || -+ (client->priority == GUC_CLIENT_PRIORITY_KMD_HIGH)) { -+ desc->attribute |= GUC_STAGE_DESC_ATTR_KERNEL; -+ } else { -+ desc->attribute |= GUC_STAGE_DESC_ATTR_PCH; -+ } -+ -+ if (is_high_priority_kmd(client)) - desc->attribute |= GUC_STAGE_DESC_ATTR_PREEMPT; - desc->stage_id = client->stage_id; - desc->priority = client->priority; -@@ -1197,7 +1213,8 @@ static void guc_interrupts_capture(struct drm_i915_private *dev_priv) - I915_WRITE(RING_MODE_GEN7(engine), irqs); - - /* route USER_INTERRUPT to Host, all others are sent to GuC. */ -- irqs = GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | -+ irqs = (GT_RENDER_USER_INTERRUPT | GT_RENDER_PIPECTL_NOTIFY_INTERRUPT) -+ << GEN8_RCS_IRQ_SHIFT | - GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT; - /* These three registers have the same bit definitions */ - I915_WRITE(GUC_BCS_RCS_IER, ~irqs); -@@ -1338,6 +1355,59 @@ void intel_guc_submission_disable(struct intel_guc *guc) - guc_clients_disable(guc); - } - -+int i915_guc_ipts_submission_enable(struct drm_i915_private *dev_priv, -+ struct i915_gem_context *ctx) -+{ -+ struct intel_guc *guc = &dev_priv->guc; -+ struct intel_guc_client *client; -+ int err; -+ int ret; -+ -+ /* client for execbuf submission */ -+ client = guc_client_alloc(dev_priv, -+ INTEL_INFO(dev_priv)->ring_mask, -+ IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GUC_CLIENT_PRIORITY_HIGH : GUC_CLIENT_PRIORITY_NORMAL, -+ ctx); -+ if (IS_ERR(client)) { -+ DRM_ERROR("Failed to create normal GuC client!\n"); -+ return -ENOMEM; -+ } -+ -+ guc->ipts_client = client; -+ -+ err = intel_guc_sample_forcewake(guc); -+ if (err) -+ return err; -+ -+ ret = __guc_client_enable(guc->ipts_client); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+void i915_guc_ipts_submission_disable(struct drm_i915_private *dev_priv) -+{ -+ struct intel_guc *guc = &dev_priv->guc; -+ -+ if (!guc->ipts_client) -+ return; -+ -+ __guc_client_disable(guc->ipts_client); -+ guc_client_free(guc->ipts_client); -+ guc->ipts_client = NULL; -+} -+ -+void i915_guc_ipts_reacquire_doorbell(struct drm_i915_private *dev_priv) -+{ -+ struct intel_guc *guc = &dev_priv->guc; -+ -+ int err = __guc_allocate_doorbell(guc, guc->ipts_client->stage_id); -+ -+ if (err) -+ DRM_ERROR("Not able to reacquire IPTS doorbell\n"); -+} -+ - #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) - #include "selftests/intel_guc.c" - #endif -diff --git a/drivers/gpu/drm/i915/intel_guc_submission.h b/drivers/gpu/drm/i915/intel_guc_submission.h -index 169c54568340..245a8de1df2a 100644 ---- a/drivers/gpu/drm/i915/intel_guc_submission.h -+++ b/drivers/gpu/drm/i915/intel_guc_submission.h -@@ -83,5 +83,9 @@ void intel_guc_submission_disable(struct intel_guc *guc); - void intel_guc_submission_fini(struct intel_guc *guc); - int intel_guc_preempt_work_create(struct intel_guc *guc); - void intel_guc_preempt_work_destroy(struct intel_guc *guc); -+int i915_guc_ipts_submission_enable(struct drm_i915_private *dev_priv, -+ struct i915_gem_context *ctx); -+void i915_guc_ipts_submission_disable(struct drm_i915_private *dev_priv); -+void i915_guc_ipts_reacquire_doorbell(struct drm_i915_private *dev_priv); - - #endif -diff --git a/drivers/gpu/drm/i915/intel_ipts.c b/drivers/gpu/drm/i915/intel_ipts.c -new file mode 100644 -index 000000000000..b276a2f7839c ---- /dev/null -+++ b/drivers/gpu/drm/i915/intel_ipts.c -@@ -0,0 +1,657 @@ -+/* -+ * Copyright 2016 Intel Corporation -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ * -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+#include "intel_guc_submission.h" -+#include "i915_drv.h" -+ -+#define SUPPORTED_IPTS_INTERFACE_VERSION 1 -+ -+#define REACQUIRE_DB_THRESHOLD 10 -+#define DB_LOST_CHECK_STEP1_INTERVAL 2500 /* ms */ -+#define DB_LOST_CHECK_STEP2_INTERVAL 1000 /* ms */ -+ -+/* intel IPTS ctx for ipts support */ -+typedef struct intel_ipts { -+ struct drm_device *dev; -+ struct i915_gem_context *ipts_context; -+ intel_ipts_callback_t ipts_clbks; -+ -+ /* buffers' list */ -+ struct { -+ spinlock_t lock; -+ struct list_head list; -+ } buffers; -+ -+ void *data; -+ -+ struct delayed_work reacquire_db_work; -+ intel_ipts_wq_info_t wq_info; -+ u32 old_tail; -+ u32 old_head; -+ bool need_reacquire_db; -+ -+ bool connected; -+ bool initialized; -+} intel_ipts_t; -+ -+intel_ipts_t intel_ipts; -+ -+typedef struct intel_ipts_object { -+ struct list_head list; -+ struct drm_i915_gem_object *gem_obj; -+ void *cpu_addr; -+} intel_ipts_object_t; -+ -+static intel_ipts_object_t *ipts_object_create(size_t size, u32 flags) -+{ -+ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); -+ intel_ipts_object_t *obj = NULL; -+ struct drm_i915_gem_object *gem_obj = NULL; -+ int ret = 0; -+ -+ obj = kzalloc(sizeof(*obj), GFP_KERNEL); -+ if (!obj) -+ return NULL; -+ -+ size = roundup(size, PAGE_SIZE); -+ if (size == 0) { -+ ret = -EINVAL; -+ goto err_out; -+ } -+ -+ /* Allocate the new object */ -+ gem_obj = i915_gem_object_create(dev_priv, size); -+ if (gem_obj == NULL) { -+ ret = -ENOMEM; -+ goto err_out; -+ } -+ -+ if (flags & IPTS_BUF_FLAG_CONTIGUOUS) { -+ ret = i915_gem_object_attach_phys(gem_obj, PAGE_SIZE); -+ if (ret) { -+ pr_info(">> ipts no contiguous : %d\n", ret); -+ goto err_out; -+ } -+ } -+ -+ obj->gem_obj = gem_obj; -+ -+ spin_lock(&intel_ipts.buffers.lock); -+ list_add_tail(&obj->list, &intel_ipts.buffers.list); -+ spin_unlock(&intel_ipts.buffers.lock); -+ -+ return obj; -+ -+err_out: -+ if (gem_obj) -+ i915_gem_free_object(&gem_obj->base); -+ -+ if (obj) -+ kfree(obj); -+ -+ return NULL; -+} -+ -+static void ipts_object_free(intel_ipts_object_t* obj) -+{ -+ spin_lock(&intel_ipts.buffers.lock); -+ list_del(&obj->list); -+ spin_unlock(&intel_ipts.buffers.lock); -+ -+ i915_gem_free_object(&obj->gem_obj->base); -+ kfree(obj); -+} -+ -+static int ipts_object_pin(intel_ipts_object_t* obj, -+ struct i915_gem_context *ipts_ctx) -+{ -+ struct i915_address_space *vm = NULL; -+ struct i915_vma *vma = NULL; -+ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); -+ int ret = 0; -+ -+ if (ipts_ctx->ppgtt) { -+ vm = &ipts_ctx->ppgtt->vm; -+ } else { -+ vm = &dev_priv->ggtt.vm; -+ } -+ -+ vma = i915_vma_instance(obj->gem_obj, vm, NULL); -+ if (IS_ERR(vma)) { -+ DRM_ERROR("cannot find or create vma\n"); -+ return -1; -+ } -+ -+ ret = i915_vma_pin(vma, 0, PAGE_SIZE, PIN_USER); -+ -+ return ret; -+} -+ -+static void ipts_object_unpin(intel_ipts_object_t *obj) -+{ -+ /* TBD: Add support */ -+} -+ -+static void* ipts_object_map(intel_ipts_object_t *obj) -+{ -+ -+ return i915_gem_object_pin_map(obj->gem_obj, I915_MAP_WB); -+} -+ -+static void ipts_object_unmap(intel_ipts_object_t* obj) -+{ -+ i915_gem_object_unpin_map(obj->gem_obj); -+ obj->cpu_addr = NULL; -+} -+ -+static int create_ipts_context(void) -+{ -+ struct i915_gem_context *ipts_ctx = NULL; -+ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); -+ struct intel_context *ce = NULL; -+ struct intel_context *pin_ret; -+ int ret = 0; -+ -+ /* Initialize the context right away.*/ -+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); -+ if (ret) { -+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); -+ return ret; -+ } -+ -+ ipts_ctx = i915_gem_context_create_ipts(intel_ipts.dev); -+ if (IS_ERR(ipts_ctx)) { -+ DRM_ERROR("Failed to create IPTS context (error %ld)\n", -+ PTR_ERR(ipts_ctx)); -+ ret = PTR_ERR(ipts_ctx); -+ goto err_unlock; -+ } -+ -+ ce = to_intel_context(ipts_ctx, dev_priv->engine[RCS]); -+ if (IS_ERR(ce)) { -+ DRM_ERROR("Failed to create intel context (error %ld)\n", -+ PTR_ERR(ce)); -+ ret = PTR_ERR(ce); -+ goto err_unlock; -+ } -+ -+ ret = execlists_context_deferred_alloc(ipts_ctx, dev_priv->engine[RCS], ce); -+ if (ret) { -+ DRM_DEBUG("lr context allocation failed : %d\n", ret); -+ goto err_ctx; -+ } -+ -+ pin_ret = execlists_context_pin(dev_priv->engine[RCS], ipts_ctx); -+ if (IS_ERR(pin_ret)) { -+ DRM_DEBUG("lr context pinning failed : %ld\n", PTR_ERR(pin_ret)); -+ goto err_ctx; -+ } -+ -+ /* Release the mutex */ -+ mutex_unlock(&intel_ipts.dev->struct_mutex); -+ -+ spin_lock_init(&intel_ipts.buffers.lock); -+ INIT_LIST_HEAD(&intel_ipts.buffers.list); -+ -+ intel_ipts.ipts_context = ipts_ctx; -+ -+ return 0; -+ -+err_ctx: -+ if (ipts_ctx) -+ i915_gem_context_put(ipts_ctx); -+ -+err_unlock: -+ mutex_unlock(&intel_ipts.dev->struct_mutex); -+ -+ return ret; -+} -+ -+static void destroy_ipts_context(void) -+{ -+ struct i915_gem_context *ipts_ctx = NULL; -+ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); -+ struct intel_context *ce = NULL; -+ int ret = 0; -+ -+ ipts_ctx = intel_ipts.ipts_context; -+ -+ ce = to_intel_context(ipts_ctx, dev_priv->engine[RCS]); -+ -+ /* Initialize the context right away.*/ -+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); -+ if (ret) { -+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); -+ return; -+ } -+ -+ execlists_context_unpin(ce); -+ i915_gem_context_put(ipts_ctx); -+ -+ mutex_unlock(&intel_ipts.dev->struct_mutex); -+} -+ -+int intel_ipts_notify_complete(void) -+{ -+ if (intel_ipts.ipts_clbks.workload_complete) -+ intel_ipts.ipts_clbks.workload_complete(intel_ipts.data); -+ -+ return 0; -+} -+ -+int intel_ipts_notify_backlight_status(bool backlight_on) -+{ -+ if (intel_ipts.ipts_clbks.notify_gfx_status) { -+ if (backlight_on) { -+ intel_ipts.ipts_clbks.notify_gfx_status( -+ IPTS_NOTIFY_STA_BACKLIGHT_ON, -+ intel_ipts.data); -+ schedule_delayed_work(&intel_ipts.reacquire_db_work, -+ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); -+ } else { -+ intel_ipts.ipts_clbks.notify_gfx_status( -+ IPTS_NOTIFY_STA_BACKLIGHT_OFF, -+ intel_ipts.data); -+ cancel_delayed_work(&intel_ipts.reacquire_db_work); -+ } -+ } -+ -+ return 0; -+} -+ -+static void intel_ipts_reacquire_db(intel_ipts_t *intel_ipts_p) -+{ -+ int ret = 0; -+ -+ ret = i915_mutex_lock_interruptible(intel_ipts_p->dev); -+ if (ret) { -+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); -+ return; -+ } -+ -+ /* Reacquire the doorbell */ -+ i915_guc_ipts_reacquire_doorbell(intel_ipts_p->dev->dev_private); -+ -+ mutex_unlock(&intel_ipts_p->dev->struct_mutex); -+ -+ return; -+} -+ -+static int intel_ipts_get_wq_info(uint64_t gfx_handle, -+ intel_ipts_wq_info_t *wq_info) -+{ -+ if (gfx_handle != (uint64_t)&intel_ipts) { -+ DRM_ERROR("invalid gfx handle\n"); -+ return -EINVAL; -+ } -+ -+ *wq_info = intel_ipts.wq_info; -+ -+ intel_ipts_reacquire_db(&intel_ipts); -+ schedule_delayed_work(&intel_ipts.reacquire_db_work, -+ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); -+ -+ return 0; -+} -+ -+static int set_wq_info(void) -+{ -+ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); -+ struct intel_guc *guc = &dev_priv->guc; -+ struct intel_guc_client *client; -+ struct guc_process_desc *desc; -+ void *base = NULL; -+ intel_ipts_wq_info_t *wq_info; -+ u64 phy_base = 0; -+ -+ wq_info = &intel_ipts.wq_info; -+ -+ client = guc->ipts_client; -+ if (!client) { -+ DRM_ERROR("IPTS GuC client is NOT available\n"); -+ return -EINVAL; -+ } -+ -+ base = client->vaddr; -+ desc = (struct guc_process_desc *)((u64)base + client->proc_desc_offset); -+ -+ desc->wq_base_addr = (u64)base + GUC_DB_SIZE; -+ desc->db_base_addr = (u64)base + client->doorbell_offset; -+ -+ /* IPTS expects physical addresses to pass it to ME */ -+ phy_base = sg_dma_address(client->vma->pages->sgl); -+ -+ wq_info->db_addr = desc->db_base_addr; -+ wq_info->db_phy_addr = phy_base + client->doorbell_offset; -+ wq_info->db_cookie_offset = offsetof(struct guc_doorbell_info, cookie); -+ wq_info->wq_addr = desc->wq_base_addr; -+ wq_info->wq_phy_addr = phy_base + GUC_DB_SIZE; -+ wq_info->wq_head_addr = (u64)&desc->head; -+ wq_info->wq_head_phy_addr = phy_base + client->proc_desc_offset + -+ offsetof(struct guc_process_desc, head); -+ wq_info->wq_tail_addr = (u64)&desc->tail; -+ wq_info->wq_tail_phy_addr = phy_base + client->proc_desc_offset + -+ offsetof(struct guc_process_desc, tail); -+ wq_info->wq_size = desc->wq_size_bytes; -+ -+ return 0; -+} -+ -+static int intel_ipts_init_wq(void) -+{ -+ int ret = 0; -+ -+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); -+ if (ret) { -+ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); -+ return ret; -+ } -+ -+ /* disable IPTS submission */ -+ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); -+ -+ /* enable IPTS submission */ -+ ret = i915_guc_ipts_submission_enable(intel_ipts.dev->dev_private, -+ intel_ipts.ipts_context); -+ if (ret) { -+ DRM_ERROR("i915_guc_ipts_submission_enable failed : %d\n", ret); -+ goto out; -+ } -+ -+ ret = set_wq_info(); -+ if (ret) { -+ DRM_ERROR("set_wq_info failed\n"); -+ goto out; -+ } -+ -+out: -+ mutex_unlock(&intel_ipts.dev->struct_mutex); -+ -+ return ret; -+} -+ -+static void intel_ipts_release_wq(void) -+{ -+ int ret = 0; -+ -+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); -+ if (ret) { -+ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); -+ return; -+ } -+ -+ /* disable IPTS submission */ -+ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); -+ -+ mutex_unlock(&intel_ipts.dev->struct_mutex); -+} -+ -+static int intel_ipts_map_buffer(u64 gfx_handle, intel_ipts_mapbuffer_t *mapbuf) -+{ -+ intel_ipts_object_t* obj; -+ struct i915_gem_context *ipts_ctx = NULL; -+ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); -+ struct i915_address_space *vm = NULL; -+ struct i915_vma *vma = NULL; -+ int ret = 0; -+ -+ if (gfx_handle != (uint64_t)&intel_ipts) { -+ DRM_ERROR("invalid gfx handle\n"); -+ return -EINVAL; -+ } -+ -+ /* Acquire mutex first */ -+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); -+ if (ret) { -+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); -+ return -EINVAL; -+ } -+ -+ obj = ipts_object_create(mapbuf->size, mapbuf->flags); -+ if (!obj) -+ return -ENOMEM; -+ -+ ipts_ctx = intel_ipts.ipts_context; -+ ret = ipts_object_pin(obj, ipts_ctx); -+ if (ret) { -+ DRM_ERROR("Not able to pin iTouch obj\n"); -+ ipts_object_free(obj); -+ mutex_unlock(&intel_ipts.dev->struct_mutex); -+ return -ENOMEM; -+ } -+ -+ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { -+ obj->cpu_addr = obj->gem_obj->phys_handle->vaddr; -+ } else { -+ obj->cpu_addr = ipts_object_map(obj); -+ } -+ -+ if (ipts_ctx->ppgtt) { -+ vm = &ipts_ctx->ppgtt->vm; -+ } else { -+ vm = &dev_priv->ggtt.vm; -+ } -+ -+ vma = i915_vma_instance(obj->gem_obj, vm, NULL); -+ if (IS_ERR(vma)) { -+ DRM_ERROR("cannot find or create vma\n"); -+ return -EINVAL; -+ } -+ -+ mapbuf->gfx_addr = (void*)vma->node.start; -+ mapbuf->cpu_addr = (void*)obj->cpu_addr; -+ mapbuf->buf_handle = (u64)obj; -+ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { -+ mapbuf->phy_addr = (u64)obj->gem_obj->phys_handle->busaddr; -+ } -+ -+ /* Release the mutex */ -+ mutex_unlock(&intel_ipts.dev->struct_mutex); -+ -+ return 0; -+} -+ -+static int intel_ipts_unmap_buffer(uint64_t gfx_handle, uint64_t buf_handle) -+{ -+ intel_ipts_object_t* obj = (intel_ipts_object_t*)buf_handle; -+ -+ if (gfx_handle != (uint64_t)&intel_ipts) { -+ DRM_ERROR("invalid gfx handle\n"); -+ return -EINVAL; -+ } -+ -+ if (!obj->gem_obj->phys_handle) -+ ipts_object_unmap(obj); -+ ipts_object_unpin(obj); -+ ipts_object_free(obj); -+ -+ return 0; -+} -+ -+int intel_ipts_connect(intel_ipts_connect_t *ipts_connect) -+{ -+ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); -+ int ret = 0; -+ -+ if (!intel_ipts.initialized) -+ return -EIO; -+ -+ if (ipts_connect && ipts_connect->if_version <= -+ SUPPORTED_IPTS_INTERFACE_VERSION) { -+ -+ /* return gpu operations for ipts */ -+ ipts_connect->ipts_ops.get_wq_info = intel_ipts_get_wq_info; -+ ipts_connect->ipts_ops.map_buffer = intel_ipts_map_buffer; -+ ipts_connect->ipts_ops.unmap_buffer = intel_ipts_unmap_buffer; -+ ipts_connect->gfx_version = INTEL_INFO(dev_priv)->gen; -+ ipts_connect->gfx_handle = (uint64_t)&intel_ipts; -+ -+ /* save callback and data */ -+ intel_ipts.data = ipts_connect->data; -+ intel_ipts.ipts_clbks = ipts_connect->ipts_cb; -+ -+ intel_ipts.connected = true; -+ } else { -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(intel_ipts_connect); -+ -+void intel_ipts_disconnect(uint64_t gfx_handle) -+{ -+ if (!intel_ipts.initialized) -+ return; -+ -+ if (gfx_handle != (uint64_t)&intel_ipts || -+ intel_ipts.connected == false) { -+ DRM_ERROR("invalid gfx handle\n"); -+ return; -+ } -+ -+ intel_ipts.data = 0; -+ memset(&intel_ipts.ipts_clbks, 0, sizeof(intel_ipts_callback_t)); -+ -+ intel_ipts.connected = false; -+} -+EXPORT_SYMBOL_GPL(intel_ipts_disconnect); -+ -+static void reacquire_db_work_func(struct work_struct *work) -+{ -+ struct delayed_work *d_work = container_of(work, struct delayed_work, -+ work); -+ intel_ipts_t *intel_ipts_p = container_of(d_work, intel_ipts_t, -+ reacquire_db_work); -+ u32 head; -+ u32 tail; -+ u32 size; -+ u32 load; -+ -+ head = *(u32*)intel_ipts_p->wq_info.wq_head_addr; -+ tail = *(u32*)intel_ipts_p->wq_info.wq_tail_addr; -+ size = intel_ipts_p->wq_info.wq_size; -+ -+ if (head >= tail) -+ load = head - tail; -+ else -+ load = head + size - tail; -+ -+ if (load < REACQUIRE_DB_THRESHOLD) { -+ intel_ipts_p->need_reacquire_db = false; -+ goto reschedule_work; -+ } -+ -+ if (intel_ipts_p->need_reacquire_db) { -+ if (intel_ipts_p->old_head == head && intel_ipts_p->old_tail == tail) -+ intel_ipts_reacquire_db(intel_ipts_p); -+ intel_ipts_p->need_reacquire_db = false; -+ } else { -+ intel_ipts_p->old_head = head; -+ intel_ipts_p->old_tail = tail; -+ intel_ipts_p->need_reacquire_db = true; -+ -+ /* recheck */ -+ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, -+ msecs_to_jiffies(DB_LOST_CHECK_STEP2_INTERVAL)); -+ return; -+ } -+ -+reschedule_work: -+ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, -+ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); -+} -+ -+/** -+ * intel_ipts_init - Initialize ipts support -+ * @dev: drm device -+ * -+ * Setup the required structures for ipts. -+ */ -+int intel_ipts_init(struct drm_device *dev) -+{ -+ int ret = 0; -+ -+ pr_info("ipts: initializing ipts\n"); -+ -+ intel_ipts.dev = dev; -+ INIT_DELAYED_WORK(&intel_ipts.reacquire_db_work, reacquire_db_work_func); -+ -+ ret = create_ipts_context(); -+ if (ret) -+ return -ENOMEM; -+ -+ ret = intel_ipts_init_wq(); -+ if (ret) -+ return ret; -+ -+ intel_ipts.initialized = true; -+ pr_info("ipts: Intel iTouch framework initialized\n"); -+ -+ return ret; -+} -+ -+void intel_ipts_cleanup(struct drm_device *dev) -+{ -+ intel_ipts_object_t *obj, *n; -+ -+ if (intel_ipts.dev == dev) { -+ list_for_each_entry_safe(obj, n, &intel_ipts.buffers.list, list) { -+ struct i915_vma *vma, *vn; -+ -+ list_for_each_entry_safe(vma, vn, -+ &obj->list, obj_link) { -+ vma->flags &= ~I915_VMA_PIN_MASK; -+ i915_vma_destroy(vma); -+ } -+ -+ list_del(&obj->list); -+ -+ if (!obj->gem_obj->phys_handle) -+ ipts_object_unmap(obj); -+ ipts_object_unpin(obj); -+ i915_gem_free_object(&obj->gem_obj->base); -+ kfree(obj); -+ } -+ -+ intel_ipts_release_wq(); -+ destroy_ipts_context(); -+ cancel_delayed_work(&intel_ipts.reacquire_db_work); -+ } -+} -+ -+int intel_ipts_resume(struct drm_device *dev) -+{ -+ return intel_ipts_init(dev); -+} -+ -+void intel_ipts_suspend(struct drm_device *dev) -+{ -+ intel_ipts_cleanup(dev); -+} -diff --git a/drivers/gpu/drm/i915/intel_ipts.h b/drivers/gpu/drm/i915/intel_ipts.h -new file mode 100644 -index 000000000000..45d7d1273adf ---- /dev/null -+++ b/drivers/gpu/drm/i915/intel_ipts.h -@@ -0,0 +1,36 @@ -+/* -+ * Copyright © 2016 Intel Corporation -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ * -+ */ -+#ifndef _INTEL_IPTS_H_ -+#define _INTEL_IPTS_H_ -+ -+struct drm_device; -+ -+int intel_ipts_init(struct drm_device *dev); -+void intel_ipts_cleanup(struct drm_device *dev); -+int intel_ipts_resume(struct drm_device *dev); -+void intel_ipts_suspend(struct drm_device *dev); -+int intel_ipts_notify_backlight_status(bool backlight_on); -+int intel_ipts_notify_complete(void); -+ -+#endif //_INTEL_IPTS_H_ -diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c -index eab9341a5152..51c38de2fb74 100644 ---- a/drivers/gpu/drm/i915/intel_lrc.c -+++ b/drivers/gpu/drm/i915/intel_lrc.c -@@ -164,9 +164,6 @@ - #define WA_TAIL_DWORDS 2 - #define WA_TAIL_BYTES (sizeof(u32) * WA_TAIL_DWORDS) - --static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, -- struct intel_engine_cs *engine, -- struct intel_context *ce); - static void execlists_init_reg_state(u32 *reg_state, - struct i915_gem_context *ctx, - struct intel_engine_cs *engine, -@@ -1113,7 +1110,7 @@ static void execlists_context_destroy(struct intel_context *ce) - i915_gem_object_put(ce->state->obj); - } - --static void execlists_context_unpin(struct intel_context *ce) -+void execlists_context_unpin(struct intel_context *ce) - { - struct intel_engine_cs *engine; - -@@ -1233,7 +1230,7 @@ static const struct intel_context_ops execlists_context_ops = { - .destroy = execlists_context_destroy, - }; - --static struct intel_context * -+struct intel_context * - execlists_context_pin(struct intel_engine_cs *engine, - struct i915_gem_context *ctx) - { -@@ -2287,6 +2284,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine) - - logical_ring_setup(engine); - -+ engine->irq_keep_mask |= GT_RENDER_PIPECTL_NOTIFY_INTERRUPT -+ << GEN8_RCS_IRQ_SHIFT; -+ - if (HAS_L3_DPF(dev_priv)) - engine->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT; - -@@ -2619,7 +2619,7 @@ populate_lr_context(struct i915_gem_context *ctx, - return ret; - } - --static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, -+int execlists_context_deferred_alloc(struct i915_gem_context *ctx, - struct intel_engine_cs *engine, - struct intel_context *ce) - { -diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h -index f5a5502ecf70..e4a3437119d6 100644 ---- a/drivers/gpu/drm/i915/intel_lrc.h -+++ b/drivers/gpu/drm/i915/intel_lrc.h -@@ -104,4 +104,12 @@ void intel_lr_context_resume(struct drm_i915_private *dev_priv); - - void intel_execlists_set_default_submission(struct intel_engine_cs *engine); - -+struct intel_context * -+execlists_context_pin(struct intel_engine_cs *engine, -+ struct i915_gem_context *ctx); -+void execlists_context_unpin(struct intel_context *ce); -+int execlists_context_deferred_alloc(struct i915_gem_context *ctx, -+ struct intel_engine_cs *engine, -+ struct intel_context *ce); -+ - #endif /* _INTEL_LRC_H_ */ -diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c -index e6cd7b55c018..08c116e885ad 100644 ---- a/drivers/gpu/drm/i915/intel_panel.c -+++ b/drivers/gpu/drm/i915/intel_panel.c -@@ -34,6 +34,7 @@ - #include - #include - #include "intel_drv.h" -+#include "intel_ipts.h" - - #define CRC_PMIC_PWM_PERIOD_NS 21333 - -@@ -659,6 +660,9 @@ static void lpt_disable_backlight(const struct drm_connector_state *old_conn_sta - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - u32 tmp; - -+ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) -+ intel_ipts_notify_backlight_status(false); -+ - intel_panel_actually_set_backlight(old_conn_state, 0); - - /* -@@ -846,6 +850,9 @@ static void lpt_enable_backlight(const struct intel_crtc_state *crtc_state, - - /* This won't stick until the above enable. */ - intel_panel_actually_set_backlight(conn_state, panel->backlight.level); -+ -+ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) -+ intel_ipts_notify_backlight_status(true); - } - - static void pch_enable_backlight(const struct intel_crtc_state *crtc_state, -diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c -index dca0a3a90fb8..719a0360e40c 100644 ---- a/drivers/hid/hid-multitouch.c -+++ b/drivers/hid/hid-multitouch.c -@@ -173,6 +173,7 @@ struct mt_device { - static void mt_post_parse_default_settings(struct mt_device *td, - struct mt_application *app); - static void mt_post_parse(struct mt_device *td, struct mt_application *app); -+static int cc_seen = 0; - - /* classes of device behavior */ - #define MT_CLS_DEFAULT 0x0001 -@@ -792,8 +793,11 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, - app->scantime_logical_max = field->logical_maximum; - return 1; - case HID_DG_CONTACTCOUNT: -- app->have_contact_count = true; -- app->raw_cc = &field->value[usage->usage_index]; -+ if(cc_seen != 1) { -+ app->have_contact_count = true; -+ app->raw_cc = &field->value[usage->usage_index]; -+ cc_seen++; -+ } - return 1; - case HID_DG_AZIMUTH: - /* -@@ -1283,9 +1287,11 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, - field->application != HID_DG_TOUCHSCREEN && - field->application != HID_DG_PEN && - field->application != HID_DG_TOUCHPAD && -+ field->application != HID_GD_MOUSE && - field->application != HID_GD_KEYBOARD && - field->application != HID_GD_SYSTEM_CONTROL && - field->application != HID_CP_CONSUMER_CONTROL && -+ field->logical != HID_DG_TOUCHSCREEN && - field->application != HID_GD_WIRELESS_RADIO_CTLS && - field->application != HID_GD_SYSTEM_MULTIAXIS && - !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && -@@ -1337,6 +1343,14 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct mt_device *td = hid_get_drvdata(hdev); - struct mt_report_data *rdata; - -+ if (field->application == HID_DG_TOUCHSCREEN || -+ field->application == HID_DG_TOUCHPAD) { -+ if (usage->type == EV_KEY || usage->type == EV_ABS) -+ set_bit(usage->type, hi->input->evbit); -+ -+ return -1; -+ } -+ - rdata = mt_find_report_data(td, field->report); - if (rdata && rdata->is_mt_collection) { - /* We own these mappings, tell hid-input to ignore them */ -@@ -1548,12 +1562,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) - /* already handled by hid core */ - break; - case HID_DG_TOUCHSCREEN: -- /* we do not set suffix = "Touchscreen" */ -+ suffix = "Touchscreen"; - hi->input->name = hdev->name; - break; - case HID_DG_STYLUS: - /* force BTN_STYLUS to allow tablet matching in udev */ - __set_bit(BTN_STYLUS, hi->input->keybit); -+ __set_bit(INPUT_PROP_DIRECT, hi->input->propbit); - break; - case HID_VD_ASUS_CUSTOM_MEDIA_KEYS: - suffix = "Custom Media Keys"; -@@ -1669,6 +1684,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) - td->hdev = hdev; - td->mtclass = *mtclass; - td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; -+ cc_seen = 0; - hid_set_drvdata(hdev, td); - - INIT_LIST_HEAD(&td->applications); -diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index f417b06e11c5..960cce957278 100644 ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -528,6 +528,7 @@ source "drivers/misc/ti-st/Kconfig" - source "drivers/misc/lis3lv02d/Kconfig" - source "drivers/misc/altera-stapl/Kconfig" - source "drivers/misc/mei/Kconfig" -+source "drivers/misc/ipts/Kconfig" - source "drivers/misc/vmw_vmci/Kconfig" - source "drivers/misc/mic/Kconfig" - source "drivers/misc/genwqe/Kconfig" -diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index e39ccbbc1b3a..7038b3968a09 100644 ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -44,6 +44,7 @@ obj-y += lis3lv02d/ - obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o - obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ - obj-$(CONFIG_INTEL_MEI) += mei/ -+obj-$(CONFIG_INTEL_IPTS) += ipts/ - obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ - obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o - obj-$(CONFIG_SRAM) += sram.o -diff --git a/drivers/misc/ipts/Kconfig b/drivers/misc/ipts/Kconfig -new file mode 100644 -index 000000000000..360ed3861b82 ---- /dev/null -+++ b/drivers/misc/ipts/Kconfig -@@ -0,0 +1,9 @@ -+config INTEL_IPTS -+ tristate "Intel Precise Touch & Stylus" -+ select INTEL_MEI -+ depends on X86 && PCI && HID -+ help -+ Intel Precise Touch & Stylus support -+ Supported SoCs: -+ Intel Skylake -+ Intel Kabylake -diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile -new file mode 100644 -index 000000000000..1783e9cf13c9 ---- /dev/null -+++ b/drivers/misc/ipts/Makefile -@@ -0,0 +1,13 @@ -+# -+# Makefile - Intel Precise Touch & Stylus device driver -+# Copyright (c) 2016, Intel Corporation. -+# -+ -+obj-$(CONFIG_INTEL_IPTS)+= intel-ipts.o -+intel-ipts-objs += ipts-mei.o -+intel-ipts-objs += ipts-hid.o -+intel-ipts-objs += ipts-msg-handler.o -+intel-ipts-objs += ipts-kernel.o -+intel-ipts-objs += ipts-resource.o -+intel-ipts-objs += ipts-gfx.o -+intel-ipts-$(CONFIG_DEBUG_FS) += ipts-dbgfs.o -diff --git a/drivers/misc/ipts/ipts-binary-spec.h b/drivers/misc/ipts/ipts-binary-spec.h -new file mode 100644 -index 000000000000..87d4bc4133c4 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-binary-spec.h -@@ -0,0 +1,118 @@ -+/* -+ * -+ * Intel Precise Touch & Stylus binary spec -+ * Copyright (c) 2016 Intel Corporation. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _IPTS_BINARY_SPEC_H -+#define _IPTS_BINARY_SPEC_H -+ -+#define IPTS_BIN_HEADER_VERSION 2 -+ -+#pragma pack(1) -+ -+/* we support 16 output buffers(1:feedback, 15:HID) */ -+#define MAX_NUM_OUTPUT_BUFFERS 16 -+ -+typedef enum { -+ IPTS_BIN_KERNEL, -+ IPTS_BIN_RO_DATA, -+ IPTS_BIN_RW_DATA, -+ IPTS_BIN_SENSOR_FRAME, -+ IPTS_BIN_OUTPUT, -+ IPTS_BIN_DYNAMIC_STATE_HEAP, -+ IPTS_BIN_PATCH_LOCATION_LIST, -+ IPTS_BIN_ALLOCATION_LIST, -+ IPTS_BIN_COMMAND_BUFFER_PACKET, -+ IPTS_BIN_TAG, -+} ipts_bin_res_type_t; -+ -+typedef struct ipts_bin_header { -+ char str[4]; -+ unsigned int version; -+ -+#if IPTS_BIN_HEADER_VERSION > 1 -+ unsigned int gfxcore; -+ unsigned int revid; -+#endif -+} ipts_bin_header_t; -+ -+typedef struct ipts_bin_alloc { -+ unsigned int handle; -+ unsigned int reserved; -+} ipts_bin_alloc_t; -+ -+typedef struct ipts_bin_alloc_list { -+ unsigned int num; -+ ipts_bin_alloc_t alloc[]; -+} ipts_bin_alloc_list_t; -+ -+typedef struct ipts_bin_cmdbuf { -+ unsigned int size; -+ char data[]; -+} ipts_bin_cmdbuf_t; -+ -+typedef struct ipts_bin_res { -+ unsigned int handle; -+ ipts_bin_res_type_t type; -+ unsigned int initialize; -+ unsigned int aligned_size; -+ unsigned int size; -+ char data[]; -+} ipts_bin_res_t; -+ -+typedef enum { -+ IPTS_INPUT, -+ IPTS_OUTPUT, -+ IPTS_CONFIGURATION, -+ IPTS_CALIBRATION, -+ IPTS_FEATURE, -+} ipts_bin_io_buffer_type_t; -+ -+typedef struct ipts_bin_io_header { -+ char str[10]; -+ unsigned short type; -+} ipts_bin_io_header_t; -+ -+typedef struct ipts_bin_res_list { -+ unsigned int num; -+ ipts_bin_res_t res[]; -+} ipts_bin_res_list_t; -+ -+typedef struct ipts_bin_patch { -+ unsigned int index; -+ unsigned int reserved1[2]; -+ unsigned int alloc_offset; -+ unsigned int patch_offset; -+ unsigned int reserved2; -+} ipts_bin_patch_t; -+ -+typedef struct ipts_bin_patch_list { -+ unsigned int num; -+ ipts_bin_patch_t patch[]; -+} ipts_bin_patch_list_t; -+ -+typedef struct ipts_bin_guc_wq_info { -+ unsigned int batch_offset; -+ unsigned int size; -+ char data[]; -+} ipts_bin_guc_wq_info_t; -+ -+typedef struct ipts_bin_bufid_patch { -+ unsigned int imm_offset; -+ unsigned int mem_offset; -+} ipts_bin_bufid_patch_t; -+ -+#pragma pack() -+ -+#endif /* _IPTS_BINARY_SPEC_H */ -diff --git a/drivers/misc/ipts/ipts-dbgfs.c b/drivers/misc/ipts/ipts-dbgfs.c -new file mode 100644 -index 000000000000..1c5c92f7d4ba ---- /dev/null -+++ b/drivers/misc/ipts/ipts-dbgfs.c -@@ -0,0 +1,152 @@ -+/* -+ * Intel Precise Touch & Stylus device driver -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ * -+ */ -+#include -+#include -+#include -+ -+#include "ipts.h" -+#include "ipts-sensor-regs.h" -+#include "ipts-msg-handler.h" -+#include "ipts-state.h" -+ -+const char sensor_mode_fmt[] = "sensor mode : %01d\n"; -+const char ipts_status_fmt[] = "sensor mode : %01d\nipts state : %01d\n"; -+ -+static ssize_t ipts_dbgfs_mode_read(struct file *fp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ ipts_info_t *ipts = fp->private_data; -+ char mode[80]; -+ int len = 0; -+ -+ if (cnt < sizeof(sensor_mode_fmt) - 3) -+ return -EINVAL; -+ -+ len = scnprintf(mode, 80, sensor_mode_fmt, ipts->sensor_mode); -+ if (len < 0) -+ return -EIO; -+ -+ return simple_read_from_buffer(ubuf, cnt, ppos, mode, len); -+} -+ -+static ssize_t ipts_dbgfs_mode_write(struct file *fp, const char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ ipts_info_t *ipts = fp->private_data; -+ ipts_state_t state; -+ int sensor_mode, len; -+ char mode[3]; -+ -+ if (cnt == 0 || cnt > 3) -+ return -EINVAL; -+ -+ state = ipts_get_state(ipts); -+ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) { -+ return -EIO; -+ } -+ -+ len = cnt; -+ if (copy_from_user(mode, ubuf, len)) -+ return -EFAULT; -+ -+ while(len > 0 && (isspace(mode[len-1]) || mode[len-1] == '\n')) -+ len--; -+ mode[len] = '\0'; -+ -+ if (sscanf(mode, "%d", &sensor_mode) != 1) -+ return -EINVAL; -+ -+ if (sensor_mode != TOUCH_SENSOR_MODE_RAW_DATA && -+ sensor_mode != TOUCH_SENSOR_MODE_HID) { -+ return -EINVAL; -+ } -+ -+ if (sensor_mode == ipts->sensor_mode) -+ return 0; -+ -+ ipts_switch_sensor_mode(ipts, sensor_mode); -+ -+ return cnt; -+} -+ -+static const struct file_operations ipts_mode_dbgfs_fops = { -+ .open = simple_open, -+ .read = ipts_dbgfs_mode_read, -+ .write = ipts_dbgfs_mode_write, -+ .llseek = generic_file_llseek, -+}; -+ -+static ssize_t ipts_dbgfs_status_read(struct file *fp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ ipts_info_t *ipts = fp->private_data; -+ char status[256]; -+ int len = 0; -+ -+ if (cnt < sizeof(ipts_status_fmt) - 3) -+ return -EINVAL; -+ -+ len = scnprintf(status, 256, ipts_status_fmt, ipts->sensor_mode, -+ ipts->state); -+ if (len < 0) -+ return -EIO; -+ -+ return simple_read_from_buffer(ubuf, cnt, ppos, status, len); -+} -+ -+static const struct file_operations ipts_status_dbgfs_fops = { -+ .open = simple_open, -+ .read = ipts_dbgfs_status_read, -+ .llseek = generic_file_llseek, -+}; -+ -+void ipts_dbgfs_deregister(ipts_info_t* ipts) -+{ -+ if (!ipts->dbgfs_dir) -+ return; -+ -+ debugfs_remove_recursive(ipts->dbgfs_dir); -+ ipts->dbgfs_dir = NULL; -+} -+ -+int ipts_dbgfs_register(ipts_info_t* ipts, const char *name) -+{ -+ struct dentry *dir, *f; -+ -+ dir = debugfs_create_dir(name, NULL); -+ if (!dir) -+ return -ENOMEM; -+ -+ f = debugfs_create_file("mode", S_IRUSR | S_IWUSR, dir, -+ ipts, &ipts_mode_dbgfs_fops); -+ if (!f) { -+ ipts_err(ipts, "debugfs mode creation failed\n"); -+ goto err; -+ } -+ -+ f = debugfs_create_file("status", S_IRUSR, dir, -+ ipts, &ipts_status_dbgfs_fops); -+ if (!f) { -+ ipts_err(ipts, "debugfs status creation failed\n"); -+ goto err; -+ } -+ -+ ipts->dbgfs_dir = dir; -+ -+ return 0; -+err: -+ ipts_dbgfs_deregister(ipts); -+ return -ENODEV; -+} -diff --git a/drivers/misc/ipts/ipts-gfx.c b/drivers/misc/ipts/ipts-gfx.c -new file mode 100644 -index 000000000000..51727770e75d ---- /dev/null -+++ b/drivers/misc/ipts/ipts-gfx.c -@@ -0,0 +1,184 @@ -+/* -+ * -+ * Intel Integrated Touch Gfx Interface Layer -+ * Copyright (c) 2016 Intel Corporation. -+ * -+ * 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. -+ * -+ */ -+#include -+#include -+#include -+ -+#include "ipts.h" -+#include "ipts-msg-handler.h" -+#include "ipts-state.h" -+ -+static void gfx_processing_complete(void *data) -+{ -+ ipts_info_t *ipts = data; -+ -+ if (ipts_get_state(ipts) == IPTS_STA_RAW_DATA_STARTED) { -+ schedule_work(&ipts->raw_data_work); -+ return; -+ } -+ -+ ipts_dbg(ipts, "not ready to handle gfx event\n"); -+} -+ -+static void notify_gfx_status(u32 status, void *data) -+{ -+ ipts_info_t *ipts = data; -+ -+ ipts->gfx_status = status; -+ schedule_work(&ipts->gfx_status_work); -+} -+ -+static int connect_gfx(ipts_info_t *ipts) -+{ -+ int ret = 0; -+ intel_ipts_connect_t ipts_connect; -+ -+ ipts_connect.if_version = IPTS_INTERFACE_V1; -+ ipts_connect.ipts_cb.workload_complete = gfx_processing_complete; -+ ipts_connect.ipts_cb.notify_gfx_status = notify_gfx_status; -+ ipts_connect.data = (void*)ipts; -+ -+ ret = intel_ipts_connect(&ipts_connect); -+ if (ret) -+ return ret; -+ -+ /* TODO: gfx version check */ -+ ipts->gfx_info.gfx_handle = ipts_connect.gfx_handle; -+ ipts->gfx_info.ipts_ops = ipts_connect.ipts_ops; -+ -+ return ret; -+} -+ -+static void disconnect_gfx(ipts_info_t *ipts) -+{ -+ intel_ipts_disconnect(ipts->gfx_info.gfx_handle); -+} -+ -+#ifdef RUN_DBG_THREAD -+#include "../mei/mei_dev.h" -+ -+static struct task_struct *dbg_thread; -+ -+static void ipts_print_dbg_info(ipts_info_t* ipts) -+{ -+ char fw_sts_str[MEI_FW_STATUS_STR_SZ]; -+ u32 *db, *head, *tail; -+ intel_ipts_wq_info_t* wq_info; -+ -+ wq_info = &ipts->resource.wq_info; -+ -+ mei_fw_status_str(ipts->cldev->bus, fw_sts_str, MEI_FW_STATUS_STR_SZ); -+ pr_info(">> tdt : fw status : %s\n", fw_sts_str); -+ -+ db = (u32*)wq_info->db_addr; -+ head = (u32*)wq_info->wq_head_addr; -+ tail = (u32*)wq_info->wq_tail_addr; -+ pr_info(">> == DB s:%x, c:%x ==\n", *db, *(db+1)); -+ pr_info(">> == WQ h:%u, t:%u ==\n", *head, *tail); -+} -+ -+static int ipts_dbg_thread(void *data) -+{ -+ ipts_info_t *ipts = (ipts_info_t *)data; -+ -+ pr_info(">> start debug thread\n"); -+ -+ while (!kthread_should_stop()) { -+ if (ipts_get_state(ipts) != IPTS_STA_RAW_DATA_STARTED) { -+ pr_info("state is not IPTS_STA_RAW_DATA_STARTED : %d\n", -+ ipts_get_state(ipts)); -+ msleep(5000); -+ continue; -+ } -+ -+ ipts_print_dbg_info(ipts); -+ -+ msleep(3000); -+ } -+ -+ return 0; -+} -+#endif -+ -+int ipts_open_gpu(ipts_info_t *ipts) -+{ -+ int ret = 0; -+ -+ ret = connect_gfx(ipts); -+ if (ret) { -+ ipts_dbg(ipts, "cannot connect GPU\n"); -+ return ret; -+ } -+ -+ ret = ipts->gfx_info.ipts_ops.get_wq_info(ipts->gfx_info.gfx_handle, -+ &ipts->resource.wq_info); -+ if (ret) { -+ ipts_dbg(ipts, "error in get_wq_info\n"); -+ return ret; -+ } -+ -+#ifdef RUN_DBG_THREAD -+ dbg_thread = kthread_run(ipts_dbg_thread, (void *)ipts, "ipts_debug"); -+#endif -+ -+ return 0; -+} -+ -+void ipts_close_gpu(ipts_info_t *ipts) -+{ -+ disconnect_gfx(ipts); -+ -+#ifdef RUN_DBG_THREAD -+ kthread_stop(dbg_thread); -+#endif -+} -+ -+intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags) -+{ -+ intel_ipts_mapbuffer_t *buf; -+ u64 handle; -+ int ret; -+ -+ buf = devm_kzalloc(&ipts->cldev->dev, sizeof(*buf), GFP_KERNEL); -+ if (!buf) -+ return NULL; -+ -+ buf->size = size; -+ buf->flags = flags; -+ -+ handle = ipts->gfx_info.gfx_handle; -+ ret = ipts->gfx_info.ipts_ops.map_buffer(handle, buf); -+ if (ret) { -+ devm_kfree(&ipts->cldev->dev, buf); -+ return NULL; -+ } -+ -+ return buf; -+} -+ -+void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf) -+{ -+ u64 handle; -+ int ret; -+ -+ if (!buf) -+ return; -+ -+ handle = ipts->gfx_info.gfx_handle; -+ ret = ipts->gfx_info.ipts_ops.unmap_buffer(handle, buf->buf_handle); -+ -+ devm_kfree(&ipts->cldev->dev, buf); -+} -diff --git a/drivers/misc/ipts/ipts-gfx.h b/drivers/misc/ipts/ipts-gfx.h -new file mode 100644 -index 000000000000..03a5f3551ddf ---- /dev/null -+++ b/drivers/misc/ipts/ipts-gfx.h -@@ -0,0 +1,24 @@ -+/* -+ * Intel Precise Touch & Stylus gpu wrapper -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+ -+#ifndef _IPTS_GFX_H_ -+#define _IPTS_GFX_H_ -+ -+int ipts_open_gpu(ipts_info_t *ipts); -+void ipts_close_gpu(ipts_info_t *ipts); -+intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags); -+void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf); -+ -+#endif // _IPTS_GFX_H_ -diff --git a/drivers/misc/ipts/ipts-hid.c b/drivers/misc/ipts/ipts-hid.c -new file mode 100644 -index 000000000000..e85844dc1158 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-hid.c -@@ -0,0 +1,456 @@ -+/* -+ * Intel Precise Touch & Stylus HID driver -+ * -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "ipts.h" -+#include "ipts-resource.h" -+#include "ipts-sensor-regs.h" -+#include "ipts-msg-handler.h" -+ -+#define BUS_MEI 0x44 -+ -+#define HID_DESC_INTEL "intel/ipts/intel_desc.bin" -+#define HID_DESC_VENDOR "intel/ipts/vendor_desc.bin" -+MODULE_FIRMWARE(HID_DESC_INTEL); -+MODULE_FIRMWARE(HID_DESC_VENDOR); -+ -+typedef enum output_buffer_payload_type { -+ OUTPUT_BUFFER_PAYLOAD_ERROR = 0, -+ OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT, -+ OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT, -+ OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD, -+ OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER -+} output_buffer_payload_type_t; -+ -+typedef struct kernel_output_buffer_header { -+ u16 length; -+ u8 payload_type; -+ u8 reserved1; -+ touch_hid_private_data_t hid_private_data; -+ u8 reserved2[28]; -+ u8 data[0]; -+} kernel_output_buffer_header_t; -+ -+typedef struct kernel_output_payload_error { -+ u16 severity; -+ u16 source; -+ u8 code[4]; -+ char string[128]; -+} kernel_output_payload_error_t; -+ -+static int ipts_hid_get_hid_descriptor(ipts_info_t *ipts, u8 **desc, int *size) -+{ -+ u8 *buf; -+ int hid_size = 0, ret = 0; -+ const struct firmware *intel_desc = NULL; -+ const struct firmware *vendor_desc = NULL; -+ const char *intel_desc_path = HID_DESC_INTEL; -+ const char *vendor_desc_path = HID_DESC_VENDOR; -+ -+ ret = request_firmware(&intel_desc, intel_desc_path, &ipts->cldev->dev); -+ if (ret) { -+ goto no_hid; -+ } -+ hid_size = intel_desc->size; -+ -+ ret = request_firmware(&vendor_desc, vendor_desc_path, &ipts->cldev->dev); -+ if (ret) { -+ ipts_dbg(ipts, "error in reading HID Vendor Descriptor\n"); -+ } else { -+ hid_size += vendor_desc->size; -+ } -+ -+ ipts_dbg(ipts, "hid size = %d\n", hid_size); -+ buf = vmalloc(hid_size); -+ if (buf == NULL) { -+ ret = -ENOMEM; -+ goto no_mem; -+ } -+ -+ memcpy(buf, intel_desc->data, intel_desc->size); -+ if (vendor_desc) { -+ memcpy(&buf[intel_desc->size], vendor_desc->data, -+ vendor_desc->size); -+ release_firmware(vendor_desc); -+ } -+ -+ release_firmware(intel_desc); -+ -+ *desc = buf; -+ *size = hid_size; -+ -+ return 0; -+no_mem : -+ if (vendor_desc) -+ release_firmware(vendor_desc); -+ release_firmware(intel_desc); -+ -+no_hid : -+ return ret; -+} -+ -+static int ipts_hid_parse(struct hid_device *hid) -+{ -+ ipts_info_t *ipts = hid->driver_data; -+ int ret = 0, size; -+ u8 *buf; -+ -+ ipts_dbg(ipts, "ipts_hid_parse() start\n"); -+ ret = ipts_hid_get_hid_descriptor(ipts, &buf, &size); -+ if (ret != 0) { -+ ipts_dbg(ipts, "ipts_hid_ipts_get_hid_descriptor ret %d\n", ret); -+ return -EIO; -+ } -+ -+ ret = hid_parse_report(hid, buf, size); -+ vfree(buf); -+ if (ret) { -+ ipts_err(ipts, "hid_parse_report error : %d\n", ret); -+ goto out; -+ } -+ -+ ipts->hid_desc_ready = true; -+out: -+ return ret; -+} -+ -+static int ipts_hid_start(struct hid_device *hid) -+{ -+ return 0; -+} -+ -+static void ipts_hid_stop(struct hid_device *hid) -+{ -+ return; -+} -+ -+static int ipts_hid_open(struct hid_device *hid) -+{ -+ return 0; -+} -+ -+static void ipts_hid_close(struct hid_device *hid) -+{ -+ ipts_info_t *ipts = hid->driver_data; -+ -+ ipts->hid_desc_ready = false; -+ -+ return; -+} -+ -+static int ipts_hid_send_hid2me_feedback(ipts_info_t *ipts, u32 fb_data_type, -+ __u8 *buf, size_t count) -+{ -+ ipts_buffer_info_t *fb_buf; -+ touch_feedback_hdr_t *feedback; -+ u8 *payload; -+ int header_size; -+ ipts_state_t state; -+ -+ header_size = sizeof(touch_feedback_hdr_t); -+ -+ if (count > ipts->resource.hid2me_buffer_size - header_size) -+ return -EINVAL; -+ -+ state = ipts_get_state(ipts); -+ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) -+ return 0; -+ -+ fb_buf = ipts_get_hid2me_buffer(ipts); -+ feedback = (touch_feedback_hdr_t *)fb_buf->addr; -+ payload = fb_buf->addr + header_size; -+ memset(feedback, 0, header_size); -+ -+ feedback->feedback_data_type = fb_data_type; -+ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; -+ feedback->payload_size_bytes = count; -+ feedback->buffer_id = TOUCH_HID_2_ME_BUFFER_ID; -+ feedback->protocol_ver = 0; -+ feedback->reserved[0] = 0xAC; -+ -+ /* copy payload */ -+ memcpy(payload, buf, count); -+ -+ ipts_send_feedback(ipts, TOUCH_HID_2_ME_BUFFER_ID, 0); -+ -+ return 0; -+} -+ -+static int ipts_hid_raw_request(struct hid_device *hid, -+ unsigned char report_number, __u8 *buf, -+ size_t count, unsigned char report_type, -+ int reqtype) -+{ -+ ipts_info_t *ipts = hid->driver_data; -+ u32 fb_data_type; -+ -+ ipts_dbg(ipts, "hid raw request => report %d, request %d\n", -+ (int)report_type, reqtype); -+ -+ if (report_type != HID_FEATURE_REPORT) -+ return 0; -+ -+ switch (reqtype) { -+ case HID_REQ_GET_REPORT: -+ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES; -+ break; -+ case HID_REQ_SET_REPORT: -+ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES; -+ break; -+ default: -+ ipts_err(ipts, "raw request not supprted: %d\n", reqtype); -+ return -EIO; -+ } -+ -+ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); -+} -+ -+static int ipts_hid_output_report(struct hid_device *hid, -+ __u8 *buf, size_t count) -+{ -+ ipts_info_t *ipts = hid->driver_data; -+ u32 fb_data_type; -+ -+ ipts_dbg(ipts, "hid output report\n"); -+ -+ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; -+ -+ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); -+} -+ -+static struct hid_ll_driver ipts_hid_ll_driver = { -+ .parse = ipts_hid_parse, -+ .start = ipts_hid_start, -+ .stop = ipts_hid_stop, -+ .open = ipts_hid_open, -+ .close = ipts_hid_close, -+ .raw_request = ipts_hid_raw_request, -+ .output_report = ipts_hid_output_report, -+}; -+ -+int ipts_hid_init(ipts_info_t *ipts) -+{ -+ int ret = 0; -+ struct hid_device *hid; -+ -+ hid = hid_allocate_device(); -+ if (IS_ERR(hid)) { -+ ret = PTR_ERR(hid); -+ goto err_dev; -+ } -+ -+ hid->driver_data = ipts; -+ hid->ll_driver = &ipts_hid_ll_driver; -+ hid->dev.parent = &ipts->cldev->dev; -+ hid->bus = BUS_MEI; -+ hid->version = ipts->device_info.fw_rev; -+ hid->vendor = ipts->device_info.vendor_id; -+ hid->product = ipts->device_info.device_id; -+ -+ snprintf(hid->phys, sizeof(hid->phys), "heci3"); -+ snprintf(hid->name, sizeof(hid->name), -+ "%s %04hX:%04hX", "ipts", hid->vendor, hid->product); -+ -+ ret = hid_add_device(hid); -+ if (ret) { -+ if (ret != -ENODEV) -+ ipts_err(ipts, "can't add hid device: %d\n", ret); -+ goto err_mem_free; -+ } -+ -+ ipts->hid = hid; -+ -+ return 0; -+ -+err_mem_free: -+ hid_destroy_device(hid); -+err_dev: -+ return ret; -+} -+ -+void ipts_hid_release(ipts_info_t *ipts) -+{ -+ if (!ipts->hid) -+ return; -+ hid_destroy_device(ipts->hid); -+} -+ -+int ipts_handle_hid_data(ipts_info_t *ipts, -+ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp) -+{ -+ touch_raw_data_hdr_t *raw_header; -+ ipts_buffer_info_t *buffer_info; -+ touch_feedback_hdr_t *feedback; -+ u8 *raw_data; -+ int touch_data_buffer_index; -+ int transaction_id; -+ int ret = 0; -+ -+ touch_data_buffer_index = (int)hid_rsp->touch_data_buffer_index; -+ buffer_info = ipts_get_touch_data_buffer_hid(ipts); -+ raw_header = (touch_raw_data_hdr_t *)buffer_info->addr; -+ transaction_id = raw_header->hid_private_data.transaction_id; -+ -+ raw_data = (u8*)raw_header + sizeof(touch_raw_data_hdr_t); -+ if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_HID_REPORT) { -+ memcpy(ipts->hid_input_report, raw_data, -+ raw_header->raw_data_size_bytes); -+ -+ ret = hid_input_report(ipts->hid, HID_INPUT_REPORT, -+ (u8*)ipts->hid_input_report, -+ raw_header->raw_data_size_bytes, 1); -+ if (ret) { -+ ipts_err(ipts, "error in hid_input_report : %d\n", ret); -+ } -+ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_GET_FEATURES) { -+ /* TODO: implement together with "get feature ioctl" */ -+ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_ERROR) { -+ touch_error_t *touch_err = (touch_error_t *)raw_data; -+ -+ ipts_err(ipts, "error type : %d, me fw error : %x, err reg : %x\n", -+ touch_err->touch_error_type, -+ touch_err->touch_me_fw_error.value, -+ touch_err->touch_error_register.reg_value); -+ } -+ -+ /* send feedback data for HID mode */ -+ buffer_info = ipts_get_feedback_buffer(ipts, touch_data_buffer_index); -+ feedback = (touch_feedback_hdr_t *)buffer_info->addr; -+ memset(feedback, 0, sizeof(touch_feedback_hdr_t)); -+ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; -+ feedback->payload_size_bytes = 0; -+ feedback->buffer_id = touch_data_buffer_index; -+ feedback->protocol_ver = 0; -+ feedback->reserved[0] = 0xAC; -+ -+ ret = ipts_send_feedback(ipts, touch_data_buffer_index, transaction_id); -+ -+ return ret; -+} -+ -+static int handle_outputs(ipts_info_t *ipts, int parallel_idx) -+{ -+ kernel_output_buffer_header_t *out_buf_hdr; -+ ipts_buffer_info_t *output_buf, *fb_buf = NULL; -+ u8 *input_report, *payload; -+ u32 transaction_id; -+ int i, payload_size, ret = 0, header_size; -+ -+ header_size = sizeof(kernel_output_buffer_header_t); -+ output_buf = ipts_get_output_buffers_by_parallel_id(ipts, parallel_idx); -+ for (i = 0; i < ipts->resource.num_of_outputs; i++) { -+ out_buf_hdr = (kernel_output_buffer_header_t*)output_buf[i].addr; -+ if (out_buf_hdr->length < header_size) -+ continue; -+ -+ payload_size = out_buf_hdr->length - header_size; -+ payload = out_buf_hdr->data; -+ -+ switch(out_buf_hdr->payload_type) { -+ case OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT: -+ input_report = ipts->hid_input_report; -+ memcpy(input_report, payload, payload_size); -+ hid_input_report(ipts->hid, HID_INPUT_REPORT, -+ input_report, payload_size, 1); -+ break; -+ case OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT: -+ ipts_dbg(ipts, "output hid feature report\n"); -+ break; -+ case OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD: -+ ipts_dbg(ipts, "output kernel load\n"); -+ break; -+ case OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER: -+ { -+ /* send feedback data for raw data mode */ -+ fb_buf = ipts_get_feedback_buffer(ipts, -+ parallel_idx); -+ transaction_id = out_buf_hdr-> -+ hid_private_data.transaction_id; -+ memcpy(fb_buf->addr, payload, payload_size); -+ break; -+ } -+ case OUTPUT_BUFFER_PAYLOAD_ERROR: -+ { -+ kernel_output_payload_error_t *err_payload; -+ -+ if (payload_size == 0) -+ break; -+ -+ err_payload = -+ (kernel_output_payload_error_t*)payload; -+ -+ ipts_err(ipts, "error : severity : %d," -+ " source : %d," -+ " code : %d:%d:%d:%d\n" -+ "string %s\n", -+ err_payload->severity, -+ err_payload->source, -+ err_payload->code[0], -+ err_payload->code[1], -+ err_payload->code[2], -+ err_payload->code[3], -+ err_payload->string); -+ -+ break; -+ } -+ default: -+ ipts_err(ipts, "invalid output buffer payload\n"); -+ break; -+ } -+ } -+ -+ if (fb_buf) { -+ ret = ipts_send_feedback(ipts, parallel_idx, transaction_id); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int handle_output_buffers(ipts_info_t *ipts, int cur_idx, int end_idx) -+{ -+ int max_num_of_buffers = ipts_get_num_of_parallel_buffers(ipts); -+ -+ do { -+ cur_idx++; /* cur_idx has last completed so starts with +1 */ -+ cur_idx %= max_num_of_buffers; -+ handle_outputs(ipts, cur_idx); -+ } while (cur_idx != end_idx); -+ -+ return 0; -+} -+ -+int ipts_handle_processed_data(ipts_info_t *ipts) -+{ -+ int ret = 0; -+ int current_buffer_idx; -+ int last_buffer_idx; -+ -+ current_buffer_idx = *ipts->last_submitted_id; -+ last_buffer_idx = ipts->last_buffer_completed; -+ -+ if (current_buffer_idx == last_buffer_idx) -+ return 0; -+ -+ ipts->last_buffer_completed = current_buffer_idx; -+ handle_output_buffers(ipts, last_buffer_idx, current_buffer_idx); -+ -+ return ret; -+} -diff --git a/drivers/misc/ipts/ipts-hid.h b/drivers/misc/ipts/ipts-hid.h -new file mode 100644 -index 000000000000..f1b22c912df7 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-hid.h -@@ -0,0 +1,34 @@ -+/* -+ * Intel Precise Touch & Stylus HID definition -+ * -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+#ifndef _IPTS_HID_H_ -+#define _IPTS_HID_H_ -+ -+#define BUS_MEI 0x44 -+ -+#if 0 /* TODO : we have special report ID. will implement them */ -+#define WRITE_CHANNEL_REPORT_ID 0xa -+#define READ_CHANNEL_REPORT_ID 0xb -+#define CONFIG_CHANNEL_REPORT_ID 0xd -+#define VENDOR_INFO_REPORT_ID 0xF -+#define SINGLE_TOUCH_REPORT_ID 0x40 -+#endif -+ -+int ipts_hid_init(ipts_info_t *ipts); -+void ipts_hid_release(ipts_info_t *ipts); -+int ipts_handle_hid_data(ipts_info_t *ipts, -+ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp); -+ -+#endif /* _IPTS_HID_H_ */ -diff --git a/drivers/misc/ipts/ipts-kernel.c b/drivers/misc/ipts/ipts-kernel.c -new file mode 100644 -index 000000000000..86fd359d2eed ---- /dev/null -+++ b/drivers/misc/ipts/ipts-kernel.c -@@ -0,0 +1,1050 @@ -+#include -+#include -+#include -+#include -+ -+#include "ipts.h" -+#include "ipts-resource.h" -+#include "ipts-binary-spec.h" -+#include "ipts-state.h" -+#include "ipts-msg-handler.h" -+#include "ipts-gfx.h" -+ -+#define MAX_IOCL_FILE_NAME_LEN 80 -+#define MAX_IOCL_FILE_PATH_LEN 256 -+ -+#pragma pack(1) -+typedef struct bin_data_file_info { -+ u32 io_buffer_type; -+ u32 flags; -+ char file_name[MAX_IOCL_FILE_NAME_LEN]; -+} bin_data_file_info_t; -+ -+typedef struct bin_fw_info { -+ char fw_name[MAX_IOCL_FILE_NAME_LEN]; -+ -+ /* list of parameters to load a kernel */ -+ s32 vendor_output; /* output index. -1 for no use */ -+ u32 num_of_data_files; -+ bin_data_file_info_t data_file[]; -+} bin_fw_info_t; -+ -+typedef struct bin_fw_list { -+ u32 num_of_fws; -+ bin_fw_info_t fw_info[]; -+} bin_fw_list_t; -+#pragma pack() -+ -+/* OpenCL kernel */ -+typedef struct bin_workload { -+ int cmdbuf_index; -+ int iobuf_input; -+ int iobuf_output[MAX_NUM_OUTPUT_BUFFERS]; -+} bin_workload_t; -+ -+typedef struct bin_buffer { -+ unsigned int handle; -+ intel_ipts_mapbuffer_t *buf; -+ bool no_unmap; /* only releasing vendor kernel unmaps output buffers */ -+} bin_buffer_t; -+ -+typedef struct bin_alloc_info { -+ bin_buffer_t *buffs; -+ int num_of_allocations; -+ int num_of_outputs; -+ -+ int num_of_buffers; -+} bin_alloc_info_t; -+ -+typedef struct bin_guc_wq_item { -+ unsigned int batch_offset; -+ unsigned int size; -+ char data[]; -+} bin_guc_wq_item_t; -+ -+typedef struct bin_kernel_info { -+ bin_workload_t *wl; -+ bin_alloc_info_t *alloc_info; -+ bin_guc_wq_item_t *guc_wq_item; -+ ipts_bin_bufid_patch_t bufid_patch; -+ -+ bool is_vendor; /* 1: vendor, 0: postprocessing */ -+} bin_kernel_info_t; -+ -+typedef struct bin_kernel_list { -+ intel_ipts_mapbuffer_t *bufid_buf; -+ int num_of_kernels; -+ bin_kernel_info_t kernels[]; -+} bin_kernel_list_t; -+ -+typedef struct bin_parse_info { -+ u8 *data; -+ int size; -+ int parsed; -+ -+ bin_fw_info_t *fw_info; -+ -+ /* only used by postprocessing */ -+ bin_kernel_info_t *vendor_kernel; -+ u32 interested_vendor_output; /* interested vendor output index */ -+} bin_parse_info_t; -+ -+#define BDW_SURFACE_BASE_ADDRESS 0x6101000e -+#define SURFACE_STATE_OFFSET_WORD 4 -+#define SBA_OFFSET_BYTES 16384 -+#define LASTSUBMITID_DEFAULT_VALUE -1 -+ -+#define IPTS_FW_PATH_FMT "intel/ipts/%s" -+#define IPTS_FW_CONFIG_FILE "intel/ipts/ipts_fw_config.bin" -+ -+MODULE_FIRMWARE(IPTS_FW_CONFIG_FILE); -+ -+#define IPTS_INPUT_ON ((u32)1 << IPTS_INPUT) -+#define IPTS_OUTPUT_ON ((u32)1 << IPTS_OUTPUT) -+#define IPTS_CONFIGURATION_ON ((u32)1 << IPTS_CONFIGURATION) -+#define IPTS_CALIBRATION_ON ((u32)1 << IPTS_CALIBRATION) -+#define IPTS_FEATURE_ON ((u32)1 << IPTS_FEATURE) -+ -+#define DATA_FILE_FLAG_SHARE 0x00000001 -+#define DATA_FILE_FLAG_ALLOC_CONTIGUOUS 0x00000002 -+ -+static int bin_read_fw(ipts_info_t *ipts, const char *fw_name, -+ u8* data, int size) -+{ -+ const struct firmware *fw = NULL; -+ char fw_path[MAX_IOCL_FILE_PATH_LEN]; -+ int ret = 0; -+ -+ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); -+ ret = request_firmware(&fw, fw_path, &ipts->cldev->dev); -+ if (ret) { -+ ipts_err(ipts, "cannot read fw %s\n", fw_path); -+ return ret; -+ } -+ -+ if (fw->size > size) { -+ ipts_dbg(ipts, "too small buffer to contain fw data\n"); -+ ret = -EINVAL; -+ goto rel_return; -+ } -+ -+ memcpy(data, fw->data, fw->size); -+ -+rel_return: -+ release_firmware(fw); -+ -+ return ret; -+} -+ -+ -+static bin_data_file_info_t* bin_get_data_file_info(bin_fw_info_t* fw_info, -+ u32 io_buffer_type) -+{ -+ int i; -+ -+ for (i = 0; i < fw_info->num_of_data_files; i++) { -+ if (fw_info->data_file[i].io_buffer_type == io_buffer_type) -+ break; -+ } -+ -+ if (i == fw_info->num_of_data_files) -+ return NULL; -+ -+ return &fw_info->data_file[i]; -+} -+ -+static inline bool is_shared_data(const bin_data_file_info_t *data_file) -+{ -+ if (data_file) -+ return (!!(data_file->flags & DATA_FILE_FLAG_SHARE)); -+ -+ return false; -+} -+ -+static inline bool is_alloc_cont_data(const bin_data_file_info_t *data_file) -+{ -+ if (data_file) -+ return (!!(data_file->flags & DATA_FILE_FLAG_ALLOC_CONTIGUOUS)); -+ -+ return false; -+} -+ -+static inline bool is_parsing_vendor_kernel(const bin_parse_info_t *parse_info) -+{ -+ /* vendor_kernel == null while loading itself(vendor kernel) */ -+ return parse_info->vendor_kernel == NULL; -+} -+ -+static int bin_read_allocation_list(ipts_info_t *ipts, -+ bin_parse_info_t *parse_info, -+ bin_alloc_info_t *alloc_info) -+{ -+ ipts_bin_alloc_list_t *alloc_list; -+ int alloc_idx, parallel_idx, num_of_parallels, buf_idx, num_of_buffers; -+ int parsed, size; -+ -+ parsed = parse_info->parsed; -+ size = parse_info->size; -+ -+ alloc_list = (ipts_bin_alloc_list_t *)&parse_info->data[parsed]; -+ -+ /* validation check */ -+ if (sizeof(alloc_list->num) > size - parsed) -+ return -EINVAL; -+ -+ /* read the number of aloocations */ -+ parsed += sizeof(alloc_list->num); -+ -+ /* validation check */ -+ if (sizeof(alloc_list->alloc[0]) * alloc_list->num > size - parsed) -+ return -EINVAL; -+ -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ num_of_buffers = num_of_parallels * alloc_list->num + num_of_parallels; -+ -+ alloc_info->buffs = vmalloc(sizeof(bin_buffer_t) * num_of_buffers); -+ if (alloc_info->buffs == NULL) -+ return -ENOMEM; -+ -+ memset(alloc_info->buffs, 0, sizeof(bin_buffer_t) * num_of_buffers); -+ for (alloc_idx = 0; alloc_idx < alloc_list->num; alloc_idx++) { -+ for (parallel_idx = 0; parallel_idx < num_of_parallels; -+ parallel_idx++) { -+ buf_idx = alloc_idx + (parallel_idx * alloc_list->num); -+ alloc_info->buffs[buf_idx].handle = -+ alloc_list->alloc[alloc_idx].handle; -+ -+ } -+ -+ parsed += sizeof(alloc_list->alloc[0]); -+ } -+ -+ parse_info->parsed = parsed; -+ alloc_info->num_of_allocations = alloc_list->num; -+ alloc_info->num_of_buffers = num_of_buffers; -+ -+ ipts_dbg(ipts, "number of allocations = %d, buffers = %d\n", -+ alloc_info->num_of_allocations, -+ alloc_info->num_of_buffers); -+ -+ return 0; -+} -+ -+static void patch_SBA(u32 *buf_addr, u64 gpu_addr, int size) -+{ -+ u64 *stateBase; -+ u64 SBA; -+ u32 inst; -+ int i; -+ -+ SBA = gpu_addr + SBA_OFFSET_BYTES; -+ -+ for (i = 0; i < size/4; i++) { -+ inst = buf_addr[i]; -+ if (inst == BDW_SURFACE_BASE_ADDRESS) { -+ stateBase = (u64*)&buf_addr[i + SURFACE_STATE_OFFSET_WORD]; -+ *stateBase |= SBA; -+ *stateBase |= 0x01; // enable -+ break; -+ } -+ } -+} -+ -+static int bin_read_cmd_buffer(ipts_info_t *ipts, -+ bin_parse_info_t *parse_info, -+ bin_alloc_info_t *alloc_info, -+ bin_workload_t *wl) -+{ -+ ipts_bin_cmdbuf_t *cmd; -+ intel_ipts_mapbuffer_t *buf; -+ int cmdbuf_idx, size, parsed, parallel_idx, num_of_parallels; -+ -+ size = parse_info->size; -+ parsed = parse_info->parsed; -+ -+ cmd = (ipts_bin_cmdbuf_t *)&parse_info->data[parsed]; -+ -+ if (sizeof(cmd->size) > size - parsed) -+ return -EINVAL; -+ -+ parsed += sizeof(cmd->size); -+ if (cmd->size > size - parsed) -+ return -EINVAL; -+ -+ ipts_dbg(ipts, "cmd buf size = %d\n", cmd->size); -+ -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ /* command buffers are located after the other allocations */ -+ cmdbuf_idx = num_of_parallels * alloc_info->num_of_allocations; -+ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { -+ buf = ipts_map_buffer(ipts, cmd->size, 0); -+ if (buf == NULL) -+ return -ENOMEM; -+ -+ ipts_dbg(ipts, "cmd_idx[%d] = %d, g:0x%p, c:0x%p\n", parallel_idx, -+ cmdbuf_idx, buf->gfx_addr, buf->cpu_addr); -+ -+ memcpy((void *)buf->cpu_addr, &(cmd->data[0]), cmd->size); -+ patch_SBA(buf->cpu_addr, (u64)buf->gfx_addr, cmd->size); -+ alloc_info->buffs[cmdbuf_idx].buf = buf; -+ wl[parallel_idx].cmdbuf_index = cmdbuf_idx; -+ -+ cmdbuf_idx++; -+ } -+ -+ parsed += cmd->size; -+ parse_info->parsed = parsed; -+ -+ return 0; -+} -+ -+static int bin_find_alloc(ipts_info_t *ipts, -+ bin_alloc_info_t *alloc_info, -+ u32 handle) -+{ -+ int i; -+ -+ for (i = 0; i < alloc_info->num_of_allocations; i++) { -+ if (alloc_info->buffs[i].handle == handle) -+ return i; -+ } -+ -+ return -1; -+} -+ -+static intel_ipts_mapbuffer_t* bin_get_vendor_kernel_output( -+ bin_parse_info_t *parse_info, -+ int parallel_idx) -+{ -+ bin_kernel_info_t *vendor = parse_info->vendor_kernel; -+ bin_alloc_info_t *alloc_info; -+ int buf_idx, vendor_output_idx; -+ -+ alloc_info = vendor->alloc_info; -+ vendor_output_idx = parse_info->interested_vendor_output; -+ -+ if (vendor_output_idx >= alloc_info->num_of_outputs) -+ return NULL; -+ -+ buf_idx = vendor->wl[parallel_idx].iobuf_output[vendor_output_idx]; -+ return alloc_info->buffs[buf_idx].buf; -+} -+ -+static int bin_read_res_list(ipts_info_t *ipts, -+ bin_parse_info_t *parse_info, -+ bin_alloc_info_t *alloc_info, -+ bin_workload_t *wl) -+{ -+ ipts_bin_res_list_t *res_list; -+ ipts_bin_res_t *res; -+ intel_ipts_mapbuffer_t *buf; -+ bin_data_file_info_t *data_file; -+ u8 *bin_data; -+ int i, size, parsed, parallel_idx, num_of_parallels, output_idx = -1; -+ int buf_idx, num_of_alloc; -+ u32 buf_size, flags, io_buf_type; -+ bool initialize; -+ -+ parsed = parse_info->parsed; -+ size = parse_info->size; -+ bin_data = parse_info->data; -+ -+ res_list = (ipts_bin_res_list_t *)&parse_info->data[parsed]; -+ if (sizeof(res_list->num) > (size - parsed)) -+ return -EINVAL; -+ parsed += sizeof(res_list->num); -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ -+ ipts_dbg(ipts, "number of resources %u\n", res_list->num); -+ for (i = 0; i < res_list->num; i++) { -+ initialize = false; -+ io_buf_type = 0; -+ flags = 0; -+ -+ /* initial data */ -+ data_file = NULL; -+ -+ res = (ipts_bin_res_t *)(&(bin_data[parsed])); -+ if (sizeof(res[0]) > (size - parsed)) { -+ return -EINVAL; -+ } -+ -+ ipts_dbg(ipts, "Resource(%d):handle 0x%08x type %u init %u" -+ " size %u alsigned %u\n", -+ i, res->handle, res->type, res->initialize, -+ res->size, res->aligned_size); -+ parsed += sizeof(res[0]); -+ -+ if (res->initialize) { -+ if (res->size > (size - parsed)) { -+ return -EINVAL; -+ } -+ parsed += res->size; -+ } -+ -+ initialize = res->initialize; -+ if (initialize && res->size > sizeof(ipts_bin_io_header_t)) { -+ ipts_bin_io_header_t *io_hdr; -+ io_hdr = (ipts_bin_io_header_t *)(&res->data[0]); -+ if (strncmp(io_hdr->str, "INTELTOUCH", 10) == 0) { -+ data_file = bin_get_data_file_info( -+ parse_info->fw_info, -+ (u32)io_hdr->type); -+ switch (io_hdr->type) { -+ case IPTS_INPUT: -+ ipts_dbg(ipts, "input detected\n"); -+ io_buf_type = IPTS_INPUT_ON; -+ flags = IPTS_BUF_FLAG_CONTIGUOUS; -+ break; -+ case IPTS_OUTPUT: -+ ipts_dbg(ipts, "output detected\n"); -+ io_buf_type = IPTS_OUTPUT_ON; -+ output_idx++; -+ break; -+ default: -+ if ((u32)io_hdr->type > 31) { -+ ipts_err(ipts, -+ "invalid io buffer : %u\n", -+ (u32)io_hdr->type); -+ continue; -+ } -+ -+ if (is_alloc_cont_data(data_file)) -+ flags = IPTS_BUF_FLAG_CONTIGUOUS; -+ -+ io_buf_type = ((u32)1 << (u32)io_hdr->type); -+ ipts_dbg(ipts, "special io buffer %u\n", -+ io_hdr->type); -+ break; -+ } -+ -+ initialize = false; -+ } -+ } -+ -+ num_of_alloc = alloc_info->num_of_allocations; -+ buf_idx = bin_find_alloc(ipts, alloc_info, res->handle); -+ if (buf_idx == -1) { -+ ipts_dbg(ipts, "cannot find alloc info\n"); -+ return -EINVAL; -+ } -+ for (parallel_idx = 0; parallel_idx < num_of_parallels; -+ parallel_idx++, buf_idx += num_of_alloc) { -+ if (!res->aligned_size) -+ continue; -+ -+ if (!(parallel_idx == 0 || -+ (io_buf_type && !is_shared_data(data_file)))) -+ continue; -+ -+ buf_size = res->aligned_size; -+ if (io_buf_type & IPTS_INPUT_ON) { -+ buf_size = max_t(u32, -+ ipts->device_info.frame_size, -+ buf_size); -+ wl[parallel_idx].iobuf_input = buf_idx; -+ } else if (io_buf_type & IPTS_OUTPUT_ON) { -+ wl[parallel_idx].iobuf_output[output_idx] = buf_idx; -+ -+ if (!is_parsing_vendor_kernel(parse_info) && -+ output_idx > 0) { -+ ipts_err(ipts, -+ "postproc with more than one inout" -+ " is not supported : %d\n", output_idx); -+ return -EINVAL; -+ } -+ } -+ -+ if (!is_parsing_vendor_kernel(parse_info) && -+ io_buf_type & IPTS_OUTPUT_ON) { -+ buf = bin_get_vendor_kernel_output( -+ parse_info, -+ parallel_idx); -+ alloc_info->buffs[buf_idx].no_unmap = true; -+ } else -+ buf = ipts_map_buffer(ipts, buf_size, flags); -+ -+ if (buf == NULL) { -+ ipts_dbg(ipts, "ipts_map_buffer failed\n"); -+ return -ENOMEM; -+ } -+ -+ if (initialize) { -+ memcpy((void *)buf->cpu_addr, &(res->data[0]), -+ res->size); -+ } else { -+ if (data_file && strlen(data_file->file_name)) { -+ bin_read_fw(ipts, data_file->file_name, -+ buf->cpu_addr, buf_size); -+ } else if (is_parsing_vendor_kernel(parse_info) || -+ !(io_buf_type & IPTS_OUTPUT_ON)) { -+ memset((void *)buf->cpu_addr, 0, res->size); -+ } -+ } -+ -+ alloc_info->buffs[buf_idx].buf = buf; -+ } -+ } -+ -+ alloc_info->num_of_outputs = output_idx + 1; -+ parse_info->parsed = parsed; -+ -+ return 0; -+} -+ -+static int bin_read_patch_list(ipts_info_t *ipts, -+ bin_parse_info_t *parse_info, -+ bin_alloc_info_t *alloc_info, -+ bin_workload_t *wl) -+{ -+ ipts_bin_patch_list_t *patch_list; -+ ipts_bin_patch_t *patch; -+ intel_ipts_mapbuffer_t *cmd = NULL; -+ u8 *batch; -+ int parsed, size, i, parallel_idx, num_of_parallels, cmd_idx, buf_idx; -+ unsigned int gtt_offset; -+ -+ parsed = parse_info->parsed; -+ size = parse_info->size; -+ patch_list = (ipts_bin_patch_list_t *)&parse_info->data[parsed]; -+ -+ if (sizeof(patch_list->num) > (size - parsed)) { -+ return -EFAULT; -+ } -+ parsed += sizeof(patch_list->num); -+ -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ patch = (ipts_bin_patch_t *)(&patch_list->patch[0]); -+ for (i = 0; i < patch_list->num; i++) { -+ if (sizeof(patch_list->patch[0]) > (size - parsed)) { -+ return -EFAULT; -+ } -+ -+ for (parallel_idx = 0; parallel_idx < num_of_parallels; -+ parallel_idx++) { -+ cmd_idx = wl[parallel_idx].cmdbuf_index; -+ buf_idx = patch[i].index + parallel_idx * -+ alloc_info->num_of_allocations; -+ -+ if (alloc_info->buffs[buf_idx].buf == NULL) { -+ /* buffer shared */ -+ buf_idx = patch[i].index; -+ } -+ -+ cmd = alloc_info->buffs[cmd_idx].buf; -+ batch = (char *)(u64)cmd->cpu_addr; -+ -+ gtt_offset = 0; -+ if(alloc_info->buffs[buf_idx].buf != NULL) { -+ gtt_offset = (u32)(u64) -+ alloc_info->buffs[buf_idx].buf->gfx_addr; -+ } -+ gtt_offset += patch[i].alloc_offset; -+ -+ batch += patch[i].patch_offset; -+ *(u32*)batch = gtt_offset; -+ } -+ -+ parsed += sizeof(patch_list->patch[0]); -+ } -+ -+ parse_info->parsed = parsed; -+ -+ return 0; -+} -+ -+static int bin_read_guc_wq_item(ipts_info_t *ipts, -+ bin_parse_info_t *parse_info, -+ bin_guc_wq_item_t **guc_wq_item) -+{ -+ ipts_bin_guc_wq_info_t *bin_guc_wq; -+ bin_guc_wq_item_t *item; -+ u8 *wi_data; -+ int size, parsed, hdr_size, wi_size; -+ int i, batch_offset; -+ -+ parsed = parse_info->parsed; -+ size = parse_info->size; -+ bin_guc_wq = (ipts_bin_guc_wq_info_t *)&parse_info->data[parsed]; -+ -+ wi_size = bin_guc_wq->size; -+ wi_data = bin_guc_wq->data; -+ batch_offset = bin_guc_wq->batch_offset; -+ ipts_dbg(ipts, "wi size = %d, bt offset = %d\n", wi_size, batch_offset); -+ for (i = 0; i < wi_size / sizeof(u32); i++) { -+ ipts_dbg(ipts, "wi[%d] = 0x%08x\n", i, *((u32*)wi_data + i)); -+ } -+ hdr_size = sizeof(bin_guc_wq->size) + sizeof(bin_guc_wq->batch_offset); -+ -+ if (hdr_size > (size - parsed)) { -+ return -EINVAL; -+ } -+ parsed += hdr_size; -+ -+ item = vmalloc(sizeof(bin_guc_wq_item_t) + wi_size); -+ if (item == NULL) -+ return -ENOMEM; -+ -+ item->size = wi_size; -+ item->batch_offset = batch_offset; -+ memcpy(item->data, wi_data, wi_size); -+ -+ *guc_wq_item = item; -+ -+ parsed += wi_size; -+ parse_info->parsed = parsed; -+ -+ return 0; -+} -+ -+static int bin_setup_guc_workqueue(ipts_info_t *ipts, -+ bin_kernel_list_t *kernel_list) -+{ -+ bin_alloc_info_t *alloc_info; -+ bin_workload_t *wl; -+ bin_kernel_info_t *kernel; -+ u8 *wq_start, *wq_addr, *wi_data; -+ bin_buffer_t *bin_buf; -+ int wq_size, wi_size, parallel_idx, cmd_idx, k_idx, iter_size; -+ int i, num_of_parallels, batch_offset, k_num, total_workload; -+ -+ wq_addr = (u8*)ipts->resource.wq_info.wq_addr; -+ wq_size = ipts->resource.wq_info.wq_size; -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ total_workload = ipts_get_wq_item_size(ipts); -+ k_num = kernel_list->num_of_kernels; -+ -+ iter_size = total_workload * num_of_parallels; -+ if (wq_size % iter_size) { -+ ipts_err(ipts, "wq item cannot fit into wq\n"); -+ return -EINVAL; -+ } -+ -+ wq_start = wq_addr; -+ for (parallel_idx = 0; parallel_idx < num_of_parallels; -+ parallel_idx++) { -+ kernel = &kernel_list->kernels[0]; -+ for (k_idx = 0; k_idx < k_num; k_idx++, kernel++) { -+ wl = kernel->wl; -+ alloc_info = kernel->alloc_info; -+ -+ batch_offset = kernel->guc_wq_item->batch_offset; -+ wi_size = kernel->guc_wq_item->size; -+ wi_data = &kernel->guc_wq_item->data[0]; -+ -+ cmd_idx = wl[parallel_idx].cmdbuf_index; -+ bin_buf = &alloc_info->buffs[cmd_idx]; -+ -+ /* Patch the WQ Data with proper batch buffer offset */ -+ *(u32*)(wi_data + batch_offset) = -+ (u32)(unsigned long)(bin_buf->buf->gfx_addr); -+ -+ memcpy(wq_addr, wi_data, wi_size); -+ -+ wq_addr += wi_size; -+ } -+ } -+ -+ for (i = 0; i < (wq_size / iter_size) - 1; i++) { -+ memcpy(wq_addr, wq_start, iter_size); -+ wq_addr += iter_size; -+ } -+ -+ return 0; -+} -+ -+static int bin_read_bufid_patch(ipts_info_t *ipts, -+ bin_parse_info_t *parse_info, -+ ipts_bin_bufid_patch_t *bufid_patch) -+{ -+ ipts_bin_bufid_patch_t *patch; -+ int size, parsed; -+ -+ parsed = parse_info->parsed; -+ size = parse_info->size; -+ patch = (ipts_bin_bufid_patch_t *)&parse_info->data[parsed]; -+ -+ if (sizeof(ipts_bin_bufid_patch_t) > (size - parsed)) { -+ ipts_dbg(ipts, "invalid bufid info\n"); -+ return -EINVAL; -+ } -+ parsed += sizeof(ipts_bin_bufid_patch_t); -+ -+ memcpy(bufid_patch, patch, sizeof(ipts_bin_bufid_patch_t)); -+ -+ parse_info->parsed = parsed; -+ -+ return 0; -+} -+ -+static int bin_setup_bufid_buffer(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) -+{ -+ intel_ipts_mapbuffer_t *buf, *cmd_buf; -+ bin_kernel_info_t *last_kernel; -+ bin_alloc_info_t *alloc_info; -+ bin_workload_t *wl; -+ u8 *batch; -+ int parallel_idx, num_of_parallels, cmd_idx; -+ u32 mem_offset, imm_offset; -+ -+ buf = ipts_map_buffer(ipts, PAGE_SIZE, 0); -+ if (!buf) { -+ return -ENOMEM; -+ } -+ -+ last_kernel = &kernel_list->kernels[kernel_list->num_of_kernels - 1]; -+ -+ mem_offset = last_kernel->bufid_patch.mem_offset; -+ imm_offset = last_kernel->bufid_patch.imm_offset; -+ wl = last_kernel->wl; -+ alloc_info = last_kernel->alloc_info; -+ -+ /* Initialize the buffer with default value */ -+ *((u32*)buf->cpu_addr) = LASTSUBMITID_DEFAULT_VALUE; -+ ipts->current_buffer_index = LASTSUBMITID_DEFAULT_VALUE; -+ ipts->last_buffer_completed = LASTSUBMITID_DEFAULT_VALUE; -+ ipts->last_submitted_id = (int*)buf->cpu_addr; -+ -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { -+ cmd_idx = wl[parallel_idx].cmdbuf_index; -+ cmd_buf = alloc_info->buffs[cmd_idx].buf; -+ batch = (u8*)(u64)cmd_buf->cpu_addr; -+ -+ *((u32*)(batch + mem_offset)) = (u32)(u64)(buf->gfx_addr); -+ *((u32*)(batch + imm_offset)) = parallel_idx; -+ } -+ -+ kernel_list->bufid_buf = buf; -+ -+ return 0; -+} -+ -+static void unmap_buffers(ipts_info_t *ipts, bin_alloc_info_t *alloc_info) -+{ -+ bin_buffer_t *buffs; -+ int i, num_of_buffers; -+ -+ num_of_buffers = alloc_info->num_of_buffers; -+ buffs = &alloc_info->buffs[0]; -+ -+ for (i = 0; i < num_of_buffers; i++) { -+ if (buffs[i].no_unmap != true && buffs[i].buf != NULL) -+ ipts_unmap_buffer(ipts, buffs[i].buf); -+ } -+} -+ -+static int load_kernel(ipts_info_t *ipts, bin_parse_info_t *parse_info, -+ bin_kernel_info_t *kernel) -+{ -+ ipts_bin_header_t *hdr; -+ bin_workload_t *wl; -+ bin_alloc_info_t *alloc_info; -+ bin_guc_wq_item_t *guc_wq_item = NULL; -+ ipts_bin_bufid_patch_t bufid_patch; -+ int num_of_parallels, ret; -+ -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ -+ /* check header version and magic numbers */ -+ hdr = (ipts_bin_header_t *)parse_info->data; -+ if (hdr->version != IPTS_BIN_HEADER_VERSION || -+ strncmp(hdr->str, "IOCL", 4) != 0) { -+ ipts_err(ipts, "binary header is not correct version = %d, " -+ "string = %c%c%c%c\n", hdr->version, -+ hdr->str[0], hdr->str[1], -+ hdr->str[2], hdr->str[3] ); -+ return -EINVAL; -+ } -+ -+ parse_info->parsed = sizeof(ipts_bin_header_t); -+ wl = vmalloc(sizeof(bin_workload_t) * num_of_parallels); -+ if (wl == NULL) -+ return -ENOMEM; -+ memset(wl, 0, sizeof(bin_workload_t) * num_of_parallels); -+ -+ alloc_info = vmalloc(sizeof(bin_alloc_info_t)); -+ if (alloc_info == NULL) { -+ vfree(wl); -+ return -ENOMEM; -+ } -+ memset(alloc_info, 0, sizeof(bin_alloc_info_t)); -+ -+ ipts_dbg(ipts, "kernel setup(size : %d)\n", parse_info->size); -+ -+ ret = bin_read_allocation_list(ipts, parse_info, alloc_info); -+ if (ret) { -+ ipts_dbg(ipts, "error read_allocation_list\n"); -+ goto setup_error; -+ } -+ -+ ret = bin_read_cmd_buffer(ipts, parse_info, alloc_info, wl); -+ if (ret) { -+ ipts_dbg(ipts, "error read_cmd_buffer\n"); -+ goto setup_error; -+ } -+ -+ ret = bin_read_res_list(ipts, parse_info, alloc_info, wl); -+ if (ret) { -+ ipts_dbg(ipts, "error read_res_list\n"); -+ goto setup_error; -+ } -+ -+ ret = bin_read_patch_list(ipts, parse_info, alloc_info, wl); -+ if (ret) { -+ ipts_dbg(ipts, "error read_patch_list\n"); -+ goto setup_error; -+ } -+ -+ ret = bin_read_guc_wq_item(ipts, parse_info, &guc_wq_item); -+ if (ret) { -+ ipts_dbg(ipts, "error read_guc_workqueue\n"); -+ goto setup_error; -+ } -+ -+ memset(&bufid_patch, 0, sizeof(bufid_patch)); -+ ret = bin_read_bufid_patch(ipts, parse_info, &bufid_patch); -+ if (ret) { -+ ipts_dbg(ipts, "error read_bufid_patch\n"); -+ goto setup_error; -+ } -+ -+ kernel->wl = wl; -+ kernel->alloc_info = alloc_info; -+ kernel->is_vendor = is_parsing_vendor_kernel(parse_info); -+ kernel->guc_wq_item = guc_wq_item; -+ memcpy(&kernel->bufid_patch, &bufid_patch, sizeof(bufid_patch)); -+ -+ return 0; -+ -+setup_error: -+ vfree(guc_wq_item); -+ -+ unmap_buffers(ipts, alloc_info); -+ -+ vfree(alloc_info->buffs); -+ vfree(alloc_info); -+ vfree(wl); -+ -+ return ret; -+} -+ -+void bin_setup_input_output(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) -+{ -+ bin_kernel_info_t *vendor_kernel; -+ bin_workload_t *wl; -+ intel_ipts_mapbuffer_t *buf; -+ bin_alloc_info_t *alloc_info; -+ int parallel_idx, num_of_parallels, i, buf_idx; -+ -+ vendor_kernel = &kernel_list->kernels[0]; -+ -+ wl = vendor_kernel->wl; -+ alloc_info = vendor_kernel->alloc_info; -+ ipts->resource.num_of_outputs = alloc_info->num_of_outputs; -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ -+ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { -+ buf_idx = wl[parallel_idx].iobuf_input; -+ buf = alloc_info->buffs[buf_idx].buf; -+ -+ ipts_dbg(ipts, "in_buf[%d](%d) c:%p, p:%p, g:%p\n", -+ parallel_idx, buf_idx, (void*)buf->cpu_addr, -+ (void*)buf->phy_addr, (void*)buf->gfx_addr); -+ -+ ipts_set_input_buffer(ipts, parallel_idx, buf->cpu_addr, -+ buf->phy_addr); -+ -+ for (i = 0; i < alloc_info->num_of_outputs; i++) { -+ buf_idx = wl[parallel_idx].iobuf_output[i]; -+ buf = alloc_info->buffs[buf_idx].buf; -+ -+ ipts_dbg(ipts, "out_buf[%d][%d] c:%p, p:%p, g:%p\n", -+ parallel_idx, i, (void*)buf->cpu_addr, -+ (void*)buf->phy_addr, (void*)buf->gfx_addr); -+ -+ ipts_set_output_buffer(ipts, parallel_idx, i, -+ buf->cpu_addr, buf->phy_addr); -+ } -+ } -+} -+ -+static void unload_kernel(ipts_info_t *ipts, bin_kernel_info_t *kernel) -+{ -+ bin_alloc_info_t *alloc_info = kernel->alloc_info; -+ bin_guc_wq_item_t *guc_wq_item = kernel->guc_wq_item; -+ -+ if (guc_wq_item) { -+ vfree(guc_wq_item); -+ } -+ -+ if (alloc_info) { -+ unmap_buffers(ipts, alloc_info); -+ -+ vfree(alloc_info->buffs); -+ vfree(alloc_info); -+ } -+} -+ -+static int setup_kernel(ipts_info_t *ipts, bin_fw_list_t *fw_list) -+{ -+ bin_kernel_list_t *kernel_list = NULL; -+ bin_kernel_info_t *kernel = NULL; -+ const struct firmware *fw = NULL; -+ bin_workload_t *wl; -+ bin_fw_info_t *fw_info; -+ char *fw_name, *fw_data; -+ bin_parse_info_t parse_info; -+ int ret = 0, kernel_idx = 0, num_of_kernels = 0; -+ int vendor_output_idx, total_workload = 0; -+ char fw_path[MAX_IOCL_FILE_PATH_LEN]; -+ -+ num_of_kernels = fw_list->num_of_fws; -+ kernel_list = vmalloc(sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); -+ if (kernel_list == NULL) -+ return -ENOMEM; -+ -+ memset(kernel_list, 0, sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); -+ kernel_list->num_of_kernels = num_of_kernels; -+ kernel = &kernel_list->kernels[0]; -+ -+ fw_data = (char *)&fw_list->fw_info[0]; -+ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { -+ fw_info = (bin_fw_info_t *)fw_data; -+ fw_name = &fw_info->fw_name[0]; -+ vendor_output_idx = fw_info->vendor_output; -+ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); -+ ret = request_firmware(&fw, (const char *)fw_path, &ipts->cldev->dev); -+ if (ret) { -+ ipts_err(ipts, "cannot read fw %s\n", fw_path); -+ goto error_exit; -+ } -+ -+ parse_info.data = (u8*)fw->data; -+ parse_info.size = fw->size; -+ parse_info.parsed = 0; -+ parse_info.fw_info = fw_info; -+ parse_info.vendor_kernel = (kernel_idx == 0) ? NULL : &kernel[0]; -+ parse_info.interested_vendor_output = vendor_output_idx; -+ -+ ret = load_kernel(ipts, &parse_info, &kernel[kernel_idx]); -+ if (ret) { -+ ipts_err(ipts, "do_setup_kernel error : %d\n", ret); -+ release_firmware(fw); -+ goto error_exit; -+ } -+ -+ release_firmware(fw); -+ -+ total_workload += kernel[kernel_idx].guc_wq_item->size; -+ -+ /* advance to the next kernel */ -+ fw_data += sizeof(bin_fw_info_t); -+ fw_data += sizeof(bin_data_file_info_t) * fw_info->num_of_data_files; -+ } -+ -+ ipts_set_wq_item_size(ipts, total_workload); -+ -+ ret = bin_setup_guc_workqueue(ipts, kernel_list); -+ if (ret) { -+ ipts_dbg(ipts, "error setup_guc_workqueue\n"); -+ goto error_exit; -+ } -+ -+ ret = bin_setup_bufid_buffer(ipts, kernel_list); -+ if (ret) { -+ ipts_dbg(ipts, "error setup_lastbubmit_buffer\n"); -+ goto error_exit; -+ } -+ -+ bin_setup_input_output(ipts, kernel_list); -+ -+ /* workload is not needed during run-time so free them */ -+ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { -+ wl = kernel[kernel_idx].wl; -+ vfree(wl); -+ } -+ -+ ipts->kernel_handle = (u64)kernel_list; -+ -+ return 0; -+ -+error_exit: -+ -+ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { -+ wl = kernel[kernel_idx].wl; -+ vfree(wl); -+ unload_kernel(ipts, &kernel[kernel_idx]); -+ } -+ -+ vfree(kernel_list); -+ -+ return ret; -+} -+ -+ -+static void release_kernel(ipts_info_t *ipts) -+{ -+ bin_kernel_list_t *kernel_list; -+ bin_kernel_info_t *kernel; -+ int k_idx, k_num; -+ -+ kernel_list = (bin_kernel_list_t *)ipts->kernel_handle; -+ k_num = kernel_list->num_of_kernels; -+ kernel = &kernel_list->kernels[0]; -+ -+ for (k_idx = 0; k_idx < k_num; k_idx++) { -+ unload_kernel(ipts, kernel); -+ kernel++; -+ } -+ -+ ipts_unmap_buffer(ipts, kernel_list->bufid_buf); -+ -+ vfree(kernel_list); -+ ipts->kernel_handle = 0; -+} -+ -+int ipts_init_kernels(ipts_info_t *ipts) -+{ -+ const struct firmware *config_fw = NULL; -+ const char *config_fw_path = IPTS_FW_CONFIG_FILE; -+ bin_fw_list_t *fw_list; -+ int ret; -+ -+ ret = ipts_open_gpu(ipts); -+ if (ret) { -+ ipts_err(ipts, "open gpu error : %d\n", ret); -+ return ret; -+ } -+ -+ ret = request_firmware(&config_fw, config_fw_path, &ipts->cldev->dev); -+ if (ret) { -+ ipts_err(ipts, "request firmware error : %d\n", ret); -+ goto close_gpu; -+ } -+ -+ fw_list = (bin_fw_list_t *)config_fw->data; -+ ret = setup_kernel(ipts, fw_list); -+ if (ret) { -+ ipts_err(ipts, "setup kernel error : %d\n", ret); -+ goto close_firmware; -+ } -+ -+ release_firmware(config_fw); -+ -+ return ret; -+ -+close_firmware: -+ release_firmware(config_fw); -+ -+close_gpu: -+ ipts_close_gpu(ipts); -+ -+ return ret; -+} -+ -+void ipts_release_kernels(ipts_info_t *ipts) -+{ -+ release_kernel(ipts); -+ ipts_close_gpu(ipts); -+} -diff --git a/drivers/misc/ipts/ipts-kernel.h b/drivers/misc/ipts/ipts-kernel.h -new file mode 100644 -index 000000000000..0e7f1393b807 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-kernel.h -@@ -0,0 +1,23 @@ -+/* -+ * -+ * Intel Precise Touch & Stylus Linux driver -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _ITPS_GFX_H -+#define _ITPS_GFX_H -+ -+int ipts_init_kernels(ipts_info_t *ipts); -+void ipts_release_kernels(ipts_info_t *ipts); -+ -+#endif -diff --git a/drivers/misc/ipts/ipts-mei-msgs.h b/drivers/misc/ipts/ipts-mei-msgs.h -new file mode 100644 -index 000000000000..8ca146800a47 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-mei-msgs.h -@@ -0,0 +1,585 @@ -+/* -+ * Precise Touch HECI Message -+ * -+ * Copyright (c) 2013-2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+#ifndef _IPTS_MEI_MSGS_H_ -+#define _IPTS_MEI_MSGS_H_ -+ -+#include "ipts-sensor-regs.h" -+ -+#pragma pack(1) -+ -+ -+// Initial protocol version -+#define TOUCH_HECI_CLIENT_PROTOCOL_VERSION 10 -+ -+// GUID that identifies the Touch HECI client. -+#define TOUCH_HECI_CLIENT_GUID \ -+ {0x3e8d0870, 0x271a, 0x4208, {0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04}} -+ -+ -+// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch -+#ifndef C_ASSERT -+#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] -+#endif -+ -+ -+// General Type Defines for compatibility with HID driver and BIOS -+#ifndef BIT0 -+#define BIT0 1 -+#endif -+#ifndef BIT1 -+#define BIT1 2 -+#endif -+#ifndef BIT2 -+#define BIT2 4 -+#endif -+ -+ -+#define TOUCH_SENSOR_GET_DEVICE_INFO_CMD 0x00000001 -+#define TOUCH_SENSOR_GET_DEVICE_INFO_RSP 0x80000001 -+ -+ -+#define TOUCH_SENSOR_SET_MODE_CMD 0x00000002 -+#define TOUCH_SENSOR_SET_MODE_RSP 0x80000002 -+ -+ -+#define TOUCH_SENSOR_SET_MEM_WINDOW_CMD 0x00000003 -+#define TOUCH_SENSOR_SET_MEM_WINDOW_RSP 0x80000003 -+ -+ -+#define TOUCH_SENSOR_QUIESCE_IO_CMD 0x00000004 -+#define TOUCH_SENSOR_QUIESCE_IO_RSP 0x80000004 -+ -+ -+#define TOUCH_SENSOR_HID_READY_FOR_DATA_CMD 0x00000005 -+#define TOUCH_SENSOR_HID_READY_FOR_DATA_RSP 0x80000005 -+ -+ -+#define TOUCH_SENSOR_FEEDBACK_READY_CMD 0x00000006 -+#define TOUCH_SENSOR_FEEDBACK_READY_RSP 0x80000006 -+ -+ -+#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD 0x00000007 -+#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP 0x80000007 -+ -+ -+#define TOUCH_SENSOR_NOTIFY_DEV_READY_CMD 0x00000008 -+#define TOUCH_SENSOR_NOTIFY_DEV_READY_RSP 0x80000008 -+ -+ -+#define TOUCH_SENSOR_SET_POLICIES_CMD 0x00000009 -+#define TOUCH_SENSOR_SET_POLICIES_RSP 0x80000009 -+ -+ -+#define TOUCH_SENSOR_GET_POLICIES_CMD 0x0000000A -+#define TOUCH_SENSOR_GET_POLICIES_RSP 0x8000000A -+ -+ -+#define TOUCH_SENSOR_RESET_CMD 0x0000000B -+#define TOUCH_SENSOR_RESET_RSP 0x8000000B -+ -+ -+#define TOUCH_SENSOR_READ_ALL_REGS_CMD 0x0000000C -+#define TOUCH_SENSOR_READ_ALL_REGS_RSP 0x8000000C -+ -+ -+#define TOUCH_SENSOR_CMD_ERROR_RSP 0x8FFFFFFF // M2H: ME sends this message to indicate previous command was unrecognized/unsupported -+ -+ -+ -+//******************************************************************* -+// -+// Touch Sensor Status Codes -+// -+//******************************************************************* -+typedef enum touch_status -+{ -+ TOUCH_STATUS_SUCCESS = 0, // 0 Requested operation was successful -+ TOUCH_STATUS_INVALID_PARAMS, // 1 Invalid parameter(s) sent -+ TOUCH_STATUS_ACCESS_DENIED, // 2 Unable to validate address range -+ TOUCH_STATUS_CMD_SIZE_ERROR, // 3 HECI message incorrect size for specified command -+ TOUCH_STATUS_NOT_READY, // 4 Memory window not set or device is not armed for operation -+ TOUCH_STATUS_REQUEST_OUTSTANDING, // 5 There is already an outstanding message of the same type, must wait for response before sending another request of that type -+ TOUCH_STATUS_NO_SENSOR_FOUND, // 6 Sensor could not be found. Either no sensor is connected, the sensor has not yet initialized, or the system is improperly configured. -+ TOUCH_STATUS_OUT_OF_MEMORY, // 7 Not enough memory/storage for requested operation -+ TOUCH_STATUS_INTERNAL_ERROR, // 8 Unexpected error occurred -+ TOUCH_STATUS_SENSOR_DISABLED, // 9 Used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP to indicate sensor has been disabled or reset and must be reinitialized. -+ TOUCH_STATUS_COMPAT_CHECK_FAIL, // 10 Used to indicate compatibility revision check between sensor and ME failed, or protocol ver between ME/HID/Kernels failed. -+ TOUCH_STATUS_SENSOR_EXPECTED_RESET, // 11 Indicates sensor went through a reset initiated by ME -+ TOUCH_STATUS_SENSOR_UNEXPECTED_RESET, // 12 Indicates sensor went through an unexpected reset -+ TOUCH_STATUS_RESET_FAILED, // 13 Requested sensor reset failed to complete -+ TOUCH_STATUS_TIMEOUT, // 14 Operation timed out -+ TOUCH_STATUS_TEST_MODE_FAIL, // 15 Test mode pattern did not match expected values -+ TOUCH_STATUS_SENSOR_FAIL_FATAL, // 16 Indicates sensor reported fatal error during reset sequence. Further progress is not possible. -+ TOUCH_STATUS_SENSOR_FAIL_NONFATAL, // 17 Indicates sensor reported non-fatal error during reset sequence. HID/BIOS logs error and attempts to continue. -+ TOUCH_STATUS_INVALID_DEVICE_CAPS, // 18 Indicates sensor reported invalid capabilities, such as not supporting required minimum frequency or I/O mode. -+ TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS, // 19 Indicates that command cannot be complete until ongoing Quiesce I/O flow has completed. -+ TOUCH_STATUS_MAX // 20 Invalid value, never returned -+} touch_status_t; -+C_ASSERT(sizeof(touch_status_t) == 4); -+ -+ -+ -+//******************************************************************* -+// -+// Defines for message structures used for Host to ME communication -+// -+//******************************************************************* -+ -+ -+typedef enum touch_sensor_mode -+{ -+ TOUCH_SENSOR_MODE_HID = 0, // Set mode to HID mode -+ TOUCH_SENSOR_MODE_RAW_DATA, // Set mode to Raw Data mode -+ TOUCH_SENSOR_MODE_SENSOR_DEBUG = 4, // Used like TOUCH_SENSOR_MODE_HID but data coming from sensor is not necessarily a HID packet. -+ TOUCH_SENSOR_MODE_MAX // Invalid value -+} touch_sensor_mode_t; -+C_ASSERT(sizeof(touch_sensor_mode_t) == 4); -+ -+typedef struct touch_sensor_set_mode_cmd_data -+{ -+ touch_sensor_mode_t sensor_mode; // Indicate desired sensor mode -+ u32 Reserved[3]; // For future expansion -+} touch_sensor_set_mode_cmd_data_t; -+C_ASSERT(sizeof(touch_sensor_set_mode_cmd_data_t) == 16); -+ -+ -+#define TOUCH_SENSOR_MAX_DATA_BUFFERS 16 -+#define TOUCH_HID_2_ME_BUFFER_ID TOUCH_SENSOR_MAX_DATA_BUFFERS -+#define TOUCH_HID_2_ME_BUFFER_SIZE_MAX 1024 -+#define TOUCH_INVALID_BUFFER_ID 0xFF -+ -+typedef struct touch_sensor_set_mem_window_cmd_data -+{ -+ u32 touch_data_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize -+ u32 touch_data_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize -+ u32 tail_offset_addr_lower; // Lower 32 bits of Tail Offset physical address -+ u32 tail_offset_addr_upper; // Upper 32 bits of Tail Offset physical address, always 32 bit, increment by WorkQueueItemSize -+ u32 doorbell_cookie_addr_lower; // Lower 32 bits of Doorbell register physical address -+ u32 doorbell_cookie_addr_upper; // Upper 32 bits of Doorbell register physical address, always 32 bit, increment as integer, rollover to 1 -+ u32 feedback_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize -+ u32 feedback_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize -+ u32 hid2me_buffer_addr_lower; // Lower 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. -+ u32 hid2me_buffer_addr_upper; // Upper 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. -+ u32 hid2me_buffer_size; // Size in bytes of Hid2MeBuffer, can be no bigger than TOUCH_HID_2_ME_BUFFER_SIZE_MAX -+ u8 reserved1; // For future expansion -+ u8 work_queue_item_size; // Size in bytes of the GuC Work Queue Item pointed to by TailOffset -+ u16 work_queue_size; // Size in bytes of the entire GuC Work Queue -+ u32 reserved[8]; // For future expansion -+} touch_sensor_set_mem_window_cmd_data_t; -+C_ASSERT(sizeof(touch_sensor_set_mem_window_cmd_data_t) == 320); -+ -+ -+#define TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET BIT0 // indicates GuC got reset and ME must re-read GuC data such as TailOffset and Doorbell Cookie values -+ -+typedef struct touch_sensor_quiesce_io_cmd_data -+{ -+ u32 quiesce_flags; // Optionally set TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET -+ u32 reserved[2]; -+} touch_sensor_quiesce_io_cmd_data_t; -+C_ASSERT(sizeof(touch_sensor_quiesce_io_cmd_data_t) == 12); -+ -+ -+typedef struct touch_sensor_feedback_ready_cmd_data -+{ -+ u8 feedback_index; // Index value from 0 to TOUCH_HID_2_ME_BUFFER_ID used to indicate which Feedback Buffer to use. Using special value TOUCH_HID_2_ME_BUFFER_ID -+ // is an indication to ME to get feedback data from the Hid2Me buffer instead of one of the standard Feedback buffers. -+ u8 reserved1[3]; // For future expansion -+ u32 transaction_id; // Transaction ID that was originally passed to host in TOUCH_HID_PRIVATE_DATA. Used to track round trip of a given transaction for performance measurements. -+ u32 reserved2[2]; // For future expansion -+} touch_sensor_feedback_ready_cmd_data_t; -+C_ASSERT(sizeof(touch_sensor_feedback_ready_cmd_data_t) == 16); -+ -+ -+#define TOUCH_DEFAULT_DOZE_TIMER_SECONDS 30 -+ -+typedef enum touch_freq_override -+{ -+ TOUCH_FREQ_OVERRIDE_NONE, // Do not apply any override -+ TOUCH_FREQ_OVERRIDE_10MHZ, // Force frequency to 10MHz (not currently supported) -+ TOUCH_FREQ_OVERRIDE_17MHZ, // Force frequency to 17MHz -+ TOUCH_FREQ_OVERRIDE_30MHZ, // Force frequency to 30MHz -+ TOUCH_FREQ_OVERRIDE_50MHZ, // Force frequency to 50MHz (not currently supported) -+ TOUCH_FREQ_OVERRIDE_MAX // Invalid value -+} touch_freq_override_t; -+C_ASSERT(sizeof(touch_freq_override_t) == 4); -+ -+typedef enum touch_spi_io_mode_override -+{ -+ TOUCH_SPI_IO_MODE_OVERRIDE_NONE, // Do not apply any override -+ TOUCH_SPI_IO_MODE_OVERRIDE_SINGLE, // Force Single I/O -+ TOUCH_SPI_IO_MODE_OVERRIDE_DUAL, // Force Dual I/O -+ TOUCH_SPI_IO_MODE_OVERRIDE_QUAD, // Force Quad I/O -+ TOUCH_SPI_IO_MODE_OVERRIDE_MAX // Invalid value -+} touch_spi_io_mode_override_t; -+C_ASSERT(sizeof(touch_spi_io_mode_override_t) == 4); -+ -+// Debug Policy bits used by TOUCH_POLICY_DATA.DebugOverride -+#define TOUCH_DBG_POLICY_OVERRIDE_STARTUP_TIMER_DIS BIT0 // Disable sensor startup timer -+#define TOUCH_DBG_POLICY_OVERRIDE_SYNC_BYTE_DIS BIT1 // Disable Sync Byte check -+#define TOUCH_DBG_POLICY_OVERRIDE_ERR_RESET_DIS BIT2 // Disable error resets -+ -+typedef struct touch_policy_data -+{ -+ u32 reserved0; // For future expansion. -+ u32 doze_timer :16; // Value in seconds, after which ME will put the sensor into Doze power state if no activity occurs. Set -+ // to 0 to disable Doze mode (not recommended). Value will be set to TOUCH_DEFAULT_DOZE_TIMER_SECONDS by -+ // default. -+ touch_freq_override_t freq_override :3; // Override frequency requested by sensor -+ touch_spi_io_mode_override_t spi_io_override :3; // Override IO mode requested by sensor -+ u32 reserved1 :10; // For future expansion -+ u32 reserved2; // For future expansion -+ u32 debug_override; // Normally all bits will be zero. Bits will be defined as needed for enabling special debug features -+} touch_policy_data_t; -+C_ASSERT(sizeof(touch_policy_data_t) == 16); -+ -+typedef struct touch_sensor_set_policies_cmd_data -+{ -+ touch_policy_data_t policy_data; // Contains the desired policy to be set -+} touch_sensor_set_policies_cmd_data_t; -+C_ASSERT(sizeof(touch_sensor_set_policies_cmd_data_t) == 16); -+ -+ -+typedef enum touch_sensor_reset_type -+{ -+ TOUCH_SENSOR_RESET_TYPE_HARD, // Hardware Reset using dedicated GPIO pin -+ TOUCH_SENSOR_RESET_TYPE_SOFT, // Software Reset using command written over SPI interface -+ TOUCH_SENSOR_RESET_TYPE_MAX // Invalid value -+} touch_sensor_reset_type_t; -+C_ASSERT(sizeof(touch_sensor_reset_type_t) == 4); -+ -+typedef struct touch_sensor_reset_cmd_data -+{ -+ touch_sensor_reset_type_t reset_type; // Indicate desired reset type -+ u32 reserved; // For future expansion -+} touch_sensor_reset_cmd_data_t; -+C_ASSERT(sizeof(touch_sensor_reset_cmd_data_t) == 8); -+ -+ -+// -+// Host to ME message -+// -+typedef struct touch_sensor_msg_h2m -+{ -+ u32 command_code; -+ union -+ { -+ touch_sensor_set_mode_cmd_data_t set_mode_cmd_data; -+ touch_sensor_set_mem_window_cmd_data_t set_window_cmd_data; -+ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd_data; -+ touch_sensor_feedback_ready_cmd_data_t feedback_ready_cmd_data; -+ touch_sensor_set_policies_cmd_data_t set_policies_cmd_data; -+ touch_sensor_reset_cmd_data_t reset_cmd_data; -+ } h2m_data; -+} touch_sensor_msg_h2m_t; -+C_ASSERT(sizeof(touch_sensor_msg_h2m_t) == 324); -+ -+ -+//******************************************************************* -+// -+// Defines for message structures used for ME to Host communication -+// -+//******************************************************************* -+ -+// I/O mode values used by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. -+typedef enum touch_spi_io_mode -+{ -+ TOUCH_SPI_IO_MODE_SINGLE = 0, // Sensor set for Single I/O SPI -+ TOUCH_SPI_IO_MODE_DUAL, // Sensor set for Dual I/O SPI -+ TOUCH_SPI_IO_MODE_QUAD, // Sensor set for Quad I/O SPI -+ TOUCH_SPI_IO_MODE_MAX // Invalid value -+} touch_spi_io_mode_t; -+C_ASSERT(sizeof(touch_spi_io_mode_t) == 4); -+ -+// -+// TOUCH_SENSOR_GET_DEVICE_INFO_RSP code is sent in response to TOUCH_SENSOR_GET_DEVICE_INFO_CMD. This code will be followed -+// by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor details are reported. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_NO_SENSOR_FOUND: Sensor has not yet been detected. Other fields will not contain valid data. -+// TOUCH_STATUS_INVALID_DEVICE_CAPS: Indicates sensor does not support minimum required Frequency or I/O Mode. ME firmware will choose best possible option for the errant -+// field. Caller should attempt to continue. -+// TOUCH_STATUS_COMPAT_CHECK_FAIL: Indicates TouchIC/ME compatibility mismatch. Caller should attempt to continue. -+// -+typedef struct touch_sensor_get_device_info_rsp_data -+{ -+ u16 vendor_id; // Touch Sensor vendor ID -+ u16 device_id; // Touch Sensor device ID -+ u32 hw_rev; // Touch Sensor Hardware Revision -+ u32 fw_rev; // Touch Sensor Firmware Revision -+ u32 frame_size; // Max size of one frame returned by Touch IC in bytes. This data will be TOUCH_RAW_DATA_HDR followed -+ // by a payload. The payload can be raw data or a HID structure depending on mode. -+ u32 feedback_size; // Max size of one Feedback structure in bytes -+ touch_sensor_mode_t sensor_mode; // Current operating mode of the sensor -+ u32 max_touch_points :8; // Maximum number of simultaneous touch points that can be reported by sensor -+ touch_freq_t spi_frequency :8; // SPI bus Frequency supported by sensor and ME firmware -+ touch_spi_io_mode_t spi_io_mode :8; // SPI bus I/O Mode supported by sensor and ME firmware -+ u32 reserved0 :8; // For future expansion -+ u8 sensor_minor_eds_rev; // Minor version number of EDS spec supported by sensor (from Compat Rev ID Reg) -+ u8 sensor_major_eds_rev; // Major version number of EDS spec supported by sensor (from Compat Rev ID Reg) -+ u8 me_minor_eds_rev; // Minor version number of EDS spec supported by ME -+ u8 me_major_eds_rev; // Major version number of EDS spec supported by ME -+ u8 sensor_eds_intf_rev; // EDS Interface Revision Number supported by sensor (from Compat Rev ID Reg) -+ u8 me_eds_intf_rev; // EDS Interface Revision Number supported by ME -+ u8 kernel_compat_ver; // EU Kernel Compatibility Version (from Compat Rev ID Reg) -+ u8 reserved1; // For future expansion -+ u32 reserved2[2]; // For future expansion -+} touch_sensor_get_device_info_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_get_device_info_rsp_data_t) == 44); -+ -+ -+// -+// TOUCH_SENSOR_SET_MODE_RSP code is sent in response to TOUCH_SENSOR_SET_MODE_CMD. This code will be followed -+// by TOUCH_SENSOR_SET_MODE_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and mode was set. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. -+// -+typedef struct touch_sensor_set_mode_rsp_data -+{ -+ u32 reserved[3]; // For future expansion -+} touch_sensor_set_mode_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_set_mode_rsp_data_t) == 12); -+ -+ -+// -+// TOUCH_SENSOR_SET_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_SET_MEM_WINDOW_CMD. This code will be followed -+// by TOUCH_SENSOR_SET_MEM_WINDOW_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. -+// TOUCH_STATUS_ACCESS_DENIED: Unable to map host address ranges for DMA. -+// TOUCH_STATUS_OUT_OF_MEMORY: Unable to allocate enough space for needed buffers. -+// -+typedef struct touch_sensor_set_mem_window_rsp_data -+{ -+ u32 reserved[3]; // For future expansion -+} touch_sensor_set_mem_window_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_set_mem_window_rsp_data_t) == 12); -+ -+ -+// -+// TOUCH_SENSOR_QUIESCE_IO_RSP code is sent in response to TOUCH_SENSOR_QUIESCE_IO_CMD. This code will be followed -+// by TOUCH_SENSOR_QUIESCE_IO_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and touch flow has stopped. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. -+// TOUCH_STATIS_TIMEOUT: Indicates ME timed out waiting for Quiesce I/O flow to complete. -+// -+typedef struct touch_sensor_quiesce_io_rsp_data -+{ -+ u32 reserved[3]; // For future expansion -+} touch_sensor_quiesce_io_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_quiesce_io_rsp_data_t) == 12); -+ -+ -+// Reset Reason values used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA -+typedef enum touch_reset_reason -+{ -+ TOUCH_RESET_REASON_UNKNOWN = 0, // Reason for sensor reset is not known -+ TOUCH_RESET_REASON_FEEDBACK_REQUEST, // Reset was requested as part of TOUCH_SENSOR_FEEDBACK_READY_CMD -+ TOUCH_RESET_REASON_HECI_REQUEST, // Reset was requested via TOUCH_SENSOR_RESET_CMD -+ TOUCH_RESET_REASON_MAX -+} touch_reset_reason_t; -+C_ASSERT(sizeof(touch_reset_reason_t) == 4); -+ -+// -+// TOUCH_SENSOR_HID_READY_FOR_DATA_RSP code is sent in response to TOUCH_SENSOR_HID_READY_FOR_DATA_CMD. This code will be followed -+// by TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and HID data was sent by DMA. This will only be sent in HID mode. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. -+// TOUCH_STATUS_NOT_READY: Indicates memory window has not yet been set by BIOS/HID. -+// TOUCH_STATUS_SENSOR_DISABLED: Indicates that ME to HID communication has been stopped either by TOUCH_SENSOR_QUIESCE_IO_CMD or TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. -+// TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: Sensor signaled a Reset Interrupt. ME did not expect this and has no info about why this occurred. -+// TOUCH_STATUS_SENSOR_EXPECTED_RESET: Sensor signaled a Reset Interrupt. ME either directly requested this reset, or it was expected as part of a defined flow in the EDS. -+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. -+// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. -+// -+typedef struct touch_sensor_hid_ready_for_data_rsp_data -+{ -+ u32 data_size; // Size of the data the ME DMA'd into a RawDataBuffer. Valid only when Status == TOUCH_STATUS_SUCCESS -+ u8 touch_data_buffer_index; // Index to indicate which RawDataBuffer was used. Valid only when Status == TOUCH_STATUS_SUCCESS -+ u8 reset_reason; // If Status is TOUCH_STATUS_SENSOR_EXPECTED_RESET, ME will provide the cause. See TOUCH_RESET_REASON. -+ u8 reserved1[2]; // For future expansion -+ u32 reserved2[5]; // For future expansion -+} touch_sensor_hid_ready_for_data_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_hid_ready_for_data_rsp_data_t) == 28); -+ -+ -+// -+// TOUCH_SENSOR_FEEDBACK_READY_RSP code is sent in response to TOUCH_SENSOR_FEEDBACK_READY_CMD. This code will be followed -+// by TOUCH_SENSOR_FEEDBACK_READY_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and any feedback or commands were sent to sensor. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. -+// TOUCH_STATUS_COMPAT_CHECK_FAIL Indicates ProtocolVer does not match ME supported version. (non-fatal error) -+// TOUCH_STATUS_INTERNAL_ERROR: Unexpected error occurred. This should not normally be seen. -+// TOUCH_STATUS_OUT_OF_MEMORY: Insufficient space to store Calibration Data -+// -+typedef struct touch_sensor_feedback_ready_rsp_data -+{ -+ u8 feedback_index; // Index value from 0 to TOUCH_SENSOR_MAX_DATA_BUFFERS used to indicate which Feedback Buffer to use -+ u8 reserved1[3]; // For future expansion -+ u32 reserved2[6]; // For future expansion -+} touch_sensor_feedback_ready_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_feedback_ready_rsp_data_t) == 28); -+ -+ -+// -+// TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. This code will be followed -+// by TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. -+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. -+// -+typedef struct touch_sensor_clear_mem_window_rsp_data -+{ -+ u32 reserved[3]; // For future expansion -+} touch_sensor_clear_mem_window_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_clear_mem_window_rsp_data_t) == 12); -+ -+ -+// -+// TOUCH_SENSOR_NOTIFY_DEV_READY_RSP code is sent in response to TOUCH_SENSOR_NOTIFY_DEV_READY_CMD. This code will be followed -+// by TOUCH_SENSOR_NOTIFY_DEV_READY_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor has been detected by ME FW. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. -+// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. -+// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. -+// TOUCH_STATUS_SENSOR_FAIL_FATAL: Sensor indicated a fatal error, further operation is not possible. Error details can be found in ErrReg. -+// TOUCH_STATUS_SENSOR_FAIL_NONFATAL: Sensor indicated a non-fatal error. Error should be logged by caller and init flow can continue. Error details can be found in ErrReg. -+// -+typedef struct touch_sensor_notify_dev_ready_rsp_data -+{ -+ touch_err_reg_t err_reg; // Value of sensor Error Register, field is only valid for Status == TOUCH_STATUS_SENSOR_FAIL_FATAL or TOUCH_STATUS_SENSOR_FAIL_NONFATAL -+ u32 reserved[2]; // For future expansion -+} touch_sensor_notify_dev_ready_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_notify_dev_ready_rsp_data_t) == 12); -+ -+ -+// -+// TOUCH_SENSOR_SET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_SET_POLICIES_CMD. This code will be followed -+// by TOUCH_SENSOR_SET_POLICIES_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. -+// -+typedef struct touch_sensor_set_policies_rsp_data -+{ -+ u32 reserved[3]; // For future expansion -+} touch_sensor_set_policies_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_set_policies_rsp_data_t) == 12); -+ -+ -+// -+// TOUCH_SENSOR_GET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_GET_POLICIES_CMD. This code will be followed -+// by TOUCH_SENSOR_GET_POLICIES_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// -+typedef struct touch_sensor_get_policies_rsp_data -+{ -+ touch_policy_data_t policy_data; // Contains the current policy -+} touch_sensor_get_policies_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_get_policies_rsp_data_t) == 16); -+ -+ -+// -+// TOUCH_SENSOR_RESET_RSP code is sent in response to TOUCH_SENSOR_RESET_CMD. This code will be followed -+// by TOUCH_SENSOR_RESET_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor reset was completed. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. -+// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. -+// TOUCH_STATUS_RESET_FAILED: Sensor generated an invalid or unexpected interrupt. -+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. -+// -+typedef struct touch_sensor_reset_rsp_data -+{ -+ u32 reserved[3]; // For future expansion -+} touch_sensor_reset_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_reset_rsp_data_t) == 12); -+ -+ -+// -+// TOUCH_SENSOR_READ_ALL_REGS_RSP code is sent in response to TOUCH_SENSOR_READ_ALL_REGS_CMD. This code will be followed -+// by TOUCH_SENSOR_READ_ALL_REGS_RSP_DATA. -+// -+// Possible Status values: -+// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. -+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. -+// -+typedef struct touch_sensor_read_all_regs_rsp_data -+{ -+ touch_reg_block_t sensor_regs; // Returns first 64 bytes of register space used for normal touch operation. Does not include test mode register. -+ u32 reserved[4]; -+} touch_sensor_read_all_regs_rsp_data_t; -+C_ASSERT(sizeof(touch_sensor_read_all_regs_rsp_data_t) == 80); -+ -+// -+// ME to Host Message -+// -+typedef struct touch_sensor_msg_m2h -+{ -+ u32 command_code; -+ touch_status_t status; -+ union -+ { -+ touch_sensor_get_device_info_rsp_data_t device_info_rsp_data; -+ touch_sensor_set_mode_rsp_data_t set_mode_rsp_data; -+ touch_sensor_set_mem_window_rsp_data_t set_mem_window_rsp_data; -+ touch_sensor_quiesce_io_rsp_data_t quiesce_io_rsp_data; -+ touch_sensor_hid_ready_for_data_rsp_data_t hid_ready_for_data_rsp_data; -+ touch_sensor_feedback_ready_rsp_data_t feedback_ready_rsp_data; -+ touch_sensor_clear_mem_window_rsp_data_t clear_mem_window_rsp_data; -+ touch_sensor_notify_dev_ready_rsp_data_t notify_dev_ready_rsp_data; -+ touch_sensor_set_policies_rsp_data_t set_policies_rsp_data; -+ touch_sensor_get_policies_rsp_data_t get_policies_rsp_data; -+ touch_sensor_reset_rsp_data_t reset_rsp_data; -+ touch_sensor_read_all_regs_rsp_data_t read_all_regs_rsp_data; -+ } m2h_data; -+} touch_sensor_msg_m2h_t; -+C_ASSERT(sizeof(touch_sensor_msg_m2h_t) == 88); -+ -+ -+#define TOUCH_MSG_SIZE_MAX_BYTES (MAX(sizeof(touch_sensor_msg_m2h_t), sizeof(touch_sensor_msg_h2m_t))) -+ -+#pragma pack() -+ -+#endif // _IPTS_MEI_MSGS_H_ -diff --git a/drivers/misc/ipts/ipts-mei.c b/drivers/misc/ipts/ipts-mei.c -new file mode 100644 -index 000000000000..199e49cb8d70 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-mei.c -@@ -0,0 +1,282 @@ -+/* -+ * MEI client driver for Intel Precise Touch and Stylus -+ * -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipts.h" -+#include "ipts-hid.h" -+#include "ipts-msg-handler.h" -+#include "ipts-mei-msgs.h" -+#include "ipts-binary-spec.h" -+#include "ipts-state.h" -+ -+#define IPTS_DRIVER_NAME "ipts" -+#define IPTS_MEI_UUID UUID_LE(0x3e8d0870, 0x271a, 0x4208, \ -+ 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) -+ -+static struct mei_cl_device_id ipts_mei_cl_tbl[] = { -+ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY}, -+ {} -+}; -+ -+static ssize_t sensor_mode_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ ipts_info_t *ipts; -+ ipts = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%d\n", ipts->sensor_mode); -+} -+ -+//TODO: Verify the function implementation -+static ssize_t sensor_mode_store(struct device *dev, -+ struct device_attribute *attr, const char *buf, -+ size_t count) -+{ -+ int ret; -+ long val; -+ ipts_info_t *ipts; -+ -+ ipts = dev_get_drvdata(dev); -+ ret = kstrtol(buf, 10, &val); -+ if (ret) -+ return ret; -+ -+ ipts_dbg(ipts, "try sensor mode = %ld\n", val); -+ -+ switch (val) { -+ case TOUCH_SENSOR_MODE_HID: -+ break; -+ case TOUCH_SENSOR_MODE_RAW_DATA: -+ break; -+ default: -+ ipts_err(ipts, "sensor mode %ld is not supported\n", val); -+ } -+ -+ return count; -+} -+ -+static ssize_t device_info_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ ipts_info_t *ipts; -+ -+ ipts = dev_get_drvdata(dev); -+ return sprintf(buf, "vendor id = 0x%04hX\n" -+ "device id = 0x%04hX\n" -+ "HW rev = 0x%08X\n" -+ "firmware rev = 0x%08X\n", -+ ipts->device_info.vendor_id, ipts->device_info.device_id, -+ ipts->device_info.hw_rev, ipts->device_info.fw_rev); -+} -+ -+static DEVICE_ATTR_RW(sensor_mode); -+static DEVICE_ATTR_RO(device_info); -+ -+static struct attribute *ipts_attrs[] = { -+ &dev_attr_sensor_mode.attr, -+ &dev_attr_device_info.attr, -+ NULL -+}; -+ -+static const struct attribute_group ipts_grp = { -+ .attrs = ipts_attrs, -+}; -+ -+MODULE_DEVICE_TABLE(mei, ipts_mei_cl_tbl); -+ -+static void raw_data_work_func(struct work_struct *work) -+{ -+ ipts_info_t *ipts = container_of(work, ipts_info_t, raw_data_work); -+ -+ ipts_handle_processed_data(ipts); -+} -+ -+static void gfx_status_work_func(struct work_struct *work) -+{ -+ ipts_info_t *ipts = container_of(work, ipts_info_t, gfx_status_work); -+ ipts_state_t state; -+ int status = ipts->gfx_status; -+ -+ ipts_dbg(ipts, "notify gfx status : %d\n", status); -+ -+ state = ipts_get_state(ipts); -+ -+ if (state == IPTS_STA_RAW_DATA_STARTED || state == IPTS_STA_HID_STARTED) { -+ if (status == IPTS_NOTIFY_STA_BACKLIGHT_ON && -+ ipts->display_status == false) { -+ ipts_send_sensor_clear_mem_window_cmd(ipts); -+ ipts->display_status = true; -+ } else if (status == IPTS_NOTIFY_STA_BACKLIGHT_OFF && -+ ipts->display_status == true) { -+ ipts_send_sensor_quiesce_io_cmd(ipts); -+ ipts->display_status = false; -+ } -+ } -+} -+ -+/* event loop */ -+static int ipts_mei_cl_event_thread(void *data) -+{ -+ ipts_info_t *ipts = (ipts_info_t *)data; -+ struct mei_cl_device *cldev = ipts->cldev; -+ ssize_t msg_len; -+ touch_sensor_msg_m2h_t m2h_msg; -+ -+ while (!kthread_should_stop()) { -+ msg_len = mei_cldev_recv(cldev, (u8*)&m2h_msg, sizeof(m2h_msg)); -+ if (msg_len <= 0) { -+ ipts_err(ipts, "error in reading m2h msg\n"); -+ continue; -+ } -+ -+ if (ipts_handle_resp(ipts, &m2h_msg, msg_len) != 0) { -+ ipts_err(ipts, "error in handling resp msg\n"); -+ } -+ } -+ -+ ipts_dbg(ipts, "!! end event loop !!\n"); -+ -+ return 0; -+} -+ -+static void init_work_func(struct work_struct *work) -+{ -+ ipts_info_t *ipts = container_of(work, ipts_info_t, init_work); -+ -+ ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; -+ ipts->display_status = true; -+ -+ ipts_start(ipts); -+} -+ -+static int ipts_mei_cl_probe(struct mei_cl_device *cldev, -+ const struct mei_cl_device_id *id) -+{ -+ int ret = 0; -+ ipts_info_t *ipts = NULL; -+ -+ pr_info("probing Intel Precise Touch & Stylus\n"); -+ -+ // setup the DMA BIT mask, the system will choose the best possible -+ if (dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)) == 0) { -+ pr_info("IPTS using DMA_BIT_MASK(64)\n"); -+ } else if (dma_coerce_mask_and_coherent(&cldev->dev, -+ DMA_BIT_MASK(32)) == 0) { -+ pr_info("IPTS using DMA_BIT_MASK(32)\n"); -+ } else { -+ pr_err("IPTS: No suitable DMA available\n"); -+ return -EFAULT; -+ } -+ -+ ret = mei_cldev_enable(cldev); -+ if (ret < 0) { -+ pr_err("cannot enable IPTS\n"); -+ return ret; -+ } -+ -+ ipts = devm_kzalloc(&cldev->dev, sizeof(ipts_info_t), GFP_KERNEL); -+ if (ipts == NULL) { -+ ret = -ENOMEM; -+ goto disable_mei; -+ } -+ ipts->cldev = cldev; -+ mei_cldev_set_drvdata(cldev, ipts); -+ -+ ipts->event_loop = kthread_run(ipts_mei_cl_event_thread, (void*)ipts, -+ "ipts_event_thread"); -+ -+ if(ipts_dbgfs_register(ipts, "ipts")) -+ pr_debug("cannot register debugfs for IPTS\n"); -+ -+ INIT_WORK(&ipts->init_work, init_work_func); -+ INIT_WORK(&ipts->raw_data_work, raw_data_work_func); -+ INIT_WORK(&ipts->gfx_status_work, gfx_status_work_func); -+ -+ ret = sysfs_create_group(&cldev->dev.kobj, &ipts_grp); -+ if (ret != 0) { -+ pr_debug("cannot create sysfs for IPTS\n"); -+ } -+ -+ schedule_work(&ipts->init_work); -+ -+ return 0; -+ -+disable_mei : -+ mei_cldev_disable(cldev); -+ -+ return ret; -+} -+ -+static int ipts_mei_cl_remove(struct mei_cl_device *cldev) -+{ -+ ipts_info_t *ipts = mei_cldev_get_drvdata(cldev); -+ -+ ipts_stop(ipts); -+ -+ sysfs_remove_group(&cldev->dev.kobj, &ipts_grp); -+ ipts_hid_release(ipts); -+ ipts_dbgfs_deregister(ipts); -+ mei_cldev_disable(cldev); -+ -+ kthread_stop(ipts->event_loop); -+ -+ pr_info("IPTS removed\n"); -+ -+ return 0; -+} -+ -+static struct mei_cl_driver ipts_mei_cl_driver = { -+ .id_table = ipts_mei_cl_tbl, -+ .name = IPTS_DRIVER_NAME, -+ .probe = ipts_mei_cl_probe, -+ .remove = ipts_mei_cl_remove, -+}; -+ -+static int ipts_mei_cl_init(void) -+{ -+ int ret; -+ -+ pr_info("IPTS %s() is called\n", __func__); -+ -+ ret = mei_cldev_driver_register(&ipts_mei_cl_driver); -+ if (ret) { -+ pr_err("unable to register IPTS mei client driver\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void __exit ipts_mei_cl_exit(void) -+{ -+ pr_info("IPTS %s() is called\n", __func__); -+ -+ mei_cldev_driver_unregister(&ipts_mei_cl_driver); -+} -+ -+module_init(ipts_mei_cl_init); -+module_exit(ipts_mei_cl_exit); -+ -+MODULE_DESCRIPTION -+ ("Intel(R) Management Engine Interface Client Driver for "\ -+ "Intel Precision Touch and Sylus"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/ipts/ipts-msg-handler.c b/drivers/misc/ipts/ipts-msg-handler.c -new file mode 100644 -index 000000000000..8b214f975c03 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-msg-handler.c -@@ -0,0 +1,431 @@ -+#include -+ -+#include "ipts.h" -+#include "ipts-hid.h" -+#include "ipts-resource.h" -+#include "ipts-mei-msgs.h" -+ -+int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size) -+{ -+ int ret = 0; -+ touch_sensor_msg_h2m_t h2m_msg; -+ int len = 0; -+ -+ memset(&h2m_msg, 0, sizeof(h2m_msg)); -+ -+ h2m_msg.command_code = cmd; -+ len = sizeof(h2m_msg.command_code) + data_size; -+ if (data != NULL && data_size != 0) -+ memcpy(&h2m_msg.h2m_data, data, data_size); /* copy payload */ -+ -+ ret = mei_cldev_send(ipts->cldev, (u8*)&h2m_msg, len); -+ if (ret < 0) { -+ ipts_err(ipts, "mei_cldev_send() error 0x%X:%d\n", -+ cmd, ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id) -+{ -+ int ret; -+ int cmd_len; -+ touch_sensor_feedback_ready_cmd_data_t fb_ready_cmd; -+ -+ cmd_len = sizeof(touch_sensor_feedback_ready_cmd_data_t); -+ memset(&fb_ready_cmd, 0, cmd_len); -+ -+ fb_ready_cmd.feedback_index = buffer_idx; -+ fb_ready_cmd.transaction_id = transaction_id; -+ -+ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_FEEDBACK_READY_CMD, -+ &fb_ready_cmd, cmd_len); -+ -+ return ret; -+} -+ -+int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts) -+{ -+ int ret; -+ int cmd_len; -+ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd; -+ -+ cmd_len = sizeof(touch_sensor_quiesce_io_cmd_data_t); -+ memset(&quiesce_io_cmd, 0, cmd_len); -+ -+ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_QUIESCE_IO_CMD, -+ &quiesce_io_cmd, cmd_len); -+ -+ return ret; -+} -+ -+int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts) -+{ -+ return ipts_handle_cmd(ipts, TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, NULL, 0); -+} -+ -+int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts) -+{ -+ return ipts_handle_cmd(ipts, TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD, NULL, 0); -+} -+ -+static int check_validity(touch_sensor_msg_m2h_t *m2h_msg, u32 msg_len) -+{ -+ int ret = 0; -+ int valid_msg_len = sizeof(m2h_msg->command_code); -+ u32 cmd_code = m2h_msg->command_code; -+ -+ switch (cmd_code) { -+ case TOUCH_SENSOR_SET_MODE_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_set_mode_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_set_mem_window_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_QUIESCE_IO_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_quiesce_io_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_hid_ready_for_data_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_FEEDBACK_READY_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_feedback_ready_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_clear_mem_window_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_notify_dev_ready_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_SET_POLICIES_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_set_policies_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_GET_POLICIES_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_get_policies_rsp_data_t); -+ break; -+ case TOUCH_SENSOR_RESET_RSP: -+ valid_msg_len += -+ sizeof(touch_sensor_reset_rsp_data_t); -+ break; -+ } -+ -+ if (valid_msg_len != msg_len) { -+ return -EINVAL; -+ } -+ -+ return ret; -+} -+ -+int ipts_start(ipts_info_t *ipts) -+{ -+ int ret = 0; -+ /* TODO : check if we need to do SET_POLICIES_CMD -+ we need to do this when protocol version doesn't match with reported one -+ how we keep vendor specific data is the first thing to solve */ -+ -+ ipts_set_state(ipts, IPTS_STA_INIT); -+ ipts->num_of_parallel_data_buffers = TOUCH_SENSOR_MAX_DATA_BUFFERS; -+ -+ ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; /* start with RAW_DATA */ -+ -+ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_NOTIFY_DEV_READY_CMD, NULL, 0); -+ -+ return ret; -+} -+ -+void ipts_stop(ipts_info_t *ipts) -+{ -+ ipts_state_t old_state; -+ -+ old_state = ipts_get_state(ipts); -+ ipts_set_state(ipts, IPTS_STA_STOPPING); -+ -+ if (old_state < IPTS_STA_RESOURCE_READY) -+ return; -+ -+ if (old_state == IPTS_STA_RAW_DATA_STARTED || -+ old_state == IPTS_STA_HID_STARTED) { -+ ipts_free_default_resource(ipts); -+ ipts_free_raw_data_resource(ipts); -+ -+ return; -+ } -+} -+ -+int ipts_restart(ipts_info_t *ipts) -+{ -+ int ret = 0; -+ -+ ipts_dbg(ipts, "ipts restart\n"); -+ -+ ipts_stop(ipts); -+ -+ ipts->retry++; -+ if (ipts->retry == IPTS_MAX_RETRY && -+ ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { -+ /* try with HID mode */ -+ ipts->sensor_mode = TOUCH_SENSOR_MODE_HID; -+ } else if (ipts->retry > IPTS_MAX_RETRY) { -+ return -EPERM; -+ } -+ -+ ipts_send_sensor_quiesce_io_cmd(ipts); -+ ipts->restart = true; -+ -+ return ret; -+} -+ -+int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode) -+{ -+ int ret = 0; -+ -+ ipts->new_sensor_mode = new_sensor_mode; -+ ipts->switch_sensor_mode = true; -+ ret = ipts_send_sensor_quiesce_io_cmd(ipts); -+ -+ return ret; -+} -+ -+#define rsp_failed(ipts, cmd, status) ipts_err(ipts, \ -+ "0x%08x failed status = %d\n", cmd, status); -+ -+int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, -+ u32 msg_len) -+{ -+ int ret = 0; -+ int rsp_status = 0; -+ int cmd_status = 0; -+ int cmd_len = 0; -+ u32 cmd; -+ -+ if (!check_validity(m2h_msg, msg_len)) { -+ ipts_err(ipts, "wrong rsp\n"); -+ return -EINVAL; -+ } -+ -+ rsp_status = m2h_msg->status; -+ cmd = m2h_msg->command_code; -+ -+ switch (cmd) { -+ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: -+ if (rsp_status != 0 && -+ rsp_status != TOUCH_STATUS_SENSOR_FAIL_NONFATAL) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ cmd_status = ipts_handle_cmd(ipts, -+ TOUCH_SENSOR_GET_DEVICE_INFO_CMD, -+ NULL, 0); -+ break; -+ case TOUCH_SENSOR_GET_DEVICE_INFO_RSP: -+ if (rsp_status != 0 && -+ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ memcpy(&ipts->device_info, -+ &m2h_msg->m2h_data.device_info_rsp_data, -+ sizeof(touch_sensor_get_device_info_rsp_data_t)); -+ -+ /* -+ TODO : support raw_request during HID init. -+ Although HID init happens here, technically most of -+ reports (for both direction) can be issued only -+ after SET_MEM_WINDOWS_CMD since they may require -+ ME or touch IC. If ipts vendor requires raw_request -+ during HID init, we need to consider to move HID init. -+ */ -+ if (ipts->hid_desc_ready == false) { -+ ret = ipts_hid_init(ipts); -+ if (ret) -+ break; -+ } -+ -+ cmd_status = ipts_send_sensor_clear_mem_window_cmd(ipts); -+ -+ break; -+ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: -+ { -+ touch_sensor_set_mode_cmd_data_t sensor_mode_cmd; -+ -+ if (rsp_status != 0 && -+ rsp_status != TOUCH_STATUS_TIMEOUT) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ /* allocate default resource : common & hid only */ -+ if (!ipts_is_default_resource_ready(ipts)) { -+ ret = ipts_allocate_default_resource(ipts); -+ if (ret) -+ break; -+ } -+ -+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA && -+ !ipts_is_raw_data_resource_ready(ipts)) { -+ ret = ipts_allocate_raw_data_resource(ipts); -+ if (ret) { -+ ipts_free_default_resource(ipts); -+ break; -+ } -+ } -+ -+ ipts_set_state(ipts, IPTS_STA_RESOURCE_READY); -+ -+ cmd_len = sizeof(touch_sensor_set_mode_cmd_data_t); -+ memset(&sensor_mode_cmd, 0, cmd_len); -+ sensor_mode_cmd.sensor_mode = ipts->sensor_mode; -+ cmd_status = ipts_handle_cmd(ipts, -+ TOUCH_SENSOR_SET_MODE_CMD, -+ &sensor_mode_cmd, cmd_len); -+ break; -+ } -+ case TOUCH_SENSOR_SET_MODE_RSP: -+ { -+ touch_sensor_set_mem_window_cmd_data_t smw_cmd; -+ -+ if (rsp_status != 0) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ cmd_len = sizeof(touch_sensor_set_mem_window_cmd_data_t); -+ memset(&smw_cmd, 0, cmd_len); -+ ipts_get_set_mem_window_cmd_data(ipts, &smw_cmd); -+ cmd_status = ipts_handle_cmd(ipts, -+ TOUCH_SENSOR_SET_MEM_WINDOW_CMD, -+ &smw_cmd, cmd_len); -+ break; -+ } -+ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: -+ if (rsp_status != 0) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ cmd_status = ipts_send_sensor_hid_ready_for_data_cmd(ipts); -+ if (cmd_status) -+ break; -+ -+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { -+ ipts_set_state(ipts, IPTS_STA_HID_STARTED); -+ } else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { -+ ipts_set_state(ipts, IPTS_STA_RAW_DATA_STARTED); -+ } -+ -+ ipts_err(ipts, "touch enabled %d\n", ipts_get_state(ipts)); -+ -+ break; -+ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: -+ { -+ touch_sensor_hid_ready_for_data_rsp_data_t *hid_data; -+ ipts_state_t state; -+ -+ if (rsp_status != 0 && -+ rsp_status != TOUCH_STATUS_SENSOR_DISABLED) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ state = ipts_get_state(ipts); -+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID && -+ state == IPTS_STA_HID_STARTED) { -+ -+ hid_data = &m2h_msg->m2h_data.hid_ready_for_data_rsp_data; -+ -+ /* HID mode only uses buffer 0 */ -+ if (hid_data->touch_data_buffer_index != 0) -+ break; -+ -+ /* handle hid data */ -+ ipts_handle_hid_data(ipts, hid_data); -+ } -+ -+ break; -+ } -+ case TOUCH_SENSOR_FEEDBACK_READY_RSP: -+ if (rsp_status != 0 && -+ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ if (m2h_msg->m2h_data.feedback_ready_rsp_data. -+ feedback_index == TOUCH_HID_2_ME_BUFFER_ID) -+ break; -+ -+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { -+ cmd_status = ipts_handle_cmd(ipts, -+ TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, -+ NULL, 0); -+ } -+ -+ /* reset retry since we are getting touch data */ -+ ipts->retry = 0; -+ -+ break; -+ case TOUCH_SENSOR_QUIESCE_IO_RSP: -+ { -+ ipts_state_t state; -+ -+ if (rsp_status != 0) { -+ rsp_failed(ipts, cmd, rsp_status); -+ break; -+ } -+ -+ state = ipts_get_state(ipts); -+ if (state == IPTS_STA_STOPPING && ipts->restart) { -+ ipts_dbg(ipts, "restart\n"); -+ ipts_start(ipts); -+ ipts->restart = 0; -+ break; -+ } -+ -+ /* support sysfs debug node for switch sensor mode */ -+ if (ipts->switch_sensor_mode) { -+ ipts_set_state(ipts, IPTS_STA_INIT); -+ ipts->sensor_mode = ipts->new_sensor_mode; -+ ipts->switch_sensor_mode = false; -+ -+ ipts_send_sensor_clear_mem_window_cmd(ipts); -+ } -+ -+ break; -+ } -+ } -+ -+ /* handle error in rsp_status */ -+ if (rsp_status != 0) { -+ switch (rsp_status) { -+ case TOUCH_STATUS_SENSOR_EXPECTED_RESET: -+ case TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: -+ ipts_dbg(ipts, "sensor reset %d\n", rsp_status); -+ ipts_restart(ipts); -+ break; -+ default: -+ ipts_dbg(ipts, "cmd : 0x%08x, status %d\n", -+ cmd, -+ rsp_status); -+ break; -+ } -+ } -+ -+ if (cmd_status) { -+ ipts_restart(ipts); -+ } -+ -+ return ret; -+} -diff --git a/drivers/misc/ipts/ipts-msg-handler.h b/drivers/misc/ipts/ipts-msg-handler.h -new file mode 100644 -index 000000000000..15038814dfec ---- /dev/null -+++ b/drivers/misc/ipts/ipts-msg-handler.h -@@ -0,0 +1,32 @@ -+/* -+ * -+ * Intel Precise Touch & Stylus ME message handler -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _IPTS_MSG_HANDLER_H -+#define _IPTS_MSG_HANDLER_H -+ -+int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size); -+int ipts_start(ipts_info_t *ipts); -+void ipts_stop(ipts_info_t *ipts); -+int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode); -+int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, -+ u32 msg_len); -+int ipts_handle_processed_data(ipts_info_t *ipts); -+int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id); -+int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts); -+int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts); -+int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts); -+ -+#endif /* _IPTS_MSG_HANDLER_H */ -diff --git a/drivers/misc/ipts/ipts-resource.c b/drivers/misc/ipts/ipts-resource.c -new file mode 100644 -index 000000000000..47607ef7c461 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-resource.c -@@ -0,0 +1,277 @@ -+#include -+ -+#include "ipts.h" -+#include "ipts-mei-msgs.h" -+#include "ipts-kernel.h" -+ -+static void free_common_resource(ipts_info_t *ipts) -+{ -+ char *addr; -+ ipts_buffer_info_t *feedback_buffer; -+ dma_addr_t dma_addr; -+ u32 buffer_size; -+ int i, num_of_parallels; -+ -+ if (ipts->resource.me2hid_buffer) { -+ devm_kfree(&ipts->cldev->dev, ipts->resource.me2hid_buffer); -+ ipts->resource.me2hid_buffer = 0; -+ } -+ -+ addr = ipts->resource.hid2me_buffer.addr; -+ dma_addr = ipts->resource.hid2me_buffer.dma_addr; -+ buffer_size = ipts->resource.hid2me_buffer_size; -+ -+ if (ipts->resource.hid2me_buffer.addr) { -+ dmam_free_coherent(&ipts->cldev->dev, buffer_size, addr, dma_addr); -+ ipts->resource.hid2me_buffer.addr = 0; -+ ipts->resource.hid2me_buffer.dma_addr = 0; -+ ipts->resource.hid2me_buffer_size = 0; -+ } -+ -+ feedback_buffer = ipts->resource.feedback_buffer; -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ for (i = 0; i < num_of_parallels; i++) { -+ if (feedback_buffer[i].addr) { -+ dmam_free_coherent(&ipts->cldev->dev, -+ ipts->device_info.feedback_size, -+ feedback_buffer[i].addr, -+ feedback_buffer[i].dma_addr); -+ feedback_buffer[i].addr = 0; -+ feedback_buffer[i].dma_addr = 0; -+ } -+ } -+} -+ -+static int allocate_common_resource(ipts_info_t *ipts) -+{ -+ char *addr, *me2hid_addr; -+ ipts_buffer_info_t *feedback_buffer; -+ dma_addr_t dma_addr; -+ int i, ret = 0, num_of_parallels; -+ u32 buffer_size; -+ -+ buffer_size = ipts->device_info.feedback_size; -+ -+ addr = dmam_alloc_coherent(&ipts->cldev->dev, -+ buffer_size, -+ &dma_addr, -+ GFP_ATOMIC|__GFP_ZERO); -+ if (addr == NULL) -+ return -ENOMEM; -+ -+ me2hid_addr = devm_kzalloc(&ipts->cldev->dev, buffer_size, GFP_KERNEL); -+ if (me2hid_addr == NULL) { -+ ret = -ENOMEM; -+ goto release_resource; -+ } -+ -+ ipts->resource.hid2me_buffer.addr = addr; -+ ipts->resource.hid2me_buffer.dma_addr = dma_addr; -+ ipts->resource.hid2me_buffer_size = buffer_size; -+ ipts->resource.me2hid_buffer = me2hid_addr; -+ -+ feedback_buffer = ipts->resource.feedback_buffer; -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ for (i = 0; i < num_of_parallels; i++) { -+ feedback_buffer[i].addr = dmam_alloc_coherent(&ipts->cldev->dev, -+ ipts->device_info.feedback_size, -+ &feedback_buffer[i].dma_addr, -+ GFP_ATOMIC|__GFP_ZERO); -+ -+ if (feedback_buffer[i].addr == NULL) { -+ ret = -ENOMEM; -+ goto release_resource; -+ } -+ } -+ -+ return 0; -+ -+release_resource: -+ free_common_resource(ipts); -+ -+ return ret; -+} -+ -+void ipts_free_raw_data_resource(ipts_info_t *ipts) -+{ -+ if (ipts_is_raw_data_resource_ready(ipts)) { -+ ipts->resource.raw_data_resource_ready = false; -+ -+ ipts_release_kernels(ipts); -+ } -+} -+ -+static int allocate_hid_resource(ipts_info_t *ipts) -+{ -+ ipts_buffer_info_t *buffer_hid; -+ -+ /* hid mode uses only one touch data buffer */ -+ buffer_hid = &ipts->resource.touch_data_buffer_hid; -+ buffer_hid->addr = dmam_alloc_coherent(&ipts->cldev->dev, -+ ipts->device_info.frame_size, -+ &buffer_hid->dma_addr, -+ GFP_ATOMIC|__GFP_ZERO); -+ if (buffer_hid->addr == NULL) { -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static void free_hid_resource(ipts_info_t *ipts) -+{ -+ ipts_buffer_info_t *buffer_hid; -+ -+ buffer_hid = &ipts->resource.touch_data_buffer_hid; -+ if (buffer_hid->addr) { -+ dmam_free_coherent(&ipts->cldev->dev, -+ ipts->device_info.frame_size, -+ buffer_hid->addr, -+ buffer_hid->dma_addr); -+ buffer_hid->addr = 0; -+ buffer_hid->dma_addr = 0; -+ } -+} -+ -+int ipts_allocate_default_resource(ipts_info_t *ipts) -+{ -+ int ret; -+ -+ ret = allocate_common_resource(ipts); -+ if (ret) { -+ ipts_dbg(ipts, "cannot allocate common resource\n"); -+ return ret; -+ } -+ -+ ret = allocate_hid_resource(ipts); -+ if (ret) { -+ ipts_dbg(ipts, "cannot allocate hid resource\n"); -+ free_common_resource(ipts); -+ return ret; -+ } -+ -+ ipts->resource.default_resource_ready = true; -+ -+ return 0; -+} -+ -+void ipts_free_default_resource(ipts_info_t *ipts) -+{ -+ if (ipts_is_default_resource_ready(ipts)) { -+ ipts->resource.default_resource_ready = false; -+ -+ free_hid_resource(ipts); -+ free_common_resource(ipts); -+ } -+} -+ -+int ipts_allocate_raw_data_resource(ipts_info_t *ipts) -+{ -+ int ret = 0; -+ -+ ret = ipts_init_kernels(ipts); -+ if (ret) { -+ return ret; -+ } -+ -+ ipts->resource.raw_data_resource_ready = true; -+ -+ return 0; -+} -+ -+static void get_hid_only_smw_cmd_data(ipts_info_t *ipts, -+ touch_sensor_set_mem_window_cmd_data_t *data, -+ ipts_resource_t *resrc) -+{ -+ ipts_buffer_info_t *touch_buf; -+ ipts_buffer_info_t *feedback_buf; -+ -+ touch_buf = &resrc->touch_data_buffer_hid; -+ feedback_buf = &resrc->feedback_buffer[0]; -+ -+ data->touch_data_buffer_addr_lower[0] = -+ lower_32_bits(touch_buf->dma_addr); -+ data->touch_data_buffer_addr_upper[0] = -+ upper_32_bits(touch_buf->dma_addr); -+ data->feedback_buffer_addr_lower[0] = -+ lower_32_bits(feedback_buf->dma_addr); -+ data->feedback_buffer_addr_upper[0] = -+ upper_32_bits(feedback_buf->dma_addr); -+} -+ -+static void get_raw_data_only_smw_cmd_data(ipts_info_t *ipts, -+ touch_sensor_set_mem_window_cmd_data_t *data, -+ ipts_resource_t *resrc) -+{ -+ u64 wq_tail_phy_addr; -+ u64 cookie_phy_addr; -+ ipts_buffer_info_t *touch_buf; -+ ipts_buffer_info_t *feedback_buf; -+ int i, num_of_parallels; -+ -+ touch_buf = resrc->touch_data_buffer_raw; -+ feedback_buf = resrc->feedback_buffer; -+ -+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); -+ for (i = 0; i < num_of_parallels; i++) { -+ data->touch_data_buffer_addr_lower[i] = -+ lower_32_bits(touch_buf[i].dma_addr); -+ data->touch_data_buffer_addr_upper[i] = -+ upper_32_bits(touch_buf[i].dma_addr); -+ data->feedback_buffer_addr_lower[i] = -+ lower_32_bits(feedback_buf[i].dma_addr); -+ data->feedback_buffer_addr_upper[i] = -+ upper_32_bits(feedback_buf[i].dma_addr); -+ } -+ -+ wq_tail_phy_addr = resrc->wq_info.wq_tail_phy_addr; -+ data->tail_offset_addr_lower = lower_32_bits(wq_tail_phy_addr); -+ data->tail_offset_addr_upper = upper_32_bits(wq_tail_phy_addr); -+ -+ cookie_phy_addr = resrc->wq_info.db_phy_addr + -+ resrc->wq_info.db_cookie_offset; -+ data->doorbell_cookie_addr_lower = lower_32_bits(cookie_phy_addr); -+ data->doorbell_cookie_addr_upper = upper_32_bits(cookie_phy_addr); -+ data->work_queue_size = resrc->wq_info.wq_size; -+ -+ data->work_queue_item_size = resrc->wq_item_size; -+} -+ -+void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, -+ touch_sensor_set_mem_window_cmd_data_t *data) -+{ -+ ipts_resource_t *resrc = &ipts->resource; -+ -+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) -+ get_raw_data_only_smw_cmd_data(ipts, data, resrc); -+ else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) -+ get_hid_only_smw_cmd_data(ipts, data, resrc); -+ -+ /* hid2me is common for "raw data" and "hid" */ -+ data->hid2me_buffer_addr_lower = -+ lower_32_bits(resrc->hid2me_buffer.dma_addr); -+ data->hid2me_buffer_addr_upper = -+ upper_32_bits(resrc->hid2me_buffer.dma_addr); -+ data->hid2me_buffer_size = resrc->hid2me_buffer_size; -+} -+ -+void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, -+ u8* cpu_addr, u64 dma_addr) -+{ -+ ipts_buffer_info_t *touch_buf; -+ -+ touch_buf = ipts->resource.touch_data_buffer_raw; -+ touch_buf[parallel_idx].dma_addr = dma_addr; -+ touch_buf[parallel_idx].addr = cpu_addr; -+} -+ -+void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, -+ u8* cpu_addr, u64 dma_addr) -+{ -+ ipts_buffer_info_t *output_buf; -+ -+ output_buf = &ipts->resource.raw_data_mode_output_buffer[parallel_idx][output_idx]; -+ -+ output_buf->dma_addr = dma_addr; -+ output_buf->addr = cpu_addr; -+} -diff --git a/drivers/misc/ipts/ipts-resource.h b/drivers/misc/ipts/ipts-resource.h -new file mode 100644 -index 000000000000..7d66ac72b475 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-resource.h -@@ -0,0 +1,30 @@ -+/* -+ * Intel Precise Touch & Stylus state codes -+ * -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+#ifndef _IPTS_RESOURCE_H_ -+#define _IPTS_RESOURCE_H_ -+ -+int ipts_allocate_default_resource(ipts_info_t *ipts); -+void ipts_free_default_resource(ipts_info_t *ipts); -+int ipts_allocate_raw_data_resource(ipts_info_t *ipts); -+void ipts_free_raw_data_resource(ipts_info_t *ipts); -+void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, -+ touch_sensor_set_mem_window_cmd_data_t *data); -+void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, -+ u8* cpu_addr, u64 dma_addr); -+void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, -+ u8* cpu_addr, u64 dma_addr); -+ -+#endif // _IPTS_RESOURCE_H_ -diff --git a/drivers/misc/ipts/ipts-sensor-regs.h b/drivers/misc/ipts/ipts-sensor-regs.h -new file mode 100644 -index 000000000000..96812b0eb980 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-sensor-regs.h -@@ -0,0 +1,700 @@ -+/* -+ * Touch Sensor Register definition -+ * -+ * Copyright (c) 2013-2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+ -+#ifndef _TOUCH_SENSOR_REGS_H -+#define _TOUCH_SENSOR_REGS_H -+ -+#pragma pack(1) -+ -+// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch -+#ifndef C_ASSERT -+#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] -+#endif -+ -+// -+// Compatibility versions for this header file -+// -+#define TOUCH_EDS_REV_MINOR 0 -+#define TOUCH_EDS_REV_MAJOR 1 -+#define TOUCH_EDS_INTF_REV 1 -+#define TOUCH_PROTOCOL_VER 0 -+ -+ -+// -+// Offset 00h: TOUCH_STS: Status Register -+// This register is read by the SPI Controller immediately following an interrupt. -+// -+#define TOUCH_STS_REG_OFFSET 0x00 -+ -+typedef enum touch_sts_reg_int_type -+{ -+ TOUCH_STS_REG_INT_TYPE_DATA_AVAIL = 0, // Touch Data Available -+ TOUCH_STS_REG_INT_TYPE_RESET_OCCURRED, // Reset Occurred -+ TOUCH_STS_REG_INT_TYPE_ERROR_OCCURRED, // Error Occurred -+ TOUCH_STS_REG_INT_TYPE_VENDOR_DATA, // Vendor specific data, treated same as raw frame -+ TOUCH_STS_REG_INT_TYPE_GET_FEATURES, // Get Features response data available -+ TOUCH_STS_REG_INT_TYPE_MAX -+} touch_sts_reg_int_type_t; -+C_ASSERT(sizeof(touch_sts_reg_int_type_t) == 4); -+ -+typedef enum touch_sts_reg_pwr_state -+{ -+ TOUCH_STS_REG_PWR_STATE_SLEEP = 0, // Sleep -+ TOUCH_STS_REG_PWR_STATE_DOZE, // Doze -+ TOUCH_STS_REG_PWR_STATE_ARMED, // Armed -+ TOUCH_STS_REG_PWR_STATE_SENSING, // Sensing -+ TOUCH_STS_REG_PWR_STATE_MAX -+} touch_sts_reg_pwr_state_t; -+C_ASSERT(sizeof(touch_sts_reg_pwr_state_t) == 4); -+ -+typedef enum touch_sts_reg_init_state -+{ -+ TOUCH_STS_REG_INIT_STATE_READY_FOR_OP = 0, // Ready for normal operation -+ TOUCH_STS_REG_INIT_STATE_FW_NEEDED, // Touch IC needs its Firmware loaded -+ TOUCH_STS_REG_INIT_STATE_DATA_NEEDED, // Touch IC needs its Data loaded -+ TOUCH_STS_REG_INIT_STATE_INIT_ERROR, // Error info in TOUCH_ERR_REG -+ TOUCH_STS_REG_INIT_STATE_MAX -+} touch_sts_reg_init_state_t; -+C_ASSERT(sizeof(touch_sts_reg_init_state_t) == 4); -+ -+#define TOUCH_SYNC_BYTE_VALUE 0x5A -+ -+typedef union touch_sts_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // When set, this indicates the hardware has data that needs to be read. -+ u32 int_status :1; -+ // see TOUCH_STS_REG_INT_TYPE -+ u32 int_type :4; -+ // see TOUCH_STS_REG_PWR_STATE -+ u32 pwr_state :2; -+ // see TOUCH_STS_REG_INIT_STATE -+ u32 init_state :2; -+ // Busy bit indicates that sensor cannot accept writes at this time -+ u32 busy :1; -+ // Reserved -+ u32 reserved :14; -+ // Synchronization bit, should always be TOUCH_SYNC_BYTE_VALUE -+ u32 sync_byte :8; -+ } fields; -+} touch_sts_reg_t; -+C_ASSERT(sizeof(touch_sts_reg_t) == 4); -+ -+ -+// -+// Offset 04h: TOUCH_FRAME_CHAR: Frame Characteristics Register -+// This registers describes the characteristics of each data frame read by the SPI Controller in -+// response to a touch interrupt. -+// -+#define TOUCH_FRAME_CHAR_REG_OFFSET 0x04 -+ -+typedef union touch_frame_char_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // Micro-Frame Size (MFS): Indicates the size of a touch micro-frame in byte increments. -+ // When a micro-frame is to be read for processing (in data mode), this is the total number of -+ // bytes that must be read per interrupt, split into multiple read commands no longer than RPS. -+ // Maximum micro-frame size is 256KB. -+ u32 microframe_size :18; -+ // Micro-Frames per Frame (MFPF): Indicates the number of micro-frames per frame. If a -+ // sensor's frame does not contain micro-frames this value will be 1. Valid values are 1-31. -+ u32 microframes_per_frame :5; -+ // Micro-Frame Index (MFI): Indicates the index of the micro-frame within a frame. This allows -+ // the SPI Controller to maintain synchronization with the sensor and determine when the final -+ // micro-frame has arrived. Valid values are 1-31. -+ u32 microframe_index :5; -+ // HID/Raw Data: This bit describes whether the data from the sensor is Raw data or a HID -+ // report. When set, the data is a HID report. -+ u32 hid_report :1; -+ // Reserved -+ u32 reserved :3; -+ } fields; -+} touch_frame_char_reg_t; -+C_ASSERT(sizeof(touch_frame_char_reg_t) == 4); -+ -+ -+// -+// Offset 08h: Touch Error Register -+// -+#define TOUCH_ERR_REG_OFFSET 0x08 -+ -+// bit definition is vendor specific -+typedef union touch_err_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ u32 invalid_fw :1; -+ u32 invalid_data :1; -+ u32 self_test_failed :1; -+ u32 reserved :12; -+ u32 fatal_error :1; -+ u32 vendor_errors :16; -+ } fields; -+} touch_err_reg_t; -+C_ASSERT(sizeof(touch_err_reg_t) == 4); -+ -+ -+// -+// Offset 0Ch: RESERVED -+// This register is reserved for future use. -+// -+ -+ -+// -+// Offset 10h: Touch Identification Register -+// -+#define TOUCH_ID_REG_OFFSET 0x10 -+ -+#define TOUCH_ID_REG_VALUE 0x43495424 -+ -+// expected value is "$TIC" or 0x43495424 -+typedef u32 touch_id_reg_t; -+C_ASSERT(sizeof(touch_id_reg_t) == 4); -+ -+ -+// -+// Offset 14h: TOUCH_DATA_SZ: Touch Data Size Register -+// This register describes the maximum size of frames and feedback data -+// -+#define TOUCH_DATA_SZ_REG_OFFSET 0x14 -+ -+#define TOUCH_MAX_FRAME_SIZE_INCREMENT 64 -+#define TOUCH_MAX_FEEDBACK_SIZE_INCREMENT 64 -+ -+#define TOUCH_SENSOR_MAX_FRAME_SIZE (32 * 1024) // Max allowed frame size 32KB -+#define TOUCH_SENSOR_MAX_FEEDBACK_SIZE (16 * 1024) // Max allowed feedback size 16KB -+ -+typedef union touch_data_sz_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // This value describes the maximum frame size in 64byte increments. -+ u32 max_frame_size :12; -+ // This value describes the maximum feedback size in 64byte increments. -+ u32 max_feedback_size :8; -+ // Reserved -+ u32 reserved :12; -+ } fields; -+} touch_data_sz_reg_t; -+C_ASSERT(sizeof(touch_data_sz_reg_t) == 4); -+ -+ -+// -+// Offset 18h: TOUCH_CAPABILITIES: Touch Capabilities Register -+// This register informs the host as to the capabilities of the touch IC. -+// -+#define TOUCH_CAPS_REG_OFFSET 0x18 -+ -+typedef enum touch_caps_reg_read_delay_time -+{ -+ TOUCH_CAPS_REG_READ_DELAY_TIME_0, -+ TOUCH_CAPS_REG_READ_DELAY_TIME_10uS, -+ TOUCH_CAPS_REG_READ_DELAY_TIME_50uS, -+ TOUCH_CAPS_REG_READ_DELAY_TIME_100uS, -+ TOUCH_CAPS_REG_READ_DELAY_TIME_150uS, -+ TOUCH_CAPS_REG_READ_DELAY_TIME_250uS, -+ TOUCH_CAPS_REG_READ_DELAY_TIME_500uS, -+ TOUCH_CAPS_REG_READ_DELAY_TIME_1mS, -+} touch_caps_reg_read_delay_time_t; -+C_ASSERT(sizeof(touch_caps_reg_read_delay_time_t) == 4); -+ -+#define TOUCH_BULK_DATA_MAX_WRITE_INCREMENT 64 -+ -+typedef union touch_caps_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // Reserved for future frequency -+ u32 reserved0 :1; -+ // 17 MHz (14 MHz on Atom) Supported: 0b - Not supported, 1b - Supported -+ u32 supported_17Mhz :1; -+ // 30 MHz (25MHz on Atom) Supported: 0b - Not supported, 1b - Supported -+ u32 supported_30Mhz :1; -+ // 50 MHz Supported: 0b - Not supported, 1b - Supported -+ u32 supported_50Mhz :1; -+ // Reserved -+ u32 reserved1 :4; -+ // Single I/O Supported: 0b - Not supported, 1b - Supported -+ u32 supported_single_io :1; -+ // Dual I/O Supported: 0b - Not supported, 1b - Supported -+ u32 supported_dual_io :1; -+ // Quad I/O Supported: 0b - Not supported, 1b - Supported -+ u32 supported_quad_io :1; -+ // Bulk Data Area Max Write Size: The amount of data the SPI Controller can write to the bulk -+ // data area before it has to poll the busy bit. This field is in multiples of 64 bytes. The -+ // SPI Controller will write the amount of data specified in this field, then check and wait -+ // for the Status.Busy bit to be zero before writing the next data chunk. This field is 6 bits -+ // long, allowing for 4KB of contiguous writes w/o a poll of the busy bit. If this field is -+ // 0x00 the Touch IC has no limit in the amount of data the SPI Controller can write to the -+ // bulk data area. -+ u32 bulk_data_max_write :6; -+ // Read Delay Timer Value: This field describes the delay the SPI Controller will initiate when -+ // a read interrupt follows a write data command. Uses values from TOUCH_CAPS_REG_READ_DELAY_TIME -+ u32 read_delay_timer_value :3; -+ // Reserved -+ u32 reserved2 :4; -+ // Maximum Touch Points: A byte value based on the HID descriptor definition. -+ u32 max_touch_points :8; -+ } fields; -+} touch_caps_reg_t; -+C_ASSERT(sizeof(touch_caps_reg_t) == 4); -+ -+ -+// -+// Offset 1Ch: TOUCH_CFG: Touch Configuration Register -+// This register allows the SPI Controller to configure the touch sensor as needed during touch -+// operations. -+// -+#define TOUCH_CFG_REG_OFFSET 0x1C -+ -+typedef enum touch_cfg_reg_bulk_xfer_size -+{ -+ TOUCH_CFG_REG_BULK_XFER_SIZE_4B = 0, // Bulk Data Transfer Size is 4 bytes -+ TOUCH_CFG_REG_BULK_XFER_SIZE_8B, // Bulk Data Transfer Size is 8 bytes -+ TOUCH_CFG_REG_BULK_XFER_SIZE_16B, // Bulk Data Transfer Size is 16 bytes -+ TOUCH_CFG_REG_BULK_XFER_SIZE_32B, // Bulk Data Transfer Size is 32 bytes -+ TOUCH_CFG_REG_BULK_XFER_SIZE_64B, // Bulk Data Transfer Size is 64 bytes -+ TOUCH_CFG_REG_BULK_XFER_SIZE_MAX -+} touch_cfg_reg_bulk_xfer_size_t; -+C_ASSERT(sizeof(touch_cfg_reg_bulk_xfer_size_t) == 4); -+ -+// Frequency values used by TOUCH_CFG_REG and TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. -+typedef enum touch_freq -+{ -+ TOUCH_FREQ_RSVD = 0, // Reserved value -+ TOUCH_FREQ_17MHZ, // Sensor set for 17MHz operation (14MHz on Atom) -+ TOUCH_FREQ_30MHZ, // Sensor set for 30MHz operation (25MHz on Atom) -+ TOUCH_FREQ_MAX // Invalid value -+} touch_freq_t; -+C_ASSERT(sizeof(touch_freq_t) == 4); -+ -+typedef union touch_cfg_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // Touch Enable (TE): This bit is used as a HW semaphore for the Touch IC to guarantee to the -+ // SPI Controller to that (when 0) no sensing operations will occur and only the Reset -+ // interrupt will be generated. When TE is cleared by the SPI Controller: -+ // - TICs must flush all output buffers -+ // - TICs must De-assert any pending interrupt -+ // - ME must throw away any partial frame and pending interrupt must be cleared/not serviced. -+ // The SPI Controller will only modify the configuration of the TIC when TE is cleared. TE is -+ // defaulted to 0h on a power-on reset. -+ u32 touch_enable :1; -+ // Data/HID Packet Mode (DHPM): Raw Data Mode: 0h, HID Packet Mode: 1h -+ u32 dhpm :1; -+ // Bulk Data Transfer Size: This field represents the amount of data written to the Bulk Data -+ // Area (SPI Offset 0x1000-0x2FFF) in a single SPI write protocol -+ u32 bulk_xfer_size :4; -+ // Frequency Select: Frequency for the TouchIC to run at. Use values from TOUCH_FREQ -+ u32 freq_select :3; -+ // Reserved -+ u32 reserved :23; -+ } fields; -+} touch_cfg_reg_t; -+C_ASSERT(sizeof(touch_cfg_reg_t) == 4); -+ -+ -+// -+// Offset 20h: TOUCH_CMD: Touch Command Register -+// This register is used for sending commands to the Touch IC. -+// -+#define TOUCH_CMD_REG_OFFSET 0x20 -+ -+typedef enum touch_cmd_reg_code -+{ -+ TOUCH_CMD_REG_CODE_NOP = 0, // No Operation -+ TOUCH_CMD_REG_CODE_SOFT_RESET, // Soft Reset -+ TOUCH_CMD_REG_CODE_PREP_4_READ, // Prepare All Registers for Read -+ TOUCH_CMD_REG_CODE_GEN_TEST_PACKETS, // Generate Test Packets according to value in TOUCH_TEST_CTRL_REG -+ TOUCH_CMD_REG_CODE_MAX -+} touch_cmd_reg_code_t; -+C_ASSERT(sizeof(touch_cmd_reg_code_t) == 4); -+ -+typedef union touch_cmd_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // Command Code: See TOUCH_CMD_REG_CODE -+ u32 command_code :8; -+ // Reserved -+ u32 reserved :24; -+ } fields; -+} touch_cmd_reg_t; -+C_ASSERT(sizeof(touch_cmd_reg_t) == 4); -+ -+ -+// -+// Offset 24h: Power Management Control -+// This register is used for active power management. The Touch IC is allowed to mover from Doze or -+// Armed to Sensing after a touch has occurred. All other transitions will be made at the request -+// of the SPI Controller. -+// -+#define TOUCH_PWR_MGMT_CTRL_REG_OFFSET 0x24 -+ -+typedef enum touch_pwr_mgmt_ctrl_reg_cmd -+{ -+ TOUCH_PWR_MGMT_CTRL_REG_CMD_NOP = 0, // No change to power state -+ TOUCH_PWR_MGMT_CTRL_REG_CMD_SLEEP, // Sleep - set when the system goes into connected standby -+ TOUCH_PWR_MGMT_CTRL_REG_CMD_DOZE, // Doze - set after 300 seconds of inactivity -+ TOUCH_PWR_MGMT_CTRL_REG_CMD_ARMED, // Armed - Set by FW when a "finger off" message is received from the EUs -+ TOUCH_PWR_MGMT_CTRL_REG_CMD_SENSING, // Sensing - not typically set by FW -+ TOUCH_PWR_MGMT_CTRL_REG_CMD_MAX // Values will result in no change to the power state of the Touch IC -+} touch_pwr_mgmt_ctrl_reg_cmd_t; -+C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_cmd_t) == 4); -+ -+typedef union touch_pwr_mgmt_ctrl_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // Power State Command: See TOUCH_PWR_MGMT_CTRL_REG_CMD -+ u32 pwr_state_cmd :3; -+ // Reserved -+ u32 reserved :29; -+ } fields; -+} touch_pwr_mgmt_ctrl_reg_t; -+C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_t) == 4); -+ -+ -+// -+// Offset 28h: Vendor HW Information Register -+// This register is used to relay Intel-assigned vendor ID information to the SPI Controller, which -+// may be forwarded to SW running on the host CPU. -+// -+#define TOUCH_VEN_HW_INFO_REG_OFFSET 0x28 -+ -+typedef union touch_ven_hw_info_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // Touch Sensor Vendor ID -+ u32 vendor_id :16; -+ // Touch Sensor Device ID -+ u32 device_id :16; -+ } fields; -+} touch_ven_hw_info_reg_t; -+C_ASSERT(sizeof(touch_ven_hw_info_reg_t) == 4); -+ -+ -+// -+// Offset 2Ch: HW Revision ID Register -+// This register is used to relay vendor HW revision information to the SPI Controller which may be -+// forwarded to SW running on the host CPU. -+// -+#define TOUCH_HW_REV_REG_OFFSET 0x2C -+ -+typedef u32 touch_hw_rev_reg_t; // bit definition is vendor specific -+C_ASSERT(sizeof(touch_hw_rev_reg_t) == 4); -+ -+ -+// -+// Offset 30h: FW Revision ID Register -+// This register is used to relay vendor FW revision information to the SPI Controller which may be -+// forwarded to SW running on the host CPU. -+// -+#define TOUCH_FW_REV_REG_OFFSET 0x30 -+ -+typedef u32 touch_fw_rev_reg_t; // bit definition is vendor specific -+C_ASSERT(sizeof(touch_fw_rev_reg_t) == 4); -+ -+ -+// -+// Offset 34h: Compatibility Revision ID Register -+// This register is used to relay vendor compatibility information to the SPI Controller which may -+// be forwarded to SW running on the host CPU. Compatibility Information is a numeric value given -+// by Intel to the Touch IC vendor based on the major and minor revision of the EDS supported. From -+// a nomenclature point of view in an x.y revision number of the EDS, the major version is the value -+// of x and the minor version is the value of y. For example, a Touch IC supporting an EDS version -+// of 0.61 would contain a major version of 0 and a minor version of 61 in the register. -+// -+#define TOUCH_COMPAT_REV_REG_OFFSET 0x34 -+ -+typedef union touch_compat_rev_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // EDS Minor Revision -+ u8 minor; -+ // EDS Major Revision -+ u8 major; -+ // Interface Revision Number (from EDS) -+ u8 intf_rev; -+ // EU Kernel Compatibility Version - vendor specific value -+ u8 kernel_compat_ver; -+ } fields; -+} touch_compat_rev_reg_t; -+C_ASSERT(sizeof(touch_compat_rev_reg_t) == 4); -+ -+ -+// -+// Touch Register Block is the full set of registers from offset 0x00h to 0x3F -+// This is the entire set of registers needed for normal touch operation. It does not include test -+// registers such as TOUCH_TEST_CTRL_REG -+// -+#define TOUCH_REG_BLOCK_OFFSET TOUCH_STS_REG_OFFSET -+ -+typedef struct touch_reg_block -+{ -+ touch_sts_reg_t sts_reg; // 0x00 -+ touch_frame_char_reg_t frame_char_reg; // 0x04 -+ touch_err_reg_t error_reg; // 0x08 -+ u32 reserved0; // 0x0C -+ touch_id_reg_t id_reg; // 0x10 -+ touch_data_sz_reg_t data_size_reg; // 0x14 -+ touch_caps_reg_t caps_reg; // 0x18 -+ touch_cfg_reg_t cfg_reg; // 0x1C -+ touch_cmd_reg_t cmd_reg; // 0x20 -+ touch_pwr_mgmt_ctrl_reg_t pwm_mgme_ctrl_reg; // 0x24 -+ touch_ven_hw_info_reg_t ven_hw_info_reg; // 0x28 -+ touch_hw_rev_reg_t hw_rev_reg; // 0x2C -+ touch_fw_rev_reg_t fw_rev_reg; // 0x30 -+ touch_compat_rev_reg_t compat_rev_reg; // 0x34 -+ u32 reserved1; // 0x38 -+ u32 reserved2; // 0x3C -+} touch_reg_block_t; -+C_ASSERT(sizeof(touch_reg_block_t) == 64); -+ -+ -+// -+// Offset 40h: Test Control Register -+// This register -+// -+#define TOUCH_TEST_CTRL_REG_OFFSET 0x40 -+ -+typedef union touch_test_ctrl_reg -+{ -+ u32 reg_value; -+ -+ struct -+ { -+ // Size of Test Frame in Raw Data Mode: This field specifies the test frame size in raw data -+ // mode in multiple of 64 bytes. For example, if this field value is 16, the test frame size -+ // will be 16x64 = 1K. -+ u32 raw_test_frame_size :16; -+ // Number of Raw Data Frames or HID Report Packets Generation. This field represents the number -+ // of test frames or HID reports to be generated when test mode is enabled. When multiple -+ // packets/frames are generated, they need be generated at 100 Hz frequency, i.e. 10ms per -+ // packet/frame. -+ u32 num_test_frames :16; -+ } fields; -+} touch_test_ctrl_reg_t; -+C_ASSERT(sizeof(touch_test_ctrl_reg_t) == 4); -+ -+ -+// -+// Offsets 0x000 to 0xFFF are reserved for Intel-defined Registers -+// -+#define TOUCH_REGISTER_LIMIT 0xFFF -+ -+ -+// -+// Data Window: Address 0x1000-0x1FFFF -+// The data window is reserved for writing and reading large quantities of data to and from the -+// sensor. -+// -+#define TOUCH_DATA_WINDOW_OFFSET 0x1000 -+#define TOUCH_DATA_WINDOW_LIMIT 0x1FFFF -+ -+#define TOUCH_SENSOR_MAX_OFFSET TOUCH_DATA_WINDOW_LIMIT -+ -+ -+// -+// The following data structures represent the headers defined in the Data Structures chapter of the -+// Intel Integrated Touch EDS -+// -+ -+// Enumeration used in TOUCH_RAW_DATA_HDR -+typedef enum touch_raw_data_types -+{ -+ TOUCH_RAW_DATA_TYPE_FRAME = 0, -+ TOUCH_RAW_DATA_TYPE_ERROR, // RawData will be the TOUCH_ERROR struct below -+ TOUCH_RAW_DATA_TYPE_VENDOR_DATA, // Set when InterruptType is Vendor Data -+ TOUCH_RAW_DATA_TYPE_HID_REPORT, -+ TOUCH_RAW_DATA_TYPE_GET_FEATURES, -+ TOUCH_RAW_DATA_TYPE_MAX -+} touch_raw_data_types_t; -+C_ASSERT(sizeof(touch_raw_data_types_t) == 4); -+ -+// Private data structure. Kernels must copy to HID driver buffer -+typedef struct touch_hid_private_data -+{ -+ u32 transaction_id; -+ u8 reserved[28]; -+} touch_hid_private_data_t; -+C_ASSERT(sizeof(touch_hid_private_data_t) == 32); -+ -+// This is the data structure sent from the PCH FW to the EU kernel -+typedef struct touch_raw_data_hdr -+{ -+ u32 data_type; // use values from TOUCH_RAW_DATA_TYPES -+ u32 raw_data_size_bytes; // The size in bytes of the raw data read from the -+ // sensor, does not include TOUCH_RAW_DATA_HDR. Will -+ // be the sum of all uFrames, or size of TOUCH_ERROR -+ // for if DataType is TOUCH_RAW_DATA_TYPE_ERROR -+ u32 buffer_id; // An ID to qualify with the feedback data to track -+ // buffer usage -+ u32 protocol_ver; // Must match protocol version of the EDS -+ u8 kernel_compat_id; // Copied from the Compatibility Revision ID Reg -+ u8 reserved[15]; // Padding to extend header to full 64 bytes and -+ // allow for growth -+ touch_hid_private_data_t hid_private_data; // Private data structure. Kernels must copy to HID -+ // driver buffer -+} touch_raw_data_hdr_t; -+C_ASSERT(sizeof(touch_raw_data_hdr_t) == 64); -+ -+typedef struct touch_raw_data -+{ -+ touch_raw_data_hdr_t header; -+ u8 raw_data[1]; // used to access the raw data as an array and keep the -+ // compilers happy. Actual size of this array is -+ // Header.RawDataSizeBytes -+} touch_raw_data_t; -+ -+ -+// The following section describes the data passed in TOUCH_RAW_DATA.RawData when DataType equals -+// TOUCH_RAW_DATA_TYPE_ERROR -+// Note: This data structure is also applied to HID mode -+typedef enum touch_err_types -+{ -+ TOUCH_RAW_DATA_ERROR = 0, -+ TOUCH_RAW_ERROR_MAX -+} touch_err_types_t; -+C_ASSERT(sizeof(touch_err_types_t) == 4); -+ -+typedef union touch_me_fw_error -+{ -+ u32 value; -+ -+ struct -+ { -+ u32 invalid_frame_characteristics : 1; -+ u32 microframe_index_invalid : 1; -+ u32 reserved : 30; -+ } fields; -+} touch_me_fw_error_t; -+C_ASSERT(sizeof(touch_me_fw_error_t) == 4); -+ -+typedef struct touch_error -+{ -+ u8 touch_error_type; // This must be a value from TOUCH_ERROR_TYPES -+ u8 reserved[3]; -+ touch_me_fw_error_t touch_me_fw_error; -+ touch_err_reg_t touch_error_register; // Contains the value copied from the Touch Error Reg -+} touch_error_t; -+C_ASSERT(sizeof(touch_error_t) == 12); -+ -+// Enumeration used in TOUCH_FEEDBACK_BUFFER -+typedef enum touch_feedback_cmd_types -+{ -+ TOUCH_FEEDBACK_CMD_TYPE_NONE = 0, -+ TOUCH_FEEDBACK_CMD_TYPE_SOFT_RESET, -+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_ARMED, -+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SENSING, -+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SLEEP, -+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_DOZE, -+ TOUCH_FEEDBACK_CMD_TYPE_HARD_RESET, -+ TOUCH_FEEDBACK_CMD_TYPE_MAX -+} touch_feedback_cmd_types_t; -+C_ASSERT(sizeof(touch_feedback_cmd_types_t) == 4); -+ -+// Enumeration used in TOUCH_FEEDBACK_HDR -+typedef enum touch_feedback_data_types -+{ -+ TOUCH_FEEDBACK_DATA_TYPE_FEEDBACK = 0, // This is vendor specific feedback to be written to the sensor -+ TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES, // This is a set features command to be written to the sensor -+ TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES, // This is a get features command to be written to the sensor -+ TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT, // This is a HID output report to be written to the sensor -+ TOUCH_FEEDBACK_DATA_TYPE_STORE_DATA, // This is calibration data to be written to system flash -+ TOUCH_FEEDBACK_DATA_TYPE_MAX -+} touch_feedback_data_types_t; -+C_ASSERT(sizeof(touch_feedback_data_types_t) == 4); -+ -+// This is the data structure sent from the EU kernels back to the ME FW. -+// In addition to "feedback" data, the FW can execute a "command" described by the command type parameter. -+// Any payload data will always be sent to the TIC first, then any command will be issued. -+typedef struct touch_feedback_hdr -+{ -+ u32 feedback_cmd_type; // use values from TOUCH_FEEDBACK_CMD_TYPES -+ u32 payload_size_bytes; // The amount of data to be written to the sensor, not including the header -+ u32 buffer_id; // The ID of the raw data buffer that generated this feedback data -+ u32 protocol_ver; // Must match protocol version of the EDS -+ u32 feedback_data_type; // use values from TOUCH_FEEDBACK_DATA_TYPES. This is not relevant if PayloadSizeBytes is 0 -+ u32 spi_offest; // The offset from TOUCH_DATA_WINDOW_OFFSET at which to write the Payload data. Maximum offset is 0x1EFFF. -+ u8 reserved[40]; // Padding to extend header to full 64 bytes and allow for growth -+} touch_feedback_hdr_t; -+C_ASSERT(sizeof(touch_feedback_hdr_t) == 64); -+ -+typedef struct touch_feedback_buffer -+{ -+ touch_feedback_hdr_t Header; -+ u8 feedback_data[1]; // used to access the feedback data as an array and keep the compilers happy. Actual size of this array is Header.PayloadSizeBytes -+} touch_feedback_buffer_t; -+ -+ -+// -+// This data structure describes the header prepended to all data -+// written to the touch IC at the bulk data write (TOUCH_DATA_WINDOW_OFFSET + TOUCH_FEEDBACK_HDR.SpiOffest) address. -+typedef enum touch_write_data_type -+{ -+ TOUCH_WRITE_DATA_TYPE_FW_LOAD = 0, -+ TOUCH_WRITE_DATA_TYPE_DATA_LOAD, -+ TOUCH_WRITE_DATA_TYPE_FEEDBACK, -+ TOUCH_WRITE_DATA_TYPE_SET_FEATURES, -+ TOUCH_WRITE_DATA_TYPE_GET_FEATURES, -+ TOUCH_WRITE_DATA_TYPE_OUTPUT_REPORT, -+ TOUCH_WRITE_DATA_TYPE_NO_DATA_USE_DEFAULTS, -+ TOUCH_WRITE_DATA_TYPE_MAX -+} touch_write_data_type_t; -+C_ASSERT(sizeof(touch_write_data_type_t) == 4); -+ -+typedef struct touch_write_hdr -+{ -+ u32 write_data_type; // Use values from TOUCH_WRITE_DATA_TYPE -+ u32 write_data_len; // This field designates the amount of data to follow -+} touch_write_hdr_t; -+C_ASSERT(sizeof(touch_write_hdr_t) == 8); -+ -+typedef struct touch_write_data -+{ -+ touch_write_hdr_t header; -+ u8 write_data[1]; // used to access the write data as an array and keep the compilers happy. Actual size of this array is Header.WriteDataLen -+} touch_write_data_t; -+ -+#pragma pack() -+ -+#endif // _TOUCH_SENSOR_REGS_H -diff --git a/drivers/misc/ipts/ipts-state.h b/drivers/misc/ipts/ipts-state.h -new file mode 100644 -index 000000000000..39a2eaf5f004 ---- /dev/null -+++ b/drivers/misc/ipts/ipts-state.h -@@ -0,0 +1,29 @@ -+/* -+ * Intel Precise Touch & Stylus state codes -+ * -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ */ -+ -+#ifndef _IPTS_STATE_H_ -+#define _IPTS_STATE_H_ -+ -+/* ipts driver states */ -+typedef enum ipts_state { -+ IPTS_STA_NONE, -+ IPTS_STA_INIT, -+ IPTS_STA_RESOURCE_READY, -+ IPTS_STA_HID_STARTED, -+ IPTS_STA_RAW_DATA_STARTED, -+ IPTS_STA_STOPPING -+} ipts_state_t; -+ -+#endif // _IPTS_STATE_H_ -diff --git a/drivers/misc/ipts/ipts.h b/drivers/misc/ipts/ipts.h -new file mode 100644 -index 000000000000..1fcd02146b50 ---- /dev/null -+++ b/drivers/misc/ipts/ipts.h -@@ -0,0 +1,200 @@ -+/* -+ * -+ * Intel Management Engine Interface (Intel MEI) Client Driver for IPTS -+ * Copyright (c) 2016, Intel Corporation. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _IPTS_H_ -+#define _IPTS_H_ -+ -+#include -+#include -+#include -+#include -+ -+#include "ipts-mei-msgs.h" -+#include "ipts-state.h" -+#include "ipts-binary-spec.h" -+ -+//#define ENABLE_IPTS_DEBUG /* enable IPTS debug */ -+ -+#ifdef ENABLE_IPTS_DEBUG -+ -+#define ipts_info(ipts, format, arg...) do {\ -+ dev_info(&ipts->cldev->dev, format, ##arg);\ -+} while (0) -+ -+#define ipts_dbg(ipts, format, arg...) do {\ -+ dev_info(&ipts->cldev->dev, format, ##arg);\ -+} while (0) -+ -+//#define RUN_DBG_THREAD -+ -+#else -+ -+#define ipts_info(ipts, format, arg...) do {} while(0); -+#define ipts_dbg(ipts, format, arg...) do {} while(0); -+ -+#endif -+ -+#define ipts_err(ipts, format, arg...) do {\ -+ dev_err(&ipts->cldev->dev, format, ##arg);\ -+} while (0) -+ -+#define HID_PARALLEL_DATA_BUFFERS TOUCH_SENSOR_MAX_DATA_BUFFERS -+ -+#define IPTS_MAX_RETRY 3 -+ -+typedef struct ipts_buffer_info { -+ char *addr; -+ dma_addr_t dma_addr; -+} ipts_buffer_info_t; -+ -+typedef struct ipts_gfx_info { -+ u64 gfx_handle; -+ intel_ipts_ops_t ipts_ops; -+} ipts_gfx_info_t; -+ -+typedef struct ipts_resource { -+ /* ME & Gfx resource */ -+ ipts_buffer_info_t touch_data_buffer_raw[HID_PARALLEL_DATA_BUFFERS]; -+ ipts_buffer_info_t touch_data_buffer_hid; -+ -+ ipts_buffer_info_t feedback_buffer[HID_PARALLEL_DATA_BUFFERS]; -+ -+ ipts_buffer_info_t hid2me_buffer; -+ u32 hid2me_buffer_size; -+ -+ u8 wq_item_size; -+ intel_ipts_wq_info_t wq_info; -+ -+ /* ME2HID buffer */ -+ char *me2hid_buffer; -+ -+ /* Gfx specific resource */ -+ ipts_buffer_info_t raw_data_mode_output_buffer -+ [HID_PARALLEL_DATA_BUFFERS][MAX_NUM_OUTPUT_BUFFERS]; -+ -+ int num_of_outputs; -+ -+ bool default_resource_ready; -+ bool raw_data_resource_ready; -+} ipts_resource_t; -+ -+typedef struct ipts_info { -+ struct mei_cl_device *cldev; -+ struct hid_device *hid; -+ -+ struct work_struct init_work; -+ struct work_struct raw_data_work; -+ struct work_struct gfx_status_work; -+ -+ struct task_struct *event_loop; -+ -+#if IS_ENABLED(CONFIG_DEBUG_FS) -+ struct dentry *dbgfs_dir; -+#endif -+ -+ ipts_state_t state; -+ -+ touch_sensor_mode_t sensor_mode; -+ touch_sensor_get_device_info_rsp_data_t device_info; -+ ipts_resource_t resource; -+ u8 hid_input_report[HID_MAX_BUFFER_SIZE]; -+ int num_of_parallel_data_buffers; -+ bool hid_desc_ready; -+ -+ int current_buffer_index; -+ int last_buffer_completed; -+ int *last_submitted_id; -+ -+ ipts_gfx_info_t gfx_info; -+ u64 kernel_handle; -+ int gfx_status; -+ bool display_status; -+ -+ bool switch_sensor_mode; -+ touch_sensor_mode_t new_sensor_mode; -+ -+ int retry; -+ bool restart; -+} ipts_info_t; -+ -+#if IS_ENABLED(CONFIG_DEBUG_FS) -+int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); -+void ipts_dbgfs_deregister(ipts_info_t *ipts); -+#else -+static int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); -+static void ipts_dbgfs_deregister(ipts_info_t *ipts); -+#endif /* CONFIG_DEBUG_FS */ -+ -+/* inline functions */ -+static inline void ipts_set_state(ipts_info_t *ipts, ipts_state_t state) -+{ -+ ipts->state = state; -+} -+ -+static inline ipts_state_t ipts_get_state(const ipts_info_t *ipts) -+{ -+ return ipts->state; -+} -+ -+static inline bool ipts_is_default_resource_ready(const ipts_info_t *ipts) -+{ -+ return ipts->resource.default_resource_ready; -+} -+ -+static inline bool ipts_is_raw_data_resource_ready(const ipts_info_t *ipts) -+{ -+ return ipts->resource.raw_data_resource_ready; -+} -+ -+static inline ipts_buffer_info_t* ipts_get_feedback_buffer(ipts_info_t *ipts, -+ int buffer_idx) -+{ -+ return &ipts->resource.feedback_buffer[buffer_idx]; -+} -+ -+static inline ipts_buffer_info_t* ipts_get_touch_data_buffer_hid(ipts_info_t *ipts) -+{ -+ return &ipts->resource.touch_data_buffer_hid; -+} -+ -+static inline ipts_buffer_info_t* ipts_get_output_buffers_by_parallel_id( -+ ipts_info_t *ipts, -+ int parallel_idx) -+{ -+ return &ipts->resource.raw_data_mode_output_buffer[parallel_idx][0]; -+} -+ -+static inline ipts_buffer_info_t* ipts_get_hid2me_buffer(ipts_info_t *ipts) -+{ -+ return &ipts->resource.hid2me_buffer; -+} -+ -+static inline void ipts_set_wq_item_size(ipts_info_t *ipts, u8 size) -+{ -+ ipts->resource.wq_item_size = size; -+} -+ -+static inline u8 ipts_get_wq_item_size(const ipts_info_t *ipts) -+{ -+ return ipts->resource.wq_item_size; -+} -+ -+static inline int ipts_get_num_of_parallel_buffers(const ipts_info_t *ipts) -+{ -+ return ipts->num_of_parallel_data_buffers; -+} -+ -+#endif // _IPTS_H_ -diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h -index bb1ee9834a02..2bbffe8aad40 100644 ---- a/drivers/misc/mei/hw-me-regs.h -+++ b/drivers/misc/mei/hw-me-regs.h -@@ -119,6 +119,7 @@ - - #define MEI_DEV_ID_SPT 0x9D3A /* Sunrise Point */ - #define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ -+#define MEI_DEV_ID_SPT_4 0x9D3E /* Sunrise Point 4 */ - #define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ - #define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ - -diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c -index 3ab946ad3257..c37daa561486 100644 ---- a/drivers/misc/mei/pci-me.c -+++ b/drivers/misc/mei/pci-me.c -@@ -86,6 +86,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { - - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, -+ {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_4, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH12_CFG)}, -diff --git a/include/linux/intel_ipts_if.h b/include/linux/intel_ipts_if.h -new file mode 100644 -index 000000000000..f329bbfb8079 ---- /dev/null -+++ b/include/linux/intel_ipts_if.h -@@ -0,0 +1,75 @@ -+/* -+ * -+ * GFX interface to support Intel Precise Touch & Stylus -+ * Copyright (c) 2016 Intel Corporation. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef INTEL_IPTS_IF_H -+#define INTEL_IPTS_IF_H -+ -+enum { -+ IPTS_INTERFACE_V1 = 1, -+}; -+ -+#define IPTS_BUF_FLAG_CONTIGUOUS 0x01 -+ -+#define IPTS_NOTIFY_STA_BACKLIGHT_OFF 0x00 -+#define IPTS_NOTIFY_STA_BACKLIGHT_ON 0x01 -+ -+typedef struct intel_ipts_mapbuffer { -+ u32 size; -+ u32 flags; -+ void *gfx_addr; -+ void *cpu_addr; -+ u64 buf_handle; -+ u64 phy_addr; -+} intel_ipts_mapbuffer_t; -+ -+typedef struct intel_ipts_wq_info { -+ u64 db_addr; -+ u64 db_phy_addr; -+ u32 db_cookie_offset; -+ u32 wq_size; -+ u64 wq_addr; -+ u64 wq_phy_addr; -+ u64 wq_head_addr; /* head of wq is managed by GPU */ -+ u64 wq_head_phy_addr; /* head of wq is managed by GPU */ -+ u64 wq_tail_addr; /* tail of wq is managed by CSME */ -+ u64 wq_tail_phy_addr; /* tail of wq is managed by CSME */ -+} intel_ipts_wq_info_t; -+ -+typedef struct intel_ipts_ops { -+ int (*get_wq_info)(uint64_t gfx_handle, intel_ipts_wq_info_t *wq_info); -+ int (*map_buffer)(uint64_t gfx_handle, intel_ipts_mapbuffer_t *mapbuffer); -+ int (*unmap_buffer)(uint64_t gfx_handle, uint64_t buf_handle); -+} intel_ipts_ops_t; -+ -+typedef struct intel_ipts_callback { -+ void (*workload_complete)(void *data); -+ void (*notify_gfx_status)(u32 status, void *data); -+} intel_ipts_callback_t; -+ -+typedef struct intel_ipts_connect { -+ intel_ipts_callback_t ipts_cb; /* input : callback addresses */ -+ void *data; /* input : callback data */ -+ u32 if_version; /* input : interface version */ -+ -+ u32 gfx_version; /* output : gfx version */ -+ u64 gfx_handle; /* output : gfx handle */ -+ intel_ipts_ops_t ipts_ops; /* output : gfx ops for IPTS */ -+} intel_ipts_connect_t; -+ -+int intel_ipts_connect(intel_ipts_connect_t *ipts_connect); -+void intel_ipts_disconnect(uint64_t gfx_handle); -+ -+#endif // INTEL_IPTS_IF_H --- -2.19.1 - diff --git a/patches/5.0/0006-hid.patch b/patches/5.0/0006-hid.patch deleted file mode 100644 index d46f4793f..000000000 --- a/patches/5.0/0006-hid.patch +++ /dev/null @@ -1,151 +0,0 @@ -From b711906ed798cb9b0b1b2787c7b149a21d1c7c95 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:41:13 -0400 -Subject: [PATCH 06/12] hid - ---- - drivers/hid/hid-ids.h | 21 +++++++++---- - drivers/hid/hid-microsoft.c | 3 +- - drivers/hid/hid-multitouch.c | 57 ++++++++++++++++++++++++++++++++++++ - drivers/hid/hid-quirks.c | 11 +++++++ - 4 files changed, 86 insertions(+), 6 deletions(-) - -diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h -index 24f846d67478..b10672a0e87e 100644 ---- a/drivers/hid/hid-ids.h -+++ b/drivers/hid/hid-ids.h -@@ -811,11 +811,22 @@ - #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732 - #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600 0x0750 - #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c --#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 --#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 --#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 --#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 --#define USB_DEVICE_ID_MS_POWER_COVER 0x07da -+#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 -+#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 -+#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 -+#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 -+#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de -+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc -+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1 0x07de -+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 -+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd -+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e8 -+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1 0x07e4 -+#define USB_DEVICE_ID_MS_SURFACE_BOOK 0x07cd -+#define USB_DEVICE_ID_MS_SURFACE_BOOK_2 0x0922 -+#define USB_DEVICE_ID_MS_SURFACE_GO 0x096f -+#define USB_DEVICE_ID_MS_SURFACE_VHF 0xf001 -+#define USB_DEVICE_ID_MS_POWER_COVER 0x07da - #define USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER 0x02fd - #define USB_DEVICE_ID_MS_PIXART_MOUSE 0x00cb - -diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c -index 330cb073cb66..f45fea460678 100644 ---- a/drivers/hid/hid-microsoft.c -+++ b/drivers/hid/hid-microsoft.c -@@ -441,7 +441,8 @@ static const struct hid_device_id ms_devices[] = { - .driver_data = MS_HIDINPUT }, - { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD), - .driver_data = MS_ERGONOMY}, -- -+ { HID_DEVICE(BUS_VIRTUAL, 0, USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_VHF), -+ .driver_data = MS_HIDINPUT}, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), - .driver_data = MS_PRESENTER }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B), -diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c -index 719a0360e40c..962aeefed5d8 100644 ---- a/drivers/hid/hid-multitouch.c -+++ b/drivers/hid/hid-multitouch.c -@@ -1970,6 +1970,63 @@ static const struct hid_device_id mt_devices[] = { - HID_USB_DEVICE(USB_VENDOR_ID_LG, - USB_DEVICE_ID_LG_MELFAS_MT) }, - -+ /* Microsoft Touch Cover */ -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TOUCH_COVER_2) }, -+ -+ /* Microsoft Type Cover */ -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_2) }, -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_3) }, -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1) }, -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) }, -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1) }, -+ -+ /* Microsoft Surface Book */ -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_SURFACE_BOOK) }, -+ -+ /* Microsoft Surface Book 2 */ -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_SURFACE_BOOK_2) }, -+ -+ /* Microsoft Surface Go */ -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_SURFACE_GO) }, -+ -+ /* Microsoft Surface Laptop */ -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, -+ USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_SURFACE_VHF) }, -+ -+ /* Microsoft Power Cover */ -+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, -+ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, -+ USB_DEVICE_ID_MS_POWER_COVER) }, -+ - /* MosArt panels */ - { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - MT_USB_DEVICE(USB_VENDOR_ID_ASUS, -diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c -index 94088c0ed68a..391b882fd6e5 100644 ---- a/drivers/hid/hid-quirks.c -+++ b/drivers/hid/hid-quirks.c -@@ -112,6 +112,17 @@ static const struct hid_device_id hid_quirks[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS }, - { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, - { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK_2), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_GO), HID_QUIRK_NO_INIT_REPORTS }, -+ { HID_DEVICE(BUS_VIRTUAL, 0, USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_VHF), HID_QUIRK_ALWAYS_POLL }, - { HID_USB_DEVICE(USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER), HID_QUIRK_MULTI_INPUT }, - { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL), HID_QUIRK_NO_INIT_REPORTS }, - { HID_USB_DEVICE(USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD), HID_QUIRK_MULTI_INPUT }, --- -2.19.1 - diff --git a/patches/5.0/0007-sdcard-reader.patch b/patches/5.0/0007-sdcard-reader.patch deleted file mode 100644 index 6bcd41634..000000000 --- a/patches/5.0/0007-sdcard-reader.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 491ef9c62c9a340dfd60887bec404a3805debff1 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:41:25 -0400 -Subject: [PATCH 07/12] sdcard-reader - ---- - drivers/usb/core/hub.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index 1d1e61e980f3..409bce666ca5 100644 ---- a/drivers/usb/core/hub.c -+++ b/drivers/usb/core/hub.c -@@ -4163,7 +4163,8 @@ void usb_enable_lpm(struct usb_device *udev) - if (!udev || !udev->parent || - udev->speed < USB_SPEED_SUPER || - !udev->lpm_capable || -- udev->state < USB_STATE_DEFAULT) -+ udev->state < USB_STATE_DEFAULT || -+ !udev->bos || !udev->bos->ss_cap) - return; - - udev->lpm_disable_count--; --- -2.19.1 - diff --git a/patches/5.0/0008-wifi.patch b/patches/5.0/0008-wifi.patch deleted file mode 100644 index 5a5a7133f..000000000 --- a/patches/5.0/0008-wifi.patch +++ /dev/null @@ -1,270 +0,0 @@ -From 49e1ad9fe50979c6c329b21997b15b05ba1bdb2f Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:41:36 -0400 -Subject: [PATCH 08/12] wifi - ---- - drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 3 +-- - drivers/net/wireless/marvell/mwifiex/cfg80211.c | 5 ++++- - drivers/net/wireless/marvell/mwifiex/cmdevt.c | 10 ++++++---- - drivers/net/wireless/marvell/mwifiex/fw.h | 1 + - drivers/net/wireless/marvell/mwifiex/main.c | 17 +++++++++++++---- - drivers/net/wireless/marvell/mwifiex/main.h | 2 ++ - drivers/net/wireless/marvell/mwifiex/pcie.c | 9 +++++++++ - drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 4 ++-- - .../net/wireless/marvell/mwifiex/sta_cmdresp.c | 11 ++++++++--- - drivers/net/wireless/marvell/mwifiex/usb.c | 2 ++ - scripts/leaking_addresses.pl | 0 - 11 files changed, 48 insertions(+), 16 deletions(-) - mode change 100755 => 100644 scripts/leaking_addresses.pl - -diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -index 042a1d07f686..fc9041f58e9f 100644 ---- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -@@ -200,8 +200,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - - do { - /* Check if AMSDU can accommodate this MSDU */ -- if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) > -- adapter->tx_buf_size) -+ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) - break; - - skb_src = skb_dequeue(&pra_list->skb_head); -diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -index 883752f640b4..a3295e7b6d0a 100644 ---- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c -+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -@@ -428,7 +428,10 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, - mwifiex_dbg(priv->adapter, INFO, - "info: ignore timeout value for IEEE Power Save\n"); - -- ps_mode = enabled; -+ //ps_mode = enabled; -+ -+ mwifiex_dbg(priv->adapter, INFO, "overriding ps_mode to false\n"); -+ ps_mode = 0; - - return mwifiex_drv_set_power(priv, &ps_mode); - } -diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c -index 60db2b969e20..efae99018c3c 100644 ---- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c -+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c -@@ -1000,6 +1000,7 @@ mwifiex_cmd_timeout_func(struct timer_list *t) - if (cmd_node->wait_q_enabled) { - adapter->cmd_wait_q.status = -ETIMEDOUT; - mwifiex_cancel_pending_ioctl(adapter); -+ adapter->cmd_sent = false; - } - } - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { -@@ -1007,11 +1008,11 @@ mwifiex_cmd_timeout_func(struct timer_list *t) - return; - } - -- if (adapter->if_ops.device_dump) -- adapter->if_ops.device_dump(adapter); -+ //if (adapter->if_ops.device_dump) -+ // adapter->if_ops.device_dump(adapter); - -- if (adapter->if_ops.card_reset) -- adapter->if_ops.card_reset(adapter); -+ //if (adapter->if_ops.card_reset) -+ // adapter->if_ops.card_reset(adapter); - } - - void -@@ -1577,6 +1578,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, - adapter->key_api_minor_ver); - break; - case FW_API_VER_ID: -+ case FW_KEY_API_VER_ID: - adapter->fw_api_ver = - api_rev->major_ver; - mwifiex_dbg(adapter, INFO, -diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h -index b73f99dc5a72..d96a0ffc9649 100644 ---- a/drivers/net/wireless/marvell/mwifiex/fw.h -+++ b/drivers/net/wireless/marvell/mwifiex/fw.h -@@ -1052,6 +1052,7 @@ struct host_cmd_ds_802_11_ps_mode_enh { - enum API_VER_ID { - KEY_API_VER_ID = 1, - FW_API_VER_ID = 2, -+ FW_KEY_API_VER_ID = 4, - }; - - struct hw_spec_api_rev { -diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c -index 20cee5c397fb..13e49a3ae812 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.c -+++ b/drivers/net/wireless/marvell/mwifiex/main.c -@@ -163,6 +163,7 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) - spin_lock_irqsave(&adapter->main_proc_lock, flags); - if (adapter->mwifiex_processing) { - adapter->more_task_flag = true; -+ adapter->more_rx_task_flag = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); -@@ -171,18 +172,20 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) - } - EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); - --static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) -+void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) - { - unsigned long flags; - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - if (adapter->rx_processing) { -+ adapter->more_rx_task_flag = true; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - queue_work(adapter->rx_workqueue, &adapter->rx_work); - } - } -+EXPORT_SYMBOL_GPL(mwifiex_queue_rx_work); - - static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - { -@@ -192,6 +195,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - if (adapter->rx_processing || adapter->rx_locked) { -+ adapter->more_rx_task_flag = true; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - goto exit_rx_proc; - } else { -@@ -199,6 +203,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } - -+rx_process_start: - /* Check for Rx data */ - while ((skb = skb_dequeue(&adapter->rx_data_q))) { - atomic_dec(&adapter->rx_pending); -@@ -220,6 +225,11 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - } - } - spin_lock_irqsave(&adapter->rx_proc_lock, flags); -+ if (adapter->more_rx_task_flag) { -+ adapter->more_rx_task_flag = false; -+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); -+ goto rx_process_start; -+ } - adapter->rx_processing = false; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - -@@ -283,11 +293,10 @@ int mwifiex_main_process(struct mwifiex_adapter *adapter) - mwifiex_process_hs_config(adapter); - if (adapter->if_ops.process_int_status) - adapter->if_ops.process_int_status(adapter); -+ if (adapter->rx_work_enabled && adapter->data_received) -+ mwifiex_queue_rx_work(adapter); - } - -- if (adapter->rx_work_enabled && adapter->data_received) -- mwifiex_queue_rx_work(adapter); -- - /* Need to wake up the card ? */ - if ((adapter->ps_state == PS_STATE_SLEEP) && - (adapter->pm_wakeup_card_req && -diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h -index b025ba164412..d4027a803079 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.h -+++ b/drivers/net/wireless/marvell/mwifiex/main.h -@@ -908,6 +908,7 @@ struct mwifiex_adapter { - spinlock_t main_proc_lock; - u32 mwifiex_processing; - u8 more_task_flag; -+ u8 more_rx_task_flag; - u16 tx_buf_size; - u16 curr_tx_buf_size; - /* sdio single port rx aggregation capability */ -@@ -1694,6 +1695,7 @@ void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); - void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); - void mwifiex_fw_dump_event(struct mwifiex_private *priv); - void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); -+void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter); - int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action, - int cmd_type, - struct mwifiex_ds_wakeup_reason *wakeup_reason); -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index 3fe81b2a929a..6e734a83e6bf 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -1743,6 +1743,15 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) - } - - rx_len = get_unaligned_le16(skb->data); -+ -+ if (rx_len == 0) { -+ mwifiex_dbg(adapter, ERROR, -+ "0 byte cmdrsp\n"); -+ mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, -+ PCI_DMA_FROMDEVICE); -+ return 0; -+ } -+ - skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); - skb_trim(skb, rx_len); - -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -index 4ed10cf82f9a..485360e8534b 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -@@ -30,8 +30,8 @@ static bool drcs; - module_param(drcs, bool, 0644); - MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0"); - --static bool disable_auto_ds; --module_param(disable_auto_ds, bool, 0); -+static bool disable_auto_ds = 1; -+module_param(disable_auto_ds, bool, 0644); - MODULE_PARM_DESC(disable_auto_ds, - "deepsleep enabled=0(default), deepsleep disabled=1"); - /* -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -index 69e3b624adbb..884bad677cc3 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -@@ -48,9 +48,14 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, - struct host_cmd_ds_802_11_ps_mode_enh *pm; - unsigned long flags; - -- mwifiex_dbg(adapter, ERROR, -- "CMD_RESP: cmd %#x error, result=%#x\n", -- resp->command, resp->result); -+ if (resp->command == 271 && resp->result == 2){ -+ // ignore this command as the firmware does not support it -+ } -+ else { -+ mwifiex_dbg(adapter, ERROR, -+ "CMD_RESP: cmd %#x error, result=%#x\n", -+ resp->command, resp->result); -+ } - - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = -1; -diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c -index d445acc4786b..ae8e60cc17cb 100644 ---- a/drivers/net/wireless/marvell/mwifiex/usb.c -+++ b/drivers/net/wireless/marvell/mwifiex/usb.c -@@ -144,6 +144,8 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, - skb_queue_tail(&adapter->rx_data_q, skb); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); -+ if (adapter->rx_work_enabled) -+ mwifiex_queue_rx_work(adapter); - break; - default: - mwifiex_dbg(adapter, ERROR, -diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl -old mode 100755 -new mode 100644 --- -2.19.1 - diff --git a/patches/5.0/0009-surface-power.patch b/patches/5.0/0009-surface-power.patch deleted file mode 100644 index 729cc97f5..000000000 --- a/patches/5.0/0009-surface-power.patch +++ /dev/null @@ -1,655 +0,0 @@ -From 85a5077165f66cbdc567f5140d61f4cbb5762294 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:41:49 -0400 -Subject: [PATCH 09/12] surface-power - ---- - drivers/platform/x86/Kconfig | 7 + - drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surface3_power.c | 604 ++++++++++++++++++++++++++ - 3 files changed, 612 insertions(+) - create mode 100644 drivers/platform/x86/surface3_power.c - -diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index a00b5f6b23ce..3d7ba7adbddd 100644 ---- a/drivers/platform/x86/Kconfig -+++ b/drivers/platform/x86/Kconfig -@@ -1300,6 +1300,13 @@ config SURFACE_3_BUTTON - ---help--- - This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. - -+config SURFACE_3_POWER_OPREGION -+ tristate "Surface 3 battery platform operation region support" -+ depends on ACPI && I2C -+ help -+ Select this option to enable support for ACPI operation -+ region of the Surface 3 battery platform driver. -+ - config INTEL_PUNIT_IPC - tristate "Intel P-Unit IPC Driver" - ---help--- -diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 8412fe7a169d..4d3f0bad00c4 100644 ---- a/drivers/platform/x86/Makefile -+++ b/drivers/platform/x86/Makefile -@@ -85,6 +85,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o - obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o - obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o - obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o -+obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o - obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o - obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o - obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ -diff --git a/drivers/platform/x86/surface3_power.c b/drivers/platform/x86/surface3_power.c -new file mode 100644 -index 000000000000..e0af01a60302 ---- /dev/null -+++ b/drivers/platform/x86/surface3_power.c -@@ -0,0 +1,604 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+ -+/* -+ * Supports for the power IC on the Surface 3 tablet. -+ * -+ * (C) Copyright 2016-2018 Red Hat, Inc -+ * (C) Copyright 2016-2018 Benjamin Tissoires -+ * (C) Copyright 2016 Stephen Just -+ * -+ */ -+ -+/* -+ * This driver has been reverse-engineered by parsing the DSDT of the Surface 3 -+ * and looking at the registers of the chips. -+ * -+ * The DSDT allowed to find out that: -+ * - the driver is required for the ACPI BAT0 device to communicate to the chip -+ * through an operation region. -+ * - the various defines for the operation region functions to communicate with -+ * this driver -+ * - the DSM 3f99e367-6220-4955-8b0f-06ef2ae79412 allows to trigger ACPI -+ * events to BAT0 (the code is all available in the DSDT). -+ * -+ * Further findings regarding the 2 chips declared in the MSHW0011 are: -+ * - there are 2 chips declared: -+ * . 0x22 seems to control the ADP1 line status (and probably the charger) -+ * . 0x55 controls the battery directly -+ * - the battery chip uses a SMBus protocol (using plain SMBus allows non -+ * destructive commands): -+ * . the commands/registers used are in the range 0x00..0x7F -+ * . if bit 8 (0x80) is set in the SMBus command, the returned value is the -+ * same as when it is not set. There is a high chance this bit is the -+ * read/write -+ * . the various registers semantic as been deduced by observing the register -+ * dumps. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define POLL_INTERVAL (2 * HZ) -+ -+struct mshw0011_data { -+ struct i2c_client *adp1; -+ struct i2c_client *bat0; -+ unsigned short notify_mask; -+ struct task_struct *poll_task; -+ bool kthread_running; -+ -+ bool charging; -+ bool bat_charging; -+ u8 trip_point; -+ s32 full_capacity; -+}; -+ -+struct mshw0011_lookup { -+ struct mshw0011_data *cdata; -+ unsigned int n; -+ unsigned int index; -+ int addr; -+}; -+ -+struct mshw0011_handler_data { -+ struct acpi_connection_info info; -+ struct i2c_client *client; -+}; -+ -+struct bix { -+ u32 revision; -+ u32 power_unit; -+ u32 design_capacity; -+ u32 last_full_charg_capacity; -+ u32 battery_technology; -+ u32 design_voltage; -+ u32 design_capacity_of_warning; -+ u32 design_capacity_of_low; -+ u32 cycle_count; -+ u32 measurement_accuracy; -+ u32 max_sampling_time; -+ u32 min_sampling_time; -+ u32 max_average_interval; -+ u32 min_average_interval; -+ u32 battery_capacity_granularity_1; -+ u32 battery_capacity_granularity_2; -+ char model[10]; -+ char serial[10]; -+ char type[10]; -+ char OEM[10]; -+} __packed; -+ -+struct bst { -+ u32 battery_state; -+ s32 battery_present_rate; -+ u32 battery_remaining_capacity; -+ u32 battery_present_voltage; -+} __packed; -+ -+struct gsb_command { -+ u8 arg0; -+ u8 arg1; -+ u8 arg2; -+} __packed; -+ -+struct gsb_buffer { -+ u8 status; -+ u8 len; -+ u8 ret; -+ union { -+ struct gsb_command cmd; -+ struct bst bst; -+ struct bix bix; -+ } __packed; -+} __packed; -+ -+ -+#define ACPI_BATTERY_STATE_DISCHARGING BIT(0) -+#define ACPI_BATTERY_STATE_CHARGING BIT(1) -+#define ACPI_BATTERY_STATE_CRITICAL BIT(2) -+ -+#define MSHW0011_CMD_DEST_BAT0 0x01 -+#define MSHW0011_CMD_DEST_ADP1 0x03 -+ -+#define MSHW0011_CMD_BAT0_STA 0x01 -+#define MSHW0011_CMD_BAT0_BIX 0x02 -+#define MSHW0011_CMD_BAT0_BCT 0x03 -+#define MSHW0011_CMD_BAT0_BTM 0x04 -+#define MSHW0011_CMD_BAT0_BST 0x05 -+#define MSHW0011_CMD_BAT0_BTP 0x06 -+#define MSHW0011_CMD_ADP1_PSR 0x07 -+#define MSHW0011_CMD_BAT0_PSOC 0x09 -+#define MSHW0011_CMD_BAT0_PMAX 0x0a -+#define MSHW0011_CMD_BAT0_PSRC 0x0b -+#define MSHW0011_CMD_BAT0_CHGI 0x0c -+#define MSHW0011_CMD_BAT0_ARTG 0x0d -+ -+#define MSHW0011_NOTIFY_GET_VERSION 0x00 -+#define MSHW0011_NOTIFY_ADP1 0x01 -+#define MSHW0011_NOTIFY_BAT0_BST 0x02 -+#define MSHW0011_NOTIFY_BAT0_BIX 0x05 -+ -+#define MSHW0011_ADP1_REG_PSR 0x04 -+ -+#define MSHW0011_BAT0_REG_CAPACITY 0x0c -+#define MSHW0011_BAT0_REG_FULL_CHG_CAPACITY 0x0e -+#define MSHW0011_BAT0_REG_DESIGN_CAPACITY 0x40 -+#define MSHW0011_BAT0_REG_VOLTAGE 0x08 -+#define MSHW0011_BAT0_REG_RATE 0x14 -+#define MSHW0011_BAT0_REG_OEM 0x45 -+#define MSHW0011_BAT0_REG_TYPE 0x4e -+#define MSHW0011_BAT0_REG_SERIAL_NO 0x56 -+#define MSHW0011_BAT0_REG_CYCLE_CNT 0x6e -+ -+#define MSHW0011_EV_2_5 0x1ff -+ -+static int -+mshw0011_notify(struct mshw0011_data *cdata, u8 arg1, u8 arg2, -+ unsigned int *ret_value) -+{ -+ static const guid_t mshw0011_guid = -+ GUID_INIT(0x3F99E367, 0x6220, 0x4955, -+ 0x8B, 0x0F, 0x06, 0xEF, 0x2A, 0xE7, 0x94, 0x12); -+ union acpi_object *obj; -+ struct acpi_device *adev; -+ acpi_handle handle; -+ unsigned int i; -+ -+ handle = ACPI_HANDLE(&cdata->adp1->dev); -+ if (!handle || acpi_bus_get_device(handle, &adev)) -+ return -ENODEV; -+ -+ obj = acpi_evaluate_dsm_typed(handle, &mshw0011_guid, arg1, arg2, NULL, -+ ACPI_TYPE_BUFFER); -+ if (!obj) { -+ dev_err(&cdata->adp1->dev, "device _DSM execution failed\n"); -+ return -ENODEV; -+ } -+ -+ *ret_value = 0; -+ for (i = 0; i < obj->buffer.length; i++) -+ *ret_value |= obj->buffer.pointer[i] << (i * 8); -+ -+ ACPI_FREE(obj); -+ return 0; -+} -+ -+static const struct bix default_bix = { -+ .revision = 0x00, -+ .power_unit = 0x01, -+ .design_capacity = 0x1dca, -+ .last_full_charg_capacity = 0x1dca, -+ .battery_technology = 0x01, -+ .design_voltage = 0x10df, -+ .design_capacity_of_warning = 0x8f, -+ .design_capacity_of_low = 0x47, -+ .cycle_count = 0xffffffff, -+ .measurement_accuracy = 0x00015f90, -+ .max_sampling_time = 0x03e8, -+ .min_sampling_time = 0x03e8, -+ .max_average_interval = 0x03e8, -+ .min_average_interval = 0x03e8, -+ .battery_capacity_granularity_1 = 0x45, -+ .battery_capacity_granularity_2 = 0x11, -+ .model = "P11G8M", -+ .serial = "", -+ .type = "LION", -+ .OEM = "", -+}; -+ -+static int mshw0011_bix(struct mshw0011_data *cdata, struct bix *bix) -+{ -+ struct i2c_client *client = cdata->bat0; -+ char buf[10]; -+ int ret; -+ -+ *bix = default_bix; -+ -+ /* get design capacity */ -+ ret = i2c_smbus_read_word_data(client, -+ MSHW0011_BAT0_REG_DESIGN_CAPACITY); -+ if (ret < 0) { -+ dev_err(&client->dev, "Error reading design capacity: %d\n", -+ ret); -+ return ret; -+ } -+ bix->design_capacity = ret; -+ -+ /* get last full charge capacity */ -+ ret = i2c_smbus_read_word_data(client, -+ MSHW0011_BAT0_REG_FULL_CHG_CAPACITY); -+ if (ret < 0) { -+ dev_err(&client->dev, -+ "Error reading last full charge capacity: %d\n", ret); -+ return ret; -+ } -+ bix->last_full_charg_capacity = ret; -+ -+ /* get serial number */ -+ ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_SERIAL_NO, -+ 10, buf); -+ if (ret != 10) { -+ dev_err(&client->dev, "Error reading serial no: %d\n", ret); -+ return ret; -+ } -+ snprintf(bix->serial, ARRAY_SIZE(bix->serial), -+ "%*pE%*pE", 3, buf + 7, 6, buf); -+ -+ /* get cycle count */ -+ ret = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CYCLE_CNT); -+ if (ret < 0) { -+ dev_err(&client->dev, "Error reading cycle count: %d\n", ret); -+ return ret; -+ } -+ bix->cycle_count = ret; -+ -+ /* get OEM name */ -+ ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_OEM, -+ 4, buf); -+ if (ret != 4) { -+ dev_err(&client->dev, "Error reading cycle count: %d\n", ret); -+ return ret; -+ } -+ snprintf(bix->OEM, ARRAY_SIZE(bix->OEM), "%*pE", 3, buf); -+ -+ return 0; -+} -+ -+static int mshw0011_bst(struct mshw0011_data *cdata, struct bst *bst) -+{ -+ struct i2c_client *client = cdata->bat0; -+ int rate, capacity, voltage, state; -+ s16 tmp; -+ -+ rate = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_RATE); -+ if (rate < 0) -+ return rate; -+ -+ capacity = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CAPACITY); -+ if (capacity < 0) -+ return capacity; -+ -+ voltage = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_VOLTAGE); -+ if (voltage < 0) -+ return voltage; -+ -+ tmp = rate; -+ bst->battery_present_rate = abs((s32)tmp); -+ -+ state = 0; -+ if ((s32) tmp > 0) -+ state |= ACPI_BATTERY_STATE_CHARGING; -+ else if ((s32) tmp < 0) -+ state |= ACPI_BATTERY_STATE_DISCHARGING; -+ bst->battery_state = state; -+ -+ bst->battery_remaining_capacity = capacity; -+ bst->battery_present_voltage = voltage; -+ -+ return 0; -+} -+ -+static int mshw0011_adp_psr(struct mshw0011_data *cdata) -+{ -+ struct i2c_client *client = cdata->adp1; -+ int ret; -+ -+ ret = i2c_smbus_read_byte_data(client, MSHW0011_ADP1_REG_PSR); -+ if (ret < 0) -+ return ret; -+ -+ return ret; -+} -+ -+static int mshw0011_isr(struct mshw0011_data *cdata) -+{ -+ struct bst bst; -+ struct bix bix; -+ int ret; -+ bool status, bat_status; -+ -+ ret = mshw0011_adp_psr(cdata); -+ if (ret < 0) -+ return ret; -+ -+ status = ret; -+ -+ if (status != cdata->charging) -+ mshw0011_notify(cdata, cdata->notify_mask, -+ MSHW0011_NOTIFY_ADP1, &ret); -+ -+ cdata->charging = status; -+ -+ ret = mshw0011_bst(cdata, &bst); -+ if (ret < 0) -+ return ret; -+ -+ bat_status = bst.battery_state; -+ -+ if (bat_status != cdata->bat_charging) -+ mshw0011_notify(cdata, cdata->notify_mask, -+ MSHW0011_NOTIFY_BAT0_BST, &ret); -+ -+ cdata->bat_charging = bat_status; -+ -+ ret = mshw0011_bix(cdata, &bix); -+ if (ret < 0) -+ return ret; -+ if (bix.last_full_charg_capacity != cdata->full_capacity) -+ mshw0011_notify(cdata, cdata->notify_mask, -+ MSHW0011_NOTIFY_BAT0_BIX, &ret); -+ -+ cdata->full_capacity = bix.last_full_charg_capacity; -+ -+ return 0; -+} -+ -+static int mshw0011_poll_task(void *data) -+{ -+ struct mshw0011_data *cdata = data; -+ int ret = 0; -+ -+ cdata->kthread_running = true; -+ -+ set_freezable(); -+ -+ while (!kthread_should_stop()) { -+ schedule_timeout_interruptible(POLL_INTERVAL); -+ try_to_freeze(); -+ ret = mshw0011_isr(data); -+ if (ret) -+ break; -+ } -+ -+ cdata->kthread_running = false; -+ return ret; -+} -+ -+static acpi_status -+mshw0011_space_handler(u32 function, acpi_physical_address command, -+ u32 bits, u64 *value64, -+ void *handler_context, void *region_context) -+{ -+ struct gsb_buffer *gsb = (struct gsb_buffer *)value64; -+ struct mshw0011_handler_data *data = handler_context; -+ struct acpi_connection_info *info = &data->info; -+ struct acpi_resource_i2c_serialbus *sb; -+ struct i2c_client *client = data->client; -+ struct mshw0011_data *cdata = i2c_get_clientdata(client); -+ struct acpi_resource *ares; -+ u32 accessor_type = function >> 16; -+ acpi_status ret; -+ int status = 1; -+ -+ ret = acpi_buffer_to_resource(info->connection, info->length, &ares); -+ if (ACPI_FAILURE(ret)) -+ return ret; -+ -+ if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { -+ ret = AE_BAD_PARAMETER; -+ goto err; -+ } -+ -+ sb = &ares->data.i2c_serial_bus; -+ if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { -+ ret = AE_BAD_PARAMETER; -+ goto err; -+ } -+ -+ if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) { -+ ret = AE_BAD_PARAMETER; -+ goto err; -+ } -+ -+ if (gsb->cmd.arg0 == MSHW0011_CMD_DEST_ADP1 && -+ gsb->cmd.arg1 == MSHW0011_CMD_ADP1_PSR) { -+ ret = mshw0011_adp_psr(cdata); -+ if (ret >= 0) { -+ status = ret; -+ ret = 0; -+ } -+ goto out; -+ } -+ -+ if (gsb->cmd.arg0 != MSHW0011_CMD_DEST_BAT0) { -+ ret = AE_BAD_PARAMETER; -+ goto err; -+ } -+ -+ switch (gsb->cmd.arg1) { -+ case MSHW0011_CMD_BAT0_STA: -+ break; -+ case MSHW0011_CMD_BAT0_BIX: -+ ret = mshw0011_bix(cdata, &gsb->bix); -+ break; -+ case MSHW0011_CMD_BAT0_BTP: -+ cdata->trip_point = gsb->cmd.arg2; -+ break; -+ case MSHW0011_CMD_BAT0_BST: -+ ret = mshw0011_bst(cdata, &gsb->bst); -+ break; -+ default: -+ pr_info("command(0x%02x) is not supported.\n", gsb->cmd.arg1); -+ ret = AE_BAD_PARAMETER; -+ goto err; -+ } -+ -+ out: -+ gsb->ret = status; -+ gsb->status = 0; -+ -+ err: -+ ACPI_FREE(ares); -+ return ret; -+} -+ -+static int mshw0011_install_space_handler(struct i2c_client *client) -+{ -+ acpi_handle handle; -+ struct mshw0011_handler_data *data; -+ acpi_status status; -+ -+ handle = ACPI_HANDLE(&client->dev); -+ -+ if (!handle) -+ return -ENODEV; -+ -+ data = kzalloc(sizeof(struct mshw0011_handler_data), -+ GFP_KERNEL); -+ if (!data) -+ return -ENOMEM; -+ -+ data->client = client; -+ status = acpi_bus_attach_private_data(handle, (void *)data); -+ if (ACPI_FAILURE(status)) { -+ kfree(data); -+ return -ENOMEM; -+ } -+ -+ status = acpi_install_address_space_handler(handle, -+ ACPI_ADR_SPACE_GSBUS, -+ &mshw0011_space_handler, -+ NULL, -+ data); -+ if (ACPI_FAILURE(status)) { -+ dev_err(&client->dev, "Error installing i2c space handler\n"); -+ acpi_bus_detach_private_data(handle); -+ kfree(data); -+ return -ENOMEM; -+ } -+ -+ acpi_walk_dep_device_list(handle); -+ return 0; -+} -+ -+static void mshw0011_remove_space_handler(struct i2c_client *client) -+{ -+ acpi_handle handle = ACPI_HANDLE(&client->dev); -+ struct mshw0011_handler_data *data; -+ acpi_status status; -+ -+ if (!handle) -+ return; -+ -+ acpi_remove_address_space_handler(handle, -+ ACPI_ADR_SPACE_GSBUS, -+ &mshw0011_space_handler); -+ -+ status = acpi_bus_get_private_data(handle, (void **)&data); -+ if (ACPI_SUCCESS(status)) -+ kfree(data); -+ -+ acpi_bus_detach_private_data(handle); -+} -+ -+static int mshw0011_probe(struct i2c_client *client) -+{ -+ struct i2c_board_info board_info; -+ struct device *dev = &client->dev; -+ struct i2c_client *bat0; -+ -+ struct mshw0011_data *data; -+ int error, mask; -+ -+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); -+ if (!data) -+ return -ENOMEM; -+ -+ data->adp1 = client; -+ i2c_set_clientdata(client, data); -+ -+ memset(&board_info, 0, sizeof(board_info)); -+ strlcpy(board_info.type, "MSHW0011-bat0", I2C_NAME_SIZE); -+ -+ bat0 = i2c_acpi_new_device(dev, 1, &board_info); -+ if (!bat0) -+ return -ENOMEM; -+ -+ data->bat0 = bat0; -+ i2c_set_clientdata(bat0, data); -+ -+ error = mshw0011_notify(data, 1, MSHW0011_NOTIFY_GET_VERSION, &mask); -+ if (error) -+ goto out_err; -+ -+ data->notify_mask = mask == MSHW0011_EV_2_5; -+ -+ data->poll_task = kthread_run(mshw0011_poll_task, data, "mshw0011_adp"); -+ if (IS_ERR(data->poll_task)) { -+ error = PTR_ERR(data->poll_task); -+ dev_err(&client->dev, "Unable to run kthread err %d\n", error); -+ goto out_err; -+ } -+ -+ error = mshw0011_install_space_handler(client); -+ if (error) -+ goto out_err; -+ -+ return 0; -+ -+out_err: -+ if (data->kthread_running) -+ kthread_stop(data->poll_task); -+ i2c_unregister_device(data->bat0); -+ return error; -+} -+ -+static int mshw0011_remove(struct i2c_client *client) -+{ -+ struct mshw0011_data *cdata = i2c_get_clientdata(client); -+ -+ mshw0011_remove_space_handler(client); -+ -+ if (cdata->kthread_running) -+ kthread_stop(cdata->poll_task); -+ -+ i2c_unregister_device(cdata->bat0); -+ -+ return 0; -+} -+ -+static const struct acpi_device_id mshw0011_acpi_match[] = { -+ { "MSHW0011", 0 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(acpi, mshw0011_acpi_match); -+ -+static struct i2c_driver mshw0011_driver = { -+ .probe_new = mshw0011_probe, -+ .remove = mshw0011_remove, -+ .driver = { -+ .name = "mshw0011", -+ .acpi_match_table = ACPI_PTR(mshw0011_acpi_match), -+ }, -+}; -+module_i2c_driver(mshw0011_driver); -+ -+MODULE_AUTHOR("Benjamin Tissoires "); -+MODULE_DESCRIPTION("mshw0011 driver"); -+MODULE_LICENSE("GPL v2"); --- -2.19.1 - diff --git a/patches/5.0/0010-surface-dock.patch b/patches/5.0/0010-surface-dock.patch deleted file mode 100644 index 2aad28abb..000000000 --- a/patches/5.0/0010-surface-dock.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 43ba9ee40098a29dd1db695e7c0d2ade71666419 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:41:59 -0400 -Subject: [PATCH 10/12] surface-dock - ---- - drivers/usb/core/quirks.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c -index 8bc35d53408b..11fa56cbd769 100644 ---- a/drivers/usb/core/quirks.c -+++ b/drivers/usb/core/quirks.c -@@ -212,6 +212,8 @@ static const struct usb_device_id usb_quirk_list[] = { - /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */ - { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME }, - -+ { USB_DEVICE(0x045e, 0x07c6), .driver_info = USB_QUIRK_NO_LPM }, -+ - /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */ - { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, - { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT }, --- -2.19.1 - diff --git a/patches/5.0/0011-mwlwifi.patch b/patches/5.0/0011-mwlwifi.patch deleted file mode 100644 index ba36c0348..000000000 --- a/patches/5.0/0011-mwlwifi.patch +++ /dev/null @@ -1,19755 +0,0 @@ -From 78e72ad03d76d46860d827b5c784cbc48827f388 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:42:12 -0400 -Subject: [PATCH 11/12] mwlwifi - ---- - drivers/net/wireless/marvell/Kconfig | 1 + - drivers/net/wireless/marvell/Makefile | 1 + - drivers/net/wireless/marvell/mwlwifi/Kconfig | 23 + - drivers/net/wireless/marvell/mwlwifi/Makefile | 19 + - .../wireless/marvell/mwlwifi/Makefile.module | 28 + - .../net/wireless/marvell/mwlwifi/README.md | 142 + - drivers/net/wireless/marvell/mwlwifi/core.c | 1086 +++++ - drivers/net/wireless/marvell/mwlwifi/core.h | 517 +++ - .../net/wireless/marvell/mwlwifi/debugfs.c | 2201 ++++++++++ - .../net/wireless/marvell/mwlwifi/debugfs.h | 24 + - .../net/wireless/marvell/mwlwifi/hif/fwcmd.c | 3852 +++++++++++++++++ - .../net/wireless/marvell/mwlwifi/hif/fwcmd.h | 285 ++ - .../wireless/marvell/mwlwifi/hif/hif-ops.h | 297 ++ - .../net/wireless/marvell/mwlwifi/hif/hif.h | 81 + - .../wireless/marvell/mwlwifi/hif/hostcmd.h | 1285 ++++++ - .../wireless/marvell/mwlwifi/hif/pcie/dev.h | 1032 +++++ - .../wireless/marvell/mwlwifi/hif/pcie/fwdl.c | 274 ++ - .../wireless/marvell/mwlwifi/hif/pcie/fwdl.h | 24 + - .../wireless/marvell/mwlwifi/hif/pcie/pcie.c | 1645 +++++++ - .../wireless/marvell/mwlwifi/hif/pcie/rx.c | 540 +++ - .../wireless/marvell/mwlwifi/hif/pcie/rx.h | 25 + - .../marvell/mwlwifi/hif/pcie/rx_ndp.c | 612 +++ - .../marvell/mwlwifi/hif/pcie/rx_ndp.h | 26 + - .../marvell/mwlwifi/hif/pcie/sc4_ddr.h | 965 +++++ - .../wireless/marvell/mwlwifi/hif/pcie/tx.c | 1396 ++++++ - .../wireless/marvell/mwlwifi/hif/pcie/tx.h | 38 + - .../marvell/mwlwifi/hif/pcie/tx_ndp.c | 693 +++ - .../marvell/mwlwifi/hif/pcie/tx_ndp.h | 30 + - ...-workaround-for-80+80-and-160-MHz-channels | 32 + - .../wireless/marvell/mwlwifi/hostapd/README | 26 + - .../net/wireless/marvell/mwlwifi/mac80211.c | 933 ++++ - .../net/wireless/marvell/mwlwifi/mu_mimo.c | 21 + - .../net/wireless/marvell/mwlwifi/mu_mimo.h | 23 + - .../net/wireless/marvell/mwlwifi/sysadpt.h | 86 + - .../net/wireless/marvell/mwlwifi/thermal.c | 182 + - .../net/wireless/marvell/mwlwifi/thermal.h | 42 + - drivers/net/wireless/marvell/mwlwifi/utils.c | 576 +++ - drivers/net/wireless/marvell/mwlwifi/utils.h | 158 + - .../net/wireless/marvell/mwlwifi/vendor_cmd.c | 136 + - .../net/wireless/marvell/mwlwifi/vendor_cmd.h | 60 + - 40 files changed, 19417 insertions(+) - create mode 100644 drivers/net/wireless/marvell/mwlwifi/Kconfig - create mode 100644 drivers/net/wireless/marvell/mwlwifi/Makefile - create mode 100644 drivers/net/wireless/marvell/mwlwifi/Makefile.module - create mode 100644 drivers/net/wireless/marvell/mwlwifi/README.md - create mode 100644 drivers/net/wireless/marvell/mwlwifi/core.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/core.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/hif.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels - create mode 100644 drivers/net/wireless/marvell/mwlwifi/hostapd/README - create mode 100644 drivers/net/wireless/marvell/mwlwifi/mac80211.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/mu_mimo.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/mu_mimo.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/sysadpt.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/thermal.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/thermal.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/utils.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/utils.h - create mode 100644 drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c - create mode 100644 drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h - -diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig -index 27038901d3ee..3982c80f6df9 100644 ---- a/drivers/net/wireless/marvell/Kconfig -+++ b/drivers/net/wireless/marvell/Kconfig -@@ -14,6 +14,7 @@ if WLAN_VENDOR_MARVELL - source "drivers/net/wireless/marvell/libertas/Kconfig" - source "drivers/net/wireless/marvell/libertas_tf/Kconfig" - source "drivers/net/wireless/marvell/mwifiex/Kconfig" -+source "drivers/net/wireless/marvell/mwlwifi/Kconfig" - - config MWL8K - tristate "Marvell 88W8xxx PCI/PCIe Wireless support" -diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile -index 1b0a7d2bc8e6..04dff3388a41 100644 ---- a/drivers/net/wireless/marvell/Makefile -+++ b/drivers/net/wireless/marvell/Makefile -@@ -2,5 +2,6 @@ obj-$(CONFIG_LIBERTAS) += libertas/ - - obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ - obj-$(CONFIG_MWIFIEX) += mwifiex/ -+obj-$(CONFIG_MWLWIFI) += mwlwifi/ - - obj-$(CONFIG_MWL8K) += mwl8k.o -diff --git a/drivers/net/wireless/marvell/mwlwifi/Kconfig b/drivers/net/wireless/marvell/mwlwifi/Kconfig -new file mode 100644 -index 000000000000..a9bcb9cd4100 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/Kconfig -@@ -0,0 +1,23 @@ -+config MWLWIFI -+ tristate "Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211 compatible)" -+ depends on PCI && MAC80211 -+ select FW_LOADER -+ ---help--- -+ Select to build the driver supporting the: -+ -+ Marvell Wireless Wi-Fi 88W8864 modules -+ Marvell Wireless Wi-Fi 88W8897 modules -+ -+ This driver uses the kernel's mac80211 subsystem. -+ -+ If you want to compile the driver as a module (= code which can be -+ inserted in and removed from the running kernel whenever you want), -+ say M here and read . The -+ module will be called mwlwifi. -+ -+ NOTE: Selecting this driver may cause conflict with MWIFIEX driver -+ that also operates on the same part number 88W8897. Users should -+ select either MWIFIEX or MWLWIFI, not both. MWIFIEX is fullmac, -+ supporting more comprehensive client functions for laptops/embedded -+ devices. MWLWIFI is mac80211-based for full AP/Wireless Bridge. -+ -diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile b/drivers/net/wireless/marvell/mwlwifi/Makefile -new file mode 100644 -index 000000000000..061833703c7f ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/Makefile -@@ -0,0 +1,19 @@ -+obj-$(CONFIG_MWLWIFI) += mwlwifi.o -+ -+mwlwifi-objs += core.o -+mwlwifi-objs += mac80211.o -+mwlwifi-objs += mu_mimo.o -+mwlwifi-objs += vendor_cmd.o -+mwlwifi-objs += utils.o -+mwlwifi-$(CONFIG_THERMAL) += thermal.o -+mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o -+mwlwifi-objs += hif/fwcmd.o -+mwlwifi-objs += hif/pcie/pcie.o -+mwlwifi-objs += hif/pcie/fwdl.o -+mwlwifi-objs += hif/pcie/tx.o -+mwlwifi-objs += hif/pcie/rx.o -+mwlwifi-objs += hif/pcie/tx_ndp.o -+mwlwifi-objs += hif/pcie/rx_ndp.o -+ -+ccflags-y += -I$(src) -+ccflags-y += -D__CHECK_ENDIAN__ -diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile.module b/drivers/net/wireless/marvell/mwlwifi/Makefile.module -new file mode 100644 -index 000000000000..d11a1b88cab6 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/Makefile.module -@@ -0,0 +1,28 @@ -+obj-m += mwlwifi.o -+ -+mwlwifi-objs += core.o -+mwlwifi-objs += mac80211.o -+mwlwifi-objs += mu_mimo.o -+mwlwifi-objs += vendor_cmd.o -+mwlwifi-objs += utils.o -+mwlwifi-$(CONFIG_THERMAL) += thermal.o -+mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o -+mwlwifi-objs += hif/fwcmd.o -+mwlwifi-objs += hif/pcie/pcie.o -+mwlwifi-objs += hif/pcie/fwdl.o -+mwlwifi-objs += hif/pcie/tx.o -+mwlwifi-objs += hif/pcie/rx.o -+mwlwifi-objs += hif/pcie/tx_ndp.o -+mwlwifi-objs += hif/pcie/rx_ndp.o -+ -+ccflags-y += -I$(src) -+ccflags-y += -O2 -funroll-loops -D__CHECK_ENDIAN__ -+ -+all: -+ $(MAKE) -C $(KDIR) M=$(PWD) -+ -+clean: -+ rm -f *.a *.s *.ko *.ko.cmd *.mod.* .mwlwifi.* modules.order Module.symvers -+ rm -rf .tmp_versions -+ find . -name ".*.o.cmd" -exec rm -f {} \; -+ find . -name "*.o" -exec rm -f {} \; -diff --git a/drivers/net/wireless/marvell/mwlwifi/README.md b/drivers/net/wireless/marvell/mwlwifi/README.md -new file mode 100644 -index 000000000000..788c5d4dc80d ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/README.md -@@ -0,0 +1,142 @@ -+# mwlwifi -+mac80211 driver for the Marvell 88W8x64 802.11ac chip -+ -+## Building mwlwifi With OpenWrt/LEDE -+1. Modify `package/kernel/mwlwifi/Makefile`: -+ ``` -+ PKG_VERSION:=10.3.0.17-20160601 -+ PKG_SOURCE_VERSION:=4bb95ba1aeccce506a95499b49b9b844ecfae8a1 -+ ``` -+ -+2. Rename `package/kernel/mwlwifi/patches` to `package/kernel/mwlwifi/patches.tmp`. -+3. Run the following commands: -+ ```sh -+ make package/kernel/mwlwifi/clean -+ make V=s (-jx) -+ ``` -+ -+### Special Considerations -+* After driver 10.3.0.17-20160603, [MAX-MPDU-7991] should be removed from vht_capab command of hostapd. -+ -+* Hostpad must include the following commit for 160 MHz operation: -+ ``` -+ commit 03a72eacda5d9a1837a74387081596a0d5466ec1 -+ Author: Jouni Malinen -+ Date: Thu Dec 17 18:39:19 2015 +0200 -+ -+ VHT: Add an interoperability workaround for 80+80 and 160 MHz channels -+ -+ Number of deployed 80 MHz capable VHT stations that do not support 80+80 -+ and 160 MHz bandwidths seem to misbehave when trying to connect to an AP -+ that advertises 80+80 or 160 MHz channel bandwidth in the VHT Operation -+ element. To avoid such issues with deployed devices, modify the design -+ based on newly proposed IEEE 802.11 standard changes. -+ -+ This allows poorly implemented VHT 80 MHz stations to connect with the -+ AP in 80 MHz mode. 80+80 and 160 MHz capable stations need to support -+ the new workaround mechanism to allow full bandwidth to be used. -+ However, there are more or less no impacted station with 80+80/160 -+ capability deployed. -+ -+ Signed-off-by: Jouni Malinen jouni@qca.qualcomm.com -+ -+ Note: After hostapd package 2016-06-15, this commit is already included. -+ ``` -+ -+* In order to let STA mode to support 160 MHz operation, mac80211 package should be 2016-10-08 or later. -+ -+* WiFi device does not use HT rates when using TKIP as the encryption cipher. If you want to have good performance, please use AES only. -+ -+* DTS parameters for mwlwifi driver (pcie@X,0): -+ ```sh -+ #Disable 2g band -+ marvell,2ghz = <0>; -+ -+ #Disable 5g band -+ marvell,5ghz = <0>; -+ -+ #Specify antenna number, default is 4x4. For WRT1200AC, you must set these values to 2x2. -+ marvell,chainmask = <4 4>; -+ -+ #Specify external power table. If your device needs external power table, you must provide the power table via this parameter, otherwise the Tx power will be pretty low. -+ marvell,powertable -+ ``` -+ -+ To see if your device needs/accepts an external power table or not, run the following: -+ ```sh -+ cat /sys/kernel/debug/ieee80211/phy0/mwlwifi/info -+ ``` -+ -+ You should see a line in the results which looks like the following: -+ ```sh -+ power table loaded from dts: no -+ ``` -+ -+ If it is "no", it does not allow you to load external power table (for newer devices due to FCC regulations). If it is "yes", you must provide power table in DTS file (for older devices). -+ -+* Changing interrupt to different CPU cores: -+ ```sh -+ #Use CPU0: -+ echo 1 > /proc/irq/irq number of phy0 or phy1/smp_affinity -+ -+ #Use CPU1: -+ echo 2 > /proc/irq/irq number of phy0 or phy1/smp_affinity -+ ``` -+ -+* Note for DFS of WRT3200ACM (88W8964): -+ -+ All WRT3200ACM devices are programmed with device power table. Mwlwifi driver will base on region code to set country code for your device and it will not allow you to change country code. There are another wifi (phy2) on WRT3200ACM which is not mwlwifi. It will allow you to change country code. Under this case, country code setting will be conflicted and it will let DFS can't work. -+ -+ There are two ways to resolve this problem: -+ * Please don't change country code and let mwlwifi set it for you. -+ * Remove phy2. Under this case, even though you change country code, mwlwifi will reject it. Because phy2 is not existed, country code setting won't be conflicted. To do this, run the following commands (for OpenWrt/LEDE): -+ -+ ```sh -+ opkg remove kmod-mwifiex-sdio -+ opkg remove mwifiex-sdio-firmware -+ reboot -+ ``` -+ -+ The better way is let mwlwifi set country code for you. -+ -+## Replacing mwlwifi on a Current OpenWrt/LEDE Build -+ -+1. Establish a symbolic link to your working mwlwifi directory with current mwlwifi package name under directory "dl": -+ ```sh -+ ls -l mwlwifi* -+ ``` -+ -+ You should see something like the following: -+ ```sh -+ lrwxrwxrwx 1 dlin dlin 48 mwlwifi-10.3.2.0-20170110 -> /home/dlin/home2/projects/github/mwlwifi -+ -+ -rw-r--r-- 1 dlin dlin 4175136 mwlwifi-10.3.2.0-20170110.tar.xz -+ ``` -+ -+2. Back up original mwlwifi package and tar your working mwlwifi to replace original mwlwifi package: -+ -+ ```sh -+ tar Jcvf mwlwifi-10.3.2.0-20170110.tar.xz mwlwifi-10.3.2.0-20170110/. -+ ``` -+ -+3. You can use `make V=s` to build the whole image or `make V=s package/kernel/mwlwifi/compile` to build mwlwifi package. The generated whole image or mwlwifi package can be found under directory "bin". -+ -+Due to package version being the same as previous one, you need to add option `--force-reinstall` when you use `opkg` to update mwlwifi package on your device. -+ -+## Monitor interface for debug -+ -+1. Create moinitor interface mon0: -+ ```sh -+ iw wlan0/wlan1 interface add mon0 type monitor -+ ifconfig mon0 up -+ ``` -+ -+2. Use tcpdump to dump dhcp packets: -+ ```sh -+ tcpdump -vvvi mon0 -n port 67 and port 68 -+ ``` -+ -+3. Use tcpdump to dump icmp packets: -+ ```sh -+ tcpdump -vvvi mon0 icmp -+ ``` -diff --git a/drivers/net/wireless/marvell/mwlwifi/core.c b/drivers/net/wireless/marvell/mwlwifi/core.c -new file mode 100644 -index 000000000000..9d2b5511607e ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/core.c -@@ -0,0 +1,1086 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements core layer related functions. */ -+ -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "vendor_cmd.h" -+#include "thermal.h" -+#include "debugfs.h" -+#include "hif/fwcmd.h" -+#include "hif/hif-ops.h" -+ -+#define CMD_BUF_SIZE 0x4000 -+#define INVALID_WATCHDOG 0xAA -+ -+static const struct ieee80211_channel mwl_channels_24[] = { -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, }, -+ { .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, }, -+}; -+ -+static const struct ieee80211_rate mwl_rates_24[] = { -+ { .bitrate = 10, .hw_value = 2, }, -+ { .bitrate = 20, .hw_value = 4, }, -+ { .bitrate = 55, .hw_value = 11, }, -+ { .bitrate = 110, .hw_value = 22, }, -+ { .bitrate = 220, .hw_value = 44, }, -+ { .bitrate = 60, .hw_value = 12, }, -+ { .bitrate = 90, .hw_value = 18, }, -+ { .bitrate = 120, .hw_value = 24, }, -+ { .bitrate = 180, .hw_value = 36, }, -+ { .bitrate = 240, .hw_value = 48, }, -+ { .bitrate = 360, .hw_value = 72, }, -+ { .bitrate = 480, .hw_value = 96, }, -+ { .bitrate = 540, .hw_value = 108, }, -+}; -+ -+static const struct ieee80211_channel mwl_channels_50[] = { -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5260, .hw_value = 52, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5280, .hw_value = 56, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5300, .hw_value = 60, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5320, .hw_value = 64, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5500, .hw_value = 100, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5520, .hw_value = 104, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5540, .hw_value = 108, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5560, .hw_value = 112, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5580, .hw_value = 116, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5600, .hw_value = 120, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5620, .hw_value = 124, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5640, .hw_value = 128, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5660, .hw_value = 132, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5680, .hw_value = 136, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5700, .hw_value = 140, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5720, .hw_value = 144, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, }, -+ { .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, }, -+}; -+ -+static const struct ieee80211_rate mwl_rates_50[] = { -+ { .bitrate = 60, .hw_value = 12, }, -+ { .bitrate = 90, .hw_value = 18, }, -+ { .bitrate = 120, .hw_value = 24, }, -+ { .bitrate = 180, .hw_value = 36, }, -+ { .bitrate = 240, .hw_value = 48, }, -+ { .bitrate = 360, .hw_value = 72, }, -+ { .bitrate = 480, .hw_value = 96, }, -+ { .bitrate = 540, .hw_value = 108, }, -+}; -+ -+static const struct ieee80211_iface_limit ap_if_limits[] = { -+ { .max = SYSADPT_NUM_OF_AP, .types = BIT(NL80211_IFTYPE_AP) }, -+#if defined(CPTCFG_MAC80211_MESH) || defined(CONFIG_MAC80211_MESH) -+ { .max = SYSADPT_NUM_OF_MESH, .types = BIT(NL80211_IFTYPE_MESH_POINT) }, -+#endif -+ { .max = SYSADPT_NUM_OF_CLIENT, .types = BIT(NL80211_IFTYPE_STATION) }, -+}; -+ -+static const struct ieee80211_iface_combination ap_if_comb = { -+ .limits = ap_if_limits, -+ .n_limits = ARRAY_SIZE(ap_if_limits), -+ .max_interfaces = SYSADPT_NUM_OF_AP, -+ .num_different_channels = 1, -+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | -+ BIT(NL80211_CHAN_WIDTH_20) | -+ BIT(NL80211_CHAN_WIDTH_40) | -+ BIT(NL80211_CHAN_WIDTH_80) | -+ BIT(NL80211_CHAN_WIDTH_160), -+}; -+ -+struct region_code_mapping { -+ const char *alpha2; -+ u32 region_code; -+}; -+ -+static const struct region_code_mapping regmap[] = { -+ {"US", 0x10}, /* US FCC */ -+ {"CA", 0x20}, /* Canada */ -+ {"FR", 0x30}, /* France */ -+ {"ES", 0x31}, /* Spain */ -+ {"FR", 0x32}, /* France */ -+ {"JP", 0x40}, /* Japan */ -+ {"TW", 0x80}, /* Taiwan */ -+ {"AU", 0x81}, /* Australia */ -+ {"CN", 0x90}, /* China (Asia) */ -+}; -+ -+static int mwl_prepare_cmd_buf(struct mwl_priv *priv) -+{ -+ priv->pcmd_buf = -+ (unsigned short *)dmam_alloc_coherent(priv->dev, -+ CMD_BUF_SIZE, -+ &priv->pphys_cmd_buf, -+ GFP_KERNEL); -+ if (!priv->pcmd_buf) { -+ wiphy_err(priv->hw->wiphy, -+ "cannot alloc memory for command buffer\n"); -+ goto err; -+ } -+ wiphy_debug(priv->hw->wiphy, -+ "priv->pcmd_buf = %p priv->pphys_cmd_buf = %p\n", -+ priv->pcmd_buf, -+ (void *)priv->pphys_cmd_buf); -+ memset(priv->pcmd_buf, 0x00, CMD_BUF_SIZE); -+ -+ return 0; -+ -+err: -+ wiphy_err(priv->hw->wiphy, "command buffer alloc fail\n"); -+ -+ return -EIO; -+} -+ -+static int mwl_init_firmware(struct mwl_priv *priv, const char *fw_name, -+ const char *cal_name, const char *txpwrlmt_name) -+{ -+ int rc = 0; -+ -+ rc = request_firmware((const struct firmware **)&priv->fw_ucode, -+ fw_name, priv->dev); -+ -+ if (rc) { -+ wiphy_err(priv->hw->wiphy, -+ "cannot find firmware image <%s>\n", fw_name); -+ goto err_load_fw; -+ } -+ -+ rc = mwl_hif_download_firmware(priv->hw); -+ if (rc) { -+ wiphy_err(priv->hw->wiphy, -+ "cannot download firmware image <%s>\n", fw_name); -+ goto err_download_fw; -+ } -+ -+ if (cal_name) { -+ if ((request_firmware((const struct firmware **)&priv->cal_data, -+ cal_name, priv->dev)) < 0) -+ wiphy_debug(priv->hw->wiphy, -+ "cannot find calibtration data\n"); -+ } -+ -+ if (txpwrlmt_name) { -+ if ((request_firmware( -+ (const struct firmware **)&priv->txpwrlmt_file, -+ txpwrlmt_name, priv->dev)) < 0) -+ wiphy_debug(priv->hw->wiphy, -+ "cannot find tx power limit data\n"); -+ } -+ -+ return rc; -+ -+err_download_fw: -+ -+ release_firmware(priv->fw_ucode); -+ -+err_load_fw: -+ -+ wiphy_err(priv->hw->wiphy, "firmware init fail\n"); -+ -+ return rc; -+} -+ -+static void mwl_process_of_dts(struct mwl_priv *priv) -+{ -+#ifdef CONFIG_OF -+ struct property *prop; -+ u32 prop_value; -+ -+ priv->dt_node = -+ of_find_node_by_name(mwl_hif_device_node(priv->hw), -+ "mwlwifi"); -+ if (!priv->dt_node) -+ return; -+ -+ /* look for all matching property names */ -+ for_each_property_of_node(priv->dt_node, prop) { -+ if (strcmp(prop->name, "marvell,2ghz") == 0) -+ priv->disable_2g = true; -+ if (strcmp(prop->name, "marvell,5ghz") == 0) -+ priv->disable_5g = true; -+ if (strcmp(prop->name, "marvell,chainmask") == 0) { -+ prop_value = be32_to_cpu(*((__be32 *)prop->value)); -+ if (prop_value == 2) -+ priv->antenna_tx = ANTENNA_TX_2; -+ else if (prop_value == 3) -+ priv->antenna_tx = ANTENNA_TX_3; -+ -+ prop_value = be32_to_cpu(*((__be32 *) -+ (prop->value + 4))); -+ if (prop_value == 2) -+ priv->antenna_rx = ANTENNA_RX_2; -+ else if (prop_value == 3) -+ priv->antenna_rx = ANTENNA_RX_3; -+ } -+ } -+ -+ priv->pwr_node = of_find_node_by_name(priv->dt_node, -+ "marvell,powertable"); -+#endif -+} -+ -+static void mwl_reg_notifier(struct wiphy *wiphy, -+ struct regulatory_request *request) -+{ -+ struct ieee80211_hw *hw; -+ struct mwl_priv *priv; -+#ifdef CONFIG_OF -+ struct property *prop; -+ struct property *fcc_prop = NULL; -+ struct property *etsi_prop = NULL; -+ struct property *specific_prop = NULL; -+ u32 prop_value; -+ int i, j, k; -+#endif -+ -+ hw = wiphy_to_ieee80211_hw(wiphy); -+ priv = hw->priv; -+ -+ if (priv->forbidden_setting) { -+ if (!priv->regulatory_set) { -+ regulatory_hint(wiphy, priv->fw_alpha2); -+ priv->regulatory_set = true; -+ } else { -+ if (memcmp(priv->fw_alpha2, request->alpha2, 2)) -+ regulatory_hint(wiphy, priv->fw_alpha2); -+ } -+ return; -+ } -+ -+ priv->dfs_region = request->dfs_region; -+ -+#ifdef CONFIG_OF -+ if ((priv->chip_type != MWL8997) && (priv->pwr_node)) { -+ for_each_property_of_node(priv->pwr_node, prop) { -+ if (strcmp(prop->name, "FCC") == 0) -+ fcc_prop = prop; -+ if (strcmp(prop->name, "ETSI") == 0) -+ etsi_prop = prop; -+ if ((prop->name[0] == request->alpha2[0]) && -+ (prop->name[1] == request->alpha2[1])) -+ specific_prop = prop; -+ } -+ -+ prop = NULL; -+ -+ if (specific_prop) { -+ prop = specific_prop; -+ } else { -+ if (priv->dfs_region == NL80211_DFS_ETSI) -+ prop = etsi_prop; -+ else -+ prop = fcc_prop; -+ } -+ -+ if (prop) { -+ /* Reset the whole table */ -+ for (i = 0; i < SYSADPT_MAX_NUM_CHANNELS; i++) -+ memset(&priv->tx_pwr_tbl[i], 0, -+ sizeof(struct mwl_tx_pwr_tbl)); -+ -+ /* Load related power table */ -+ i = 0; -+ j = 0; -+ while (i < prop->length) { -+ prop_value = -+ be32_to_cpu(*(__be32 *) -+ (prop->value + i)); -+ priv->tx_pwr_tbl[j].channel = prop_value; -+ i += 4; -+ prop_value = -+ be32_to_cpu(*(__be32 *) -+ (prop->value + i)); -+ priv->tx_pwr_tbl[j].setcap = prop_value; -+ i += 4; -+ for (k = 0; k < SYSADPT_TX_POWER_LEVEL_TOTAL; -+ k++) { -+ prop_value = -+ be32_to_cpu(*(__be32 *) -+ (prop->value + i)); -+ priv->tx_pwr_tbl[j].tx_power[k] = -+ prop_value; -+ i += 4; -+ } -+ prop_value = -+ be32_to_cpu(*(__be32 *) -+ (prop->value + i)); -+ priv->tx_pwr_tbl[j].cdd = -+ (prop_value == 0) ? false : true; -+ i += 4; -+ prop_value = -+ be32_to_cpu(*(__be32 *) -+ (prop->value + i)); -+ priv->tx_pwr_tbl[j].txantenna2 = prop_value; -+ i += 4; -+ j++; -+ } -+ -+ /* Dump loaded power tabel */ -+ wiphy_debug(hw->wiphy, "regdomain: %s\n", prop->name); -+ for (i = 0; i < SYSADPT_MAX_NUM_CHANNELS; i++) { -+ struct mwl_tx_pwr_tbl *pwr_tbl; -+ char disp_buf[64]; -+ char *disp_ptr; -+ -+ pwr_tbl = &priv->tx_pwr_tbl[i]; -+ if (pwr_tbl->channel == 0) -+ break; -+ wiphy_debug(hw->wiphy, -+ "Channel: %d: 0x%x 0x%x 0x%x\n", -+ pwr_tbl->channel, -+ pwr_tbl->setcap, -+ pwr_tbl->cdd, -+ pwr_tbl->txantenna2); -+ disp_ptr = disp_buf; -+ for (j = 0; j < SYSADPT_TX_POWER_LEVEL_TOTAL; -+ j++) { -+ disp_ptr += -+ sprintf(disp_ptr, "%x ", -+ pwr_tbl->tx_power[j]); -+ } -+ wiphy_debug(hw->wiphy, "%s\n", disp_buf); -+ } -+ } -+ } -+#endif -+} -+ -+static void mwl_regd_init(struct mwl_priv *priv) -+{ -+ u8 region_code; -+ int rc; -+ int i; -+ -+ /* hook regulatory domain change notification */ -+ priv->hw->wiphy->reg_notifier = mwl_reg_notifier; -+ -+ if (priv->chip_type == MWL8964) -+ rc = mwl_fwcmd_get_pwr_tbl_sc4(priv->hw, -+ &priv->device_pwr_tbl[0], -+ ®ion_code, -+ &priv->number_of_channels, -+ 0); -+ else -+ rc = mwl_fwcmd_get_device_pwr_tbl(priv->hw, -+ &priv->device_pwr_tbl[0], -+ ®ion_code, -+ &priv->number_of_channels, -+ 0); -+ if (rc) -+ return; -+ -+ priv->forbidden_setting = true; -+ -+ for (i = 1; i < priv->number_of_channels; i++) { -+ if (priv->chip_type == MWL8964) -+ mwl_fwcmd_get_pwr_tbl_sc4(priv->hw, -+ &priv->device_pwr_tbl[i], -+ ®ion_code, -+ &priv->number_of_channels, -+ i); -+ else -+ mwl_fwcmd_get_device_pwr_tbl(priv->hw, -+ &priv->device_pwr_tbl[i], -+ ®ion_code, -+ &priv->number_of_channels, -+ i); -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(regmap); i++) -+ if (regmap[i].region_code == priv->fw_region_code) { -+ memcpy(priv->fw_alpha2, regmap[i].alpha2, 2); -+ break; -+ } -+} -+ -+static void mwl_set_ht_caps(struct mwl_priv *priv, -+ struct ieee80211_supported_band *band) -+{ -+ struct ieee80211_hw *hw; -+ const u8 ant_rx_no[ANTENNA_RX_MAX] = { 3, 1, 2, 3}; -+ int i; -+ -+ hw = priv->hw; -+ -+ band->ht_cap.ht_supported = 1; -+ if (priv->chip_type == MWL8964) -+ band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; -+ band->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; -+ band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; -+ band->ht_cap.cap |= IEEE80211_HT_CAP_SM_PS; -+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; -+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; -+ band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; -+ -+ if ((priv->chip_type == MWL8997) && -+ (priv->antenna_tx != ANTENNA_TX_1)) { -+ band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; -+ band->ht_cap.cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); -+ } -+ -+ ieee80211_hw_set(hw, AMPDU_AGGREGATION); -+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); -+ band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; -+ band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; -+ -+ for (i = 0; i < ant_rx_no[priv->antenna_rx]; i++) -+ band->ht_cap.mcs.rx_mask[i] = 0xff; -+ band->ht_cap.mcs.rx_mask[4] = 0x01; -+ -+ band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; -+} -+ -+static void mwl_set_vht_caps(struct mwl_priv *priv, -+ struct ieee80211_supported_band *band) -+{ -+ u32 antenna_num = 4; -+ -+ band->vht_cap.vht_supported = 1; -+ -+ if (priv->chip_type == MWL8964) { -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; -+ } else -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1; -+ if (priv->antenna_tx != ANTENNA_TX_1) { -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; -+ if (priv->chip_type == MWL8964) -+ band->vht_cap.cap |= -+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; -+ } -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; -+ if (priv->chip_type == MWL8964) -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; -+ if (priv->chip_type == MWL8997) { -+ if (priv->antenna_tx != ANTENNA_TX_1) -+ band->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC; -+ } -+ -+ if (priv->antenna_rx == ANTENNA_RX_1) -+ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xfffe); -+ else if (priv->antenna_rx == ANTENNA_RX_2) -+ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xfffa); -+ else -+ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xffea); -+ -+ if (priv->antenna_tx == ANTENNA_TX_1) { -+ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xfffe); -+ antenna_num = 1; -+ } else if (priv->antenna_tx == ANTENNA_TX_2) { -+ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xfffa); -+ antenna_num = 2; -+ } else -+ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xffea); -+ -+ if (band->vht_cap.cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | -+ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { -+ band->vht_cap.cap |= -+ ((antenna_num - 1) << -+ IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT) & -+ IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; -+ } -+ -+ if (band->vht_cap.cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | -+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { -+ band->vht_cap.cap |= -+ ((antenna_num - 1) << -+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT) & -+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; -+ } -+} -+ -+static void mwl_set_caps(struct mwl_priv *priv) -+{ -+ struct ieee80211_hw *hw; -+ -+ hw = priv->hw; -+ -+ /* set up band information for 2.4G */ -+ if (!priv->disable_2g) { -+ BUILD_BUG_ON(sizeof(priv->channels_24) != -+ sizeof(mwl_channels_24)); -+ memcpy(priv->channels_24, mwl_channels_24, -+ sizeof(mwl_channels_24)); -+ -+ BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl_rates_24)); -+ memcpy(priv->rates_24, mwl_rates_24, sizeof(mwl_rates_24)); -+ -+ priv->band_24.band = NL80211_BAND_2GHZ; -+ priv->band_24.channels = priv->channels_24; -+ priv->band_24.n_channels = ARRAY_SIZE(mwl_channels_24); -+ priv->band_24.bitrates = priv->rates_24; -+ priv->band_24.n_bitrates = ARRAY_SIZE(mwl_rates_24); -+ -+ mwl_set_ht_caps(priv, &priv->band_24); -+ mwl_set_vht_caps(priv, &priv->band_24); -+ -+ hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band_24; -+ } -+ -+ /* set up band information for 5G */ -+ if (!priv->disable_5g) { -+ BUILD_BUG_ON(sizeof(priv->channels_50) != -+ sizeof(mwl_channels_50)); -+ memcpy(priv->channels_50, mwl_channels_50, -+ sizeof(mwl_channels_50)); -+ -+ BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl_rates_50)); -+ memcpy(priv->rates_50, mwl_rates_50, sizeof(mwl_rates_50)); -+ -+ priv->band_50.band = NL80211_BAND_5GHZ; -+ priv->band_50.channels = priv->channels_50; -+ priv->band_50.n_channels = ARRAY_SIZE(mwl_channels_50); -+ priv->band_50.bitrates = priv->rates_50; -+ priv->band_50.n_bitrates = ARRAY_SIZE(mwl_rates_50); -+ -+ mwl_set_ht_caps(priv, &priv->band_50); -+ mwl_set_vht_caps(priv, &priv->band_50); -+ -+ hw->wiphy->bands[NL80211_BAND_5GHZ] = &priv->band_50; -+ } -+} -+ -+static void mwl_heartbeat_handle(struct work_struct *work) -+{ -+ struct mwl_priv *priv = -+ container_of(work, struct mwl_priv, heartbeat_handle); -+ u32 val; -+ -+ mwl_fwcmd_get_addr_value(priv->hw, 0, 1, &val, 0); -+ priv->heartbeating = false; -+} -+ -+static void mwl_watchdog_ba_events(struct work_struct *work) -+{ -+ int rc; -+ u8 bitmap = 0, stream_index; -+ struct mwl_ampdu_stream *streams; -+ struct mwl_priv *priv = -+ container_of(work, struct mwl_priv, watchdog_ba_handle); -+ -+ rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap); -+ -+ if (rc) -+ return; -+ -+ spin_lock_bh(&priv->stream_lock); -+ -+ /* the bitmap is the hw queue number. Map it to the ampdu queue. */ -+ if (bitmap != INVALID_WATCHDOG) { -+ if (bitmap == priv->ampdu_num) -+ stream_index = 0; -+ else if (bitmap > priv->ampdu_num) -+ stream_index = bitmap - priv->ampdu_num; -+ else -+ stream_index = bitmap + 3; /** queue 0 is stream 3*/ -+ -+ if (bitmap != 0xFF) { -+ /* Check if the stream is in use before disabling it */ -+ streams = &priv->ampdu[stream_index]; -+ -+ if (streams->state == AMPDU_STREAM_ACTIVE) -+ ieee80211_stop_tx_ba_session(streams->sta, -+ streams->tid); -+ } else { -+ for (stream_index = 0; -+ stream_index < priv->ampdu_num; -+ stream_index++) { -+ streams = &priv->ampdu[stream_index]; -+ -+ if (streams->state != AMPDU_STREAM_ACTIVE) -+ continue; -+ -+ ieee80211_stop_tx_ba_session(streams->sta, -+ streams->tid); -+ } -+ } -+ } -+ -+ spin_unlock_bh(&priv->stream_lock); -+} -+ -+static void mwl_account_handle(struct work_struct *work) -+{ -+ struct mwl_priv *priv = -+ container_of(work, struct mwl_priv, account_handle); -+ -+ mwl_hif_process_account(priv->hw); -+} -+ -+static void mwl_wds_check_handle(struct work_struct *work) -+{ -+ struct mwl_priv *priv = -+ container_of(work, struct mwl_priv, wds_check_handle); -+ struct mwl_sta *sta_info; -+ struct ieee80211_sta *sta; -+ bool wds_sta = false; -+ -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ if (sta_info->wds) -+ continue; -+ sta = container_of((void *)sta_info, struct ieee80211_sta, -+ drv_priv); -+ if (ether_addr_equal(sta->addr, priv->wds_check_sta)) { -+ wds_sta = true; -+ break; -+ } -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ if (wds_sta) { -+ mwl_fwcmd_set_new_stn_wds_sc4(priv->hw, sta->addr); -+ sta_info->wds = true; -+ } -+ -+ priv->wds_check = false; -+} -+ -+static void mwl_chnl_switch_event(struct work_struct *work) -+{ -+ struct mwl_priv *priv = -+ container_of(work, struct mwl_priv, chnl_switch_handle); -+ struct mwl_vif *mwl_vif; -+ struct ieee80211_vif *vif; -+ -+ if (!priv->csa_active) { -+ wiphy_err(priv->hw->wiphy, -+ "csa is not active (got channel switch event)\n"); -+ return; -+ } -+ -+ spin_lock_bh(&priv->vif_lock); -+ list_for_each_entry(mwl_vif, &priv->vif_list, list) { -+ vif = container_of((void *)mwl_vif, struct ieee80211_vif, -+ drv_priv); -+ -+ if (vif->csa_active) -+ ieee80211_csa_finish(vif); -+ } -+ spin_unlock_bh(&priv->vif_lock); -+ -+ wiphy_info(priv->hw->wiphy, "channel switch is done\n"); -+ -+ priv->csa_active = false; -+} -+ -+static irqreturn_t mwl_isr(int irq, void *dev_id) -+{ -+ struct ieee80211_hw *hw = dev_id; -+ -+ return mwl_hif_irq_handler(hw); -+} -+ -+#ifdef timer_setup -+static void timer_routine(struct timer_list *t) -+{ -+ struct mwl_priv *priv = from_timer(priv, t, period_timer); -+ struct ieee80211_hw *hw = priv->hw; -+#else -+static void timer_routine(unsigned long data) -+{ -+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; -+ struct mwl_priv *priv = hw->priv; -+#endif -+ if (priv->heartbeat) { -+ if ((jiffies - priv->pre_jiffies) >= -+ msecs_to_jiffies(priv->heartbeat * 1000)) { -+ if (!priv->heartbeating) { -+ priv->heartbeating = true; -+ ieee80211_queue_work(hw, -+ &priv->heartbeat_handle); -+ } -+ priv->pre_jiffies = jiffies; -+ } -+ } -+ -+ mwl_hif_timer_routine(hw); -+ -+ mod_timer(&priv->period_timer, jiffies + -+ msecs_to_jiffies(SYSADPT_TIMER_WAKEUP_TIME)); -+} -+ -+static int mwl_wl_init(struct mwl_priv *priv) -+{ -+ struct ieee80211_hw *hw = priv->hw; -+ int rc; -+ u16 addr_num; -+ struct mac_address *mac_addr; -+ u8 last_nibble; -+ -+ hw->extra_tx_headroom = mwl_hif_get_tx_head_room(hw); -+ hw->queues = SYSADPT_TX_WMM_QUEUES; -+ -+ /* Set rssi values to dBm */ -+ ieee80211_hw_set(hw, SIGNAL_DBM); -+ ieee80211_hw_set(hw, HAS_RATE_CONTROL); -+ -+ /* Ask mac80211 not to trigger PS mode -+ * based on PM bit of incoming frames. -+ */ -+ ieee80211_hw_set(hw, AP_LINK_PS); -+ -+ ieee80211_hw_set(hw, SUPPORTS_PER_STA_GTK); -+ ieee80211_hw_set(hw, MFP_CAPABLE); -+ -+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; -+ hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; -+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; -+ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; -+ -+ hw->vif_data_size = sizeof(struct mwl_vif); -+ hw->sta_data_size = sizeof(struct mwl_sta); -+ -+ priv->ap_macids_supported = 0x0000ffff; -+ priv->sta_macids_supported = 0x00010000; -+ priv->macids_used = 0; -+ INIT_LIST_HEAD(&priv->vif_list); -+ INIT_LIST_HEAD(&priv->sta_list); -+ -+ /* Set default radio state, preamble and wmm */ -+ priv->noise = -104; -+ priv->radio_on = false; -+ priv->radio_short_preamble = false; -+ priv->wmm_enabled = false; -+ priv->powinited = 0; -+ priv->wds_check = false; -+ if (priv->chip_type == MWL8997) -+ priv->pwr_level = SYSADPT_TX_GRP_PWR_LEVEL_TOTAL; -+ else -+ priv->pwr_level = SYSADPT_TX_POWER_LEVEL_TOTAL; -+ priv->dfs_test = false; -+ priv->csa_active = false; -+ priv->dfs_chirp_count_min = 5; -+ priv->dfs_chirp_time_interval = 1000; -+ priv->dfs_pw_filter = 0; -+ priv->dfs_min_num_radar = 5; -+ priv->dfs_min_pri_count = 4; -+ priv->bf_type = TXBF_MODE_AUTO; -+ -+ /* Handle watchdog ba events */ -+ INIT_WORK(&priv->heartbeat_handle, mwl_heartbeat_handle); -+ INIT_WORK(&priv->watchdog_ba_handle, mwl_watchdog_ba_events); -+ INIT_WORK(&priv->account_handle, mwl_account_handle); -+ INIT_WORK(&priv->wds_check_handle, mwl_wds_check_handle); -+ INIT_WORK(&priv->chnl_switch_handle, mwl_chnl_switch_event); -+ -+ mutex_init(&priv->fwcmd_mutex); -+ spin_lock_init(&priv->vif_lock); -+ spin_lock_init(&priv->sta_lock); -+ spin_lock_init(&priv->stream_lock); -+ spin_lock_init(&priv->stnid_lock); -+ -+ rc = mwl_thermal_register(priv); -+ if (rc) { -+ wiphy_err(hw->wiphy, "fail to register thermal framework\n"); -+ goto err_thermal_register; -+ } -+ -+ rc = mwl_hif_init(hw); -+ if (rc) { -+ wiphy_err(hw->wiphy, "fail to initialize host interface\n"); -+ goto err_hif_init; -+ } -+ -+ SET_IEEE80211_PERM_ADDR(hw, priv->hw_data.mac_addr); -+ -+ if (priv->chip_type == MWL8964) { -+ addr_num = SYSADPT_NUM_OF_AP + SYSADPT_NUM_OF_CLIENT; -+ hw->wiphy->n_addresses = addr_num; -+ hw->wiphy->addresses = -+ kzalloc(addr_num * sizeof(*mac_addr), GFP_KERNEL); -+ -+ mac_addr = &hw->wiphy->addresses[0]; -+ ether_addr_copy(mac_addr->addr, priv->hw_data.mac_addr); -+ last_nibble = mac_addr->addr[5] & 0x0F; -+ for (addr_num = 0; addr_num < SYSADPT_NUM_OF_AP; addr_num++) { -+ mac_addr = &hw->wiphy->addresses[addr_num + 1]; -+ ether_addr_copy(mac_addr->addr, priv->hw_data.mac_addr); -+ if (!strcmp(wiphy_name(hw->wiphy), "phy0")) { -+ last_nibble++; -+ if (last_nibble == 0x10) -+ last_nibble = 0; -+ } else { -+ last_nibble--; -+ if (last_nibble == 0xFF) -+ last_nibble = 0x0F; -+ } -+ mac_addr->addr[5] = -+ (mac_addr->addr[5] & 0xF0) | last_nibble; -+ mac_addr->addr[0] |= 0x2; -+ } -+ } -+ -+ wiphy_info(hw->wiphy, -+ "firmware version: 0x%x\n", priv->hw_data.fw_release_num); -+ -+ if (priv->chip_type == MWL8997) { -+ mwl_fwcmd_set_cfg_data(hw, 2); -+ mwl_fwcmd_set_txpwrlmt_cfg_data(hw); -+ mwl_fwcmd_get_txpwrlmt_cfg_data(hw); -+ } -+ -+ if (priv->chip_type == MWL8964) -+ rc = mwl_fwcmd_get_fw_region_code_sc4(hw, -+ &priv->fw_region_code); -+ else -+ rc = mwl_fwcmd_get_fw_region_code(hw, &priv->fw_region_code); -+ if (!rc) { -+ priv->fw_device_pwrtbl = true; -+ mwl_regd_init(priv); -+ wiphy_info(hw->wiphy, -+ "firmware region code: %x\n", priv->fw_region_code); -+ } -+ -+ if (priv->chip_type == MWL8997) -+ mwl_fwcmd_dump_otp_data(hw); -+ -+ mwl_fwcmd_radio_disable(hw); -+ mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_TX, priv->antenna_tx); -+ mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_RX, priv->antenna_rx); -+ -+ hw->wiphy->interface_modes = 0; -+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); -+#if defined(CPTCFG_MAC80211_MESH) || defined(CONFIG_MAC80211_MESH) -+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); -+#endif -+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); -+ hw->wiphy->iface_combinations = &ap_if_comb; -+ hw->wiphy->n_iface_combinations = 1; -+ -+ mwl_set_caps(priv); -+ -+ priv->led_blink_enable = 1; -+ priv->led_blink_rate = LED_BLINK_RATE_MID; -+ mwl_fwcmd_led_ctrl(hw, priv->led_blink_enable, priv->led_blink_rate); -+ -+ vendor_cmd_register(hw->wiphy); -+ -+ rc = ieee80211_register_hw(hw); -+ if (rc) { -+ wiphy_err(hw->wiphy, "fail to register device\n"); -+ goto err_register_hw; -+ } -+ -+ priv->irq = mwl_hif_get_irq_num(hw); -+ rc = request_irq(priv->irq, mwl_isr, IRQF_SHARED, -+ mwl_hif_get_driver_name(hw), hw); -+ if (rc) { -+ priv->irq = -1; -+ wiphy_err(hw->wiphy, "fail to register IRQ handler\n"); -+ goto err_register_irq; -+ } -+#ifdef timer_setup -+ timer_setup(&priv->period_timer, timer_routine, 0); -+#else -+ setup_timer(&priv->period_timer, timer_routine, (unsigned long)hw); -+#endif -+ mod_timer(&priv->period_timer, jiffies + -+ msecs_to_jiffies(SYSADPT_TIMER_WAKEUP_TIME)); -+ -+ return rc; -+ -+err_register_hw: -+err_register_irq: -+ mwl_hif_deinit(hw); -+ -+err_hif_init: -+err_thermal_register: -+ -+ wiphy_err(hw->wiphy, "init fail\n"); -+ -+ return rc; -+} -+ -+static void mwl_wl_deinit(struct mwl_priv *priv) -+{ -+ struct ieee80211_hw *hw = priv->hw; -+ -+ del_timer_sync(&priv->period_timer); -+ -+ if (priv->irq != -1) { -+ free_irq(priv->irq, hw); -+ priv->irq = -1; -+ } -+ -+ if (priv->chip_type == MWL8964) -+ kfree(hw->wiphy->addresses); -+ ieee80211_unregister_hw(hw); -+ mwl_thermal_unregister(priv); -+ cancel_work_sync(&priv->chnl_switch_handle); -+ cancel_work_sync(&priv->account_handle); -+ cancel_work_sync(&priv->wds_check_handle); -+ cancel_work_sync(&priv->watchdog_ba_handle); -+ cancel_work_sync(&priv->heartbeat_handle); -+ mwl_hif_deinit(hw); -+} -+ -+struct ieee80211_hw *mwl_alloc_hw(int bus_type, -+ int chip_type, -+ struct device *dev, -+ const struct mwl_hif_ops *ops, -+ size_t hif_data_len) -+{ -+ struct ieee80211_hw *hw; -+ struct mwl_priv *priv; -+ int priv_size; -+ -+ priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + hif_data_len; -+ -+ hw = ieee80211_alloc_hw(priv_size, &mwl_mac80211_ops); -+ if (!hw) { -+ pr_err("ieee80211 alloc hw failed\n"); -+ return NULL; -+ } -+ -+ priv = hw->priv; -+ priv->hw = hw; -+ priv->dev = dev; -+ priv->chip_type = chip_type; -+ priv->fw_device_pwrtbl = false; -+ priv->forbidden_setting = false; -+ priv->regulatory_set = false; -+ priv->use_short_slot = false; -+ priv->use_short_preamble = false; -+ priv->disable_2g = false; -+ priv->disable_5g = false; -+ priv->tx_amsdu = true; -+ priv->hif.bus = bus_type; -+ priv->hif.ops = ops; -+ priv->hif.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN); -+ priv->ampdu_num = mwl_hif_get_ampdu_num(hw); -+ priv->ampdu = -+ kzalloc(priv->ampdu_num * sizeof(*priv->ampdu), GFP_KERNEL); -+ if (!priv->ampdu) { -+ ieee80211_free_hw(hw); -+ pr_err("alloc ampdu stream failed\n"); -+ return NULL; -+ } -+ -+ if (chip_type == MWL8964) -+ priv->stnid_num = SYSADPT_MAX_STA_SC4; -+ else -+ priv->stnid_num = SYSADPT_MAX_STA; -+ priv->stnid = -+ kzalloc(priv->stnid_num * sizeof(struct mwl_stnid), GFP_KERNEL); -+ if (!priv->stnid) { -+ kfree(priv->ampdu); -+ ieee80211_free_hw(hw); -+ pr_err("alloc stnid failed\n"); -+ return NULL; -+ } -+ priv->available_stnid = 0; -+ -+ SET_IEEE80211_DEV(hw, dev); -+ -+ return hw; -+} -+ -+void mwl_free_hw(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ kfree(priv->stnid); -+ kfree(priv->ampdu); -+ ieee80211_free_hw(hw); -+} -+ -+int mwl_init_hw(struct ieee80211_hw *hw, const char *fw_name, -+ const char *cal_name, const char *txpwrlmt_name) -+{ -+ struct mwl_priv *priv = hw->priv; -+ int rc; -+ int tx_num = 4, rx_num = 4; -+ -+ -+ rc = mwl_prepare_cmd_buf(priv); -+ if (rc) { -+ wiphy_err(hw->wiphy, "fail to prepare command buffer\n"); -+ return -ENOMEM; -+ } -+ -+ rc = mwl_init_firmware(priv, fw_name, cal_name, txpwrlmt_name); -+ if (rc) { -+ wiphy_err(hw->wiphy, "fail to initialize firmware\n"); -+ return -EIO; -+ } -+ -+ /* firmware is loaded to H/W, it can be released now */ -+ release_firmware(priv->fw_ucode); -+ -+ mwl_process_of_dts(priv); -+ -+ rc = mwl_wl_init(priv); -+ if (rc) { -+ wiphy_err(hw->wiphy, "fail to initialize wireless lan\n"); -+ return -EIO; -+ } -+ -+ wiphy_info(priv->hw->wiphy, "2G %s, 5G %s\n", -+ priv->disable_2g ? "disabled" : "enabled", -+ priv->disable_5g ? "disabled" : "enabled"); -+ -+ if (priv->antenna_tx == ANTENNA_TX_2) -+ tx_num = 2; -+ else if (priv->antenna_tx == ANTENNA_TX_3) -+ tx_num = 3; -+ if (priv->antenna_rx == ANTENNA_RX_2) -+ rx_num = 2; -+ else if (priv->antenna_rx == ANTENNA_RX_3) -+ rx_num = 3; -+ wiphy_info(priv->hw->wiphy, "%d TX antennas, %d RX antennas\n", -+ tx_num, rx_num); -+ -+#ifdef CONFIG_DEBUG_FS -+ mwl_debugfs_init(hw); -+#endif -+ -+ return 0; -+} -+ -+void mwl_deinit_hw(struct ieee80211_hw *hw) -+{ -+#ifdef CONFIG_DEBUG_FS -+ mwl_debugfs_remove(hw); -+#endif -+ -+ mwl_wl_deinit(hw->priv); -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/core.h b/drivers/net/wireless/marvell/mwlwifi/core.h -new file mode 100644 -index 000000000000..00069c4f0b44 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/core.h -@@ -0,0 +1,517 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines core layer related functions. */ -+ -+#ifndef _CORE_H_ -+#define _CORE_H_ -+ -+#include -+#include -+#include -+#include -+ -+#include "hif/hif.h" -+ -+/* antenna control */ -+#define ANTENNA_TX_4_AUTO 0 -+#define ANTENNA_TX_1 1 -+#define ANTENNA_TX_2 3 -+#define ANTENNA_TX_3 7 -+#define ANTENNA_RX_4_AUTO 0 -+#define ANTENNA_RX_1 1 -+#define ANTENNA_RX_2 2 -+#define ANTENNA_RX_3 3 -+#define ANTENNA_RX_MAX 4 -+ -+/* band related constants */ -+#define BAND_24_CHANNEL_NUM 14 -+#define BAND_24_RATE_NUM 13 -+#define BAND_50_CHANNEL_NUM 24 -+#define BAND_50_RATE_NUM 8 -+ -+#define NUM_WEP_KEYS 4 -+#define MWL_MAX_TID 8 -+#define MWL_AMSDU_SIZE_4K 1 -+#define MWL_AMSDU_SIZE_8K 2 -+#define MWL_AMSDU_SIZE_11K 3 -+ -+/* power init */ -+#define MWL_POWER_INIT_1 1 -+#define MWL_POWER_INIT_2 2 -+ -+/* tx rate information constants */ -+#define TX_RATE_FORMAT_LEGACY 0 -+#define TX_RATE_FORMAT_11N 1 -+#define TX_RATE_FORMAT_11AC 2 -+ -+#define TX_RATE_BANDWIDTH_20 0 -+#define TX_RATE_BANDWIDTH_40 1 -+#define TX_RATE_BANDWIDTH_80 2 -+#define TX_RATE_BANDWIDTH_160 3 -+ -+#define TX_RATE_INFO_STD_GI 0 -+#define TX_RATE_INFO_SHORT_GI 1 -+ -+/* tx rate information */ -+/* 0: legacy format 1: 11n format 2: 11ac format */ -+#define MWL_TX_RATE_FORMAT_MASK 0x00000003 -+#define MWL_TX_RATE_STBC_MASK 0x00000004 -+#define MWL_TX_RATE_STBC_SHIFT 2 -+/* 0: 20 MHz 1: 40 MHz 2: 80 MHz 3: 160 MHz */ -+#define MWL_TX_RATE_BANDWIDTH_MASK 0x00000030 -+#define MWL_TX_RATE_BANDWIDTH_SHIFT 4 -+/* 0: normal 1: short */ -+#define MWL_TX_RATE_SHORTGI_MASK 0x00000040 -+#define MWL_TX_RATE_SHORTGI_SHIFT 6 -+#define MWL_TX_RATE_RATEIDMCS_MASK 0x00007F00 -+#define MWL_TX_RATE_RATEIDMCS_SHIFT 8 -+/* 0: long 1: short */ -+#define MWL_TX_RATE_PREAMBLE_MASK 0x00008000 -+#define MWL_TX_RATE_PREAMBLE_SHIFT 15 -+#define MWL_TX_RATE_POWERID_MASK 0x003F0000 -+#define MWL_TX_RATE_POWERID_SHIFT 16 -+#define MWL_TX_RATE_ADVCODING_MASK 0x00400000 -+#define MWL_TX_RATE_ADVCODING_SHIFT 22 -+/* 0: beam forming off 1: beam forming on */ -+#define MWL_TX_RATE_BF_MASK 0x00800000 -+#define MWL_TX_RATE_BF_SHIFT 23 -+#define MWL_TX_RATE_ANTSELECT_MASK 0xFF000000 -+#define MWL_TX_RATE_ANTSELECT_SHIFT 24 -+ -+#define ACNT_BA_SIZE 1000 -+ -+/* Q stats */ -+#define QS_MAX_DATA_RATES_G 14 -+#define QS_NUM_SUPPORTED_11N_BW 2 -+#define QS_NUM_SUPPORTED_GI 2 -+#define QS_NUM_SUPPORTED_MCS 24 -+#define QS_NUM_SUPPORTED_11AC_NSS 3 -+#define QS_NUM_SUPPORTED_11AC_BW 4 -+#define QS_NUM_SUPPORTED_11AC_MCS 10 -+#define TX_RATE_HISTO_CUSTOM_CNT 1 -+#define TX_RATE_HISTO_PER_CNT 5 -+#define MAX_DATA_RATES_G 14 -+#define MAX_SUPPORTED_MCS 24 -+#define MAX_SUPPORTED_11AC_RATES 20 -+/* MAX_DATA_RATES_G + MAX_SUPPORTED_MCS + MAX_SUPPORTED_11AC_RATES */ -+#define MAX_SUPPORTED_RATES 58 -+#define SU_MIMO 0 -+#define MU_MIMO 1 -+#define SU_MU_TYPE_CNT 2 /* traffic type, SU and MU */ -+ -+/* BF operation mode */ -+#define TXBF_MODE_OFF 0x05 -+#define TXBF_MODE_AUTO 0x06 -+#define TXBF_MODE_BFMER_AUTO 0x07 -+ -+static const u8 TX_HISTO_PER_THRES[TX_RATE_HISTO_PER_CNT - 1] = {6, 12, 20, 30}; -+ -+enum { -+ MWL8864 = 0, -+ MWL8897, -+ MWL8964, -+ MWL8997, -+ MWLUNKNOWN, -+}; -+ -+enum mwl_bus { -+ MWL_BUS_PCIE, -+ MWL_BUS_SDIO, -+}; -+ -+enum { -+ AP_MODE_11AC = 0x10, /* generic 11ac indication mode */ -+ AP_MODE_2_4GHZ_11AC_MIXED = 0x17, -+}; -+ -+enum { -+ AMPDU_NO_STREAM = 0, -+ AMPDU_STREAM_NEW, -+ AMPDU_STREAM_IN_PROGRESS, -+ AMPDU_STREAM_ACTIVE, -+}; -+ -+enum { -+ LED_BLINK_RATE_LOW = 0x1, -+ LED_BLINK_RATE_MID, -+ LED_BLINK_RATE_HIGH, -+}; -+ -+struct mwl_chip_info { -+ const char *part_name; -+ const char *fw_image; -+ const char *cal_file; -+ const char *txpwrlmt_file; -+ int antenna_tx; -+ int antenna_rx; -+}; -+ -+struct mwl_device_pwr_tbl { -+ u8 channel; -+ u8 tx_pwr[SYSADPT_TX_PWR_LEVEL_TOTAL_SC4]; -+ u8 dfs_capable; -+ u8 ax_ant; -+ u8 cdd; -+}; -+ -+struct mwl_tx_pwr_tbl { -+ u8 channel; -+ u8 setcap; -+ u16 txantenna2; -+ u16 tx_power[SYSADPT_TX_POWER_LEVEL_TOTAL]; -+ bool cdd; -+}; -+ -+struct mwl_hw_data { -+ u32 fw_release_num; /* MajNbr:MinNbr:SubMin:PatchLevel */ -+ u8 hw_version; /* plain number indicating version */ -+ unsigned char mac_addr[ETH_ALEN]; /* well known -> AA:BB:CC:DD:EE:FF */ -+}; -+ -+struct mwl_ampdu_stream { -+ struct ieee80211_sta *sta; -+ u8 tid; -+ u8 state; -+ int idx; -+}; -+ -+struct mwl_stnid { -+ int macid; /* keep macid for related stnid */ -+ u16 aid; /* keep aid for related stnid */ -+}; -+ -+struct otp_data { -+ u8 buf[SYSADPT_OTP_BUF_SIZE]; -+ u32 len; /* Actual size of data in buf[] */ -+}; -+ -+struct txpwrlmt_cfg_data { -+ u8 buf[SYSADPT_TXPWRLMT_CFG_BUF_SIZE]; -+ u32 len; /* Actual size of data in buf[] */ -+}; -+ -+struct mwl_priv { -+ struct ieee80211_hw *hw; -+ struct device *dev; -+ struct firmware *fw_ucode; -+ struct firmware *cal_data; -+ struct firmware *txpwrlmt_file; -+ struct otp_data otp_data; -+ struct txpwrlmt_cfg_data txpwrlmt_data; -+ bool fw_device_pwrtbl; -+ bool forbidden_setting; -+ bool regulatory_set; -+ u32 fw_region_code; -+ char fw_alpha2[2]; -+ u8 number_of_channels; -+ struct mwl_device_pwr_tbl device_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS]; -+ int chip_type; -+ -+ bool use_short_slot; -+ bool use_short_preamble; -+ -+ struct { -+ enum mwl_bus bus; -+ const struct mwl_hif_ops *ops; -+ void *priv; -+ } hif; -+ -+ struct device_node *dt_node; -+ struct device_node *pwr_node; -+ bool disable_2g; -+ bool disable_5g; -+ int antenna_tx; -+ int antenna_rx; -+ bool tx_amsdu; -+ bool dump_hostcmd; -+ bool dump_probe; -+ -+ struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS]; -+ bool cdd; -+ u16 txantenna2; -+ u8 powinited; -+ u8 pwr_level; -+ u16 max_tx_pow[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; /* max tx power (dBm) */ -+ u16 target_powers[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; /* target powers */ -+ -+ struct mutex fwcmd_mutex; /* for firmware command */ -+ unsigned short *pcmd_buf; /* pointer to CmdBuf (virtual) */ -+ dma_addr_t pphys_cmd_buf; /* pointer to CmdBuf (physical) */ -+ bool in_send_cmd; -+ bool cmd_timeout; -+ bool rmmod; -+ int heartbeat; -+ u32 pre_jiffies; -+ bool heartbeating; -+ struct work_struct heartbeat_handle; -+ -+ int irq; -+ struct mwl_hw_data hw_data; /* Adapter HW specific info */ -+ -+ struct timer_list period_timer; -+ -+ /* keep survey information */ -+ bool sw_scanning; -+ int survey_info_idx; -+ struct mwl_survey_info survey_info[SYSADPT_MAX_NUM_CHANNELS]; -+ struct mwl_survey_info cur_survey_info; -+ -+ s8 noise; /* Most recently reported noise in dBm */ -+ -+ struct ieee80211_supported_band band_24; -+ struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM]; -+ struct ieee80211_rate rates_24[BAND_24_RATE_NUM]; -+ struct ieee80211_supported_band band_50; -+ struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM]; -+ struct ieee80211_rate rates_50[BAND_50_RATE_NUM]; -+ -+ u32 ap_macids_supported; -+ u32 sta_macids_supported; -+ u32 macids_used; -+ u32 running_bsses; /* bitmap of running BSSes */ -+ -+ struct { -+ spinlock_t vif_lock; /* for private interface info */ -+ struct list_head vif_list; /* List of interfaces. */ -+ } ____cacheline_aligned_in_smp; -+ -+ struct { -+ spinlock_t sta_lock; /* for private sta info */ -+ struct list_head sta_list; /* List of stations */ -+ } ____cacheline_aligned_in_smp; -+ -+ /* ampdu stream information */ -+ /* for ampdu stream */ -+ int ampdu_num; -+ struct { -+ spinlock_t stream_lock; /* for BA stream */ -+ struct mwl_ampdu_stream *ampdu; -+ } ____cacheline_aligned_in_smp; -+ struct work_struct watchdog_ba_handle; -+ -+ /* station id */ -+ int stnid_num; -+ struct { -+ spinlock_t stnid_lock; /* for station id */ -+ struct mwl_stnid *stnid; -+ u16 available_stnid; -+ } ____cacheline_aligned_in_smp; -+ -+ bool radio_on; -+ bool radio_short_preamble; -+ bool wmm_enabled; -+ struct ieee80211_tx_queue_params wmm_params[SYSADPT_TX_WMM_QUEUES]; -+ -+ struct work_struct account_handle; -+ -+ bool wds_check; -+ struct work_struct wds_check_handle; -+ u8 wds_check_sta[ETH_ALEN]; -+ -+ bool dfs_test; -+ bool csa_active; -+ struct work_struct chnl_switch_handle; -+ enum nl80211_dfs_regions dfs_region; -+ u16 dfs_chirp_count_min; -+ u16 dfs_chirp_time_interval; -+ u16 dfs_pw_filter; -+ u16 dfs_min_num_radar; -+ u16 dfs_min_pri_count; -+ -+ u8 bf_type; -+ -+ struct thermal_cooling_device *cdev; -+ u32 throttle_state; -+ u32 quiet_period; -+ int temperature; -+ -+ u8 led_blink_enable; -+ u8 led_blink_rate; -+ -+ struct dentry *debugfs_phy; -+ u32 reg_type; -+ u32 reg_offset; -+ u32 reg_value; -+ int ra_aid; -+ int ba_aid; -+ int fixed_rate; -+ bool coredump_text; -+ u32 ra_tx_attempt[2][6]; -+}; -+ -+struct beacon_info { -+ bool valid; -+ u16 cap_info; -+ u8 power_constraint; -+ u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G]; -+ u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G]; -+ u8 ie_list_ht[148]; -+ u8 ie_list_vht[24]; -+ u8 *ie_wmm_ptr; -+ u8 *ie_wsc_ptr; -+ u8 *ie_rsn_ptr; -+ u8 *ie_rsn48_ptr; -+ u8 *ie_mde_ptr; -+ u8 *ie_ht_ptr; -+ u8 *ie_vht_ptr; -+ u8 *ie_country_ptr; -+ u8 *ie_meshid_ptr; -+ u8 *ie_meshcfg_ptr; -+ u8 *ie_meshchsw_ptr; -+ u8 ie_wmm_len; -+ u8 ie_wsc_len; -+ u8 ie_rsn_len; -+ u8 ie_rsn48_len; -+ u8 ie_mde_len; -+ u8 ie_ht_len; -+ u8 ie_vht_len; -+ u8 ie_country_len; -+ u8 ie_meshid_len; -+ u8 ie_meshcfg_len; -+ u8 ie_meshchsw_len; -+}; -+ -+struct mwl_vif { -+ struct list_head list; -+ enum nl80211_iftype type; -+ int macid; /* Firmware macid for this vif. */ -+ u16 seqno; /* Non AMPDU sequence number assigned by driver. */ -+ struct { /* Saved WEP keys */ -+ u8 enabled; -+ u8 key[sizeof(struct ieee80211_key_conf) + WLAN_KEY_LEN_WEP104]; -+ } wep_key_conf[NUM_WEP_KEYS]; -+ u8 bssid[ETH_ALEN]; /* BSSID */ -+ u8 sta_mac[ETH_ALEN]; /* Station mac address */ -+ /* A flag to indicate is HW crypto is enabled for this bssid */ -+ bool is_hw_crypto_enabled; -+ /* Indicate if this is station mode */ -+ struct beacon_info beacon_info; -+ bool set_beacon; -+ int basic_rate_idx; -+ u8 broadcast_ssid; -+ u16 iv16; -+ u32 iv32; -+ s8 keyidx; -+}; -+ -+struct mwl_tx_info { -+ unsigned long start_time; -+ u32 pkts; -+}; -+ -+struct mwl_amsdu_frag { -+ struct sk_buff *skb; -+ u8 *cur_pos; -+ unsigned long jiffies; -+ u8 pad; -+ u8 num; -+}; -+ -+struct mwl_amsdu_ctrl { -+ struct mwl_amsdu_frag frag[SYSADPT_TX_WMM_QUEUES]; -+ u8 cap; -+}; -+ -+struct mwl_tx_ba_stats { -+ u8 ba_hole; /* Total pkt not acked in a BA bitmap */ -+ u8 ba_expected; /* Total Tx pkt expected to be acked */ -+ u8 no_ba; /* No BA is received */ -+ u8 pad; /* Unused */ -+}; -+ -+struct mwl_tx_ba_hist { -+ u16 index; /* Current buffer index */ -+ u8 type; /* 0:SU, 1: MU */ -+ bool enable; -+ struct mwl_tx_ba_stats *ba_stats; -+}; -+ -+struct mwl_tx_hist_data { -+ u32 rateinfo; -+ u32 cnt; -+ /* store according to TX_HISTO_PER_THRES threshold */ -+ u32 per[TX_RATE_HISTO_PER_CNT]; -+}; -+ -+struct mwl_tx_hist { -+ struct mwl_tx_hist_data su_rate[MAX_SUPPORTED_RATES]; -+ struct mwl_tx_hist_data mu_rate -+ [QS_NUM_SUPPORTED_11AC_NSS - 1][QS_NUM_SUPPORTED_11AC_BW] -+ [QS_NUM_SUPPORTED_GI][QS_NUM_SUPPORTED_11AC_MCS]; -+ struct mwl_tx_hist_data custom_rate[TX_RATE_HISTO_CUSTOM_CNT]; -+ /* Current rate for 0:SU, 1:MU */ -+ u32 cur_rate_info[SU_MU_TYPE_CNT]; -+ /* Total tx attempt cnt for 0:SU, 1:MU */ -+ u32 total_tx_cnt[SU_MU_TYPE_CNT]; -+}; -+ -+struct mwl_sta { -+ struct list_head list; -+ struct mwl_vif *mwl_vif; -+ u16 stnid; -+ u16 sta_stnid; -+ bool wds; -+ bool is_mesh_node; -+ bool is_ampdu_allowed; -+ struct mwl_tx_info tx_stats[MWL_MAX_TID]; -+ u32 check_ba_failed[MWL_MAX_TID]; -+ struct mwl_tx_ba_hist ba_hist; -+ bool is_amsdu_allowed; -+ bool is_key_set; -+ /* for amsdu aggregation */ -+ struct { -+ spinlock_t amsdu_lock; /* for amsdu */ -+ struct mwl_amsdu_ctrl amsdu_ctrl; -+ } ____cacheline_aligned_in_smp; -+ struct mwl_tx_hist tx_hist; -+ u32 tx_rate_info; -+ u16 rx_format; -+ u16 rx_nss; -+ u16 rx_bw; -+ u16 rx_gi; -+ u16 rx_rate_mcs; -+ u8 rx_signal; -+ u16 iv16; -+ u32 iv32; -+}; -+ -+static inline struct mwl_vif *mwl_dev_get_vif(const struct ieee80211_vif *vif) -+{ -+ return (struct mwl_vif *)&vif->drv_priv; -+} -+ -+static inline struct mwl_sta *mwl_dev_get_sta(const struct ieee80211_sta *sta) -+{ -+ return (struct mwl_sta *)&sta->drv_priv; -+} -+ -+struct ieee80211_hw *mwl_alloc_hw(int bus_type, -+ int chip_type, -+ struct device *dev, -+ const struct mwl_hif_ops *ops, -+ size_t hif_data_len); -+ -+void mwl_free_hw(struct ieee80211_hw *hw); -+ -+int mwl_init_hw(struct ieee80211_hw *hw, const char *fw_name, -+ const char *cal_name, const char *txpwrlmt_name); -+ -+void mwl_deinit_hw(struct ieee80211_hw *hw); -+ -+/* Defined in mac80211.c. */ -+extern const struct ieee80211_ops mwl_mac80211_ops; -+ -+#endif /* _CORE_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.c b/drivers/net/wireless/marvell/mwlwifi/debugfs.c -new file mode 100644 -index 000000000000..e375806a7111 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.c -@@ -0,0 +1,2201 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements debug fs related functions. */ -+ -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "thermal.h" -+#include "hif/fwcmd.h" -+#include "hif/hif-ops.h" -+#include "debugfs.h" -+ -+#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \ -+ if (!debugfs_create_file(#name, 0644, priv->debugfs_phy, \ -+ priv, &mwl_debugfs_##name##_fops)) \ -+ return; \ -+} while (0) -+ -+#define MWLWIFI_DEBUGFS_FILE_OPS(name) \ -+static const struct file_operations mwl_debugfs_##name##_fops = { \ -+ .read = mwl_debugfs_##name##_read, \ -+ .write = mwl_debugfs_##name##_write, \ -+ .open = simple_open, \ -+} -+ -+#define MWLWIFI_DEBUGFS_FILE_READ_OPS(name) \ -+static const struct file_operations mwl_debugfs_##name##_fops = { \ -+ .read = mwl_debugfs_##name##_read, \ -+ .open = simple_open, \ -+} -+ -+#define MWLWIFI_DEBUGFS_FILE_WRITE_OPS(name) \ -+static const struct file_operations mwl_debugfs_##name##_fops = { \ -+ .write = mwl_debugfs_##name##_write, \ -+ .open = simple_open, \ -+} -+ -+static const char chipname[MWLUNKNOWN][8] = { -+ "88W8864", -+ "88W8897", -+ "88W8964", -+ "88W8997" -+}; -+ -+static void dump_data(char *p, int size, int *len, u8 *data, -+ int data_len, char *title) -+{ -+ int cur_byte = 0; -+ int i; -+ -+ *len += scnprintf(p + *len, size - *len, "%s\n", title); -+ -+ for (cur_byte = 0; cur_byte < data_len; cur_byte += 8) { -+ if ((cur_byte + 8) < data_len) { -+ for (i = 0; i < 8; i++) -+ *len += scnprintf(p + *len, size - *len, -+ "0x%02x ", -+ *(data + cur_byte + i)); -+ *len += scnprintf(p + *len, size - *len, "\n"); -+ } else { -+ for (i = 0; i < (data_len - cur_byte); i++) -+ *len += scnprintf(p + *len, size - *len, -+ "0x%02x ", -+ *(data + cur_byte + i)); -+ *len += scnprintf(p + *len, size - *len, "\n"); -+ break; -+ } -+ } -+} -+ -+static void _dump_tx_hist_mu(char *p, int size, int *len, bool *printed, -+ u32 *total, u8 nss, u8 bw, u8 mcs, u8 sgi, -+ struct mwl_sta *sta_info) -+{ -+ char *bw_str[4] = {"ht20", "ht40", "ht80", "ht160"}; -+ char *sgi_str[2] = {"lgi", "sgi"}; -+ struct mwl_tx_hist_data *tx_hist_data; -+ u32 cnt, rateinfo, per0, per1, per2, per3, per4, ratemask; -+ -+ tx_hist_data = &sta_info->tx_hist.mu_rate[nss][bw][sgi][mcs]; -+ cnt = le32_to_cpu(tx_hist_data->cnt); -+ rateinfo = le32_to_cpu(tx_hist_data->rateinfo); -+ if (cnt && (rateinfo > 0)) { -+ *total += cnt; -+ per4 = le32_to_cpu(tx_hist_data->per[4]); -+ per3 = le32_to_cpu(tx_hist_data->per[3]); -+ per2 = le32_to_cpu(tx_hist_data->per[2]); -+ per1 = le32_to_cpu(tx_hist_data->per[1]); -+ per0 = le32_to_cpu(tx_hist_data->per[0]); -+ if (!*printed) { -+ *len += scnprintf(p + *len, size - *len, -+ "%s %26s <%2d %8s%2d%8s%2d%8s%2d%8s%2d\n", -+ "MU_MIMO rate", " PER%", TX_HISTO_PER_THRES[0], -+ ">=", TX_HISTO_PER_THRES[0], -+ ">=", TX_HISTO_PER_THRES[1], -+ ">=", TX_HISTO_PER_THRES[2], -+ ">=", TX_HISTO_PER_THRES[3]); -+ *len += scnprintf(p + *len, size - *len, -+ "TOTAL MPDU tx pkt: %d\n", -+ sta_info->tx_hist.total_tx_cnt[MU_MIMO]); -+ *printed = true; -+ } -+ if ((rateinfo & 0x3) == 0) -+ ratemask = 0xfff; -+ else -+ ratemask = 0xffff; -+ if ((sta_info->tx_hist.cur_rate_info[MU_MIMO] & ratemask) == -+ (rateinfo & ratemask)) -+ /* mark as current rate */ -+ *len += scnprintf(p + *len, size - *len, "*"); -+ else -+ *len += scnprintf(p + *len, size - *len, " "); -+ *len += scnprintf(p + *len, size - *len, -+ "%5s_%3s_%1dSS_MCS%2d: %10u, %9d, %9d, %9d, %9d, %9d\n", -+ bw_str[bw], sgi_str[sgi], (nss + 1), mcs, cnt, per0, -+ per1, per2, per3, per4); -+ } -+} -+ -+static void dump_tx_hist_mu(char *p, int size, int *len, bool *printed, -+ u32 *total, struct mwl_sta *sta_info) -+{ -+ u8 nss, bw, mcs, sgi; -+ -+ for (nss = 0; nss < (QS_NUM_SUPPORTED_11AC_NSS - 1); nss++) { -+ for (bw = 0; bw < QS_NUM_SUPPORTED_11AC_BW; bw++) { -+ for (mcs = 0; mcs < QS_NUM_SUPPORTED_11AC_MCS; mcs++) { -+ for (sgi = 0; sgi < QS_NUM_SUPPORTED_GI; -+ sgi++) { -+ _dump_tx_hist_mu(p, size, len, printed, -+ total, nss, bw, mcs, -+ sgi, sta_info); -+ } -+ } -+ } -+ } -+} -+ -+ -+static void dump_tx_hist_su(char *p, int size, int *len, bool su, bool *printed, -+ u32 *total, struct mwl_sta *sta_info) -+{ -+ int g_rate[14] = {1, 2, 5, 11, 22, 6, 9, 12, 18, 24, 36, 48, 54, 72}; -+ char *bw_str[4] = {"ht20", "ht40", "ht80", "ht160"}; -+ char *sgi_str[2] = {"lgi", "sgi"}; -+ char title_str[32]; -+ struct mwl_tx_hist *tx_hist; -+ struct mwl_tx_hist_data *tx_hist_data; -+ u32 j, loopcnt; -+ u32 cnt, rateinfo, per0, per1, per2, per3, per4, ratemask; -+ u8 format, bw, sgi, mcs, nss; -+ -+ tx_hist = &sta_info->tx_hist; -+ if (su) { -+ loopcnt = MAX_SUPPORTED_RATES; -+ tx_hist_data = &tx_hist->su_rate[0]; -+ } else { -+ loopcnt = TX_RATE_HISTO_CUSTOM_CNT; -+ tx_hist_data = &tx_hist->custom_rate[0]; -+ } -+ -+ for (j = 0; j < loopcnt; j++) { -+ cnt = le32_to_cpu(tx_hist_data[j].cnt); -+ rateinfo = le32_to_cpu(tx_hist_data[j].rateinfo); -+ if (cnt && (rateinfo > 0)) { -+ *total += cnt; -+ per4 = le32_to_cpu(tx_hist_data[j].per[4]); -+ per3 = le32_to_cpu(tx_hist_data[j].per[3]); -+ per2 = le32_to_cpu(tx_hist_data[j].per[2]); -+ per1 = le32_to_cpu(tx_hist_data[j].per[1]); -+ per0 = le32_to_cpu(tx_hist_data[j].per[0]); -+ if (!*printed) { -+ *len += scnprintf(p + *len, size - *len, -+ "%s %26s <%2d %8s%2d%8s%2d%8s%2d%8s%2d\n", -+ su ? "SU_MIMO rate" : " Custom rate", -+ " PER%", TX_HISTO_PER_THRES[0], -+ ">=", TX_HISTO_PER_THRES[0], -+ ">=", TX_HISTO_PER_THRES[1], -+ ">=", TX_HISTO_PER_THRES[2], -+ ">=", TX_HISTO_PER_THRES[3]); -+ *len += scnprintf(p + *len, size - *len, -+ "TOTAL MPDU tx pkt: %d\n", -+ tx_hist->total_tx_cnt[SU_MIMO]); -+ *printed = true; -+ } -+ format = rateinfo & MWL_TX_RATE_FORMAT_MASK; -+ bw = (rateinfo & MWL_TX_RATE_BANDWIDTH_MASK) >> -+ MWL_TX_RATE_BANDWIDTH_SHIFT; -+ sgi = (rateinfo & MWL_TX_RATE_SHORTGI_MASK) >> -+ MWL_TX_RATE_SHORTGI_SHIFT; -+ mcs = (rateinfo & MWL_TX_RATE_RATEIDMCS_MASK) >> -+ MWL_TX_RATE_RATEIDMCS_SHIFT; -+ if (format == TX_RATE_FORMAT_LEGACY) -+ ratemask = 0xfff; -+ else -+ ratemask = 0xffff; -+ if ((tx_hist->cur_rate_info[SU_MIMO] & ratemask) == -+ (rateinfo & ratemask)) -+ /* mark as current rate */ -+ *len += scnprintf(p + *len, size - *len, "*"); -+ else -+ *len += scnprintf(p + *len, size - *len, " "); -+ if (format == TX_RATE_FORMAT_LEGACY) { -+ if (mcs == 2) { -+ *len += scnprintf(p + *len, size - *len, -+ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", -+ "5.5Mbps :", cnt, per0, -+ per1, per2, per3, per4); -+ } else { -+ sprintf(title_str, -+ "%-3dMbps :", -+ g_rate[mcs]); -+ *len += scnprintf(p + *len, size - *len, -+ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", -+ title_str, cnt, per0, per1, per2, per3, -+ per4); -+ } -+ } else if (format == TX_RATE_FORMAT_11N) { -+ sprintf(title_str, "%4s_%3s_MCS%2d :", -+ bw_str[bw], sgi_str[sgi], mcs); -+ *len += scnprintf(p + *len, size - *len, -+ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", -+ title_str, cnt, per0, per1, per2, per3, -+ per4); -+ } else { -+ nss = (mcs >> 4); -+ sprintf(title_str, "%5s_%3s_%1dSS_MCS%2d :", -+ bw_str[bw], sgi_str[sgi], (nss+1), -+ (mcs & 0xf)); -+ *len += scnprintf(p + *len, size - *len, -+ "%s %10u, %9d, %9d, %9d, %9d, %9d\n", -+ title_str, cnt, per0, per1, per2, per3, -+ per4); -+ } -+ } -+ } -+} -+ -+static void dump_tx_hist(char *p, int size, int *len, struct mwl_sta *sta_info) -+{ -+ int type; -+ bool printed, su; -+ u32 total; -+ -+ for (type = 0; type <= SU_MU_TYPE_CNT; type++) { -+ printed = false; -+ total = 0; -+ if (type == MU_MIMO) { -+ dump_tx_hist_mu(p, size, len, &printed, -+ &total, sta_info); -+ } else { -+ su = (type == SU_MIMO) ? true : false; -+ dump_tx_hist_su(p, size, len, su, &printed, -+ &total, sta_info); -+ } -+ if (printed) -+ *len += scnprintf(p + *len, size - *len, -+ " TOTAL : %10u\n\n", -+ total); -+ } -+} -+ -+static void core_dump_file(u8 *valbuf, u32 length, u32 region, u32 address, -+ u32 append, u32 totallen, bool textmode) -+{ -+ struct file *filp_core = NULL; -+ char file_name[40]; -+ u8 *buf = kmalloc(length * 3, GFP_KERNEL); -+ u8 *data_p = buf; -+ u32 i, j = 0; -+ -+ if (!buf) -+ return; -+ -+ memset(file_name, 0, sizeof(file_name)); -+ sprintf(file_name, "/dev/shm/coredump-%x-%x", -+ region, (region + totallen)); -+ -+ if (append) -+ filp_core = filp_open(file_name, O_RDWR | O_APPEND, 0); -+ else -+ filp_core = filp_open(file_name, O_RDWR | O_CREAT | O_TRUNC, 0); -+ -+ if (!IS_ERR(filp_core)) { -+ if (textmode) { -+ for (i = 0; i < length; i += 4) { -+ u32 val = 0; -+ -+ val = le32_to_cpu(*(__le32 *)(&valbuf[i])); -+ -+ if (i % 16 == 0) { -+ sprintf(buf + j, "\n0x%08x", -+ (int)(address + i)); -+ j = strlen(buf); -+ } -+ sprintf(buf + j, " %08x", val); -+ j = strlen(buf); -+ } -+ data_p = buf + j; -+ data_p += sprintf(data_p, "\n"); -+ __kernel_write(filp_core, buf, strlen(buf), -+ &filp_core->f_pos); -+ } else -+ __kernel_write(filp_core, valbuf, length, -+ &filp_core->f_pos); -+ -+ filp_close(filp_core, current->files); -+ } -+ -+ kfree(buf); -+} -+ -+static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ int tx_num = 4, rx_num = 4; -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, -+ "driver name: %s\n", -+ mwl_hif_get_driver_name(priv->hw)); -+ len += scnprintf(p + len, size - len, "chip type: %s\n", -+ chipname[priv->chip_type]); -+ len += scnprintf(p + len, size - len, -+ "hw version: %X\n", priv->hw_data.hw_version); -+ len += scnprintf(p + len, size - len, -+ "driver version: %s\n", -+ mwl_hif_get_driver_version(priv->hw)); -+ len += scnprintf(p + len, size - len, "firmware version: 0x%08x\n", -+ priv->hw_data.fw_release_num); -+ len += scnprintf(p + len, size - len, -+ "power table loaded from dts: %s\n", -+ priv->forbidden_setting ? "no" : "yes"); -+ len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n", -+ priv->fw_region_code); -+ len += scnprintf(p + len, size - len, -+ "mac address: %pM\n", priv->hw_data.mac_addr); -+ len += scnprintf(p + len, size - len, -+ "2g: %s\n", priv->disable_2g ? "disable" : "enable"); -+ len += scnprintf(p + len, size - len, -+ "5g: %s\n", priv->disable_5g ? "disable" : "enable"); -+ if (priv->antenna_tx == ANTENNA_TX_2) -+ tx_num = 2; -+ else if (priv->antenna_tx == ANTENNA_TX_3) -+ tx_num = 3; -+ if (priv->antenna_rx == ANTENNA_RX_2) -+ rx_num = 2; -+ else if (priv->antenna_rx == ANTENNA_RX_3) -+ rx_num = 3; -+ len += scnprintf(p + len, size - len, "antenna: %d %d\n", -+ tx_num, rx_num); -+ len += scnprintf(p + len, size - len, "irq number: %d\n", priv->irq); -+ len += scnprintf(p + len, size - len, "ap macid support: %08x\n", -+ priv->ap_macids_supported); -+ len += scnprintf(p + len, size - len, "sta macid support: %08x\n", -+ priv->sta_macids_supported); -+ len += scnprintf(p + len, size - len, -+ "macid used: %08x\n", priv->macids_used); -+ len += scnprintf(p + len, size - len, -+ "radio: %s\n", priv->radio_on ? "enable" : "disable"); -+ len += mwl_hif_get_info(priv->hw, p + len, size - len); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_tx_status_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += mwl_hif_get_tx_status(priv->hw, p + len, size - len); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_rx_status_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += mwl_hif_get_rx_status(priv->hw, p + len, size - len); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct mwl_vif *mwl_vif; -+ struct ieee80211_vif *vif; -+ char ssid[IEEE80211_MAX_SSID_LEN + 1]; -+ struct cfg80211_chan_def *chan_def; -+ struct beacon_info *beacon_info; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ spin_lock_bh(&priv->vif_lock); -+ list_for_each_entry(mwl_vif, &priv->vif_list, list) { -+ vif = container_of((void *)mwl_vif, struct ieee80211_vif, -+ drv_priv); -+ len += scnprintf(p + len, size - len, -+ "macid: %d\n", mwl_vif->macid); -+ switch (vif->type) { -+ case NL80211_IFTYPE_AP: -+ len += scnprintf(p + len, size - len, "type: ap\n"); -+ memcpy(ssid, vif->bss_conf.ssid, -+ vif->bss_conf.ssid_len); -+ ssid[vif->bss_conf.ssid_len] = 0; -+ len += scnprintf(p + len, size - len, -+ "ssid: %s\n", ssid); -+ len += scnprintf(p + len, size - len, -+ "mac address: %pM\n", mwl_vif->bssid); -+ break; -+ case NL80211_IFTYPE_MESH_POINT: -+ len += scnprintf(p + len, size - len, "type: mesh\n"); -+ len += scnprintf(p + len, size - len, -+ "mac address: %pM\n", mwl_vif->bssid); -+ break; -+ case NL80211_IFTYPE_STATION: -+ len += scnprintf(p + len, size - len, "type: sta\n"); -+ len += scnprintf(p + len, size - len, -+ "mac address: %pM\n", -+ mwl_vif->sta_mac); -+ break; -+ default: -+ len += scnprintf(p + len, size - len, -+ "type: unknown\n"); -+ break; -+ } -+ if (vif->chanctx_conf) { -+ chan_def = &vif->chanctx_conf->def; -+ len += scnprintf(p + len, size - len, -+ "channel: %d: width: %d\n", -+ chan_def->chan->hw_value, -+ chan_def->width); -+ len += scnprintf(p + len, size - len, -+ "freq: %d freq1: %d freq2: %d\n", -+ chan_def->chan->center_freq, -+ chan_def->center_freq1, -+ chan_def->center_freq2); -+ } -+ len += scnprintf(p + len, size - len, "hw_crypto_enabled: %s\n", -+ mwl_vif->is_hw_crypto_enabled ? -+ "true" : "false"); -+ len += scnprintf(p + len, size - len, -+ "key idx: %d\n", mwl_vif->keyidx); -+ len += scnprintf(p + len, size - len, -+ "IV: %08x%04x\n", mwl_vif->iv32, -+ mwl_vif->iv16); -+ beacon_info = &mwl_vif->beacon_info; -+ dump_data(p, size, &len, beacon_info->ie_wmm_ptr, -+ beacon_info->ie_wmm_len, "WMM:"); -+ dump_data(p, size, &len, beacon_info->ie_rsn_ptr, -+ beacon_info->ie_rsn_len, "RSN:"); -+ dump_data(p, size, &len, beacon_info->ie_rsn48_ptr, -+ beacon_info->ie_rsn48_len, "RSN48:"); -+ dump_data(p, size, &len, beacon_info->ie_mde_ptr, -+ beacon_info->ie_mde_len, "MDE:"); -+ dump_data(p, size, &len, beacon_info->ie_ht_ptr, -+ beacon_info->ie_ht_len, "HT:"); -+ dump_data(p, size, &len, beacon_info->ie_vht_ptr, -+ beacon_info->ie_vht_len, "VHT:"); -+ if (vif->type == NL80211_IFTYPE_MESH_POINT) { -+ dump_data(p, size, &len, beacon_info->ie_meshid_ptr, -+ beacon_info->ie_meshid_len, "MESHID:"); -+ dump_data(p, size, &len, beacon_info->ie_meshcfg_ptr, -+ beacon_info->ie_meshcfg_len, "MESHCFG:"); -+ dump_data(p, size, &len, beacon_info->ie_meshchsw_ptr, -+ beacon_info->ie_meshchsw_len, "MESHCHSW:"); -+ } -+ len += scnprintf(p + len, size - len, "\n"); -+ } -+ spin_unlock_bh(&priv->vif_lock); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_sta_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct mwl_sta *sta_info; -+ struct ieee80211_sta *sta; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ sta = container_of((void *)sta_info, struct ieee80211_sta, -+ drv_priv); -+ len += scnprintf(p + len, size - len, -+ "mac address: %pM\n", sta->addr); -+ len += scnprintf(p + len, size - len, "aid: %u\n", sta->aid); -+ len += scnprintf(p + len, size - len, "ampdu: %s\n", -+ sta_info->is_ampdu_allowed ? "true" : "false"); -+ len += scnprintf(p + len, size - len, "amsdu: %s\n", -+ sta_info->is_amsdu_allowed ? "true" : "false"); -+ len += scnprintf(p + len, size - len, "wds: %s\n", -+ sta_info->wds ? "true" : "false"); -+ len += scnprintf(p + len, size - len, "ba_hist: %s\n", -+ sta_info->ba_hist.enable ? -+ "enable" : "disable"); -+ if (sta_info->is_amsdu_allowed) { -+ len += scnprintf(p + len, size - len, -+ "amsdu cap: 0x%02x\n", -+ sta_info->amsdu_ctrl.cap); -+ } -+ if (sta->ht_cap.ht_supported) { -+ len += scnprintf(p + len, size - len, -+ "ht_cap: 0x%04x, ampdu: %02x, %02x\n", -+ sta->ht_cap.cap, -+ sta->ht_cap.ampdu_factor, -+ sta->ht_cap.ampdu_density); -+ len += scnprintf(p + len, size - len, -+ "rx_mask: 0x%02x, %02x, %02x, %02x\n", -+ sta->ht_cap.mcs.rx_mask[0], -+ sta->ht_cap.mcs.rx_mask[1], -+ sta->ht_cap.mcs.rx_mask[2], -+ sta->ht_cap.mcs.rx_mask[3]); -+ } -+ if (sta->vht_cap.vht_supported) { -+ len += scnprintf(p + len, size - len, -+ "vht_cap: 0x%08x, mcs: %02x, %02x\n", -+ sta->vht_cap.cap, -+ sta->vht_cap.vht_mcs.rx_mcs_map, -+ sta->vht_cap.vht_mcs.tx_mcs_map); -+ } -+ len += scnprintf(p + len, size - len, "rx_bw: %d, rx_nss: %d\n", -+ sta->bandwidth, sta->rx_nss); -+ len += scnprintf(p + len, size - len, -+ "tdls: %d, tdls_init: %d\n", -+ sta->tdls, sta->tdls_initiator); -+ len += scnprintf(p + len, size - len, "wme: %d, mfp: %d\n", -+ sta->wme, sta->mfp); -+ len += scnprintf(p + len, size - len, "IV: %08x%04x\n", -+ sta_info->iv32, sta_info->iv16); -+ len += scnprintf(p + len, size - len, "\n"); -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct mwl_ampdu_stream *stream; -+ int i; -+ struct mwl_sta *sta_info; -+ struct ieee80211_sta *sta; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ spin_lock_bh(&priv->stream_lock); -+ for (i = 0; i < priv->ampdu_num; i++) { -+ stream = &priv->ampdu[i]; -+ if (!stream->state) -+ continue; -+ len += scnprintf(p + len, size - len, "stream: %d\n", i); -+ len += scnprintf(p + len, size - len, "idx: %u\n", stream->idx); -+ len += scnprintf(p + len, size - len, -+ "state: %u\n", stream->state); -+ if (stream->sta) { -+ len += scnprintf(p + len, size - len, -+ "mac address: %pM\n", -+ stream->sta->addr); -+ len += scnprintf(p + len, size - len, -+ "tid: %u\n", stream->tid); -+ } -+ } -+ spin_unlock_bh(&priv->stream_lock); -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ for (i = 0; i < MWL_MAX_TID; i++) { -+ if (sta_info->check_ba_failed[i]) { -+ sta = container_of((void *)sta_info, -+ struct ieee80211_sta, -+ drv_priv); -+ len += scnprintf(p + len, size - len, -+ "%pM(%d): %d\n", -+ sta->addr, i, -+ sta_info->check_ba_failed[i]); -+ } -+ } -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_stnid_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct mwl_stnid *stnid; -+ int i; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ spin_lock_bh(&priv->stnid_lock); -+ for (i = 0; i < priv->stnid_num; i++) { -+ stnid = &priv->stnid[i]; -+ if (!stnid->aid) -+ continue; -+ len += scnprintf(p + len, size - len, -+ "stnid: %d macid: %d aid: %d\n", -+ i + 1, stnid->macid, stnid->aid); -+ } -+ spin_unlock_bh(&priv->stnid_lock); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_device_pwrtbl_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ int i, j; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, -+ "power table loaded from dts: %s\n", -+ priv->forbidden_setting ? "no" : "yes"); -+ len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n", -+ priv->fw_region_code); -+ len += scnprintf(p + len, size - len, "number of channel: %d\n", -+ priv->number_of_channels); -+ for (i = 0; i < priv->number_of_channels; i++) { -+ len += scnprintf(p + len, size - len, "%3d ", -+ priv->device_pwr_tbl[i].channel); -+ for (j = 0; j < SYSADPT_TX_POWER_LEVEL_TOTAL; j++) -+ len += scnprintf(p + len, size - len, "%3d ", -+ priv->device_pwr_tbl[i].tx_pwr[j]); -+ len += scnprintf(p + len, size - len, "%3d ", -+ priv->device_pwr_tbl[i].dfs_capable); -+ len += scnprintf(p + len, size - len, "%3d ", -+ priv->device_pwr_tbl[i].ax_ant); -+ len += scnprintf(p + len, size - len, "%3d\n", -+ priv->device_pwr_tbl[i].cdd); -+ } -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_txpwrlmt_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ -+ return simple_read_from_buffer(ubuf, count, ppos, -+ priv->txpwrlmt_data.buf, -+ priv->txpwrlmt_data.len); -+} -+ -+static ssize_t mwl_debugfs_tx_amsdu_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "tx amsdu: %s\n", -+ priv->tx_amsdu ? "enable" : "disable"); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_tx_amsdu_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int value; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &value)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ priv->tx_amsdu = value ? true : false; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dump_hostcmd_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "dump_hostcmd: %s\n", -+ priv->dump_hostcmd ? "enable" : "disable"); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dump_hostcmd_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int value; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &value)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ priv->dump_hostcmd = value ? true : false; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dump_probe_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "dump_probe: %s\n", -+ priv->dump_probe ? "enable" : "disable"); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dump_probe_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int value; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &value)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ priv->dump_probe = value ? true : false; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_heartbeat_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "heartbeat: %d\n", -+ priv->heartbeat); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_heartbeat_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &priv->heartbeat)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ priv->pre_jiffies = jiffies; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dfs_test_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "dfs_test: %s\n", -+ priv->dfs_test ? "enable" : "disable"); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dfs_test_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int value; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &value)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ priv->dfs_test = value ? true : false; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dfs_channel_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct ieee80211_supported_band *sband; -+ struct ieee80211_channel *channel; -+ int i; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ sband = priv->hw->wiphy->bands[NL80211_BAND_5GHZ]; -+ if (!sband) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ for (i = 0; i < sband->n_channels; i++) { -+ channel = &sband->channels[i]; -+ if (channel->flags & IEEE80211_CHAN_RADAR) { -+ len += scnprintf(p + len, size - len, -+ "%d(%d): flags: %08x dfs_state: %d\n", -+ channel->hw_value, -+ channel->center_freq, -+ channel->flags, channel->dfs_state); -+ len += scnprintf(p + len, size - len, -+ "cac timer: %d ms\n", -+ channel->dfs_cac_ms); -+ } -+ } -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ -+err: -+ free_page(page); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dfs_channel_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ struct ieee80211_supported_band *sband; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int dfs_state = 0; -+ int cac_time = -1; -+ struct ieee80211_channel *channel; -+ int i; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ sband = priv->hw->wiphy->bands[NL80211_BAND_5GHZ]; -+ if (!sband) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ ret = sscanf(buf, "%d %d", &dfs_state, &cac_time); -+ -+ if ((ret < 1) || (ret > 2)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ for (i = 0; i < sband->n_channels; i++) { -+ channel = &sband->channels[i]; -+ if (channel->flags & IEEE80211_CHAN_RADAR) { -+ channel->dfs_state = dfs_state; -+ if (cac_time != -1) -+ channel->dfs_cac_ms = cac_time * 1000; -+ } -+ } -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dfs_radar_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, -+ "csa_active: %d\n", priv->csa_active); -+ len += scnprintf(p + len, size - len, -+ "dfs_region: %d\n", priv->dfs_region); -+ len += scnprintf(p + len, size - len, -+ "chirp_count_min: %d\n", priv->dfs_chirp_count_min); -+ len += scnprintf(p + len, size - len, "chirp_time_interval: %d\n", -+ priv->dfs_chirp_time_interval); -+ len += scnprintf(p + len, size - len, -+ "pw_filter: %d\n", priv->dfs_pw_filter); -+ len += scnprintf(p + len, size - len, -+ "min_num_radar: %d\n", priv->dfs_min_num_radar); -+ len += scnprintf(p + len, size - len, -+ "min_pri_count: %d\n", priv->dfs_min_pri_count); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_dfs_radar_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ -+ wiphy_info(priv->hw->wiphy, "simulate radar detected\n"); -+ ieee80211_radar_detected(priv->hw); -+ -+ return count; -+} -+ -+static ssize_t mwl_debugfs_thermal_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ mwl_fwcmd_get_temp(priv->hw, &priv->temperature); -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "quiet period: %d\n", -+ priv->quiet_period); -+ len += scnprintf(p + len, size - len, "throttle state: %d\n", -+ priv->throttle_state); -+ len += scnprintf(p + len, size - len, "temperature: %d\n", -+ priv->temperature); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_thermal_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int throttle_state; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &throttle_state)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) { -+ wiphy_warn(priv->hw->wiphy, -+ "throttle state %d is exceeding the limit %d\n", -+ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX); -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ priv->throttle_state = throttle_state; -+ mwl_thermal_set_throttling(priv); -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_led_ctrl_read(struct file *file, -+ char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "led blink %s\n", -+ priv->led_blink_enable ? "enable" : "disable"); -+ len += scnprintf(p + len, size - len, "led blink rate: %d\n", -+ priv->led_blink_rate); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_led_ctrl_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int enable, rate; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ ret = sscanf(buf, "%x %x", &enable, &rate); -+ -+ if ((ret != 1) && (ret != 2)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if (enable && (ret != 2)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ ret = mwl_fwcmd_led_ctrl(priv->hw, enable, rate); -+ -+ if (ret) -+ goto err; -+ -+ priv->led_blink_enable = enable; -+ if (enable) -+ priv->led_blink_rate = rate; -+ else -+ priv->led_blink_rate = 0; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_regrdwr_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (*ppos) -+ return len; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ if (!priv->reg_type) { -+ /* No command has been given */ -+ len += scnprintf(p + len, size - len, "0"); -+ ret = -EINVAL; -+ goto none; -+ } -+ -+ /* Set command has been given */ -+ if (priv->reg_value != UINT_MAX) { -+ ret = mwl_hif_reg_access(priv->hw, true); -+ goto done; -+ } -+ /* Get command has been given */ -+ ret = mwl_hif_reg_access(priv->hw, false); -+ -+done: -+ if (!ret) -+ len += scnprintf(p + len, size - len, "%u 0x%08x 0x%08x\n", -+ priv->reg_type, priv->reg_offset, -+ priv->reg_value); -+ else -+ len += scnprintf(p + len, size - len, -+ "error: %d(%u 0x%08x 0x%08x)\n", -+ ret, priv->reg_type, priv->reg_offset, -+ priv->reg_value); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ -+none: -+ free_page(page); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_regrdwr_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ ssize_t ret; -+ u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ ret = sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); -+ -+ if (!reg_type) { -+ ret = -EINVAL; -+ goto err; -+ } else { -+ priv->reg_type = reg_type; -+ priv->reg_offset = reg_offset; -+ priv->reg_value = reg_value; -+ ret = count; -+ } -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_ratetable_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct mwl_sta *sta_info; -+ struct ieee80211_sta *sta; -+ u8 addr[ETH_ALEN]; -+ int table_size = (sizeof(__le32) * 2 * SYSADPT_MAX_RATE_ADAPT_RATES); -+ u8 *rate_table, *rate_idx; -+ u32 rate_info; -+ u8 fmt, stbc, bw, sgi, mcs, preamble_gf, power_id, ldpc, bf, ant; -+ int idx, rate, nss; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ if (!priv->ra_aid) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ sta = container_of((void *)sta_info, struct ieee80211_sta, -+ drv_priv); -+ if (priv->ra_aid == sta->aid) { -+ ether_addr_copy(addr, sta->addr); -+ break; -+ } -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ rate_table = kzalloc(size, GFP_KERNEL); -+ if (!rate_table) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ ret = mwl_fwcmd_get_ratetable(priv->hw, addr, rate_table, -+ table_size, 0); -+ if (ret) { -+ kfree(rate_table); -+ goto err; -+ } -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, -+ "%3s %6s %5s %5s %5s %5s %5s %4s %2s %5s %4s %5s %5s\n", -+ "Num", "Fmt", "STBC", "BW", "SGI", "Nss", "RateId", -+ "GF/Pre", "PId", "LDPC", "BF", "TxAnt", "Rate"); -+ idx = 0; -+ rate_idx = rate_table; -+ rate_info = le32_to_cpu(*(__le32 *)rate_idx); -+ while (rate_info) { -+ fmt = rate_info & MWL_TX_RATE_FORMAT_MASK; -+ stbc = (rate_info & MWL_TX_RATE_STBC_MASK) >> -+ MWL_TX_RATE_STBC_SHIFT; -+ bw = (rate_info & MWL_TX_RATE_BANDWIDTH_MASK) >> -+ MWL_TX_RATE_BANDWIDTH_SHIFT; -+ sgi = (rate_info & MWL_TX_RATE_SHORTGI_MASK) >> -+ MWL_TX_RATE_SHORTGI_SHIFT; -+ mcs = (rate_info & MWL_TX_RATE_RATEIDMCS_MASK) >> -+ MWL_TX_RATE_RATEIDMCS_SHIFT; -+ preamble_gf = (rate_info & MWL_TX_RATE_PREAMBLE_MASK) >> -+ MWL_TX_RATE_PREAMBLE_SHIFT; -+ power_id = (rate_info & MWL_TX_RATE_POWERID_MASK) >> -+ MWL_TX_RATE_POWERID_SHIFT; -+ ldpc = (rate_info & MWL_TX_RATE_ADVCODING_MASK) >> -+ MWL_TX_RATE_ADVCODING_SHIFT; -+ bf = (rate_info & MWL_TX_RATE_BF_MASK) >> -+ MWL_TX_RATE_BF_SHIFT; -+ ant = (rate_info & MWL_TX_RATE_ANTSELECT_MASK) >> -+ MWL_TX_RATE_ANTSELECT_SHIFT; -+ -+ if (fmt == TX_RATE_FORMAT_11AC) { -+ rate = mcs & 0xf; /* 11ac, mcs[3:0]: rate */ -+ nss = mcs >> 4; /* 11ac, mcs[6:4] = nss code */ -+ nss++; /* ddd 1 to correct Nss representation */ -+ } else { -+ rate = mcs; -+ nss = 0; -+ if (fmt == TX_RATE_FORMAT_11N) { -+ if ((mcs >= 0) && (mcs < 8)) -+ nss = 1; -+ else if ((mcs >= 8) && (mcs < 16)) -+ nss = 2; -+ else if ((mcs >= 16) && (mcs < 24)) -+ nss = 3; -+ } -+ } -+ -+ len += scnprintf(p + len, size - len, -+ "%3d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", -+ idx, (int)fmt, (int)stbc, (int)bw, (int)sgi, nss, rate, -+ (int)preamble_gf, (int)power_id, (int)ldpc, (int)bf, -+ (int)ant, -+ utils_get_phy_rate(fmt, bw, sgi, mcs)); -+ -+ idx++; -+ rate_idx += (2 * sizeof(__le32)); -+ rate_info = le32_to_cpu(*(__le32 *)rate_idx); -+ } -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ kfree(rate_table); -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ -+err: -+ free_page(page); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_ratetable_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int sta_aid; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &sta_aid)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if ((sta_aid <= 0) || (sta_aid > SYSADPT_MAX_STA_SC4)) { -+ wiphy_warn(priv->hw->wiphy, -+ "station aid is exceeding the limit %d\n", sta_aid); -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ priv->ra_aid = sta_aid; -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_tx_hist_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct ieee80211_sta *sta; -+ struct mwl_sta *sta_info; -+ ssize_t ret; -+ -+ if (priv->chip_type != MWL8964) -+ return -EPERM; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, -+ "SU: <4:%d >=4:%d >=15:%d >=50:%d >=100:%d >=250:%d\n", -+ priv->ra_tx_attempt[SU_MIMO][0], -+ priv->ra_tx_attempt[SU_MIMO][1], -+ priv->ra_tx_attempt[SU_MIMO][2], -+ priv->ra_tx_attempt[SU_MIMO][3], -+ priv->ra_tx_attempt[SU_MIMO][4], -+ priv->ra_tx_attempt[SU_MIMO][5]); -+ len += scnprintf(p + len, size - len, -+ "MU: <4:%d >=4:%d >=15:%d >=50:%d >=100:%d >=250:%d\n", -+ priv->ra_tx_attempt[MU_MIMO][0], -+ priv->ra_tx_attempt[MU_MIMO][1], -+ priv->ra_tx_attempt[MU_MIMO][2], -+ priv->ra_tx_attempt[MU_MIMO][3], -+ priv->ra_tx_attempt[MU_MIMO][4], -+ priv->ra_tx_attempt[MU_MIMO][5]); -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ sta = container_of((void *)sta_info, struct ieee80211_sta, -+ drv_priv); -+ len += scnprintf(p + len, size - len, "\nSTA %pM\n", sta->addr); -+ len += scnprintf(p + len, size - len, -+ "============================\n"); -+ dump_tx_hist(p, size, &len, sta_info); -+ len += scnprintf(p + len, size - len, -+ "============================\n"); -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_tx_hist_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int reset; -+ struct mwl_sta *sta_info; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &reset)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if (!reset) { -+ memset(&priv->ra_tx_attempt, 0, 2 * 6 * sizeof(u32)); -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ memset(&sta_info->tx_hist, 0, -+ sizeof(sta_info->tx_hist)); -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ } -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_ba_hist_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct mwl_sta *sta_info; -+ struct mwl_tx_ba_stats *ba_stats; -+ u32 i, data; -+ u32 baholecnt, baexpcnt, bmap0cnt, nobacnt; -+ u8 bmap0flag, nobaflag; -+ char buff[500], file_location[20]; -+ struct file *filp_bahisto; -+ u8 *data_p = buff; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ if (!priv->ba_aid) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ memset(buff, 0, sizeof(buff)); -+ memset(file_location, 0, sizeof(file_location)); -+ sprintf(file_location, "/tmp/ba_histo-%d", priv->ba_aid); -+ -+ filp_bahisto = filp_open(file_location, -+ O_RDWR | O_CREAT | O_TRUNC, 0); -+ -+ if (IS_ERR(filp_bahisto)) { -+ ret = -EIO; -+ goto err; -+ } -+ -+ sta_info = utils_find_sta_by_aid(priv, priv->ba_aid); -+ if (sta_info && sta_info->ba_hist.enable && -+ sta_info->ba_hist.ba_stats) { -+ ba_stats = sta_info->ba_hist.ba_stats; -+ len += scnprintf(p + len, size - len, -+ "BA histogram aid: %d, stnid: %d type: %s\n", -+ priv->ba_aid, sta_info->stnid, -+ sta_info->ba_hist.type ? "MU" : "SU"); -+ data_p += sprintf(data_p, -+ "BA histogram aid: %d, stnid: %d type: %s\n", -+ priv->ba_aid, sta_info->stnid, -+ sta_info->ba_hist.type ? "MU" : "SU"); -+ data_p += sprintf(data_p, "%8s,%8s,%8s,%8s\n", -+ "BAhole", "Expect", "Bmap0", "NoBA"); -+ data = *(u32 *)&ba_stats[0]; -+ baholecnt = 0; -+ baexpcnt = 0; -+ bmap0cnt = 0; -+ nobacnt = 0; -+ for (i = 0; i < ACNT_BA_SIZE && data; i++) { -+ data = *(u32 *)&ba_stats[i]; -+ if (data == 0) -+ break; -+ -+ /* If no BA event does not happen, check BA hole and BA -+ * expected to mark BA bitmap all 0 event -+ */ -+ if (!ba_stats[i].no_ba) -+ bmap0flag = (ba_stats[i].ba_hole == -+ ba_stats[i].ba_expected) ? 1 : 0; -+ else -+ bmap0flag = 0; -+ nobaflag = ba_stats[i].no_ba; -+ -+ /* Buffer is full. Write to file and reset buf */ -+ if ((strlen(buff) + 36) >= 500) { -+ __kernel_write(filp_bahisto, buff, strlen(buff), -+ &filp_bahisto->f_pos); -+ mdelay(2); -+ memset(buff, 0, sizeof(buff)); -+ data_p = buff; -+ } -+ -+ data_p += sprintf(data_p, "%8d,%8d,", -+ ba_stats[i].ba_hole, -+ ba_stats[i].ba_expected); -+ -+ baholecnt += ba_stats[i].ba_hole; -+ baexpcnt += ba_stats[i].ba_expected; -+ if (bmap0flag) { -+ data_p += sprintf(data_p, " #,"); -+ bmap0cnt++; -+ } else -+ data_p += sprintf(data_p, "%8d,", bmap0flag); -+ if (nobaflag) { -+ data_p += sprintf(data_p, " *\n"); -+ nobacnt++; -+ } else -+ data_p += sprintf(data_p, "%8d\n", nobaflag); -+ } -+ -+ __kernel_write(filp_bahisto, buff, strlen(buff), -+ &filp_bahisto->f_pos); -+ len += scnprintf(p + len, size - len, -+ "hole: %d, expect: %d, bmap0: %d, noba: %d\n", -+ baholecnt, baexpcnt, bmap0cnt, nobacnt); -+ len += scnprintf(p + len, size - len, -+ "BA histogram data written to %s\n", -+ file_location); -+ } else -+ len += scnprintf(p + len, size - len, -+ "No BA histogram for sta aid: %d\n", -+ priv->ba_aid); -+ -+ filp_close(filp_bahisto, current->files); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ -+err: -+ free_page(page); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_ba_hist_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int sta_aid; -+ struct mwl_sta *sta_info; -+ int size; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &sta_aid)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if ((sta_aid <= 0) || (sta_aid > SYSADPT_MAX_STA_SC4)) { -+ wiphy_warn(priv->hw->wiphy, -+ "station aid is exceeding the limit %d\n", sta_aid); -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if (priv->ba_aid) { -+ sta_info = utils_find_sta_by_aid(priv, priv->ba_aid); -+ if (sta_info) { -+ sta_info->ba_hist.enable = false; -+ kfree(sta_info->ba_hist.ba_stats); -+ } -+ } -+ priv->ba_aid = 0; -+ sta_info = utils_find_sta_by_aid(priv, sta_aid); -+ if (sta_info) { -+ sta_info->ba_hist.enable = true; -+ sta_info->ba_hist.index = 0; -+ size = sizeof(struct mwl_tx_ba_stats) * ACNT_BA_SIZE; -+ sta_info->ba_hist.ba_stats = kmalloc(size, GFP_KERNEL); -+ if (sta_info->ba_hist.ba_stats) { -+ memset(sta_info->ba_hist.ba_stats, 0, size); -+ priv->ba_aid = sta_aid; -+ } -+ ret = count; -+ } else -+ ret = -EINVAL; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_fixed_rate_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ ssize_t ret; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "fixed rate: 0x%08x\n", -+ priv->fixed_rate); -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ free_page(page); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_fixed_rate_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ ssize_t ret; -+ int fixed_rate = 0, fwcmd_ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ ret = sscanf(buf, "%08x", &fixed_rate); -+ if (!ret) { -+ ret = -EIO; -+ goto err; -+ } -+ -+ priv->fixed_rate = fixed_rate; -+ -+ if (fixed_rate != 0) -+ fwcmd_ret = mwl_fwcmd_set_rate_drop(priv->hw, 3, -+ priv->fixed_rate, 0); -+ else -+ fwcmd_ret = mwl_fwcmd_set_rate_drop(priv->hw, 1, -+ priv->fixed_rate, 0); -+ if (fwcmd_ret) -+ ret = -EIO; -+ else -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_core_dump_read(struct file *file, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long page = get_zeroed_page(GFP_KERNEL); -+ char *p = (char *)page; -+ int len = 0, size = PAGE_SIZE; -+ struct coredump_cmd *core_dump = NULL; -+ struct coredump *cd = NULL; -+ char *buff = NULL; -+ u32 i, offset; -+ u32 address, length; -+ ssize_t ret; -+ -+ if (priv->chip_type != MWL8964) -+ return -EPERM; -+ -+ if (*ppos) -+ return len; -+ -+ if (!p) -+ return -ENOMEM; -+ -+ core_dump = kmalloc(sizeof(*core_dump), GFP_ATOMIC); -+ if (!core_dump) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ buff = kmalloc(MAX_CORE_DUMP_BUFFER, GFP_ATOMIC); -+ if (!buff) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ memset((char *)buff, 0, MAX_CORE_DUMP_BUFFER); -+ -+ cd = kmalloc(sizeof(*cd), GFP_ATOMIC); -+ if (!cd) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ core_dump->context = 0; -+ core_dump->flags = 0; -+ core_dump->size_kb = 0; -+ if (mwl_fwcmd_get_fw_core_dump(priv->hw, core_dump, buff)) { -+ ret = -EIO; -+ goto err; -+ } -+ memcpy(cd, buff, sizeof(*cd)); -+ -+ len += scnprintf(p + len, size - len, "\n"); -+ len += scnprintf(p + len, size - len, "Major Version : %d\n", -+ cd->version_major); -+ len += scnprintf(p + len, size - len, "Minor Version : %d\n", -+ cd->version_minor); -+ len += scnprintf(p + len, size - len, "Patch Version : %d\n", -+ cd->version_patch); -+ len += scnprintf(p + len, size - len, "Num of Regions: %d\n", -+ cd->num_regions); -+ len += scnprintf(p + len, size - len, "Num of Symbols: %d\n", -+ cd->num_symbols); -+ -+ for (i = 0; i < cd->num_regions; i++) { -+ address = le32_to_cpu(cd->region[i].address); -+ length = le32_to_cpu(cd->region[i].length); -+ len += scnprintf(p + len, size - len, -+ "\ncd.region[%d]: address=%x, length=%x\n", -+ i, address, length); -+ -+ for (offset = 0; offset < length; -+ offset += MAX_CORE_DUMP_BUFFER) { -+ core_dump->context = cpu_to_le32((i << 28) | offset); -+ core_dump->flags = 0; -+ core_dump->size_kb = 0; -+ if (mwl_fwcmd_get_fw_core_dump(priv->hw, -+ core_dump, buff)) { -+ wiphy_info(priv->hw->wiphy, -+ "region:%d offset:%x\n", i, offset); -+ break; -+ } -+ core_dump_file(buff, MAX_CORE_DUMP_BUFFER, -+ address, address + offset, -+ offset, length, priv->coredump_text); -+ } -+ } -+ len += scnprintf(p + len, size - len, "\n"); -+ -+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len); -+ -+err: -+ kfree(core_dump); -+ kfree(buff); -+ kfree(cd); -+ free_page(page); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_core_dump_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int text_mode; -+ ssize_t ret; -+ -+ if (priv->chip_type != MWL8964) -+ return -EPERM; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &text_mode)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ if ((text_mode < 0) || (text_mode > 1)) { -+ wiphy_warn(priv->hw->wiphy, -+ "text mode should be 0 (false) or 1 (true): %d\n", -+ text_mode); -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ mwl_fwcmd_core_dump_diag_mode(priv->hw, 1); -+ priv->coredump_text = text_mode ? true : false; -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_mcast_cts_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ int cts_enable = 0; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ if (kstrtoint(buf, 0, &cts_enable)) { -+ ret = -EINVAL; -+ goto err; -+ } -+ -+ ret = mwl_hif_mcast_cts(priv->hw, cts_enable ? true : false); -+ if (ret) -+ goto err; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+static ssize_t mwl_debugfs_wmmedcaap_write(struct file *file, -+ const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data; -+ unsigned long addr = get_zeroed_page(GFP_KERNEL); -+ char *buf = (char *)addr; -+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); -+ u32 index = 0, cw_min = 0, cw_max = 0, aifsn = 0, txop = 0; -+ ssize_t ret; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, ubuf, buf_size)) { -+ ret = -EFAULT; -+ goto err; -+ } -+ -+ ret = sscanf(buf, "%u %x %x %u %x", &index, &cw_min, -+ &cw_max, &aifsn, &txop); -+ if (ret != 5) { -+ ret = -EINVAL; -+ goto err; -+ } -+ wiphy_info(priv->hw->wiphy, "set TCQ%d wmm edca with:\n", index); -+ wiphy_info(priv->hw->wiphy, -+ "cw_min=0x%x, cw_max=0x%x, aifs_num=%d, txop=0x%x\n", -+ cw_min, cw_max, aifsn, txop); -+ -+ ret = mwl_fwcmd_set_edca_params(priv->hw, index, -+ cw_min, cw_max, aifsn, txop); -+ if (ret) -+ goto err; -+ -+ ret = count; -+ -+err: -+ free_page(addr); -+ return ret; -+} -+ -+MWLWIFI_DEBUGFS_FILE_READ_OPS(info); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(tx_status); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(rx_status); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(vif); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(sta); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(stnid); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(device_pwrtbl); -+MWLWIFI_DEBUGFS_FILE_READ_OPS(txpwrlmt); -+MWLWIFI_DEBUGFS_FILE_OPS(tx_amsdu); -+MWLWIFI_DEBUGFS_FILE_OPS(dump_hostcmd); -+MWLWIFI_DEBUGFS_FILE_OPS(dump_probe); -+MWLWIFI_DEBUGFS_FILE_OPS(heartbeat); -+MWLWIFI_DEBUGFS_FILE_OPS(dfs_test); -+MWLWIFI_DEBUGFS_FILE_OPS(dfs_channel); -+MWLWIFI_DEBUGFS_FILE_OPS(dfs_radar); -+MWLWIFI_DEBUGFS_FILE_OPS(thermal); -+MWLWIFI_DEBUGFS_FILE_OPS(led_ctrl); -+MWLWIFI_DEBUGFS_FILE_OPS(regrdwr); -+MWLWIFI_DEBUGFS_FILE_OPS(ratetable); -+MWLWIFI_DEBUGFS_FILE_OPS(tx_hist); -+MWLWIFI_DEBUGFS_FILE_OPS(ba_hist); -+MWLWIFI_DEBUGFS_FILE_OPS(fixed_rate); -+MWLWIFI_DEBUGFS_FILE_OPS(core_dump); -+MWLWIFI_DEBUGFS_FILE_WRITE_OPS(mcast_cts); -+MWLWIFI_DEBUGFS_FILE_WRITE_OPS(wmmedcaap); -+ -+void mwl_debugfs_init(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (!priv->debugfs_phy) -+ priv->debugfs_phy = debugfs_create_dir("mwlwifi", -+ hw->wiphy->debugfsdir); -+ -+ if (!priv->debugfs_phy) -+ return; -+ -+ MWLWIFI_DEBUGFS_ADD_FILE(info); -+ MWLWIFI_DEBUGFS_ADD_FILE(tx_status); -+ MWLWIFI_DEBUGFS_ADD_FILE(rx_status); -+ MWLWIFI_DEBUGFS_ADD_FILE(vif); -+ MWLWIFI_DEBUGFS_ADD_FILE(sta); -+ MWLWIFI_DEBUGFS_ADD_FILE(ampdu); -+ MWLWIFI_DEBUGFS_ADD_FILE(stnid); -+ MWLWIFI_DEBUGFS_ADD_FILE(device_pwrtbl); -+ MWLWIFI_DEBUGFS_ADD_FILE(txpwrlmt); -+ MWLWIFI_DEBUGFS_ADD_FILE(tx_amsdu); -+ MWLWIFI_DEBUGFS_ADD_FILE(dump_hostcmd); -+ MWLWIFI_DEBUGFS_ADD_FILE(dump_probe); -+ MWLWIFI_DEBUGFS_ADD_FILE(heartbeat); -+ MWLWIFI_DEBUGFS_ADD_FILE(dfs_test); -+ MWLWIFI_DEBUGFS_ADD_FILE(dfs_channel); -+ MWLWIFI_DEBUGFS_ADD_FILE(dfs_radar); -+ MWLWIFI_DEBUGFS_ADD_FILE(thermal); -+ MWLWIFI_DEBUGFS_ADD_FILE(led_ctrl); -+ MWLWIFI_DEBUGFS_ADD_FILE(regrdwr); -+ MWLWIFI_DEBUGFS_ADD_FILE(ratetable); -+ MWLWIFI_DEBUGFS_ADD_FILE(tx_hist); -+ MWLWIFI_DEBUGFS_ADD_FILE(ba_hist); -+ MWLWIFI_DEBUGFS_ADD_FILE(fixed_rate); -+ MWLWIFI_DEBUGFS_ADD_FILE(core_dump); -+ MWLWIFI_DEBUGFS_ADD_FILE(mcast_cts); -+ MWLWIFI_DEBUGFS_ADD_FILE(wmmedcaap); -+} -+ -+void mwl_debugfs_remove(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ debugfs_remove(priv->debugfs_phy); -+ priv->debugfs_phy = NULL; -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h b/drivers/net/wireless/marvell/mwlwifi/debugfs.h -new file mode 100644 -index 000000000000..e7595f563348 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h -@@ -0,0 +1,24 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines debug fs related functions. */ -+ -+#ifndef _MWL_DEBUGFS_H_ -+#define _MWL_DEBUGFS_H_ -+ -+void mwl_debugfs_init(struct ieee80211_hw *hw); -+void mwl_debugfs_remove(struct ieee80211_hw *hw); -+ -+#endif /* _MWL_DEBUGFS_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c -new file mode 100644 -index 000000000000..ff943d6fe447 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.c -@@ -0,0 +1,3852 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements firmware host command related -+ * functions. -+ */ -+ -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "hif/fwcmd.h" -+#include "hif/hif-ops.h" -+ -+#define MAX_WAIT_GET_HW_SPECS_ITERATONS 3 -+ -+struct cmd_header { -+ __le16 command; -+ __le16 len; -+} __packed; -+ -+char *mwl_fwcmd_get_cmd_string(unsigned short cmd) -+{ -+ int max_entries = 0; -+ int curr_cmd = 0; -+ -+ static const struct { -+ u16 cmd; -+ char *cmd_string; -+ } cmds[] = { -+ { HOSTCMD_CMD_GET_HW_SPEC, "GetHwSpecifications" }, -+ { HOSTCMD_CMD_SET_HW_SPEC, "SetHwSepcifications" }, -+ { HOSTCMD_CMD_802_11_GET_STAT, "80211GetStat" }, -+ { HOSTCMD_CMD_BBP_REG_ACCESS, "BBPRegAccess" }, -+ { HOSTCMD_CMD_RF_REG_ACCESS, "RFRegAccess" }, -+ { HOSTCMD_CMD_802_11_RADIO_CONTROL, "80211RadioControl" }, -+ { HOSTCMD_CMD_MEM_ADDR_ACCESS, "MEMAddrAccess" }, -+ { HOSTCMD_CMD_802_11_TX_POWER, "80211TxPower" }, -+ { HOSTCMD_CMD_802_11_RF_ANTENNA, "80211RfAntenna" }, -+ { HOSTCMD_CMD_BROADCAST_SSID_ENABLE, "BroadcastSsidEnable" }, -+ { HOSTCMD_CMD_SET_CFG, "SetCfg" }, -+ { HOSTCMD_CMD_SET_RF_CHANNEL, "SetRfChannel" }, -+ { HOSTCMD_CMD_SET_AID, "SetAid" }, -+ { HOSTCMD_CMD_SET_INFRA_MODE, "SetInfraMode" }, -+ { HOSTCMD_CMD_802_11_RTS_THSD, "80211RtsThreshold" }, -+ { HOSTCMD_CMD_SET_EDCA_PARAMS, "SetEDCAParams" }, -+ { HOSTCMD_CMD_802_11H_DETECT_RADAR, "80211hDetectRadar" }, -+ { HOSTCMD_CMD_SET_WMM_MODE, "SetWMMMode" }, -+ { HOSTCMD_CMD_HT_GUARD_INTERVAL, "HtGuardInterval" }, -+ { HOSTCMD_CMD_SET_FIXED_RATE, "SetFixedRate" }, -+ { HOSTCMD_CMD_SET_IES, "SetInformationElements" }, -+ { HOSTCMD_CMD_SET_LINKADAPT_CS_MODE, "LinkAdaptCsMode" }, -+ { HOSTCMD_CMD_DUMP_OTP_DATA, "DumpOtpData" }, -+ { HOSTCMD_CMD_SET_MAC_ADDR, "SetMacAddr" }, -+ { HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" }, -+ { HOSTCMD_CMD_GET_WATCHDOG_BITMAP, "GetWatchdogBitMap" }, -+ { HOSTCMD_CMD_DEL_MAC_ADDR, "DelMacAddr" }, -+ { HOSTCMD_CMD_BSS_START, "BssStart" }, -+ { HOSTCMD_CMD_AP_BEACON, "SetApBeacon" }, -+ { HOSTCMD_CMD_SET_NEW_STN, "SetNewStation" }, -+ { HOSTCMD_CMD_SET_APMODE, "SetApMode" }, -+ { HOSTCMD_CMD_SET_SWITCH_CHANNEL, "SetSwitchChannel" }, -+ { HOSTCMD_CMD_UPDATE_ENCRYPTION, "UpdateEncryption" }, -+ { HOSTCMD_CMD_BASTREAM, "BAStream" }, -+ { HOSTCMD_CMD_SET_SPECTRUM_MGMT, "SetSpectrumMgmt" }, -+ { HOSTCMD_CMD_SET_POWER_CONSTRAINT, "SetPowerConstraint" }, -+ { HOSTCMD_CMD_SET_COUNTRY_CODE, "SetCountryCode" }, -+ { HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL, "SetOptimizationLevel" }, -+ { HOSTCMD_CMD_SET_WSC_IE, "SetWscIE" }, -+ { HOSTCMD_CMD_GET_RATETABLE, "GetRateTable" }, -+ { HOSTCMD_CMD_GET_SEQNO, "GetSeqno" }, -+ { HOSTCMD_CMD_DWDS_ENABLE, "DwdsEnable" }, -+ { HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" }, -+ { HOSTCMD_CMD_SET_CDD, "SetCDD" }, -+ { HOSTCMD_CMD_SET_BFTYPE, "SetBFType" }, -+ { HOSTCMD_CMD_CAU_REG_ACCESS, "CAURegAccess" }, -+ { HOSTCMD_CMD_GET_TEMP, "GetTemp" }, -+ { HOSTCMD_CMD_LED_CTRL, "LedCtrl" }, -+ { HOSTCMD_CMD_GET_FW_REGION_CODE, "GetFwRegionCode" }, -+ { HOSTCMD_CMD_GET_DEVICE_PWR_TBL, "GetDevicePwrTbl" }, -+ { HOSTCMD_CMD_SET_RATE_DROP, "SetRateDrop" }, -+ { HOSTCMD_CMD_NEWDP_DMATHREAD_START, "NewdpDMAThreadStart" }, -+ { HOSTCMD_CMD_GET_FW_REGION_CODE_SC4, "GetFwRegionCodeSC4" }, -+ { HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4, "GetDevicePwrTblSC4" }, -+ { HOSTCMD_CMD_QUIET_MODE, "QuietMode" }, -+ { HOSTCMD_CMD_CORE_DUMP_DIAG_MODE, "CoreDumpDiagMode" }, -+ { HOSTCMD_CMD_802_11_SLOT_TIME, "80211SlotTime" }, -+ { HOSTCMD_CMD_GET_FW_CORE_DUMP, "GetFwCoreDump" }, -+ { HOSTCMD_CMD_EDMAC_CTRL, "EDMACCtrl" }, -+ { HOSTCMD_CMD_TXPWRLMT_CFG, "TxpwrlmtCfg" }, -+ { HOSTCMD_CMD_MCAST_CTS, "McastCts" }, -+ }; -+ -+ max_entries = ARRAY_SIZE(cmds); -+ -+ for (curr_cmd = 0; curr_cmd < max_entries; curr_cmd++) -+ if ((cmd & 0x7fff) == cmds[curr_cmd].cmd) -+ return cmds[curr_cmd].cmd_string; -+ -+ return "unknown"; -+} -+ -+static int mwl_fwcmd_802_11_radio_control(struct mwl_priv *priv, -+ bool enable, bool force) -+{ -+ struct hostcmd_cmd_802_11_radio_control *pcmd; -+ -+ if (enable == priv->radio_on && !force) -+ return 0; -+ -+ pcmd = (struct hostcmd_cmd_802_11_radio_control *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RADIO_CONTROL); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(WL_SET); -+ pcmd->control = cpu_to_le16(priv->radio_short_preamble ? -+ WL_AUTO_PREAMBLE : WL_LONG_PREAMBLE); -+ pcmd->radio_on = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE); -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_802_11_RADIO_CONTROL)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ priv->radio_on = enable; -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, -+ u8 action, u16 ch, u16 band, -+ u16 width, u16 sub_ch) -+{ -+ struct hostcmd_cmd_802_11_tx_power *pcmd; -+ int i; -+ -+ pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ if (priv->chip_type == MWL8997) { -+ memset(pcmd, 0x00, -+ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); -+ pcmd->cmd_hdr.len = cpu_to_le16( -+ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); -+ } else { -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ } -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER); -+ pcmd->action = cpu_to_le16(action); -+ pcmd->ch = cpu_to_le16(ch); -+ pcmd->bw = cpu_to_le16(width); -+ pcmd->band = cpu_to_le16(band); -+ pcmd->sub_ch = cpu_to_le16(sub_ch); -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_802_11_TX_POWER)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ for (i = 0; i < priv->pwr_level; i++) -+ powlist[i] = le16_to_cpu(pcmd->power_level_list[i]); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+static int mwl_fwcmd_set_tx_powers(struct mwl_priv *priv, u16 txpow[], -+ u8 action, u16 ch, u16 band, -+ u16 width, u16 sub_ch) -+{ -+ struct hostcmd_cmd_802_11_tx_power *pcmd; -+ int i; -+ -+ pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ if (priv->chip_type == MWL8997) { -+ memset(pcmd, 0x00, -+ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); -+ pcmd->cmd_hdr.len = cpu_to_le16( -+ sizeof(struct hostcmd_cmd_802_11_tx_power_kf2)); -+ } else { -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ } -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER); -+ pcmd->action = cpu_to_le16(action); -+ pcmd->ch = cpu_to_le16(ch); -+ pcmd->bw = cpu_to_le16(width); -+ pcmd->band = cpu_to_le16(band); -+ pcmd->sub_ch = cpu_to_le16(sub_ch); -+ -+ for (i = 0; i < priv->pwr_level; i++) -+ pcmd->power_level_list[i] = cpu_to_le16(txpow[i]); -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_802_11_TX_POWER)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+static u8 mwl_fwcmd_get_80m_pri_chnl(u8 channel) -+{ -+ u8 act_primary = ACT_PRIMARY_CHAN_0; -+ -+ switch (channel) { -+ case 36: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 40: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 44: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 48: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 52: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 56: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 60: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 64: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 100: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 104: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 108: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 112: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 116: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 120: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 124: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 128: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 132: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 136: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 140: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 144: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 149: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 153: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 157: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 161: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ } -+ -+ return act_primary; -+} -+ -+static u8 mwl_fwcmd_get_160m_pri_chnl(u8 channel) -+{ -+ u8 act_primary = ACT_PRIMARY_CHAN_0; -+ -+ switch (channel) { -+ case 36: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 40: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 44: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 48: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 52: -+ act_primary = ACT_PRIMARY_CHAN_4; -+ break; -+ case 56: -+ act_primary = ACT_PRIMARY_CHAN_5; -+ break; -+ case 60: -+ act_primary = ACT_PRIMARY_CHAN_6; -+ break; -+ case 64: -+ act_primary = ACT_PRIMARY_CHAN_7; -+ break; -+ case 100: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 104: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 108: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 112: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 116: -+ act_primary = ACT_PRIMARY_CHAN_4; -+ break; -+ case 120: -+ act_primary = ACT_PRIMARY_CHAN_5; -+ break; -+ case 124: -+ act_primary = ACT_PRIMARY_CHAN_6; -+ break; -+ case 128: -+ act_primary = ACT_PRIMARY_CHAN_7; -+ break; -+ case 149: -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case 153: -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case 157: -+ act_primary = ACT_PRIMARY_CHAN_2; -+ break; -+ case 161: -+ act_primary = ACT_PRIMARY_CHAN_3; -+ break; -+ case 165: -+ act_primary = ACT_PRIMARY_CHAN_4; -+ break; -+ case 169: -+ act_primary = ACT_PRIMARY_CHAN_5; -+ break; -+ case 173: -+ act_primary = ACT_PRIMARY_CHAN_6; -+ break; -+ case 177: -+ act_primary = ACT_PRIMARY_CHAN_7; -+ break; -+ } -+ -+ return act_primary; -+} -+ -+static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv, -+ struct mwl_vif *vif, u8 *beacon, int len) -+{ -+ struct ieee80211_mgmt *mgmt; -+ struct beacon_info *beacon_info; -+ int baselen; -+ u8 *pos; -+ size_t left; -+ bool elem_parse_failed; -+ -+ mgmt = (struct ieee80211_mgmt *)beacon; -+ -+ baselen = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt; -+ if (baselen > len) -+ return; -+ -+ beacon_info = &vif->beacon_info; -+ memset(beacon_info, 0, sizeof(struct beacon_info)); -+ beacon_info->valid = false; -+ beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0]; -+ beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0]; -+ -+ beacon_info->cap_info = le16_to_cpu(mgmt->u.beacon.capab_info); -+ beacon_info->power_constraint = 0; -+ -+ pos = (u8 *)mgmt->u.beacon.variable; -+ left = len - baselen; -+ -+ elem_parse_failed = false; -+ -+ while (left >= 2) { -+ u8 id, elen; -+ -+ id = *pos++; -+ elen = *pos++; -+ left -= 2; -+ -+ if (elen > left) { -+ elem_parse_failed = true; -+ break; -+ } -+ -+ switch (id) { -+ case WLAN_EID_COUNTRY: -+ beacon_info->ie_country_len = (elen + 2); -+ beacon_info->ie_country_ptr = (pos - 2); -+ break; -+ case WLAN_EID_SUPP_RATES: -+ case WLAN_EID_EXT_SUPP_RATES: -+ { -+ int idx, bi, oi; -+ u8 rate; -+ -+ for (bi = 0; bi < SYSADPT_MAX_DATA_RATES_G; -+ bi++) { -+ if (beacon_info->b_rate_set[bi] == 0) -+ break; -+ } -+ -+ for (oi = 0; oi < SYSADPT_MAX_DATA_RATES_G; -+ oi++) { -+ if (beacon_info->op_rate_set[oi] == 0) -+ break; -+ } -+ -+ for (idx = 0; idx < elen; idx++) { -+ rate = pos[idx]; -+ if ((rate & 0x80) != 0) { -+ if (bi < SYSADPT_MAX_DATA_RATES_G) -+ beacon_info->b_rate_set[bi++] -+ = rate & 0x7f; -+ else { -+ elem_parse_failed = true; -+ break; -+ } -+ } -+ if (oi < SYSADPT_MAX_DATA_RATES_G) -+ beacon_info->op_rate_set[oi++] = -+ rate & 0x7f; -+ else { -+ elem_parse_failed = true; -+ break; -+ } -+ } -+ } -+ break; -+ case WLAN_EID_PWR_CONSTRAINT: -+ if (elen == 1) -+ beacon_info->power_constraint = *pos; -+ break; -+ case WLAN_EID_RSN: -+ beacon_info->ie_rsn48_len = (elen + 2); -+ beacon_info->ie_rsn48_ptr = (pos - 2); -+ break; -+ case WLAN_EID_MOBILITY_DOMAIN: -+ beacon_info->ie_mde_len = (elen + 2); -+ beacon_info->ie_mde_ptr = (pos - 2); -+ break; -+ case WLAN_EID_HT_CAPABILITY: -+ case WLAN_EID_HT_OPERATION: -+ case WLAN_EID_OVERLAP_BSS_SCAN_PARAM: -+ case WLAN_EID_EXT_CAPABILITY: -+ beacon_info->ie_ht_len += (elen + 2); -+ if (beacon_info->ie_ht_len > -+ sizeof(beacon_info->ie_list_ht)) { -+ elem_parse_failed = true; -+ } else { -+ *beacon_info->ie_ht_ptr++ = id; -+ *beacon_info->ie_ht_ptr++ = elen; -+ memcpy(beacon_info->ie_ht_ptr, pos, elen); -+ beacon_info->ie_ht_ptr += elen; -+ } -+ break; -+ case WLAN_EID_MESH_CONFIG: -+ beacon_info->ie_meshcfg_len = (elen + 2); -+ beacon_info->ie_meshcfg_ptr = (pos - 2); -+ break; -+ case WLAN_EID_MESH_ID: -+ beacon_info->ie_meshid_len = (elen + 2); -+ beacon_info->ie_meshid_ptr = (pos - 2); -+ break; -+ case WLAN_EID_CHAN_SWITCH_PARAM: -+ beacon_info->ie_meshchsw_len = (elen + 2); -+ beacon_info->ie_meshchsw_ptr = (pos - 2); -+ break; -+ case WLAN_EID_VHT_CAPABILITY: -+ case WLAN_EID_VHT_OPERATION: -+ case WLAN_EID_OPMODE_NOTIF: -+ beacon_info->ie_vht_len += (elen + 2); -+ if (beacon_info->ie_vht_len > -+ sizeof(beacon_info->ie_list_vht)) { -+ elem_parse_failed = true; -+ } else { -+ *beacon_info->ie_vht_ptr++ = id; -+ *beacon_info->ie_vht_ptr++ = elen; -+ memcpy(beacon_info->ie_vht_ptr, pos, elen); -+ beacon_info->ie_vht_ptr += elen; -+ } -+ break; -+ case WLAN_EID_VENDOR_SPECIFIC: -+ if ((pos[0] == 0x00) && (pos[1] == 0x50) && -+ (pos[2] == 0xf2)) { -+ if (pos[3] == 0x01) { -+ beacon_info->ie_rsn_len = (elen + 2); -+ beacon_info->ie_rsn_ptr = (pos - 2); -+ } -+ -+ if (pos[3] == 0x02) { -+ beacon_info->ie_wmm_len = (elen + 2); -+ beacon_info->ie_wmm_ptr = (pos - 2); -+ } -+ -+ if (pos[3] == 0x04) { -+ beacon_info->ie_wsc_len = (elen + 2); -+ beacon_info->ie_wsc_ptr = (pos - 2); -+ } -+ } -+ break; -+ default: -+ break; -+ } -+ -+ left -= elen; -+ pos += elen; -+ } -+ -+ if (!elem_parse_failed) { -+ beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0]; -+ beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0]; -+ beacon_info->valid = true; -+ } -+} -+ -+static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif) -+{ -+ struct hostcmd_cmd_set_ies *pcmd; -+ struct beacon_info *beacon = &mwl_vif->beacon_info; -+ u16 ie_list_len_proprietary = 0; -+ -+ if (beacon->ie_ht_len > sizeof(pcmd->ie_list_ht)) -+ goto einval; -+ -+ if (beacon->ie_vht_len > sizeof(pcmd->ie_list_vht)) -+ goto einval; -+ -+ pcmd = (struct hostcmd_cmd_set_ies *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_IES); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); -+ -+ memcpy(pcmd->ie_list_ht, beacon->ie_ht_ptr, beacon->ie_ht_len); -+ pcmd->ie_list_len_ht = cpu_to_le16(beacon->ie_ht_len); -+ -+ memcpy(pcmd->ie_list_vht, beacon->ie_vht_ptr, beacon->ie_vht_len); -+ pcmd->ie_list_len_vht = cpu_to_le16(beacon->ie_vht_len); -+ -+ memcpy(pcmd->ie_list_proprietary, beacon->ie_meshid_ptr, -+ beacon->ie_meshid_len); -+ ie_list_len_proprietary = beacon->ie_meshid_len; -+ -+ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, -+ beacon->ie_meshcfg_ptr, beacon->ie_meshcfg_len); -+ ie_list_len_proprietary += beacon->ie_meshcfg_len; -+ -+ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, -+ beacon->ie_meshchsw_ptr, beacon->ie_meshchsw_len); -+ ie_list_len_proprietary += beacon->ie_meshchsw_len; -+ -+ if (priv->chip_type == MWL8897) { -+ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, -+ beacon->ie_wmm_ptr, beacon->ie_wmm_len); -+ ie_list_len_proprietary += mwl_vif->beacon_info.ie_wmm_len; -+ } -+ -+ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary, -+ beacon->ie_mde_ptr, beacon->ie_mde_len); -+ ie_list_len_proprietary += mwl_vif->beacon_info.ie_mde_len; -+ -+ pcmd->ie_list_len_proprietary = cpu_to_le16(ie_list_len_proprietary); -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_IES)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+ -+einval: -+ -+ wiphy_err(priv->hw->wiphy, "length of IE is too long\n"); -+ -+ return -EINVAL; -+} -+ -+static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv, -+ struct mwl_vif *mwl_vif, -+ struct ieee80211_bss_conf *bss_conf) -+{ -+ struct hostcmd_cmd_ap_beacon *pcmd; -+ struct ds_params *phy_ds_param_set; -+ -+ /* wmm structure of start command is defined less one byte, -+ * due to following field country is not used, add byte one -+ * to bypass the check. -+ */ -+ if (mwl_vif->beacon_info.ie_wmm_len > -+ (sizeof(pcmd->start_cmd.wmm_param) + 1)) -+ goto ielenerr; -+ -+ if (mwl_vif->beacon_info.ie_rsn_len > sizeof(pcmd->start_cmd.rsn_ie)) -+ goto ielenerr; -+ -+ if (mwl_vif->beacon_info.ie_rsn48_len > -+ sizeof(pcmd->start_cmd.rsn48_ie)) -+ goto ielenerr; -+ -+ pcmd = (struct hostcmd_cmd_ap_beacon *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_AP_BEACON); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ ether_addr_copy(pcmd->start_cmd.sta_mac_addr, mwl_vif->bssid); -+ memcpy(pcmd->start_cmd.ssid, bss_conf->ssid, bss_conf->ssid_len); -+ if (priv->chip_type == MWL8997) -+ ether_addr_copy(pcmd->start_cmd.bssid, mwl_vif->bssid); -+ pcmd->start_cmd.bss_type = 1; -+ pcmd->start_cmd.bcn_period = cpu_to_le16(bss_conf->beacon_int); -+ pcmd->start_cmd.dtim_period = bss_conf->dtim_period; /* 8bit */ -+ -+ phy_ds_param_set = &pcmd->start_cmd.phy_param_set.ds_param_set; -+ phy_ds_param_set->elem_id = WLAN_EID_DS_PARAMS; -+ phy_ds_param_set->len = sizeof(phy_ds_param_set->current_chnl); -+ phy_ds_param_set->current_chnl = bss_conf->chandef.chan->hw_value; -+ -+ pcmd->start_cmd.probe_delay = cpu_to_le16(10); -+ pcmd->start_cmd.cap_info = cpu_to_le16(mwl_vif->beacon_info.cap_info); -+ -+ memcpy(&pcmd->start_cmd.wmm_param, mwl_vif->beacon_info.ie_wmm_ptr, -+ mwl_vif->beacon_info.ie_wmm_len); -+ -+ memcpy(&pcmd->start_cmd.rsn_ie, mwl_vif->beacon_info.ie_rsn_ptr, -+ mwl_vif->beacon_info.ie_rsn_len); -+ -+ memcpy(&pcmd->start_cmd.rsn48_ie, mwl_vif->beacon_info.ie_rsn48_ptr, -+ mwl_vif->beacon_info.ie_rsn48_len); -+ -+ memcpy(pcmd->start_cmd.b_rate_set, mwl_vif->beacon_info.b_rate_set, -+ SYSADPT_MAX_DATA_RATES_G); -+ -+ memcpy(pcmd->start_cmd.op_rate_set, mwl_vif->beacon_info.op_rate_set, -+ SYSADPT_MAX_DATA_RATES_G); -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_AP_BEACON)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+ -+ielenerr: -+ -+ wiphy_err(priv->hw->wiphy, "length of IE is too long\n"); -+ -+ return -EINVAL; -+} -+ -+static int mwl_fwcmd_set_spectrum_mgmt(struct mwl_priv *priv, bool enable) -+{ -+ struct hostcmd_cmd_set_spectrum_mgmt *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_spectrum_mgmt *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SPECTRUM_MGMT); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->spectrum_mgmt = cpu_to_le32(enable); -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_SPECTRUM_MGMT)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+static int mwl_fwcmd_set_power_constraint(struct mwl_priv *priv, -+ u32 power_constraint) -+{ -+ struct hostcmd_cmd_set_power_constraint *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_power_constraint *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_POWER_CONSTRAINT); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->power_constraint = cpu_to_le32(power_constraint); -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_POWER_CONSTRAINT)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+static int mwl_fwcmd_set_country_code(struct mwl_priv *priv, -+ struct mwl_vif *mwl_vif, -+ struct ieee80211_bss_conf *bss_conf) -+{ -+ struct hostcmd_cmd_set_country_code *pcmd; -+ struct beacon_info *b_inf = &mwl_vif->beacon_info; -+ u8 chnl_len; -+ bool a_band; -+ bool enable = false; -+ -+ if (b_inf->ie_country_ptr) { -+ if (bss_conf->chandef.chan->band == NL80211_BAND_2GHZ) -+ a_band = false; -+ else if (bss_conf->chandef.chan->band == NL80211_BAND_5GHZ) -+ a_band = true; -+ else -+ return -EINVAL; -+ -+ chnl_len = b_inf->ie_country_len - 5; -+ if (a_band) { -+ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_a)) -+ return -EINVAL; -+ } else { -+ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_g)) -+ return -EINVAL; -+ } -+ -+ enable = true; -+ } -+ -+ pcmd = (struct hostcmd_cmd_set_country_code *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_COUNTRY_CODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le32(enable); -+ if (enable) { -+ memcpy(pcmd->domain_info.country_string, -+ b_inf->ie_country_ptr + 2, 3); -+ if (a_band) { -+ pcmd->domain_info.g_chnl_len = 0; -+ pcmd->domain_info.a_chnl_len = chnl_len; -+ memcpy(pcmd->domain_info.domain_entry_a, -+ b_inf->ie_country_ptr + 5, chnl_len); -+ } else { -+ pcmd->domain_info.a_chnl_len = 0; -+ pcmd->domain_info.g_chnl_len = chnl_len; -+ memcpy(pcmd->domain_info.domain_entry_g, -+ b_inf->ie_country_ptr + 5, chnl_len); -+ } -+ } -+ -+ if (mwl_hif_exec_cmd(priv->hw, HOSTCMD_CMD_SET_COUNTRY_CODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+static int mwl_fwcmd_encryption_set_cmd_info(struct hostcmd_cmd_set_key *cmd, -+ u8 *addr, -+ struct ieee80211_key_conf *key) -+{ -+ cmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); -+ cmd->cmd_hdr.len = cpu_to_le16(sizeof(*cmd)); -+ cmd->key_param.length = cpu_to_le16(sizeof(*cmd) - -+ offsetof(struct hostcmd_cmd_set_key, key_param)); -+ cmd->key_param.key_index = cpu_to_le32(key->keyidx); -+ cmd->key_param.key_len = cpu_to_le16(key->keylen); -+ ether_addr_copy(cmd->key_param.mac_addr, addr); -+ -+ switch (key->cipher) { -+ case WLAN_CIPHER_SUITE_WEP40: -+ case WLAN_CIPHER_SUITE_WEP104: -+ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_WEP); -+ if (key->keyidx == 0) -+ cmd->key_param.key_info = -+ cpu_to_le32(ENCR_KEY_FLAG_WEP_TXKEY); -+ break; -+ case WLAN_CIPHER_SUITE_TKIP: -+ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_TKIP); -+ cmd->key_param.key_info = -+ (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? -+ cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) : -+ cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY); -+ cmd->key_param.key_info |= -+ cpu_to_le32(ENCR_KEY_FLAG_MICKEY_VALID | -+ ENCR_KEY_FLAG_TSC_VALID); -+ break; -+ case WLAN_CIPHER_SUITE_CCMP: -+ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_AES); -+ cmd->key_param.key_info = -+ (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? -+ cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) : -+ cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY); -+ break; -+ case WLAN_CIPHER_SUITE_AES_CMAC: -+ return 1; -+ default: -+ return -ENOTSUPP; -+ } -+ -+ return 0; -+} -+ -+static __le16 mwl_fwcmd_parse_cal_cfg(const u8 *src, size_t len, u8 *dst) -+{ -+ const u8 *ptr; -+ u8 *dptr; -+ char byte_str[3]; -+ long res; -+ -+ ptr = src; -+ dptr = dst; -+ byte_str[2] = '\0'; -+ -+ while (ptr - src < len) { -+ if (*ptr && (isspace(*ptr) || iscntrl(*ptr))) { -+ ptr++; -+ continue; -+ } -+ -+ if (isxdigit(*ptr)) { -+ byte_str[0] = *ptr++; -+ byte_str[1] = *ptr++; -+ kstrtol(byte_str, 16, &res); -+ *dptr++ = res; -+ } else { -+ ptr++; -+ } -+ } -+ -+ return cpu_to_le16(dptr - dst); -+} -+ -+static u16 mwl_fwcmd_parse_txpwrlmt_cfg(const u8 *src, size_t len, -+ u16 parse_len, u8 *dst) -+{ -+ const u8 *ptr; -+ u8 *dptr; -+ char byte_str[3]; -+ long res; -+ -+ ptr = src; -+ dptr = dst; -+ byte_str[2] = '\0'; -+ -+ while ((ptr - src < len) && (dptr - dst < parse_len)) { -+ if (*ptr && (isspace(*ptr) || iscntrl(*ptr))) { -+ ptr++; -+ continue; -+ } -+ -+ if (isxdigit(*ptr)) { -+ byte_str[0] = *ptr++; -+ byte_str[1] = *ptr++; -+ kstrtol(byte_str, 16, &res); -+ *dptr++ = res; -+ } else { -+ ptr++; -+ } -+ } -+ -+ return (ptr - src); -+} -+ -+const struct hostcmd_get_hw_spec -+*mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_hw_spec *pcmd; -+ int retry; -+ -+ pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd); -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ eth_broadcast_addr(pcmd->hw_spec.permanent_addr); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_HW_SPEC); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->hw_spec.fw_awake_cookie = cpu_to_le32(priv->pphys_cmd_buf + 2048); -+ -+ retry = 0; -+ while (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_HW_SPEC)) { -+ if (retry++ > MAX_WAIT_GET_HW_SPECS_ITERATONS) { -+ wiphy_err(hw->wiphy, "can't get hw specs\n"); -+ mutex_unlock(&priv->fwcmd_mutex); -+ return NULL; -+ } -+ -+ msleep(1000); -+ wiphy_debug(hw->wiphy, -+ "repeat command = %p\n", pcmd); -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return &pcmd->hw_spec; -+} -+ -+int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw, -+ struct hostcmd_set_hw_spec *spec) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_hw_spec *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_HW_SPEC); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ memcpy(&pcmd->hw_spec, spec, sizeof(*spec)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_HW_SPEC)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_stat(struct ieee80211_hw *hw, -+ struct ieee80211_low_level_stats *stats) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_802_11_get_stat *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_GET_STAT); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_GET_STAT)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ stats->dot11ACKFailureCount = -+ le32_to_cpu(pcmd->ack_failures); -+ stats->dot11RTSFailureCount = -+ le32_to_cpu(pcmd->rts_failures); -+ stats->dot11FCSErrorCount = -+ le32_to_cpu(pcmd->rx_fcs_errors); -+ stats->dot11RTSSuccessCount = -+ le32_to_cpu(pcmd->rts_successes); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_reg_bb(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_bbp_reg_access *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_bbp_reg_access *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BBP_REG_ACCESS); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->offset = cpu_to_le16(reg); -+ pcmd->action = cpu_to_le16(flag); -+ pcmd->value = *val; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BBP_REG_ACCESS)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ *val = pcmd->value; -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_reg_rf(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_rf_reg_access *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_rf_reg_access *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_RF_REG_ACCESS); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->offset = cpu_to_le16(reg); -+ pcmd->action = cpu_to_le16(flag); -+ pcmd->value = *val; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_RF_REG_ACCESS)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ *val = pcmd->value; -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw) -+{ -+ return mwl_fwcmd_802_11_radio_control(hw->priv, true, false); -+} -+ -+int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw) -+{ -+ return mwl_fwcmd_802_11_radio_control(hw->priv, false, false); -+} -+ -+int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) -+{ -+ struct mwl_priv *priv = hw->priv; -+ int rc; -+ -+ priv->radio_short_preamble = short_preamble; -+ rc = mwl_fwcmd_802_11_radio_control(priv, true, true); -+ -+ return rc; -+} -+ -+int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len, -+ u32 *val, u16 set) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_mem_addr_access *pcmd; -+ int i; -+ -+ pcmd = (struct hostcmd_cmd_mem_addr_access *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_MEM_ADDR_ACCESS); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->address = cpu_to_le32(addr); -+ pcmd->length = cpu_to_le16(len); -+ pcmd->value[0] = cpu_to_le32(*val); -+ pcmd->reserved = cpu_to_le16(set); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_MEM_ADDR_ACCESS)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ for (i = 0; i < len; i++) -+ val[i] = le32_to_cpu(pcmd->value[i]); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw, -+ struct ieee80211_conf *conf, u8 fraction) -+{ -+ struct ieee80211_channel *channel = conf->chandef.chan; -+ struct mwl_priv *priv = hw->priv; -+ int reduce_val = 0; -+ u16 band = 0, width = 0, sub_ch = 0; -+ u16 maxtxpow[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; -+ int i, tmp; -+ int rc = 0; -+ -+ if ((priv->chip_type != MWL8997) && (priv->forbidden_setting)) -+ return rc; -+ -+ switch (fraction) { -+ case 0: -+ reduce_val = 0; /* Max */ -+ break; -+ case 1: -+ reduce_val = 2; /* 75% -1.25db */ -+ break; -+ case 2: -+ reduce_val = 3; /* 50% -3db */ -+ break; -+ case 3: -+ reduce_val = 6; /* 25% -6db */ -+ break; -+ default: -+ /* larger than case 3, pCmd->MaxPowerLevel is min */ -+ reduce_val = 0xff; -+ break; -+ } -+ -+ if (channel->band == NL80211_BAND_2GHZ) -+ band = FREQ_BAND_2DOT4GHZ; -+ else if (channel->band == NL80211_BAND_5GHZ) -+ band = FREQ_BAND_5GHZ; -+ -+ switch (conf->chandef.width) { -+ case NL80211_CHAN_WIDTH_20_NOHT: -+ case NL80211_CHAN_WIDTH_20: -+ width = CH_20_MHZ_WIDTH; -+ sub_ch = NO_EXT_CHANNEL; -+ break; -+ case NL80211_CHAN_WIDTH_40: -+ width = CH_40_MHZ_WIDTH; -+ if (conf->chandef.center_freq1 > channel->center_freq) -+ sub_ch = EXT_CH_ABOVE_CTRL_CH; -+ else -+ sub_ch = EXT_CH_BELOW_CTRL_CH; -+ break; -+ case NL80211_CHAN_WIDTH_80: -+ width = CH_80_MHZ_WIDTH; -+ if (conf->chandef.center_freq1 > channel->center_freq) -+ sub_ch = EXT_CH_ABOVE_CTRL_CH; -+ else -+ sub_ch = EXT_CH_BELOW_CTRL_CH; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (priv->chip_type == MWL8997) { -+ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow, -+ HOSTCMD_ACT_GET_MAX_TX_PWR, -+ channel->hw_value, band, width, sub_ch); -+ -+ for (i = 0; i < priv->pwr_level; i++) { -+ tmp = priv->max_tx_pow[i]; -+ maxtxpow[i] = ((tmp - reduce_val) > 0) ? -+ (tmp - reduce_val) : 0; -+ } -+ -+ rc = mwl_fwcmd_set_tx_powers(priv, maxtxpow, -+ HOSTCMD_ACT_SET_MAX_TX_PWR, -+ channel->hw_value, band, -+ width, sub_ch); -+ return rc; -+ } -+ -+ if ((priv->powinited & MWL_POWER_INIT_2) == 0) { -+ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow, -+ HOSTCMD_ACT_GEN_GET_LIST, -+ channel->hw_value, band, width, sub_ch); -+ priv->powinited |= MWL_POWER_INIT_2; -+ } -+ -+ if ((priv->powinited & MWL_POWER_INIT_1) == 0) { -+ mwl_fwcmd_get_tx_powers(priv, priv->target_powers, -+ HOSTCMD_ACT_GEN_GET_LIST, -+ channel->hw_value, band, width, sub_ch); -+ priv->powinited |= MWL_POWER_INIT_1; -+ } -+ -+ for (i = 0; i < priv->pwr_level; i++) { -+ if (priv->target_powers[i] > priv->max_tx_pow[i]) -+ tmp = priv->max_tx_pow[i]; -+ else -+ tmp = priv->target_powers[i]; -+ maxtxpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0; -+ } -+ -+ rc = mwl_fwcmd_set_tx_powers(priv, maxtxpow, HOSTCMD_ACT_GEN_SET, -+ channel->hw_value, band, width, sub_ch); -+ -+ return rc; -+} -+ -+int mwl_fwcmd_tx_power(struct ieee80211_hw *hw, -+ struct ieee80211_conf *conf, u8 fraction) -+{ -+ struct ieee80211_channel *channel = conf->chandef.chan; -+ struct mwl_priv *priv = hw->priv; -+ int reduce_val = 0; -+ u16 band = 0, width = 0, sub_ch = 0; -+ u16 txpow[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; -+ int index, found = 0; -+ int i, tmp; -+ int rc = 0; -+ -+ if ((priv->chip_type != MWL8997) && (priv->forbidden_setting)) -+ return rc; -+ -+ switch (fraction) { -+ case 0: -+ reduce_val = 0; /* Max */ -+ break; -+ case 1: -+ reduce_val = 2; /* 75% -1.25db */ -+ break; -+ case 2: -+ reduce_val = 3; /* 50% -3db */ -+ break; -+ case 3: -+ reduce_val = 6; /* 25% -6db */ -+ break; -+ default: -+ /* larger than case 3, pCmd->MaxPowerLevel is min */ -+ reduce_val = 0xff; -+ break; -+ } -+ -+ if (channel->band == NL80211_BAND_2GHZ) -+ band = FREQ_BAND_2DOT4GHZ; -+ else if (channel->band == NL80211_BAND_5GHZ) -+ band = FREQ_BAND_5GHZ; -+ -+ switch (conf->chandef.width) { -+ case NL80211_CHAN_WIDTH_20_NOHT: -+ case NL80211_CHAN_WIDTH_20: -+ width = CH_20_MHZ_WIDTH; -+ sub_ch = NO_EXT_CHANNEL; -+ break; -+ case NL80211_CHAN_WIDTH_40: -+ width = CH_40_MHZ_WIDTH; -+ if (conf->chandef.center_freq1 > channel->center_freq) -+ sub_ch = EXT_CH_ABOVE_CTRL_CH; -+ else -+ sub_ch = EXT_CH_BELOW_CTRL_CH; -+ break; -+ case NL80211_CHAN_WIDTH_80: -+ width = CH_80_MHZ_WIDTH; -+ if (conf->chandef.center_freq1 > channel->center_freq) -+ sub_ch = EXT_CH_ABOVE_CTRL_CH; -+ else -+ sub_ch = EXT_CH_BELOW_CTRL_CH; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (priv->chip_type == MWL8997) { -+ mwl_fwcmd_get_tx_powers(priv, priv->target_powers, -+ HOSTCMD_ACT_GET_TARGET_TX_PWR, -+ channel->hw_value, band, width, sub_ch); -+ -+ for (i = 0; i < priv->pwr_level; i++) { -+ tmp = priv->target_powers[i]; -+ txpow[i] = ((tmp - reduce_val) > 0) ? -+ (tmp - reduce_val) : 0; -+ } -+ -+ rc = mwl_fwcmd_set_tx_powers(priv, txpow, -+ HOSTCMD_ACT_SET_TARGET_TX_PWR, -+ channel->hw_value, band, -+ width, sub_ch); -+ -+ return rc; -+ } -+ -+ /* search tx power table if exist */ -+ for (index = 0; index < SYSADPT_MAX_NUM_CHANNELS; index++) { -+ struct mwl_tx_pwr_tbl *tx_pwr; -+ -+ tx_pwr = &priv->tx_pwr_tbl[index]; -+ -+ /* do nothing if table is not loaded */ -+ if (tx_pwr->channel == 0) -+ break; -+ -+ if (tx_pwr->channel == channel->hw_value) { -+ priv->cdd = tx_pwr->cdd; -+ priv->txantenna2 = tx_pwr->txantenna2; -+ -+ if (tx_pwr->setcap) -+ priv->powinited = MWL_POWER_INIT_1; -+ else -+ priv->powinited = MWL_POWER_INIT_2; -+ -+ for (i = 0; i < priv->pwr_level; i++) { -+ if (tx_pwr->setcap) -+ priv->max_tx_pow[i] = -+ tx_pwr->tx_power[i]; -+ else -+ priv->target_powers[i] = -+ tx_pwr->tx_power[i]; -+ } -+ -+ found = 1; -+ break; -+ } -+ } -+ -+ if ((priv->powinited & MWL_POWER_INIT_2) == 0) { -+ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow, -+ HOSTCMD_ACT_GEN_GET_LIST, -+ channel->hw_value, band, width, sub_ch); -+ -+ priv->powinited |= MWL_POWER_INIT_2; -+ } -+ -+ if ((priv->powinited & MWL_POWER_INIT_1) == 0) { -+ mwl_fwcmd_get_tx_powers(priv, priv->target_powers, -+ HOSTCMD_ACT_GEN_GET_LIST, -+ channel->hw_value, band, width, sub_ch); -+ -+ priv->powinited |= MWL_POWER_INIT_1; -+ } -+ -+ for (i = 0; i < priv->pwr_level; i++) { -+ if (found) { -+ if ((priv->tx_pwr_tbl[index].setcap) && -+ (priv->tx_pwr_tbl[index].tx_power[i] > -+ priv->max_tx_pow[i])) -+ tmp = priv->max_tx_pow[i]; -+ else -+ tmp = priv->tx_pwr_tbl[index].tx_power[i]; -+ } else { -+ if (priv->target_powers[i] > priv->max_tx_pow[i]) -+ tmp = priv->max_tx_pow[i]; -+ else -+ tmp = priv->target_powers[i]; -+ } -+ -+ txpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0; -+ } -+ -+ rc = mwl_fwcmd_set_tx_powers(priv, txpow, HOSTCMD_ACT_GEN_SET_LIST, -+ channel->hw_value, band, width, sub_ch); -+ -+ return rc; -+} -+ -+int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_802_11_rf_antenna *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_802_11_rf_antenna *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RF_ANTENNA); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ pcmd->action = cpu_to_le16(dir); -+ -+ if (dir == WL_ANTENNATYPE_RX) { -+ u8 rx_antenna; -+ -+ if (priv->chip_type == MWL8964) { -+ if (antenna == ANTENNA_RX_4_AUTO) -+ rx_antenna = 0xf; -+ else if (antenna == ANTENNA_RX_3) -+ rx_antenna = 7; -+ else if (antenna == ANTENNA_RX_2) -+ rx_antenna = 4; -+ else -+ rx_antenna = 1; -+ -+ pcmd->antenna_mode = cpu_to_le16(rx_antenna); -+ } else { -+ rx_antenna = 4; -+ -+ if (antenna != 0) -+ pcmd->antenna_mode = cpu_to_le16(antenna); -+ else -+ pcmd->antenna_mode = cpu_to_le16(rx_antenna); -+ } -+ } else { -+ u8 tx_antenna = 0xf; -+ -+ if (antenna != 0) -+ pcmd->antenna_mode = cpu_to_le16(antenna); -+ else -+ pcmd->antenna_mode = cpu_to_le16(tx_antenna); -+ } -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_RF_ANTENNA)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, bool enable) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_broadcast_ssid_enable *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_broadcast_ssid_enable *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BROADCAST_SSID_ENABLE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ pcmd->enable = cpu_to_le32(enable); -+ if (priv->chip_type == MWL8997) -+ pcmd->hidden_ssid_info = enable ? 0 : 2; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BROADCAST_SSID_ENABLE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_cfg_data(struct ieee80211_hw *hw, u16 type) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_cfg *pcmd; -+ -+ if (!priv->cal_data) -+ return 0; -+ -+ pcmd = (struct hostcmd_cmd_set_cfg *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->data_len = mwl_fwcmd_parse_cal_cfg(priv->cal_data->data, -+ priv->cal_data->size, -+ pcmd->data); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_CFG); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd) + -+ le16_to_cpu(pcmd->data_len) - sizeof(pcmd->data)); -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); -+ pcmd->type = cpu_to_le16(type); -+ -+ utils_dump_data_debug("CalData:", pcmd->data, -+ le16_to_cpu(pcmd->data_len)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_CFG)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ release_firmware(priv->cal_data); -+ priv->cal_data = NULL; -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ release_firmware(priv->cal_data); -+ priv->cal_data = NULL; -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw, -+ struct ieee80211_conf *conf) -+{ -+ struct ieee80211_channel *channel = conf->chandef.chan; -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_rf_channel *pcmd; -+ u32 chnl_flags, freq_band, chnl_width, act_primary; -+ -+ pcmd = (struct hostcmd_cmd_set_rf_channel *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ if (priv->chip_type == MWL8997) { -+ memset(pcmd, 0x00, -+ sizeof(struct hostcmd_cmd_set_rf_channel_kf2)); -+ pcmd->cmd_hdr.len = cpu_to_le16( -+ sizeof(struct hostcmd_cmd_set_rf_channel_kf2)); -+ } else { -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ } -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RF_CHANNEL); -+ pcmd->action = cpu_to_le16(WL_SET); -+ pcmd->curr_chnl = channel->hw_value; -+ -+ if (channel->band == NL80211_BAND_2GHZ) { -+ freq_band = FREQ_BAND_2DOT4GHZ; -+ } else if (channel->band == NL80211_BAND_5GHZ) { -+ freq_band = FREQ_BAND_5GHZ; -+ } else { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EINVAL; -+ } -+ -+ switch (conf->chandef.width) { -+ case NL80211_CHAN_WIDTH_20_NOHT: -+ case NL80211_CHAN_WIDTH_20: -+ chnl_width = CH_20_MHZ_WIDTH; -+ act_primary = ACT_PRIMARY_CHAN_0; -+ break; -+ case NL80211_CHAN_WIDTH_40: -+ chnl_width = CH_40_MHZ_WIDTH; -+ if (conf->chandef.center_freq1 > channel->center_freq) -+ act_primary = ACT_PRIMARY_CHAN_0; -+ else -+ act_primary = ACT_PRIMARY_CHAN_1; -+ break; -+ case NL80211_CHAN_WIDTH_80: -+ chnl_width = CH_80_MHZ_WIDTH; -+ act_primary = -+ mwl_fwcmd_get_80m_pri_chnl(pcmd->curr_chnl); -+ break; -+ case NL80211_CHAN_WIDTH_160: -+ chnl_width = CH_160_MHZ_WIDTH; -+ act_primary = -+ mwl_fwcmd_get_160m_pri_chnl(pcmd->curr_chnl); -+ break; -+ default: -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EINVAL; -+ } -+ -+ chnl_flags = (freq_band & FREQ_BAND_MASK) | -+ ((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) | -+ ((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK); -+ -+ pcmd->chnl_flags = cpu_to_le32(chnl_flags); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_RF_CHANNEL)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (pcmd->cmd_hdr.result != 0) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EINVAL; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ if (priv->sw_scanning) { -+ priv->survey_info_idx++; -+ mwl_fwcmd_get_survey(hw, priv->survey_info_idx); -+ } else { -+ mwl_fwcmd_get_survey(hw, 0); -+ memset(&priv->cur_survey_info, 0, -+ sizeof(struct mwl_survey_info)); -+ } -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_aid(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *bssid, u16 aid) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_aid *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_aid *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_AID); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ pcmd->aid = cpu_to_le16(aid); -+ ether_addr_copy(pcmd->mac_addr, bssid); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_AID)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_infra_mode *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_infra_mode *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_INFRA_MODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_INFRA_MODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, int threshold) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_802_11_rts_thsd *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_802_11_rts_thsd *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RTS_THSD); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(WL_SET); -+ pcmd->threshold = cpu_to_le16(threshold); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_RTS_THSD)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index, -+ u16 cw_min, u16 cw_max, u8 aifs, u16 txop) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_edca_params *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_edca_params *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_EDCA_PARAMS); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ pcmd->action = cpu_to_le16(0xffff); -+ pcmd->txop = cpu_to_le16(txop); -+ pcmd->cw_max = cpu_to_le32(cw_max); -+ pcmd->cw_min = cpu_to_le32(cw_min); -+ pcmd->aifsn = aifs; -+ pcmd->txq_num = index; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_EDCA_PARAMS)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_radar_detect(struct ieee80211_hw *hw, u16 action) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_802_11h_detect_radar *pcmd; -+ u16 radar_type = RADAR_TYPE_CODE_0; -+ u8 channel = hw->conf.chandef.chan->hw_value; -+ -+ pcmd = (struct hostcmd_cmd_802_11h_detect_radar *)&priv->pcmd_buf[0]; -+ -+ if (priv->dfs_region == NL80211_DFS_JP) { -+ if (channel >= 52 && channel <= 64) -+ radar_type = RADAR_TYPE_CODE_53; -+ else if (channel >= 100 && channel <= 140) -+ radar_type = RADAR_TYPE_CODE_56; -+ else -+ radar_type = RADAR_TYPE_CODE_0; -+ } else if (priv->dfs_region == NL80211_DFS_ETSI) { -+ radar_type = RADAR_TYPE_CODE_ETSI; -+ } -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11H_DETECT_RADAR); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(action); -+ pcmd->radar_type_code = cpu_to_le16(radar_type); -+ pcmd->min_chirp_cnt = cpu_to_le16(priv->dfs_chirp_count_min); -+ pcmd->chirp_time_intvl = cpu_to_le16(priv->dfs_chirp_time_interval); -+ pcmd->pw_filter = cpu_to_le16(priv->dfs_pw_filter); -+ pcmd->min_num_radar = cpu_to_le16(priv->dfs_min_num_radar); -+ pcmd->pri_min_num = cpu_to_le16(priv->dfs_min_pri_count); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11H_DETECT_RADAR)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_wmm_mode *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_wmm_mode *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_WMM_MODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_WMM_MODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_ht_guard_interval *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_ht_guard_interval *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_HT_GUARD_INTERVAL); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le32(WL_SET); -+ pcmd->gi_type = cpu_to_le32(gi_type); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_HT_GUARD_INTERVAL)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_fixed_rate *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_fixed_rate *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_FIXED_RATE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ pcmd->action = cpu_to_le32(HOSTCMD_ACT_NOT_USE_FIXED_RATE); -+ pcmd->multicast_rate = mcast; -+ pcmd->management_rate = mgmt; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_FIXED_RATE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, u16 cs_mode) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_linkadapt_cs_mode *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_linkadapt_cs_mode *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_LINKADAPT_CS_MODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); -+ pcmd->cs_mode = cpu_to_le16(cs_mode); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_LINKADAPT_CS_MODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_dump_otp_data(struct ieee80211_hw *hw) -+{ -+ int otp_data_len; -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_dump_otp_data *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_dump_otp_data *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DUMP_OTP_DATA); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_DUMP_OTP_DATA)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ otp_data_len = pcmd->cmd_hdr.len - cpu_to_le16(sizeof(*pcmd)); -+ -+ if (otp_data_len <= SYSADPT_OTP_BUF_SIZE) { -+ wiphy_info(hw->wiphy, "OTP data len = %d\n", otp_data_len); -+ priv->otp_data.len = otp_data_len; -+ memcpy(priv->otp_data.buf, pcmd->pload, otp_data_len); -+ } else { -+ wiphy_err(hw->wiphy, "Driver OTP buf size is less\n"); -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_rate_adapt_mode *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_rate_adapt_mode *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RATE_ADAPT_MODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(WL_SET); -+ pcmd->rate_adapt_mode = cpu_to_le16(mode); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_RATE_ADAPT_MODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *mac_addr) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_mac_addr *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_MAC_ADDR); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ pcmd->mac_type = cpu_to_le16(WL_MAC_TYPE_SECONDARY_CLIENT); -+ ether_addr_copy(pcmd->mac_addr, mac_addr); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_MAC_ADDR)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_watchdog_bitmap *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_get_watchdog_bitmap *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_WATCHDOG_BITMAP); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_WATCHDOG_BITMAP)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ *bitmap = pcmd->watchdog_bitmap; -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *mac_addr) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_mac_addr *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DEL_MAC_ADDR); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ ether_addr_copy(pcmd->mac_addr, mac_addr); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_DEL_MAC_ADDR)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_bss_start(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, bool enable) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_bss_start *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ if (enable && (priv->running_bsses & (1 << mwl_vif->macid))) -+ return 0; -+ -+ if (!enable && !(priv->running_bsses & (1 << mwl_vif->macid))) -+ return 0; -+ -+ pcmd = (struct hostcmd_cmd_bss_start *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BSS_START); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ if (enable) { -+ pcmd->enable = cpu_to_le32(WL_ENABLE); -+ } else { -+ if (mwl_vif->macid == 0) -+ pcmd->enable = cpu_to_le32(WL_DISABLE); -+ else -+ pcmd->enable = cpu_to_le32(WL_DISABLE_VMAC); -+ } -+ if (priv->chip_type == MWL8964) -+ pcmd->amsdu = MWL_AMSDU_SIZE_11K; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BSS_START)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (enable) -+ priv->running_bsses |= (1 << mwl_vif->macid); -+ else -+ priv->running_bsses &= ~(1 << mwl_vif->macid); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *beacon, int len) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct beacon_info *b_inf; -+ int rc; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ b_inf = &mwl_vif->beacon_info; -+ -+ mwl_fwcmd_parse_beacon(priv, mwl_vif, beacon, len); -+ -+ if (!b_inf->valid) -+ goto err; -+ -+ if (mwl_fwcmd_set_ies(priv, mwl_vif)) -+ goto err; -+ -+ if (mwl_fwcmd_set_wsc_ie(hw, b_inf->ie_wsc_len, b_inf->ie_wsc_ptr)) -+ goto err; -+ -+ if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, &vif->bss_conf)) -+ goto err; -+ -+ if (b_inf->cap_info & WLAN_CAPABILITY_SPECTRUM_MGMT) -+ rc = mwl_fwcmd_set_spectrum_mgmt(priv, true); -+ else -+ rc = mwl_fwcmd_set_spectrum_mgmt(priv, false); -+ if (rc) -+ goto err; -+ -+ if (b_inf->power_constraint) -+ rc = mwl_fwcmd_set_power_constraint(priv, -+ b_inf->power_constraint); -+ if (rc) -+ goto err; -+ -+ if (mwl_fwcmd_set_country_code(priv, mwl_vif, &vif->bss_conf)) -+ goto err; -+ -+ b_inf->valid = false; -+ -+ return 0; -+ -+err: -+ -+ b_inf->valid = false; -+ -+ return -EIO; -+} -+ -+int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct mwl_sta *sta_info; -+ struct hostcmd_cmd_set_new_stn *pcmd; -+ u32 rates; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ sta_info = mwl_dev_get_sta(sta); -+ -+ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD); -+ pcmd->aid = cpu_to_le16(sta->aid); -+ pcmd->stn_id = cpu_to_le16(sta_info->stnid); -+ if (priv->chip_type == MWL8997) -+ pcmd->if_type = cpu_to_le16(vif->type); -+ else -+ pcmd->if_type = cpu_to_le16(1); -+ ether_addr_copy(pcmd->mac_addr, sta->addr); -+ -+ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) -+ rates = sta->supp_rates[NL80211_BAND_2GHZ]; -+ else -+ rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5; -+ pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates); -+ -+ if (sta->ht_cap.ht_supported) { -+ int i; -+ -+ for (i = 0; i < 4; i++) { -+ if (i < sta->rx_nss) { -+ pcmd->peer_info.ht_rates[i] = -+ sta->ht_cap.mcs.rx_mask[i]; -+ } else { -+ pcmd->peer_info.ht_rates[i] = 0; -+ } -+ } -+ pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap); -+ pcmd->peer_info.mac_ht_param_info = -+ (sta->ht_cap.ampdu_factor & 3) | -+ ((sta->ht_cap.ampdu_density & 7) << 2); -+ } -+ -+ if (sta->vht_cap.vht_supported) { -+ u32 rx_mcs_map_mask = 0; -+ -+ rx_mcs_map_mask = ((0x0000FFFF) >> (sta->rx_nss * 2)) -+ << (sta->rx_nss * 2); -+ pcmd->peer_info.vht_max_rx_mcs = -+ cpu_to_le32((*((u32 *) -+ &sta->vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask); -+ pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap); -+ pcmd->peer_info.vht_rx_channel_width = sta->bandwidth; -+ } -+ -+ pcmd->is_qos_sta = sta->wme; -+ pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (vif->type == NL80211_IFTYPE_STATION) { -+ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); -+ pcmd->aid = cpu_to_le16(sta->aid + 1); -+ pcmd->stn_id = cpu_to_le16(sta_info->sta_stnid); -+ pcmd->if_type = cpu_to_le16(0); -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_new_stn_add_sc4(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta, -+ u32 wds) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct mwl_sta *sta_info; -+ struct hostcmd_cmd_set_new_stn_sc4 *pcmd; -+ u32 rates; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ sta_info = mwl_dev_get_sta(sta); -+ -+ pcmd = (struct hostcmd_cmd_set_new_stn_sc4 *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD); -+ pcmd->aid = cpu_to_le16(sta->aid); -+ pcmd->stn_id = cpu_to_le16(sta_info->stnid); -+ ether_addr_copy(pcmd->mac_addr, sta->addr); -+ -+ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) -+ rates = sta->supp_rates[NL80211_BAND_2GHZ]; -+ else -+ rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5; -+ pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates); -+ -+ if (sta->ht_cap.ht_supported) { -+ int i; -+ -+ for (i = 0; i < 4; i++) { -+ if (i < sta->rx_nss) { -+ pcmd->peer_info.ht_rates[i] = -+ sta->ht_cap.mcs.rx_mask[i]; -+ } else { -+ pcmd->peer_info.ht_rates[i] = 0; -+ } -+ } -+ pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap); -+ pcmd->peer_info.mac_ht_param_info = -+ (sta->ht_cap.ampdu_factor & 3) | -+ ((sta->ht_cap.ampdu_density & 7) << 2); -+ } -+ -+ if (sta->vht_cap.vht_supported) { -+ u32 rx_mcs_map_mask = 0; -+ -+ rx_mcs_map_mask = ((0x0000FFFF) >> (sta->rx_nss * 2)) -+ << (sta->rx_nss * 2); -+ pcmd->peer_info.vht_max_rx_mcs = -+ cpu_to_le32((*((u32 *) -+ &sta->vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask); -+ pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap); -+ pcmd->peer_info.vht_rx_channel_width = sta->bandwidth; -+ } -+ -+ pcmd->is_qos_sta = sta->wme; -+ pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1)); -+ pcmd->wds = cpu_to_le32(wds); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (vif->type == NL80211_IFTYPE_STATION) { -+ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); -+ pcmd->aid = cpu_to_le16(sta->aid + 1); -+ pcmd->stn_id = cpu_to_le16(sta_info->sta_stnid); -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_new_stn_wds_sc4(struct ieee80211_hw *hw, u8 *addr) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_new_stn_sc4 *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_new_stn_sc4 *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_MODIFY); -+ ether_addr_copy(pcmd->mac_addr, addr); -+ pcmd->wds = cpu_to_le32(WDS_MODE); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_new_stn *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ if (priv->chip_type == MWL8964) { -+ memset(pcmd, 0x00, sizeof(struct hostcmd_cmd_set_new_stn_sc4)); -+ pcmd->cmd_hdr.len = -+ cpu_to_le16(sizeof(struct hostcmd_cmd_set_new_stn_sc4)); -+ } else { -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ } -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD); -+ ether_addr_copy(pcmd->mac_addr, vif->addr); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *addr) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_new_stn *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ if (priv->chip_type == MWL8964) { -+ memset(pcmd, 0x00, sizeof(struct hostcmd_cmd_set_new_stn_sc4)); -+ pcmd->cmd_hdr.len = -+ cpu_to_le16(sizeof(struct hostcmd_cmd_set_new_stn_sc4)); -+ } else { -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ } -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_REMOVE); -+ ether_addr_copy(pcmd->mac_addr, addr); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (vif->type == NL80211_IFTYPE_STATION) { -+ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_NEW_STN)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_apmode *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_apmode *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_APMODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->apmode = apmode; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_APMODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_switch_channel(struct ieee80211_hw *hw, -+ struct ieee80211_channel_switch *ch_switch) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_switch_channel *pcmd; -+ struct cfg80211_chan_def *chandef = &ch_switch->chandef; -+ struct ieee80211_channel *channel = chandef->chan; -+ u32 chnl_flags, freq_band, chnl_width, act_primary, sec_chnl_offset; -+ -+ if (priv->csa_active) -+ return 0; -+ -+ if (channel->band == NL80211_BAND_2GHZ) -+ freq_band = FREQ_BAND_2DOT4GHZ; -+ else if (channel->band == NL80211_BAND_5GHZ) -+ freq_band = FREQ_BAND_5GHZ; -+ else -+ return -EINVAL; -+ -+ switch (chandef->width) { -+ case NL80211_CHAN_WIDTH_20_NOHT: -+ case NL80211_CHAN_WIDTH_20: -+ chnl_width = CH_20_MHZ_WIDTH; -+ act_primary = ACT_PRIMARY_CHAN_0; -+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; -+ break; -+ case NL80211_CHAN_WIDTH_40: -+ chnl_width = CH_40_MHZ_WIDTH; -+ if (chandef->center_freq1 > channel->center_freq) { -+ act_primary = ACT_PRIMARY_CHAN_0; -+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; -+ } else { -+ act_primary = ACT_PRIMARY_CHAN_1; -+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; -+ } -+ break; -+ case NL80211_CHAN_WIDTH_80: -+ chnl_width = CH_80_MHZ_WIDTH; -+ act_primary = -+ mwl_fwcmd_get_80m_pri_chnl(channel->hw_value); -+ if ((act_primary == ACT_PRIMARY_CHAN_0) || -+ (act_primary == ACT_PRIMARY_CHAN_2)) -+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; -+ else -+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ chnl_flags = (freq_band & FREQ_BAND_MASK) | -+ ((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) | -+ ((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK); -+ -+ pcmd = (struct hostcmd_cmd_set_switch_channel *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SWITCH_CHANNEL); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->next_11h_chnl = cpu_to_le32(channel->hw_value); -+ pcmd->mode = cpu_to_le32(ch_switch->block_tx); -+ pcmd->init_count = cpu_to_le32(ch_switch->count + 1); -+ pcmd->chnl_flags = cpu_to_le32(chnl_flags); -+ pcmd->next_ht_extchnl_offset = cpu_to_le32(sec_chnl_offset); -+ pcmd->dfs_test_mode = cpu_to_le32(priv->dfs_test); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_SWITCH_CHANNEL)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ priv->csa_active = true; -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ u8 *addr, u8 encr_type) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_update_encryption *pcmd; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_update_encryption *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ pcmd->action_type = cpu_to_le32(ENCR_ACTION_ENABLE_HW_ENCR); -+ ether_addr_copy(pcmd->mac_addr, addr); -+ pcmd->action_data[0] = encr_type; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if ((vif->type == NL80211_IFTYPE_STATION) && -+ (priv->chip_type != MWL8964)) { -+ if (ether_addr_equal(mwl_vif->bssid, addr)) -+ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); -+ else -+ ether_addr_copy(pcmd->mac_addr, mwl_vif->bssid); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *addr, -+ struct ieee80211_key_conf *key) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_key *pcmd; -+ int rc; -+ int keymlen; -+ u32 action; -+ u8 idx; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key); -+ if (rc) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ if (rc != 1) -+ wiphy_err(hw->wiphy, "encryption not support\n"); -+ return rc; -+ } -+ -+ idx = key->keyidx; -+ -+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) -+ action = ENCR_ACTION_TYPE_SET_KEY; -+ else { -+ action = ENCR_ACTION_TYPE_SET_GROUP_KEY; -+ if (vif->type == NL80211_IFTYPE_MESH_POINT && -+ !ether_addr_equal(mwl_vif->bssid, addr)) -+ pcmd->key_param.key_info |= -+ cpu_to_le32(ENCR_KEY_FLAG_RXGROUPKEY); -+ } -+ -+ switch (key->cipher) { -+ case WLAN_CIPHER_SUITE_WEP40: -+ case WLAN_CIPHER_SUITE_WEP104: -+ if (!mwl_vif->wep_key_conf[idx].enabled) { -+ memcpy(mwl_vif->wep_key_conf[idx].key, key, -+ sizeof(*key) + key->keylen); -+ mwl_vif->wep_key_conf[idx].enabled = 1; -+ } -+ -+ keymlen = key->keylen; -+ action = ENCR_ACTION_TYPE_SET_KEY; -+ break; -+ case WLAN_CIPHER_SUITE_TKIP: -+ keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; -+ break; -+ case WLAN_CIPHER_SUITE_CCMP: -+ keymlen = key->keylen; -+ break; -+ default: -+ mutex_unlock(&priv->fwcmd_mutex); -+ wiphy_err(hw->wiphy, "encryption not support\n"); -+ return -ENOTSUPP; -+ } -+ -+ memcpy((void *)&pcmd->key_param.key, key->key, keymlen); -+ pcmd->action_type = cpu_to_le32(action); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (vif->type == NL80211_IFTYPE_STATION) { -+ if (ether_addr_equal(mwl_vif->bssid, addr)) -+ ether_addr_copy(pcmd->key_param.mac_addr, -+ mwl_vif->sta_mac); -+ else -+ ether_addr_copy(pcmd->key_param.mac_addr, -+ mwl_vif->bssid); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *addr, -+ struct ieee80211_key_conf *key) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_set_key *pcmd; -+ int rc; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ -+ rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key); -+ if (rc) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ if (rc != 1) -+ wiphy_err(hw->wiphy, "encryption not support\n"); -+ return rc; -+ } -+ -+ pcmd->action_type = cpu_to_le32(ENCR_ACTION_TYPE_REMOVE_KEY); -+ -+ if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || -+ key->cipher == WLAN_CIPHER_SUITE_WEP104) -+ mwl_vif->wep_key_conf[key->keyidx].enabled = 0; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_UPDATE_ENCRYPTION)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_check_ba(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, -+ struct ieee80211_vif *vif, -+ u32 direction) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_bastream *pcmd; -+ u32 ba_flags, ba_type, ba_direction; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ pcmd->cmd_hdr.result = cpu_to_le16(0xffff); -+ -+ pcmd->action_type = cpu_to_le32(BA_CHECK_STREAM); -+ ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0], -+ stream->sta->addr); -+ pcmd->ba_info.create_params.tid = stream->tid; -+ ba_type = BA_FLAG_IMMEDIATE_TYPE; -+ ba_direction = direction; -+ ba_flags = (ba_type & BA_TYPE_MASK) | -+ ((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK); -+ pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags); -+ pcmd->ba_info.create_params.queue_id = stream->idx; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BASTREAM)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (pcmd->cmd_hdr.result != 0) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EINVAL; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_create_ba(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, -+ struct ieee80211_vif *vif, -+ u32 direction, u8 buf_size, u16 seqno, bool amsdu) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ struct hostcmd_cmd_bastream *pcmd; -+ u32 ba_flags, ba_type, ba_direction; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_hdr.macid = mwl_vif->macid; -+ pcmd->cmd_hdr.result = cpu_to_le16(0xffff); -+ -+ pcmd->action_type = cpu_to_le32(BA_CREATE_STREAM); -+ pcmd->ba_info.create_params.bar_thrs = cpu_to_le32(buf_size); -+ pcmd->ba_info.create_params.window_size = cpu_to_le32(buf_size); -+ pcmd->ba_info.create_params.idle_thrs = cpu_to_le32(0x22000); -+ ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0], -+ stream->sta->addr); -+ pcmd->ba_info.create_params.tid = stream->tid; -+ ba_direction = direction; -+ if (priv->chip_type == MWL8964) { -+ ba_type = amsdu ? MWL_AMSDU_SIZE_11K : 0; -+ ba_flags = (ba_type & BA_TYPE_MASK_NDP) | -+ ((ba_direction << BA_DIRECTION_SHIFT_NDP) & -+ BA_DIRECTION_MASK_NDP); -+ } else { -+ ba_type = BA_FLAG_IMMEDIATE_TYPE; -+ ba_flags = (ba_type & BA_TYPE_MASK) | -+ ((ba_direction << BA_DIRECTION_SHIFT) & -+ BA_DIRECTION_MASK); -+ } -+ pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags); -+ pcmd->ba_info.create_params.queue_id = stream->idx; -+ pcmd->ba_info.create_params.param_info = -+ (stream->sta->ht_cap.ampdu_factor & -+ IEEE80211_HT_AMPDU_PARM_FACTOR) | -+ ((stream->sta->ht_cap.ampdu_density << 2) & -+ IEEE80211_HT_AMPDU_PARM_DENSITY); -+ if (direction == BA_FLAG_DIRECTION_UP) { -+ pcmd->ba_info.create_params.reset_seq_no = 0; -+ pcmd->ba_info.create_params.current_seq = cpu_to_le16(seqno); -+ } else { -+ pcmd->ba_info.create_params.reset_seq_no = 1; -+ pcmd->ba_info.create_params.current_seq = cpu_to_le16(0); -+ } -+ if (priv->chip_type == MWL8964 && -+ stream->sta->vht_cap.vht_supported) { -+ pcmd->ba_info.create_params.vht_rx_factor = -+ cpu_to_le32((stream->sta->vht_cap.cap & -+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >> -+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT); -+ } -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BASTREAM)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (pcmd->cmd_hdr.result != 0) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ wiphy_err(hw->wiphy, "create ba result error %d\n", -+ le16_to_cpu(pcmd->cmd_hdr.result)); -+ return -EINVAL; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, -+ u32 direction) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_bastream *pcmd; -+ u32 ba_flags, ba_type, ba_direction; -+ -+ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ pcmd->action_type = cpu_to_le32(BA_DESTROY_STREAM); -+ ba_type = 0; -+ ba_direction = direction; -+ if (priv->chip_type == MWL8964) -+ ba_flags = (ba_type & BA_TYPE_MASK_NDP) | -+ ((ba_direction << BA_DIRECTION_SHIFT_NDP) & -+ BA_DIRECTION_MASK_NDP); -+ else -+ ba_flags = (ba_type & BA_TYPE_MASK) | -+ ((ba_direction << BA_DIRECTION_SHIFT) & -+ BA_DIRECTION_MASK); -+ pcmd->ba_info.destroy_params.flags = cpu_to_le32(ba_flags); -+ pcmd->ba_info.destroy_params.fw_ba_context.context = -+ cpu_to_le32(stream->idx); -+ pcmd->ba_info.destroy_params.tid = stream->tid; -+ ether_addr_copy(&pcmd->ba_info.destroy_params.peer_mac_addr[0], -+ stream->sta->addr); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_BASTREAM)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+/* caller must hold priv->stream_lock when calling the stream functions */ -+struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, -+ u8 tid) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_ampdu_stream *stream; -+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); -+ int idx; -+ -+ if (priv->chip_type == MWL8964) { -+ idx = ((sta_info->stnid - 1) * SYSADPT_MAX_TID) + tid; -+ -+ if (idx < priv->ampdu_num) { -+ stream = &priv->ampdu[idx]; -+ stream->sta = sta; -+ stream->state = AMPDU_STREAM_NEW; -+ stream->tid = tid; -+ stream->idx = idx; -+ return stream; -+ } -+ } else { -+ for (idx = 0; idx < priv->ampdu_num; idx++) { -+ stream = &priv->ampdu[idx]; -+ -+ if (stream->state == AMPDU_NO_STREAM) { -+ stream->sta = sta; -+ stream->state = AMPDU_STREAM_NEW; -+ stream->tid = tid; -+ stream->idx = idx; -+ return stream; -+ } -+ } -+ } -+ -+ return NULL; -+} -+ -+void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_ampdu_stream *stream; -+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); -+ int i, idx; -+ -+ spin_lock_bh(&priv->stream_lock); -+ if (priv->chip_type == MWL8964) { -+ idx = (sta_info->stnid - 1) * SYSADPT_MAX_TID; -+ for (i = 0; i < SYSADPT_MAX_TID; i++) { -+ stream = &priv->ampdu[idx + i]; -+ -+ if (stream->sta == sta) { -+ spin_unlock_bh(&priv->stream_lock); -+ mwl_fwcmd_destroy_ba(hw, stream, -+ BA_FLAG_DIRECTION_UP); -+ spin_lock_bh(&priv->stream_lock); -+ mwl_fwcmd_remove_stream(hw, stream); -+ } -+ } -+ } else { -+ for (idx = 0; idx < priv->ampdu_num; idx++) { -+ stream = &priv->ampdu[idx]; -+ -+ if (stream->sta == sta) { -+ spin_unlock_bh(&priv->stream_lock); -+ mwl_fwcmd_destroy_ba(hw, stream, -+ BA_FLAG_DIRECTION_UP); -+ spin_lock_bh(&priv->stream_lock); -+ mwl_fwcmd_remove_stream(hw, stream); -+ } -+ } -+ } -+ spin_unlock_bh(&priv->stream_lock); -+} -+ -+int mwl_fwcmd_start_stream(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream) -+{ -+ /* if the stream has already been started, don't start it again */ -+ if (stream->state != AMPDU_STREAM_NEW) -+ return 0; -+ -+ return ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0); -+} -+ -+void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream) -+{ -+ memset(stream, 0, sizeof(*stream)); -+} -+ -+struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, -+ u8 tid) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_ampdu_stream *stream; -+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); -+ int idx; -+ -+ if (priv->chip_type == MWL8964) { -+ idx = ((sta_info->stnid - 1) * SYSADPT_MAX_TID) + tid; -+ if (idx < priv->ampdu_num) -+ return &priv->ampdu[idx]; -+ } else { -+ for (idx = 0; idx < priv->ampdu_num; idx++) { -+ stream = &priv->ampdu[idx]; -+ if (stream->state == AMPDU_NO_STREAM) -+ continue; -+ -+ if ((stream->sta == sta) && (stream->tid == tid)) -+ return stream; -+ } -+ } -+ -+ return NULL; -+} -+ -+bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) -+{ -+ struct mwl_sta *sta_info; -+ struct mwl_tx_info *tx_stats; -+ -+ if (WARN_ON(tid >= SYSADPT_MAX_TID)) -+ return false; -+ -+ sta_info = mwl_dev_get_sta(sta); -+ -+ tx_stats = &sta_info->tx_stats[tid]; -+ -+ return (sta_info->is_ampdu_allowed && -+ tx_stats->pkts > SYSADPT_AMPDU_PACKET_THRESHOLD); -+} -+ -+int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_optimization_level *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_optimization_level *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->opt_level = opt_level; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_wsc_ie(struct ieee80211_hw *hw, u8 len, u8 *data) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_wsc_ie *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_wsc_ie *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_WSC_IE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->len = cpu_to_le16(len); -+ memcpy(pcmd->data, data, len); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_WSC_IE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ pcmd->ie_type = cpu_to_le16(WSC_IE_SET_PROBE_RESPONSE); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_WSC_IE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_ratetable(struct ieee80211_hw *hw, u8 *addr, u8 *rate_table, -+ u32 size, u8 type) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_ratetable *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_get_ratetable *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_RATETABLE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->type = type; -+ ether_addr_copy(pcmd->addr, addr); -+ memset(rate_table, 0x00, size); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_RATETABLE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ memcpy(rate_table, &pcmd->sorted_rates_idx_map, size); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_seqno(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, u16 *start_seqno) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_seqno *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_get_seqno *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_SEQNO); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ ether_addr_copy(pcmd->mac_addr, stream->sta->addr); -+ pcmd->tid = stream->tid; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_SEQNO)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ *start_seqno = le16_to_cpu(pcmd->seq_no); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_dwds_enable *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_dwds_enable *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DWDS_ENABLE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->enable = cpu_to_le32(enable); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_DWDS_ENABLE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_fw_flush_timer *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_fw_flush_timer *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_FW_FLUSH_TIMER); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->value = cpu_to_le32(value); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_FW_FLUSH_TIMER)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_cdd *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_cdd *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_CDD); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->enable = cpu_to_le32(priv->cdd); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_CDD)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_bftype(struct ieee80211_hw *hw, int mode) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_bftype *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_bftype *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_BFTYPE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le32(WL_SET); -+ pcmd->mode = cpu_to_le32(mode); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_BFTYPE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_reg_cau(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_bbp_reg_access *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_bbp_reg_access *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_CAU_REG_ACCESS); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->offset = cpu_to_le16(reg); -+ pcmd->action = cpu_to_le16(flag); -+ pcmd->value = *val; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_CAU_REG_ACCESS)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ *val = pcmd->value; -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_temp *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_get_temp *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_TEMP); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_TEMP)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ *temp = le32_to_cpu(pcmd->celcius); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_led_ctrl(struct ieee80211_hw *hw, u8 enable, u8 rate) -+{ -+ struct hostcmd_cmd_led_ctrl *pcmd; -+ struct mwl_priv *priv = hw->priv; -+ -+ pcmd = (struct hostcmd_cmd_led_ctrl *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_LED_CTRL); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = 1; /* 1: set */ -+ pcmd->led_enable = enable; -+ pcmd->led_control = 1; /* 1: SW */ -+ -+ switch (rate) { -+ case LED_BLINK_RATE_LOW: -+ case LED_BLINK_RATE_MID: -+ case LED_BLINK_RATE_HIGH: -+ pcmd->led_blink_rate = rate; -+ break; -+ default: -+ if (enable) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EINVAL; -+ } -+ break; -+ } -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_LED_CTRL)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw, -+ u32 *fw_region_code) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_fw_region_code *pcmd; -+ u16 cmd; -+ int status; -+ -+ pcmd = (struct hostcmd_cmd_get_fw_region_code *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ cmd = HOSTCMD_CMD_GET_FW_REGION_CODE; -+ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, cmd)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (pcmd->cmd_hdr.result != 0) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EINVAL; -+ } -+ -+ status = le32_to_cpu(pcmd->status); -+ -+ if (!status) -+ *fw_region_code = le32_to_cpu(pcmd->fw_region_code); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw, -+ struct mwl_device_pwr_tbl *device_ch_pwrtbl, -+ u8 *region_code, -+ u8 *number_of_channels, -+ u32 channel_index) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_device_pwr_tbl *pcmd; -+ int status; -+ u16 cmd; -+ -+ pcmd = (struct hostcmd_cmd_get_device_pwr_tbl *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ cmd = HOSTCMD_CMD_GET_DEVICE_PWR_TBL; -+ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->status = cpu_to_le16(cmd); -+ pcmd->current_channel_index = cpu_to_le32(channel_index); -+ -+ if (mwl_hif_exec_cmd(hw, cmd)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ device_ch_pwrtbl->channel = pcmd->channel_pwr_tbl.channel; -+ memcpy(device_ch_pwrtbl->tx_pwr, pcmd->channel_pwr_tbl.tx_pwr, -+ priv->pwr_level); -+ device_ch_pwrtbl->dfs_capable = pcmd->channel_pwr_tbl.dfs_capable; -+ device_ch_pwrtbl->ax_ant = pcmd->channel_pwr_tbl.ax_ant; -+ device_ch_pwrtbl->cdd = pcmd->channel_pwr_tbl.cdd; -+ *region_code = pcmd->region_code; -+ *number_of_channels = pcmd->number_of_channels; -+ status = le16_to_cpu(pcmd->status); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return status; -+} -+ -+int mwl_fwcmd_set_rate_drop(struct ieee80211_hw *hw, int enable, -+ int value, int staid) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_set_rate_drop *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_set_rate_drop *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RATE_DROP); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->enable = cpu_to_le32(enable); -+ pcmd->rate_index = cpu_to_le32(value); -+ pcmd->sta_index = cpu_to_le32(staid); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_SET_RATE_DROP)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_newdp_dmathread_start(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_newdp_dmathread_start *pcmd; -+ u16 cmd; -+ -+ pcmd = (struct hostcmd_cmd_newdp_dmathread_start *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ cmd = HOSTCMD_CMD_NEWDP_DMATHREAD_START; -+ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, cmd)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+ -+int mwl_fwcmd_get_fw_region_code_sc4(struct ieee80211_hw *hw, -+ u32 *fw_region_code) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_fw_region_code_sc4 *pcmd; -+ u16 cmd; -+ -+ pcmd = (struct hostcmd_cmd_get_fw_region_code_sc4 *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ cmd = HOSTCMD_CMD_GET_FW_REGION_CODE_SC4; -+ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, cmd)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ if (pcmd->cmd_hdr.result != 0) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EINVAL; -+ } -+ -+ if (pcmd->status) -+ *fw_region_code = (pcmd->status == 1) ? 0 : pcmd->status; -+ else -+ *fw_region_code = le32_to_cpu(pcmd->fw_region_code); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_pwr_tbl_sc4(struct ieee80211_hw *hw, -+ struct mwl_device_pwr_tbl *device_ch_pwrtbl, -+ u8 *region_code, -+ u8 *number_of_channels, -+ u32 channel_index) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_device_pwr_tbl_sc4 *pcmd; -+ int status; -+ u16 cmd; -+ -+ pcmd = (struct hostcmd_cmd_get_device_pwr_tbl_sc4 *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ cmd = HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4; -+ pcmd->cmd_hdr.cmd = cpu_to_le16(cmd); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->status = cpu_to_le16(cmd); -+ pcmd->current_channel_index = cpu_to_le32(channel_index); -+ -+ if (mwl_hif_exec_cmd(hw, cmd)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ device_ch_pwrtbl->channel = pcmd->channel_pwr_tbl.channel; -+ memcpy(device_ch_pwrtbl->tx_pwr, pcmd->channel_pwr_tbl.tx_pwr, -+ SYSADPT_TX_PWR_LEVEL_TOTAL_SC4); -+ device_ch_pwrtbl->dfs_capable = pcmd->channel_pwr_tbl.dfs_capable; -+ device_ch_pwrtbl->ax_ant = pcmd->channel_pwr_tbl.ax_ant; -+ device_ch_pwrtbl->cdd = pcmd->channel_pwr_tbl.cdd; -+ *region_code = pcmd->region_code; -+ *number_of_channels = pcmd->number_of_channels; -+ status = le16_to_cpu(pcmd->status); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return status; -+} -+ -+int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period, -+ u32 duration, u32 next_offset) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_quiet_mode *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_quiet_mode *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_QUIET_MODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(WL_SET); -+ pcmd->enable = cpu_to_le32(enable); -+ if (enable) { -+ pcmd->period = cpu_to_le32(period); -+ pcmd->duration = cpu_to_le32(duration); -+ pcmd->next_offset = cpu_to_le32(next_offset); -+ } -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_QUIET_MODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_core_dump_diag_mode(struct ieee80211_hw *hw, u16 status) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_core_dump_diag_mode *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_core_dump_diag_mode *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_CORE_DUMP_DIAG_MODE); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->status = cpu_to_le16(status); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_CORE_DUMP_DIAG_MODE)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_fw_core_dump(struct ieee80211_hw *hw, -+ struct coredump_cmd *core_dump, char *buff) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_get_fw_core_dump *pcmd; -+ -+ if (priv->chip_type != MWL8964) -+ return -EPERM; -+ -+ pcmd = (struct hostcmd_cmd_get_fw_core_dump *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_FW_CORE_DUMP); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->cmd_data.coredump.context = core_dump->context; -+ pcmd->cmd_data.coredump.buffer = cpu_to_le32(priv->pphys_cmd_buf + -+ sizeof(struct hostcmd_cmd_get_fw_core_dump) - -+ sizeof(struct hostcmd_cmd_get_fw_core_dump_)); -+ pcmd->cmd_data.coredump.buffer_len = cpu_to_le32(MAX_CORE_DUMP_BUFFER); -+ pcmd->cmd_data.coredump.size_kb = core_dump->size_kb; -+ pcmd->cmd_data.coredump.flags = core_dump->flags; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_GET_FW_CORE_DUMP)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ /* update core dump buffer */ -+ core_dump->context = pcmd->cmd_data.coredump.context; -+ core_dump->size_kb = pcmd->cmd_data.coredump.size_kb; -+ core_dump->flags = pcmd->cmd_data.coredump.flags; -+ memcpy(buff, -+ (const void *)((u32)pcmd + -+ sizeof(struct hostcmd_cmd_get_fw_core_dump) - -+ sizeof(struct hostcmd_cmd_get_fw_core_dump_)), -+ MAX_CORE_DUMP_BUFFER); -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_slot_time(struct ieee80211_hw *hw, bool short_slot) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_802_11_slot_time *pcmd; -+ -+ wiphy_debug(priv->hw->wiphy, "%s(): short_slot_time=%d\n", -+ __func__, short_slot); -+ -+ pcmd = (struct hostcmd_cmd_802_11_slot_time *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_SLOT_TIME); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(WL_SET); -+ pcmd->short_slot = cpu_to_le16(short_slot ? 1 : 0); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_802_11_SLOT_TIME)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_config_EDMACCtrl(struct ieee80211_hw *hw, int EDMAC_Ctrl) -+{ -+ struct hostcmd_cmd_edmac_ctrl *pcmd; -+ struct mwl_priv *priv = hw->priv; -+ -+ pcmd = (struct hostcmd_cmd_edmac_ctrl *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_EDMAC_CTRL); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(WL_SET); -+ pcmd->ed_ctrl_2g = cpu_to_le16((EDMAC_Ctrl & EDMAC_2G_ENABLE_MASK) -+ >> EDMAC_2G_ENABLE_SHIFT); -+ pcmd->ed_ctrl_5g = cpu_to_le16((EDMAC_Ctrl & EDMAC_5G_ENABLE_MASK) -+ >> EDMAC_5G_ENABLE_SHIFT); -+ pcmd->ed_offset_2g = cpu_to_le16((EDMAC_Ctrl & -+ EDMAC_2G_THRESHOLD_OFFSET_MASK) -+ >> EDMAC_2G_THRESHOLD_OFFSET_SHIFT); -+ pcmd->ed_offset_5g = cpu_to_le16((EDMAC_Ctrl & -+ EDMAC_5G_THRESHOLD_OFFSET_MASK) -+ >> EDMAC_5G_THRESHOLD_OFFSET_SHIFT); -+ pcmd->ed_bitmap_txq_lock = cpu_to_le16((EDMAC_Ctrl & -+ EDMAC_QLOCK_BITMAP_MASK) -+ >> EDMAC_QLOCK_BITMAP_SHIFT); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_EDMAC_CTRL)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+int mwl_fwcmd_set_txpwrlmt_cfg_data(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_txpwrlmt_cfg *pcmd; -+ struct mwl_txpwrlmt_cfg_entry_hdr hdr; -+ u16 id, parsed_len, size; -+ __le32 txpwr_cfg_sig; -+ u8 version[TXPWRLMT_CFG_VERSION_INFO_LEN]; -+ const u8 *ptr; -+ -+ if (!priv->txpwrlmt_file) -+ return 0; -+ -+ ptr = priv->txpwrlmt_file->data; -+ size = priv->txpwrlmt_file->size; -+ -+ /* Parsing TxPwrLmit Conf file Signature */ -+ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, -+ TXPWRLMT_CFG_SIG_LEN, -+ (u8 *)&txpwr_cfg_sig); -+ ptr += parsed_len; -+ size -= parsed_len; -+ -+ if (le32_to_cpu(txpwr_cfg_sig) != TXPWRLMT_CFG_SIGNATURE) { -+ wiphy_err(hw->wiphy, -+ "txpwrlmt config signature mismatch\n"); -+ release_firmware(priv->txpwrlmt_file); -+ priv->txpwrlmt_file = NULL; -+ return 0; -+ } -+ -+ /* Parsing TxPwrLmit Conf file Version */ -+ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, -+ TXPWRLMT_CFG_VERSION_INFO_LEN, -+ version); -+ ptr += parsed_len; -+ size -= parsed_len; -+ -+ for (id = 0; id < TXPWRLMT_CFG_MAX_SUBBAND_INFO; id++) { -+ u16 data_len; -+ -+ /*Parsing tx pwr cfg subband header info*/ -+ parsed_len = sizeof(struct mwl_txpwrlmt_cfg_entry_hdr); -+ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, -+ parsed_len, -+ (u8 *)&hdr); -+ ptr += parsed_len; -+ size -= parsed_len; -+ data_len = le16_to_cpu(hdr.len) - -+ sizeof(struct mwl_txpwrlmt_cfg_entry_hdr); -+ -+ pcmd = (struct hostcmd_cmd_txpwrlmt_cfg *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); -+ pcmd->subband_id = hdr.id; -+ pcmd->data_len = cpu_to_le16(data_len); -+ pcmd->num_entries = hdr.num_entries; -+ -+ /* Parsing tx pwr cfg subband header info */ -+ parsed_len = mwl_fwcmd_parse_txpwrlmt_cfg(ptr, size, -+ data_len, pcmd->data); -+ ptr += parsed_len; -+ size -= parsed_len; -+ -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_TXPWRLMT_CFG); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd) + -+ data_len - sizeof(pcmd->data)); -+ -+ if (size < sizeof(struct mwl_txpwrlmt_cfg_entry_hdr)) -+ pcmd->cfgComplete = 1; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_TXPWRLMT_CFG)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ release_firmware(priv->txpwrlmt_file); -+ priv->txpwrlmt_file = NULL; -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ } -+ -+ release_firmware(priv->txpwrlmt_file); -+ priv->txpwrlmt_file = NULL; -+ -+ return 0; -+} -+ -+int mwl_fwcmd_get_txpwrlmt_cfg_data(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_txpwrlmt_cfg *pcmd; -+ u16 subband_len, total_len = 0; -+ u8 id; -+ -+ for (id = 0; id < TXPWRLMT_CFG_MAX_SUBBAND_INFO; id++) { -+ pcmd = (struct hostcmd_cmd_txpwrlmt_cfg *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->action = 0; -+ pcmd->subband_id = id; -+ pcmd->data_len = 0; -+ pcmd->num_entries = 0; -+ -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_TXPWRLMT_CFG); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_TXPWRLMT_CFG)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ subband_len = le16_to_cpu(pcmd->cmd_hdr.len) - -+ sizeof(struct hostcmd_header) - 2; -+ if (total_len <= SYSADPT_TXPWRLMT_CFG_BUF_SIZE) { -+ wiphy_debug(hw->wiphy, "Subband len = %d\n", -+ subband_len); -+ memcpy(priv->txpwrlmt_data.buf + total_len, -+ &pcmd->subband_id, subband_len); -+ total_len += subband_len; -+ priv->txpwrlmt_data.buf[total_len] = '\n'; -+ total_len++; -+ priv->txpwrlmt_data.len = total_len; -+ } else { -+ wiphy_err(hw->wiphy, -+ "TxPwrLmt cfg buf size is not enough\n"); -+ } -+ } -+ -+ return 0; -+} -+ -+int mwl_fwcmd_mcast_cts(struct ieee80211_hw *hw, u8 enable) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct hostcmd_cmd_mcast_cts *pcmd; -+ -+ pcmd = (struct hostcmd_cmd_mcast_cts *)&priv->pcmd_buf[0]; -+ -+ mutex_lock(&priv->fwcmd_mutex); -+ -+ memset(pcmd, 0x00, sizeof(*pcmd)); -+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_MCAST_CTS); -+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); -+ pcmd->enable = enable; -+ -+ if (mwl_hif_exec_cmd(hw, HOSTCMD_CMD_MCAST_CTS)) { -+ mutex_unlock(&priv->fwcmd_mutex); -+ return -EIO; -+ } -+ -+ mutex_unlock(&priv->fwcmd_mutex); -+ -+ return 0; -+} -+ -+void mwl_fwcmd_get_survey(struct ieee80211_hw *hw, int idx) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct ieee80211_conf *conf = &hw->conf; -+ struct mwl_survey_info *survey_info; -+ -+ if (idx) -+ survey_info = &priv->survey_info[idx - 1]; -+ else -+ survey_info = &priv->cur_survey_info; -+ -+ memcpy(&survey_info->channel, conf->chandef.chan, -+ sizeof(struct ieee80211_channel)); -+ mwl_hif_get_survey(hw, survey_info); -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h -new file mode 100644 -index 000000000000..9565cc447dc6 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/fwcmd.h -@@ -0,0 +1,285 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines firmware host command related -+ * functions. -+ */ -+ -+#ifndef _FWCMD_H_ -+#define _FWCMD_H_ -+ -+#include "hif/hostcmd.h" -+ -+/* Define OpMode for SoftAP/Station mode -+ * -+ * The following mode signature has to be written to PCI scratch register#0 -+ * right after successfully downloading the last block of firmware and -+ * before waiting for firmware ready signature -+ */ -+ -+#define HOSTCMD_STA_MODE 0x5A -+#define HOSTCMD_SOFTAP_MODE 0xA5 -+ -+#define HOSTCMD_STA_FWRDY_SIGNATURE 0xF0F1F2F4 -+#define HOSTCMD_SOFTAP_FWRDY_SIGNATURE 0xF1F2F4A5 -+ -+#define GUARD_INTERVAL_STANDARD 1 -+#define GUARD_INTERVAL_SHORT 2 -+#define GUARD_INTERVAL_AUTO 3 -+ -+#define LINK_CS_STATE_CONSERV 0 -+#define LINK_CS_STATE_AGGR 1 -+#define LINK_CS_STATE_AUTO 2 -+#define LINK_CS_STATE_AUTO_DISABLED 3 -+ -+#define STOP_DETECT_RADAR 0 -+#define CAC_START 1 -+#define MONITOR_START 3 -+ -+#define WDS_MODE 4 -+ -+enum { -+ WL_ANTENNATYPE_RX = 1, -+ WL_ANTENNATYPE_TX = 2, -+}; -+ -+enum encr_type { -+ ENCR_TYPE_WEP = 0, -+ ENCR_TYPE_DISABLE = 1, -+ ENCR_TYPE_TKIP = 4, -+ ENCR_TYPE_AES = 6, -+ ENCR_TYPE_MIX = 7, -+}; -+ -+char *mwl_fwcmd_get_cmd_string(unsigned short cmd); -+ -+const struct hostcmd_get_hw_spec -+*mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw, -+ struct hostcmd_set_hw_spec *spec); -+ -+int mwl_fwcmd_get_stat(struct ieee80211_hw *hw, -+ struct ieee80211_low_level_stats *stats); -+ -+int mwl_fwcmd_reg_bb(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); -+ -+int mwl_fwcmd_reg_rf(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); -+ -+int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, -+ bool short_preamble); -+ -+int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len, -+ u32 *val, u16 set); -+ -+int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw, -+ struct ieee80211_conf *conf, u8 fraction); -+ -+int mwl_fwcmd_tx_power(struct ieee80211_hw *hw, -+ struct ieee80211_conf *conf, u8 fraction); -+ -+int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna); -+ -+int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, bool enable); -+ -+int mwl_fwcmd_set_cfg_data(struct ieee80211_hw *hw, u16 type); -+ -+int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw, -+ struct ieee80211_conf *conf); -+ -+int mwl_fwcmd_set_aid(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *bssid, u16 aid); -+ -+int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif); -+ -+int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, -+ int threshold); -+ -+int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index, -+ u16 cw_min, u16 cw_max, u8 aifs, u16 txop); -+ -+int mwl_fwcmd_set_radar_detect(struct ieee80211_hw *hw, u16 action); -+ -+int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable); -+ -+int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type); -+ -+int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, -+ int mcast, int mgmt); -+ -+int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, -+ u16 cs_mode); -+ -+int mwl_fwcmd_dump_otp_data(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, -+ u16 mode); -+ -+int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *mac_addr); -+ -+int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, -+ u8 *bitmap); -+ -+int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *mac_addr); -+ -+int mwl_fwcmd_bss_start(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, bool enable); -+ -+int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *beacon, int len); -+ -+int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta); -+ -+int mwl_fwcmd_set_new_stn_add_sc4(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta, -+ u32 wds); -+ -+int mwl_fwcmd_set_new_stn_wds_sc4(struct ieee80211_hw *hw, u8 *addr); -+ -+int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif); -+ -+int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *addr); -+ -+int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode); -+ -+int mwl_fwcmd_set_switch_channel(struct ieee80211_hw *hw, -+ struct ieee80211_channel_switch *ch_switch); -+ -+int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ u8 *addr, u8 encr_type); -+ -+int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *addr, -+ struct ieee80211_key_conf *key); -+ -+int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, u8 *addr, -+ struct ieee80211_key_conf *key); -+ -+int mwl_fwcmd_check_ba(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, -+ struct ieee80211_vif *vif, -+ u32 direction); -+ -+int mwl_fwcmd_create_ba(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, -+ struct ieee80211_vif *vif, -+ u32 direction, u8 buf_size, u16 seqno, bool amsdu); -+ -+int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, -+ u32 direction); -+ -+struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, -+ u8 tid); -+ -+void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta); -+ -+int mwl_fwcmd_start_stream(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream); -+ -+void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream); -+ -+struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, -+ u8 tid); -+ -+bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid); -+ -+int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level); -+ -+int mwl_fwcmd_set_wsc_ie(struct ieee80211_hw *hw, u8 len, u8 *data); -+ -+int mwl_fwcmd_get_ratetable(struct ieee80211_hw *hw, u8 *addr, u8 *rate_table, -+ u32 size, u8 type); -+ -+int mwl_fwcmd_get_seqno(struct ieee80211_hw *hw, -+ struct mwl_ampdu_stream *stream, u16 *start_seqno); -+ -+int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable); -+ -+int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value); -+ -+int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_set_bftype(struct ieee80211_hw *hw, int mode); -+ -+int mwl_fwcmd_reg_cau(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); -+ -+int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp); -+ -+int mwl_fwcmd_led_ctrl(struct ieee80211_hw *hw, u8 enable, u8 rate); -+ -+int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw, -+ u32 *fw_region_code); -+ -+int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw, -+ struct mwl_device_pwr_tbl *device_ch_pwrtbl, -+ u8 *region_code, -+ u8 *number_of_channels, -+ u32 channel_index); -+ -+int mwl_fwcmd_set_rate_drop(struct ieee80211_hw *hw, int enable, -+ int value, int staid); -+ -+int mwl_fwcmd_newdp_dmathread_start(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_get_fw_region_code_sc4(struct ieee80211_hw *hw, -+ u32 *fw_region_code); -+ -+int mwl_fwcmd_get_pwr_tbl_sc4(struct ieee80211_hw *hw, -+ struct mwl_device_pwr_tbl *device_ch_pwrtbl, -+ u8 *region_code, -+ u8 *number_of_channels, -+ u32 channel_index); -+ -+int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period, -+ u32 duration, u32 next_offset); -+ -+int mwl_fwcmd_core_dump_diag_mode(struct ieee80211_hw *hw, u16 status); -+ -+int mwl_fwcmd_get_fw_core_dump(struct ieee80211_hw *hw, -+ struct coredump_cmd *core_dump, char *buff); -+ -+int mwl_fwcmd_set_slot_time(struct ieee80211_hw *hw, bool short_slot); -+ -+int mwl_fwcmd_config_EDMACCtrl(struct ieee80211_hw *hw, int EDMAC_Ctrl); -+ -+int mwl_fwcmd_set_txpwrlmt_cfg_data(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_get_txpwrlmt_cfg_data(struct ieee80211_hw *hw); -+ -+int mwl_fwcmd_mcast_cts(struct ieee80211_hw *hw, u8 enable); -+ -+void mwl_fwcmd_get_survey(struct ieee80211_hw *hw, int idx); -+ -+#endif /* _FWCMD_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h b/drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h -new file mode 100644 -index 000000000000..f5c7144b3c1b ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/hif-ops.h -@@ -0,0 +1,297 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines host interface related operations. */ -+ -+#ifndef _HIF_OPS_H_ -+#define _HIF_OPS_H_ -+static inline const char *mwl_hif_get_driver_name(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ return priv->hif.ops->driver_name; -+} -+ -+static inline const char *mwl_hif_get_driver_version(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ return priv->hif.ops->driver_version; -+} -+ -+static inline unsigned int mwl_hif_get_tx_head_room(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ return priv->hif.ops->tx_head_room; -+} -+ -+static inline unsigned int mwl_hif_get_ampdu_num(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ return priv->hif.ops->ampdu_num; -+} -+ -+static inline void mwl_hif_reset(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->reset) -+ priv->hif.ops->reset(hw); -+} -+ -+static inline int mwl_hif_init(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->init) -+ return priv->hif.ops->init(hw); -+ else -+ return -ENOTSUPP; -+} -+ -+static inline void mwl_hif_deinit(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->deinit) -+ priv->hif.ops->deinit(hw); -+} -+ -+static inline int mwl_hif_get_info(struct ieee80211_hw *hw, -+ char *buf, size_t size) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->get_info) -+ return priv->hif.ops->get_info(hw, buf, size); -+ else -+ return 0; -+} -+ -+static inline int mwl_hif_get_tx_status(struct ieee80211_hw *hw, -+ char *buf, size_t size) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->get_tx_status) -+ return priv->hif.ops->get_tx_status(hw, buf, size); -+ else -+ return 0; -+} -+ -+static inline int mwl_hif_get_rx_status(struct ieee80211_hw *hw, -+ char *buf, size_t size) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->get_rx_status) -+ return priv->hif.ops->get_rx_status(hw, buf, size); -+ else -+ return 0; -+} -+ -+static inline void mwl_hif_enable_data_tasks(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->enable_data_tasks) -+ priv->hif.ops->enable_data_tasks(hw); -+} -+ -+static inline void mwl_hif_disable_data_tasks(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->disable_data_tasks) -+ priv->hif.ops->disable_data_tasks(hw); -+} -+ -+static inline int mwl_hif_exec_cmd(struct ieee80211_hw *hw, unsigned short cmd) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->exec_cmd) -+ return priv->hif.ops->exec_cmd(hw, cmd); -+ else -+ return -ENOTSUPP; -+} -+ -+static inline int mwl_hif_get_irq_num(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->get_irq_num) -+ return priv->hif.ops->get_irq_num(hw); -+ else -+ return -ENOTSUPP; -+} -+ -+static inline irqreturn_t mwl_hif_irq_handler(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->irq_handler) -+ return priv->hif.ops->irq_handler(hw); -+ else -+ return -ENOTSUPP; -+} -+ -+static inline void mwl_hif_irq_enable(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->irq_enable) -+ priv->hif.ops->irq_enable(hw); -+} -+ -+static inline void mwl_hif_irq_disable(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->irq_disable) -+ priv->hif.ops->irq_disable(hw); -+} -+ -+static inline int mwl_hif_download_firmware(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->download_firmware) -+ return priv->hif.ops->download_firmware(hw); -+ else -+ return -ENOTSUPP; -+} -+ -+static inline void mwl_hif_timer_routine(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->timer_routine) -+ priv->hif.ops->timer_routine(hw); -+} -+ -+static inline void mwl_hif_tx_xmit(struct ieee80211_hw *hw, -+ struct ieee80211_tx_control *control, -+ struct sk_buff *skb) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->tx_xmit) -+ priv->hif.ops->tx_xmit(hw, control, skb); -+} -+ -+static inline void mwl_hif_tx_del_pkts_via_vif(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->tx_del_pkts_via_vif) -+ priv->hif.ops->tx_del_pkts_via_vif(hw, vif); -+} -+ -+static inline void mwl_hif_tx_del_pkts_via_sta(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->tx_del_pkts_via_sta) -+ priv->hif.ops->tx_del_pkts_via_sta(hw, sta); -+} -+ -+static inline void mwl_hif_tx_del_ampdu_pkts(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, u8 tid) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->tx_del_ampdu_pkts) -+ priv->hif.ops->tx_del_ampdu_pkts(hw, sta, tid); -+} -+ -+static inline void mwl_hif_tx_del_sta_amsdu_pkts(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->tx_del_sta_amsdu_pkts) -+ priv->hif.ops->tx_del_sta_amsdu_pkts(hw, sta); -+} -+ -+static inline void mwl_hif_tx_return_pkts(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->tx_return_pkts) -+ priv->hif.ops->tx_return_pkts(hw); -+} -+ -+static inline struct device_node *mwl_hif_device_node(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->get_device_node) -+ return priv->hif.ops->get_device_node(hw); -+ else -+ return NULL; -+} -+ -+static inline void mwl_hif_get_survey(struct ieee80211_hw *hw, -+ struct mwl_survey_info *survey_info) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->get_survey) -+ priv->hif.ops->get_survey(hw, survey_info); -+} -+ -+static inline int mwl_hif_reg_access(struct ieee80211_hw *hw, bool write) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->reg_access) -+ return priv->hif.ops->reg_access(hw, write); -+ else -+ return -ENOTSUPP; -+} -+ -+static inline void mwl_hif_set_sta_id(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, -+ bool sta_mode, bool set) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->set_sta_id) -+ priv->hif.ops->set_sta_id(hw, sta, sta_mode, set); -+} -+ -+static inline void mwl_hif_process_account(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->process_account) -+ priv->hif.ops->process_account(hw); -+} -+ -+static inline int mwl_hif_mcast_cts(struct ieee80211_hw *hw, bool enable) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->hif.ops->mcast_cts) -+ return priv->hif.ops->mcast_cts(hw, enable); -+ else -+ return -ENOTSUPP; -+} -+#endif /* _HIF_OPS_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/hif.h b/drivers/net/wireless/marvell/mwlwifi/hif/hif.h -new file mode 100644 -index 000000000000..6ea6192ac5e0 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/hif.h -@@ -0,0 +1,81 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines host interface data structure. */ -+ -+#ifndef _HIF_H_ -+#define _HIF_H_ -+ -+/* memory/register access */ -+#define MWL_ACCESS_MAC 1 -+#define MWL_ACCESS_RF 2 -+#define MWL_ACCESS_BBP 3 -+#define MWL_ACCESS_CAU 4 -+#define MWL_ACCESS_ADDR0 5 -+#define MWL_ACCESS_ADDR1 6 -+#define MWL_ACCESS_ADDR 7 -+ -+struct mwl_survey_info { -+ struct ieee80211_channel channel; -+ u32 filled; -+ u32 time_period; -+ u32 time_busy; -+ u32 time_tx; -+ s8 noise; -+}; -+ -+struct mwl_hif_ops { -+ const char *driver_name; -+ const char *driver_version; -+ unsigned int tx_head_room; -+ int ampdu_num; -+ void (*reset)(struct ieee80211_hw *hw); -+ int (*init)(struct ieee80211_hw *hw); -+ void (*deinit)(struct ieee80211_hw *hw); -+ int (*get_info)(struct ieee80211_hw *hw, char *buf, size_t size); -+ int (*get_tx_status)(struct ieee80211_hw *hw, char *buf, size_t size); -+ int (*get_rx_status)(struct ieee80211_hw *hw, char *buf, size_t size); -+ void (*enable_data_tasks)(struct ieee80211_hw *hw); -+ void (*disable_data_tasks)(struct ieee80211_hw *hw); -+ int (*exec_cmd)(struct ieee80211_hw *hw, unsigned short cmd); -+ int (*get_irq_num)(struct ieee80211_hw *hw); -+ irqreturn_t (*irq_handler)(struct ieee80211_hw *hw); -+ void (*irq_enable)(struct ieee80211_hw *hw); -+ void (*irq_disable)(struct ieee80211_hw *hw); -+ int (*download_firmware)(struct ieee80211_hw *hw); -+ void (*timer_routine)(struct ieee80211_hw *hw); -+ void (*tx_xmit)(struct ieee80211_hw *hw, -+ struct ieee80211_tx_control *control, -+ struct sk_buff *skb); -+ void (*tx_del_pkts_via_vif)(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif); -+ void (*tx_del_pkts_via_sta)(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta); -+ void (*tx_del_ampdu_pkts)(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, u8 tid); -+ void (*tx_del_sta_amsdu_pkts)(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta); -+ void (*tx_return_pkts)(struct ieee80211_hw *hw); -+ struct device_node *(*get_device_node)(struct ieee80211_hw *hw); -+ void (*get_survey)(struct ieee80211_hw *hw, -+ struct mwl_survey_info *survey_info); -+ int (*reg_access)(struct ieee80211_hw *hw, bool write); -+ void (*set_sta_id)(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, -+ bool sta_mode, bool set); -+ void (*process_account)(struct ieee80211_hw *hw); -+ int (*mcast_cts)(struct ieee80211_hw *hw, bool enable); -+}; -+#endif /* _HIF_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h b/drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h -new file mode 100644 -index 000000000000..b14f161f1410 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/hostcmd.h -@@ -0,0 +1,1285 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines firmware host command related -+ * structure. -+ */ -+ -+#ifndef _HOSTCMD_H_ -+#define _HOSTCMD_H_ -+ -+/* 16 bit host command code */ -+#define HOSTCMD_CMD_GET_HW_SPEC 0x0003 -+#define HOSTCMD_CMD_SET_HW_SPEC 0x0004 -+#define HOSTCMD_CMD_802_11_GET_STAT 0x0014 -+#define HOSTCMD_CMD_BBP_REG_ACCESS 0x001a -+#define HOSTCMD_CMD_RF_REG_ACCESS 0x001b -+#define HOSTCMD_CMD_802_11_RADIO_CONTROL 0x001c -+#define HOSTCMD_CMD_MEM_ADDR_ACCESS 0x001d -+#define HOSTCMD_CMD_802_11_TX_POWER 0x001f -+#define HOSTCMD_CMD_802_11_RF_ANTENNA 0x0020 -+#define HOSTCMD_CMD_BROADCAST_SSID_ENABLE 0x0050 /* per-vif */ -+#define HOSTCMD_CMD_SET_CFG 0x008f -+#define HOSTCMD_CMD_SET_RF_CHANNEL 0x010a -+#define HOSTCMD_CMD_SET_AID 0x010d /* per-vif */ -+#define HOSTCMD_CMD_SET_INFRA_MODE 0x010e /* per-vif */ -+#define HOSTCMD_CMD_802_11_RTS_THSD 0x0113 -+#define HOSTCMD_CMD_SET_EDCA_PARAMS 0x0115 -+#define HOSTCMD_CMD_802_11H_DETECT_RADAR 0x0120 -+#define HOSTCMD_CMD_SET_WMM_MODE 0x0123 -+#define HOSTCMD_CMD_HT_GUARD_INTERVAL 0x0124 -+#define HOSTCMD_CMD_SET_FIXED_RATE 0x0126 -+#define HOSTCMD_CMD_SET_IES 0x0127 -+#define HOSTCMD_CMD_SET_LINKADAPT_CS_MODE 0x0129 -+#define HOSTCMD_CMD_DUMP_OTP_DATA 0x0142 -+#define HOSTCMD_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ -+#define HOSTCMD_CMD_SET_RATE_ADAPT_MODE 0x0203 -+#define HOSTCMD_CMD_GET_WATCHDOG_BITMAP 0x0205 -+#define HOSTCMD_CMD_DEL_MAC_ADDR 0x0206 /* per-vif */ -+#define HOSTCMD_CMD_BSS_START 0x1100 /* per-vif */ -+#define HOSTCMD_CMD_AP_BEACON 0x1101 /* per-vif */ -+#define HOSTCMD_CMD_SET_NEW_STN 0x1111 /* per-vif */ -+#define HOSTCMD_CMD_SET_APMODE 0x1114 -+#define HOSTCMD_CMD_SET_SWITCH_CHANNEL 0x1121 -+#define HOSTCMD_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ -+#define HOSTCMD_CMD_BASTREAM 0x1125 -+#define HOSTCMD_CMD_SET_SPECTRUM_MGMT 0x1128 -+#define HOSTCMD_CMD_SET_POWER_CONSTRAINT 0x1129 -+#define HOSTCMD_CMD_SET_COUNTRY_CODE 0x1130 -+#define HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL 0x1133 -+#define HOSTCMD_CMD_SET_WSC_IE 0x1136 /* per-vif */ -+#define HOSTCMD_CMD_GET_RATETABLE 0x1137 -+#define HOSTCMD_CMD_GET_SEQNO 0x1143 -+#define HOSTCMD_CMD_DWDS_ENABLE 0x1144 -+#define HOSTCMD_CMD_FW_FLUSH_TIMER 0x1148 -+#define HOSTCMD_CMD_SET_CDD 0x1150 -+#define HOSTCMD_CMD_SET_BFTYPE 0x1155 -+#define HOSTCMD_CMD_CAU_REG_ACCESS 0x1157 -+#define HOSTCMD_CMD_GET_TEMP 0x1159 -+#define HOSTCMD_CMD_LED_CTRL 0x1169 -+#define HOSTCMD_CMD_GET_FW_REGION_CODE 0x116A -+#define HOSTCMD_CMD_GET_DEVICE_PWR_TBL 0x116B -+#define HOSTCMD_CMD_SET_RATE_DROP 0x1172 -+#define HOSTCMD_CMD_NEWDP_DMATHREAD_START 0x1189 -+#define HOSTCMD_CMD_GET_FW_REGION_CODE_SC4 0x118A -+#define HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4 0x118B -+#define HOSTCMD_CMD_QUIET_MODE 0x1201 -+#define HOSTCMD_CMD_CORE_DUMP_DIAG_MODE 0x1202 -+#define HOSTCMD_CMD_802_11_SLOT_TIME 0x1203 -+#define HOSTCMD_CMD_GET_FW_CORE_DUMP 0x1203 -+#define HOSTCMD_CMD_EDMAC_CTRL 0x1204 -+#define HOSTCMD_CMD_TXPWRLMT_CFG 0x1211 -+#define HOSTCMD_CMD_MCAST_CTS 0x4001 -+ -+/* Define general result code for each command */ -+#define HOSTCMD_RESULT_OK 0x0000 -+/* General error */ -+#define HOSTCMD_RESULT_ERROR 0x0001 -+/* Command is not valid */ -+#define HOSTCMD_RESULT_NOT_SUPPORT 0x0002 -+/* Command is pending (will be processed) */ -+#define HOSTCMD_RESULT_PENDING 0x0003 -+/* System is busy (command ignored) */ -+#define HOSTCMD_RESULT_BUSY 0x0004 -+/* Data buffer is not big enough */ -+#define HOSTCMD_RESULT_PARTIAL_DATA 0x0005 -+ -+/* Define channel related constants */ -+#define FREQ_BAND_2DOT4GHZ 0x1 -+#define FREQ_BAND_4DOT9GHZ 0x2 -+#define FREQ_BAND_5GHZ 0x4 -+#define FREQ_BAND_5DOT2GHZ 0x8 -+#define CH_AUTO_WIDTH 0 -+#define CH_10_MHZ_WIDTH 0x1 -+#define CH_20_MHZ_WIDTH 0x2 -+#define CH_40_MHZ_WIDTH 0x4 -+#define CH_80_MHZ_WIDTH 0x5 -+#define CH_160_MHZ_WIDTH 0x6 -+#define EXT_CH_ABOVE_CTRL_CH 0x1 -+#define EXT_CH_AUTO 0x2 -+#define EXT_CH_BELOW_CTRL_CH 0x3 -+#define NO_EXT_CHANNEL 0x0 -+ -+#define ACT_PRIMARY_CHAN_0 0 -+#define ACT_PRIMARY_CHAN_1 1 -+#define ACT_PRIMARY_CHAN_2 2 -+#define ACT_PRIMARY_CHAN_3 3 -+#define ACT_PRIMARY_CHAN_4 4 -+#define ACT_PRIMARY_CHAN_5 5 -+#define ACT_PRIMARY_CHAN_6 6 -+#define ACT_PRIMARY_CHAN_7 7 -+ -+/* Define rate related constants */ -+#define HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 -+ -+/* Define station related constants */ -+#define HOSTCMD_ACT_STA_ACTION_ADD 0 -+#define HOSTCMD_ACT_STA_ACTION_MODIFY 1 -+#define HOSTCMD_ACT_STA_ACTION_REMOVE 2 -+ -+/* Define key related constants */ -+#define MAX_ENCR_KEY_LENGTH 16 -+#define MIC_KEY_LENGTH 8 -+ -+#define KEY_TYPE_ID_WEP 0x00 -+#define KEY_TYPE_ID_TKIP 0x01 -+#define KEY_TYPE_ID_AES 0x02 -+ -+/* Group key for RX only */ -+#define ENCR_KEY_FLAG_RXGROUPKEY 0x00000002 -+#define ENCR_KEY_FLAG_TXGROUPKEY 0x00000004 -+#define ENCR_KEY_FLAG_PAIRWISE 0x00000008 -+#define ENCR_KEY_FLAG_TSC_VALID 0x00000040 -+#define ENCR_KEY_FLAG_WEP_TXKEY 0x01000000 -+#define ENCR_KEY_FLAG_MICKEY_VALID 0x02000000 -+ -+/* Define block ack related constants */ -+#define BA_FLAG_IMMEDIATE_TYPE 1 -+#define BA_FLAG_DIRECTION_UP 0 -+#define BA_FLAG_DIRECTION_DOWN 1 -+ -+/* Define general purpose action */ -+#define HOSTCMD_ACT_GEN_SET 0x0001 -+#define HOSTCMD_ACT_GEN_SET_LIST 0x0002 -+#define HOSTCMD_ACT_GEN_GET_LIST 0x0003 -+ -+/* Define TXPower control action*/ -+#define HOSTCMD_ACT_GET_TARGET_TX_PWR 0x0000 -+#define HOSTCMD_ACT_GET_MAX_TX_PWR 0x0001 -+#define HOSTCMD_ACT_SET_TARGET_TX_PWR 0x0002 -+#define HOSTCMD_ACT_SET_MAX_TX_PWR 0x0003 -+ -+/* Misc */ -+#define WSC_IE_MAX_LENGTH 251 -+#define WSC_IE_SET_BEACON 0 -+#define WSC_IE_SET_PROBE_RESPONSE 1 -+ -+#define HW_SET_PARMS_FEATURES_HOST_PROBE_RESP 0x00000020 -+ -+#define EDMAC_2G_ENABLE_MASK 0x00000001 -+#define EDMAC_2G_ENABLE_SHIFT 0x0 -+#define EDMAC_5G_ENABLE_MASK 0x00000002 -+#define EDMAC_5G_ENABLE_SHIFT 0x1 -+#define EDMAC_2G_THRESHOLD_OFFSET_MASK 0x00000FF0 -+#define EDMAC_2G_THRESHOLD_OFFSET_SHIFT 0x4 -+#define EDMAC_5G_THRESHOLD_OFFSET_MASK 0x000FF000 -+#define EDMAC_5G_THRESHOLD_OFFSET_SHIFT 0xC -+#define EDMAC_QLOCK_BITMAP_MASK 0x0FF00000 -+#define EDMAC_QLOCK_BITMAP_SHIFT 0x14 -+ -+enum { -+ WL_DISABLE = 0, -+ WL_ENABLE = 1, -+ WL_DISABLE_VMAC = 0x80, -+}; -+ -+enum { -+ WL_GET = 0, -+ WL_SET = 1, -+ WL_RESET = 2, -+}; -+ -+enum { -+ WL_LONG_PREAMBLE = 1, -+ WL_SHORT_PREAMBLE = 3, -+ WL_AUTO_PREAMBLE = 5, -+}; -+ -+enum encr_action_type { -+ /* request to enable/disable HW encryption */ -+ ENCR_ACTION_ENABLE_HW_ENCR, -+ /* request to set encryption key */ -+ ENCR_ACTION_TYPE_SET_KEY, -+ /* request to remove one or more keys */ -+ ENCR_ACTION_TYPE_REMOVE_KEY, -+ ENCR_ACTION_TYPE_SET_GROUP_KEY, -+}; -+ -+enum ba_action_type { -+ BA_CREATE_STREAM, -+ BA_UPDATE_STREAM, -+ BA_DESTROY_STREAM, -+ BA_FLUSH_STREAM, -+ BA_CHECK_STREAM, -+}; -+ -+enum mac_type { -+ WL_MAC_TYPE_PRIMARY_CLIENT, -+ WL_MAC_TYPE_SECONDARY_CLIENT, -+ WL_MAC_TYPE_PRIMARY_AP, -+ WL_MAC_TYPE_SECONDARY_AP, -+}; -+ -+/* General host command header */ -+struct hostcmd_header { -+ __le16 cmd; -+ __le16 len; -+ u8 seq_num; -+ u8 macid; -+ __le16 result; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_HW_SPEC */ -+struct hostcmd_get_hw_spec { -+ u8 version; /* version of the HW */ -+ u8 host_if; /* host interface */ -+ __le16 num_wcb; /* Max. number of WCB FW can handle */ -+ __le16 num_mcast_addr; /* MaxNbr of MC addresses FW can handle */ -+ u8 permanent_addr[ETH_ALEN]; /* MAC address programmed in HW */ -+ __le16 region_code; -+ __le16 num_antenna; /* Number of antenna used */ -+ __le32 fw_release_num; /* 4 byte of FW release number */ -+ __le32 wcb_base0; -+ __le32 rxpd_wr_ptr; -+ __le32 rxpd_rd_ptr; -+ __le32 fw_awake_cookie; -+ __le32 wcb_base[SYSADPT_TOTAL_TX_QUEUES - 1]; -+} __packed; -+ -+struct hostcmd_cmd_get_hw_spec { -+ struct hostcmd_header cmd_hdr; -+ struct hostcmd_get_hw_spec hw_spec; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_HW_SPEC */ -+struct hostcmd_set_hw_spec { -+ /* HW revision */ -+ u8 version; -+ /* Host interface */ -+ u8 host_if; -+ /* Max. number of Multicast address FW can handle */ -+ __le16 num_mcast_addr; -+ /* MAC address */ -+ u8 permanent_addr[ETH_ALEN]; -+ /* Region Code */ -+ __le16 region_code; -+ /* 4 byte of FW release number, example 0x1234=1.2.3.4 */ -+ __le32 fw_release_num; -+ /* Firmware awake cookie - used to ensure that the device -+ * is not in sleep mode -+ */ -+ __le32 fw_awake_cookie; -+ /* Device capabilities (see above) */ -+ __le32 device_caps; -+ /* Rx shared memory queue */ -+ __le32 rxpd_wr_ptr; -+ /* Actual number of TX queues in WcbBase array */ -+ __le32 num_tx_queues; -+ /* TX WCB Rings */ -+ __le32 wcb_base[4 + SYSADPT_NUM_OF_AP]; -+ /* Max AMSDU size (00 - AMSDU Disabled, -+ * 01 - 4K, 10 - 8K, 11 - not defined) -+ */ -+ __le32 features; -+ __le32 tx_wcb_num_per_queue; -+ __le32 total_rx_wcb; -+ __le32 acnt_buf_size; -+ __le32 acnt_base_addr; -+} __packed; -+ -+struct hostcmd_cmd_set_hw_spec { -+ struct hostcmd_header cmd_hdr; -+ struct hostcmd_set_hw_spec hw_spec; -+} __packed; -+ -+/* HOSTCMD_CMD_802_11_GET_STAT */ -+struct hostcmd_cmd_802_11_get_stat { -+ struct hostcmd_header cmd_hdr; -+ __le32 tx_retry_successes; -+ __le32 tx_multiple_retry_successes; -+ __le32 tx_failures; -+ __le32 rts_successes; -+ __le32 rts_failures; -+ __le32 ack_failures; -+ __le32 rx_duplicate_frames; -+ __le32 rx_fcs_errors; -+ __le32 tx_watchdog_timeouts; -+ __le32 rx_overflows; -+ __le32 rx_frag_errors; -+ __le32 rx_mem_errors; -+ __le32 pointer_errors; -+ __le32 tx_underflows; -+ __le32 tx_done; -+ __le32 tx_done_buf_try_put; -+ __le32 tx_done_buf_put; -+ /* Put size of requested buffer in here */ -+ __le32 wait_for_tx_buf; -+ __le32 tx_attempts; -+ __le32 tx_successes; -+ __le32 tx_fragments; -+ __le32 tx_multicasts; -+ __le32 rx_non_ctl_pkts; -+ __le32 rx_multicasts; -+ __le32 rx_undecryptable_frames; -+ __le32 rx_icv_errors; -+ __le32 rx_excluded_frames; -+ __le32 rx_weak_iv_count; -+ __le32 rx_unicasts; -+ __le32 rx_bytes; -+ __le32 rx_errors; -+ __le32 rx_rts_count; -+ __le32 tx_cts_count; -+} __packed; -+ -+/* HOSTCMD_CMD_BBP_REG_ACCESS */ -+struct hostcmd_cmd_bbp_reg_access { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 offset; -+ u8 value; -+ u8 reserverd[3]; -+} __packed; -+ -+/* HOSTCMD_CMD_RF_REG_ACCESS */ -+struct hostcmd_cmd_rf_reg_access { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 offset; -+ u8 value; -+ u8 reserverd[3]; -+} __packed; -+ -+/* HOSTCMD_CMD_802_11_RADIO_CONTROL */ -+struct hostcmd_cmd_802_11_radio_control { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ /* @bit0: 1/0,on/off, @bit1: 1/0, long/short @bit2: 1/0,auto/fix */ -+ __le16 control; -+ __le16 radio_on; -+} __packed; -+ -+/* HOSTCMD_CMD_MEM_ADDR_ACCESS */ -+struct hostcmd_cmd_mem_addr_access { -+ struct hostcmd_header cmd_hdr; -+ __le32 address; -+ __le16 length; -+ __le16 reserved; -+ __le32 value[64]; -+} __packed; -+ -+/* HOSTCMD_CMD_802_11_TX_POWER */ -+struct hostcmd_cmd_802_11_tx_power { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 band; -+ __le16 ch; -+ __le16 bw; -+ __le16 sub_ch; -+ __le16 power_level_list[SYSADPT_TX_POWER_LEVEL_TOTAL]; -+} __packed; -+ -+struct hostcmd_cmd_802_11_tx_power_kf2 { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 band; -+ __le16 ch; -+ __le16 bw; -+ __le16 sub_ch; -+ __le16 power_level_list[SYSADPT_TX_GRP_PWR_LEVEL_TOTAL]; -+} __packed; -+ -+/* HOSTCMD_CMD_802_11_RF_ANTENNA */ -+struct hostcmd_cmd_802_11_rf_antenna { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 antenna_mode; /* Number of antennas or 0xffff(diversity) */ -+} __packed; -+ -+/* HOSTCMD_CMD_BROADCAST_SSID_ENABLE */ -+struct hostcmd_cmd_broadcast_ssid_enable { -+ struct hostcmd_header cmd_hdr; -+ __le32 enable; -+ __le32 hidden_ssid_info; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_CFG */ -+struct hostcmd_cmd_set_cfg { -+ struct hostcmd_header cmd_hdr; -+ /* Action */ -+ __le16 action; -+ /* Type */ -+ __le16 type; -+ /* Data length */ -+ __le16 data_len; -+ /* Data */ -+ u8 data[1]; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_RF_CHANNEL */ -+#define FREQ_BAND_MASK 0x0000003f -+#define CHNL_WIDTH_MASK 0x000007c0 -+#define CHNL_WIDTH_SHIFT 6 -+#define ACT_PRIMARY_MASK 0x00003800 -+#define ACT_PRIMARY_SHIFT 11 -+ -+struct hostcmd_cmd_set_rf_channel { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ u8 curr_chnl; -+ __le32 chnl_flags; -+} __packed; -+ -+struct hostcmd_cmd_set_rf_channel_kf2 { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ u8 curr_chnl; -+ __le32 chnl_flags; -+ u8 remain_on_chan; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_AID */ -+struct hostcmd_cmd_set_aid { -+ struct hostcmd_header cmd_hdr; -+ __le16 aid; -+ u8 mac_addr[ETH_ALEN]; /* AP's Mac Address(BSSID) */ -+ __le32 gprotect; -+ u8 ap_rates[SYSADPT_MAX_DATA_RATES_G]; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_INFRA_MODE */ -+struct hostcmd_cmd_set_infra_mode { -+ struct hostcmd_header cmd_hdr; -+} __packed; -+ -+/* HOSTCMD_CMD_802_11_RTS_THSD */ -+struct hostcmd_cmd_802_11_rts_thsd { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 threshold; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_EDCA_PARAMS */ -+struct hostcmd_cmd_set_edca_params { -+ struct hostcmd_header cmd_hdr; -+ /* 0 = get all, 0x1 =set CWMin/Max, 0x2 = set TXOP , 0x4 =set AIFSN */ -+ __le16 action; -+ __le16 txop; /* in unit of 32 us */ -+ __le32 cw_max; /* 0~15 */ -+ __le32 cw_min; /* 0~15 */ -+ u8 aifsn; -+ u8 txq_num; /* Tx Queue number. */ -+} __packed; -+ -+/* HOSTCMD_CMD_802_11H_DETECT_RADAR */ -+#define RADAR_TYPE_CODE_0 0 -+#define RADAR_TYPE_CODE_53 53 -+#define RADAR_TYPE_CODE_56 56 -+#define RADAR_TYPE_CODE_ETSI 151 -+ -+struct hostcmd_cmd_802_11h_detect_radar { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 radar_type_code; -+ __le16 min_chirp_cnt; -+ __le16 chirp_time_intvl; -+ __le16 pw_filter; -+ __le16 min_num_radar; -+ __le16 pri_min_num; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_WMM_MODE */ -+struct hostcmd_cmd_set_wmm_mode { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; /* 0->unset, 1->set */ -+} __packed; -+ -+/* HOSTCMD_CMD_HT_GUARD_INTERVAL */ -+struct hostcmd_cmd_ht_guard_interval { -+ struct hostcmd_header cmd_hdr; -+ __le32 action; -+ __le32 gi_type; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_FIXED_RATE */ -+struct fix_rate_flag { /* lower rate after the retry count */ -+ /* 0: legacy, 1: HT */ -+ __le32 fix_rate_type; -+ /* 0: retry count is not valid, 1: use retry count specified */ -+ __le32 retry_count_valid; -+} __packed; -+ -+struct fix_rate_entry { -+ struct fix_rate_flag fix_rate_type_flags; -+ /* depending on the flags above, this can be either a legacy -+ * rate(not index) or an MCS code. -+ */ -+ __le32 fixed_rate; -+ __le32 retry_count; -+} __packed; -+ -+struct hostcmd_cmd_set_fixed_rate { -+ struct hostcmd_header cmd_hdr; -+ /* HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 */ -+ __le32 action; -+ /* use fixed rate specified but firmware can drop to */ -+ __le32 allow_rate_drop; -+ __le32 entry_count; -+ struct fix_rate_entry fixed_rate_table[4]; -+ u8 multicast_rate; -+ u8 multi_rate_tx_type; -+ u8 management_rate; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_IES */ -+struct hostcmd_cmd_set_ies { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; /* 0->unset, 1->set */ -+ __le16 ie_list_len_ht; -+ __le16 ie_list_len_vht; -+ __le16 ie_list_len_proprietary; -+ /*Buffer size same as Generic_Beacon*/ -+ u8 ie_list_ht[148]; -+ u8 ie_list_vht[24]; -+ u8 ie_list_proprietary[112]; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_LINKADAPT_CS_MODE */ -+struct hostcmd_cmd_set_linkadapt_cs_mode { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 cs_mode; -+} __packed; -+ -+/* HOSTCMD_CMD_DUMP_OTP_DATA */ -+struct hostcmd_cmd_dump_otp_data { -+ struct hostcmd_header cmd_hdr; -+ u8 pload[0]; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_MAC_ADDR, HOSTCMD_CMD_DEL_MAC_ADDR */ -+struct hostcmd_cmd_set_mac_addr { -+ struct hostcmd_header cmd_hdr; -+ __le16 mac_type; -+ u8 mac_addr[ETH_ALEN]; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_RATE_ADAPT_MODE */ -+struct hostcmd_cmd_set_rate_adapt_mode { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 rate_adapt_mode; /* 0:Indoor, 1:Outdoor */ -+} __packed; -+ -+/* HOSTCMD_CMD_GET_WATCHDOG_BITMAP */ -+struct hostcmd_cmd_get_watchdog_bitmap { -+ struct hostcmd_header cmd_hdr; -+ u8 watchdog_bitmap; /* for SW/BA */ -+} __packed; -+ -+/* HOSTCMD_CMD_BSS_START */ -+struct hostcmd_cmd_bss_start { -+ struct hostcmd_header cmd_hdr; -+ __le32 enable; /* FALSE: Disable or TRUE: Enable */ -+ u8 amsdu; -+} __packed; -+ -+/* HOSTCMD_CMD_AP_BEACON */ -+struct cf_params { -+ u8 elem_id; -+ u8 len; -+ u8 cfp_cnt; -+ u8 cfp_period; -+ __le16 cfp_max_duration; -+ __le16 cfp_duration_remaining; -+} __packed; -+ -+struct ibss_params { -+ u8 elem_id; -+ u8 len; -+ __le16 atim_window; -+} __packed; -+ -+union ss_params { -+ struct cf_params cf_param_set; -+ struct ibss_params ibss_param_set; -+} __packed; -+ -+struct fh_params { -+ u8 elem_id; -+ u8 len; -+ __le16 dwell_time; -+ u8 hop_set; -+ u8 hop_pattern; -+ u8 hop_index; -+} __packed; -+ -+struct ds_params { -+ u8 elem_id; -+ u8 len; -+ u8 current_chnl; -+} __packed; -+ -+union phy_params { -+ struct fh_params fh_param_set; -+ struct ds_params ds_param_set; -+} __packed; -+ -+struct rsn_ie { -+ u8 elem_id; -+ u8 len; -+ u8 oui_type[4]; /* 00:50:f2:01 */ -+ u8 ver[2]; -+ u8 grp_key_cipher[4]; -+ u8 pws_key_cnt[2]; -+ u8 pws_key_cipher_list[4]; -+ u8 auth_key_cnt[2]; -+ u8 auth_key_list[4]; -+} __packed; -+ -+struct rsn48_ie { -+ u8 elem_id; -+ u8 len; -+ u8 ver[2]; -+ u8 grp_key_cipher[4]; -+ u8 pws_key_cnt[2]; -+ u8 pws_key_cipher_list[4]; -+ u8 auth_key_cnt[2]; -+ u8 auth_key_list[4]; -+ u8 rsn_cap[2]; -+ u8 pmk_id_cnt[2]; -+ u8 pmk_id_list[16]; /* Should modify to 16 * S */ -+ u8 reserved[8]; -+} __packed; -+ -+struct ac_param_rcd { -+ u8 aci_aifsn; -+ u8 ecw_min_max; -+ __le16 txop_lim; -+} __packed; -+ -+struct wmm_param_elem { -+ u8 elem_id; -+ u8 len; -+ u8 oui[3]; -+ u8 type; -+ u8 sub_type; -+ u8 version; -+ u8 qos_info; -+ u8 rsvd; -+ struct ac_param_rcd ac_be; -+ struct ac_param_rcd ac_bk; -+ struct ac_param_rcd ac_vi; -+ struct ac_param_rcd ac_vo; -+} __packed; -+ -+struct channel_info { -+ u8 first_channel_num; -+ u8 num_channels; -+ u8 max_tx_pwr_level; -+} __packed; -+ -+struct country { -+ u8 elem_id; -+ u8 len; -+ u8 country_str[3]; -+ struct channel_info channel_info[40]; -+} __packed; -+ -+struct start_cmd { -+ u8 sta_mac_addr[ETH_ALEN]; -+ u8 ssid[IEEE80211_MAX_SSID_LEN]; -+ u8 bss_type; -+ __le16 bcn_period; -+ u8 dtim_period; -+ union ss_params ss_param_set; -+ union phy_params phy_param_set; -+ __le16 probe_delay; -+ __le16 cap_info; -+ u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G]; -+ u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G]; -+ struct rsn_ie rsn_ie; -+ struct rsn48_ie rsn48_ie; -+ struct wmm_param_elem wmm_param; -+ struct country country; -+ __le32 ap_rf_type; /* 0->B, 1->G, 2->Mixed, 3->A, 4->11J */ -+ u8 rsvd[3]; -+ u8 bssid[ETH_ALEN]; /* only for 88W8997 */ -+} __packed; -+ -+struct hostcmd_cmd_ap_beacon { -+ struct hostcmd_header cmd_hdr; -+ struct start_cmd start_cmd; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_NEW_STN */ -+struct add_ht_info { -+ u8 control_chnl; -+ u8 add_chnl; -+ __le16 op_mode; -+ __le16 stbc; -+} __packed; -+ -+struct peer_info { -+ __le32 legacy_rate_bitmap; -+ u8 ht_rates[4]; -+ __le16 cap_info; -+ __le16 ht_cap_info; -+ u8 mac_ht_param_info; -+ u8 mrvl_sta; -+ struct add_ht_info add_ht_info; -+ __le32 tx_bf_capabilities; /* EXBF_SUPPORT */ -+ __le32 vht_max_rx_mcs; -+ __le32 vht_cap; -+ /* 0:20Mhz, 1:40Mhz, 2:80Mhz, 3:160 or 80+80Mhz */ -+ u8 vht_rx_channel_width; -+} __packed; -+ -+struct hostcmd_cmd_set_new_stn { -+ struct hostcmd_header cmd_hdr; -+ __le16 aid; -+ u8 mac_addr[ETH_ALEN]; -+ __le16 stn_id; -+ __le16 action; -+ __le16 if_type; -+ struct peer_info peer_info; -+ /* UAPSD_SUPPORT */ -+ u8 qos_info; -+ u8 is_qos_sta; -+ __le32 fw_sta_ptr; -+} __packed; -+ -+struct retry_cnt_qos { -+ u8 retry_cfg_enable; -+ u8 retry_cnt_BK; -+ u8 retry_cnt_BE; -+ u8 retry_cnt_VI; -+ u8 retry_cnt_VO; -+} __packed; -+ -+struct peer_info_sc4 { -+ __le32 legacy_rate_bitmap; -+ u8 ht_rates[4]; -+ __le16 cap_info; -+ __le16 ht_cap_info; -+ u8 mac_ht_param_info; -+ u8 mrvl_sta; -+ struct add_ht_info add_ht_info; -+ __le32 tx_bf_capabilities; /* EXBF_SUPPORT */ -+ __le32 vht_max_rx_mcs; -+ __le32 vht_cap; -+ /* 0:20Mhz, 1:40Mhz, 2:80Mhz, 3:160 or 80+80Mhz */ -+ u8 vht_rx_channel_width; -+ struct retry_cnt_qos retry_cnt_qos; -+ u8 assoc_rssi; -+} __packed; -+ -+struct hostcmd_cmd_set_new_stn_sc4 { -+ struct hostcmd_header cmd_hdr; -+ __le16 aid; -+ u8 mac_addr[ETH_ALEN]; -+ __le16 stn_id; -+ __le16 action; -+ __le16 reserved; -+ struct peer_info_sc4 peer_info; -+ /* UAPSD_SUPPORT */ -+ u8 qos_info; -+ u8 is_qos_sta; -+ __le32 fw_sta_ptr; -+ __le32 wds; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_APMODE */ -+struct hostcmd_cmd_set_apmode { -+ struct hostcmd_header cmd_hdr; -+ u8 apmode; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_SWITCH_CHANNEL */ -+struct hostcmd_cmd_set_switch_channel { -+ struct hostcmd_header cmd_hdr; -+ __le32 next_11h_chnl; -+ __le32 mode; -+ __le32 init_count; -+ __le32 chnl_flags; -+ __le32 next_ht_extchnl_offset; -+ __le32 dfs_test_mode; -+} __packed; -+ -+/* HOSTCMD_CMD_UPDATE_ENCRYPTION */ -+struct hostcmd_cmd_update_encryption { -+ struct hostcmd_header cmd_hdr; -+ /* Action type - see encr_action_type */ -+ __le32 action_type; /* encr_action_type */ -+ /* size of the data buffer attached. */ -+ __le32 data_length; -+ u8 mac_addr[ETH_ALEN]; -+ u8 action_data[1]; -+} __packed; -+ -+struct wep_type_key { -+ /* WEP key material (max 128bit) */ -+ u8 key_material[MAX_ENCR_KEY_LENGTH]; -+} __packed; -+ -+struct encr_tkip_seqcnt { -+ __le16 low; -+ __le32 high; -+} __packed; -+ -+struct tkip_type_key { -+ /* TKIP Key material. Key type (group or pairwise key) is -+ * determined by flags -+ */ -+ /* in KEY_PARAM_SET structure. */ -+ u8 key_material[MAX_ENCR_KEY_LENGTH]; -+ /* MIC keys */ -+ u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; -+ u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; -+ struct encr_tkip_seqcnt tkip_rsc; -+ struct encr_tkip_seqcnt tkip_tsc; -+} __packed; -+ -+struct aes_type_key { -+ /* AES Key material */ -+ u8 key_material[MAX_ENCR_KEY_LENGTH]; -+} __packed; -+ -+union mwl_key_type { -+ struct wep_type_key wep_key; -+ struct tkip_type_key tkip_key; -+ struct aes_type_key aes_key; -+} __packed; -+ -+struct key_param_set { -+ /* Total length of this structure (Key is variable size array) */ -+ __le16 length; -+ /* Key type - WEP, TKIP or AES-CCMP. */ -+ /* See definitions above */ -+ __le16 key_type_id; -+ /* key flags (ENCR_KEY_FLAG_XXX_ */ -+ __le32 key_info; -+ /* For WEP only - actual key index */ -+ __le32 key_index; -+ /* Size of the key */ -+ __le16 key_len; -+ /* Key material (variable size array) */ -+ union mwl_key_type key; -+ u8 mac_addr[ETH_ALEN]; -+} __packed; -+ -+struct hostcmd_cmd_set_key { -+ struct hostcmd_header cmd_hdr; -+ /* Action type - see encr_action_type */ -+ __le32 action_type; /* encr_action_type */ -+ /* size of the data buffer attached. */ -+ __le32 data_length; -+ /* data buffer - maps to one KEY_PARAM_SET structure */ -+ struct key_param_set key_param; -+} __packed; -+ -+/* HOSTCMD_CMD_BASTREAM */ -+#define BA_TYPE_MASK 0x00000001 -+#define BA_DIRECTION_MASK 0x0000000e -+#define BA_DIRECTION_SHIFT 1 -+ -+#define BA_TYPE_MASK_NDP 0x00000003 -+#define BA_DIRECTION_MASK_NDP 0x0000001c -+#define BA_DIRECTION_SHIFT_NDP 2 -+ -+struct ba_context { -+ __le32 context; -+} __packed; -+ -+/* parameters for block ack creation */ -+struct create_ba_params { -+ /* BA Creation flags - see above */ -+ __le32 flags; -+ /* idle threshold */ -+ __le32 idle_thrs; -+ /* block ack transmit threshold (after how many pkts should we -+ * send BAR?) -+ */ -+ __le32 bar_thrs; -+ /* receiver window size */ -+ __le32 window_size; -+ /* MAC Address of the BA partner */ -+ u8 peer_mac_addr[ETH_ALEN]; -+ /* Dialog Token */ -+ u8 dialog_token; -+ /* TID for the traffic stream in this BA */ -+ u8 tid; -+ /* shared memory queue ID (not sure if this is required) */ -+ u8 queue_id; -+ u8 param_info; -+ /* returned by firmware - firmware context pointer. */ -+ /* this context pointer will be passed to firmware for all -+ * future commands. -+ */ -+ struct ba_context fw_ba_context; -+ u8 reset_seq_no; /** 0 or 1**/ -+ __le16 current_seq; -+ __le32 vht_rx_factor; -+ /* This is for virtual station in Sta proxy mode for V6FW */ -+ u8 sta_src_mac_addr[ETH_ALEN]; -+} __packed; -+ -+/* new transmit sequence number information */ -+struct ba_update_seq_num { -+ /* BA flags - see above */ -+ __le32 flags; -+ /* returned by firmware in the create ba stream response */ -+ struct ba_context fw_ba_context; -+ /* new sequence number for this block ack stream */ -+ __le16 ba_seq_num; -+} __packed; -+ -+struct ba_stream_context { -+ /* BA Stream flags */ -+ __le32 flags; -+ /* returned by firmware in the create ba stream response */ -+ struct ba_context fw_ba_context; -+ u8 tid; -+ u8 peer_mac_addr[ETH_ALEN]; -+} __packed; -+ -+union ba_info { -+ /* information required to create BA Stream... */ -+ struct create_ba_params create_params; -+ /* update starting/new sequence number etc. */ -+ struct ba_update_seq_num updt_seq_num; -+ /* destroy an existing stream... */ -+ struct ba_stream_context destroy_params; -+ /* destroy an existing stream... */ -+ struct ba_stream_context flush_params; -+} __packed; -+ -+struct hostcmd_cmd_bastream { -+ struct hostcmd_header cmd_hdr; -+ __le32 action_type; -+ union ba_info ba_info; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_SPECTRUM_MGMT */ -+struct hostcmd_cmd_set_spectrum_mgmt { -+ struct hostcmd_header cmd_hdr; -+ __le32 spectrum_mgmt; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_POWER_CONSTRAINT */ -+struct hostcmd_cmd_set_power_constraint { -+ struct hostcmd_header cmd_hdr; -+ __le32 power_constraint; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_COUNTRY_CODE */ -+struct domain_chnl_entry { -+ u8 first_chnl_num; -+ u8 chnl_num; -+ u8 max_transmit_pw; -+} __packed; -+ -+struct domain_country_info { -+ u8 country_string[3]; -+ u8 g_chnl_len; -+ struct domain_chnl_entry domain_entry_g[1]; -+ u8 a_chnl_len; -+ struct domain_chnl_entry domain_entry_a[20]; -+} __packed; -+ -+struct hostcmd_cmd_set_country_code { -+ struct hostcmd_header cmd_hdr; -+ __le32 action ; /* 0 -> unset, 1 ->set */ -+ struct domain_country_info domain_info; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL */ -+struct hostcmd_cmd_set_optimization_level { -+ struct hostcmd_header cmd_hdr; -+ u8 opt_level; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_WSC_IE */ -+struct hostcmd_cmd_set_wsc_ie { -+ struct hostcmd_header cmd_hdr; -+ __le16 ie_type; /* 0 -- beacon. or 1 -- probe response. */ -+ __le16 len; -+ u8 data[WSC_IE_MAX_LENGTH]; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_RATETABLE */ -+struct hostcmd_cmd_get_ratetable { -+ struct hostcmd_header cmd_hdr; -+ u8 addr[ETH_ALEN]; -+ u8 type; /* 0: SU, 1: MU */ -+ /* multiply 2 because 2 DWORD in rate info */ -+ __le32 sorted_rates_idx_map[2 * SYSADPT_MAX_RATE_ADAPT_RATES]; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_SEQNO */ -+struct hostcmd_cmd_get_seqno { -+ struct hostcmd_header cmd_hdr; -+ u8 mac_addr[ETH_ALEN]; -+ u8 tid; -+ __le16 seq_no; -+ u8 reserved; -+} __packed; -+ -+/* HOSTCMD_CMD_DWDS_ENABLE */ -+struct hostcmd_cmd_dwds_enable { -+ struct hostcmd_header cmd_hdr; -+ __le32 enable; /* 0 -- Disable. or 1 -- Enable. */ -+} __packed; -+ -+/* HOSTCMD_CMD_FW_FLUSH_TIMER */ -+struct hostcmd_cmd_fw_flush_timer { -+ struct hostcmd_header cmd_hdr; -+ /* 0 -- Disable. > 0 -- holds time value in usecs. */ -+ __le32 value; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_CDD */ -+struct hostcmd_cmd_set_cdd { -+ struct hostcmd_header cmd_hdr; -+ __le32 enable; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_BFTYPE */ -+struct hostcmd_cmd_set_bftype { -+ struct hostcmd_header cmd_hdr; -+ __le32 action; -+ __le32 mode; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_TEMP */ -+struct hostcmd_cmd_get_temp { -+ struct hostcmd_header cmd_hdr; -+ __le32 celcius; -+ __le32 raw_data; -+} __packed; -+ -+/* HOSTCMD_CMD_LED_CTRL */ -+struct hostcmd_cmd_led_ctrl { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; /* 0: GET, 1: SET (only SET is supported) */ -+ u8 led_enable; /* 0: Disable, 1: Enable */ -+ u8 led_control; /* 0: HW 1: SW (only SW is supported) */ -+ u8 led_blink_rate; /* 1: Slow, 2: Medium, 3: Fast blink */ -+ u8 reserved; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_FW_REGION_CODE */ -+struct hostcmd_cmd_get_fw_region_code { -+ struct hostcmd_header cmd_hdr; -+ __le32 status; /* 0 = Found, 1 = Error */ -+ __le32 fw_region_code; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_DEVICE_PWR_TBL */ -+#define HAL_TRPC_ID_MAX 16 -+ -+struct channel_power_tbl { -+ u8 channel; -+ u8 tx_pwr[HAL_TRPC_ID_MAX]; -+ u8 dfs_capable; -+ u8 ax_ant; -+ u8 cdd; -+} __packed; -+ -+struct hostcmd_cmd_get_device_pwr_tbl { -+ struct hostcmd_header cmd_hdr; -+ __le16 status; /* 0 = Found, 1 = Error */ -+ u8 region_code; -+ u8 number_of_channels; -+ __le32 current_channel_index; -+ /* Only for 1 channel, so, 1 channel at a time */ -+ struct channel_power_tbl channel_pwr_tbl; -+} __packed; -+ -+/* HOSTCMD_CMD_SET_RATE_DROP */ -+struct hostcmd_cmd_set_rate_drop { -+ struct hostcmd_header cmd_hdr; -+ __le32 enable; -+ __le32 rate_index; -+ __le32 sta_index; -+} __packed; -+ -+/* HOSTCMD_CMD_NEWDP_DMATHREAD_START */ -+struct hostcmd_cmd_newdp_dmathread_start { -+ struct hostcmd_header cmd_hdr; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_FW_REGION_CODE_SC4 */ -+struct hostcmd_cmd_get_fw_region_code_sc4 { -+ struct hostcmd_header cmd_hdr; -+ __le32 status; /* 0 = Found, 1 = Error */ -+ __le32 fw_region_code; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_DEVICE_PWR_TBL_SC4 */ -+#define HAL_TRPC_ID_MAX_SC4 32 -+#define MAX_GROUP_PER_CHANNEL_5G 39 -+#define MAX_GROUP_PER_CHANNEL_2G 21 -+#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -+#define MAX_GROUP_PER_CHANNEL_RATE \ -+ MAX(MAX_GROUP_PER_CHANNEL_5G, MAX_GROUP_PER_CHANNEL_2G) -+ -+struct channel_power_tbl_sc4 { -+ u8 channel; -+ u8 grp_pwr[MAX_GROUP_PER_CHANNEL_RATE]; -+ u8 tx_pwr[HAL_TRPC_ID_MAX_SC4]; -+ u8 dfs_capable; -+ u8 ax_ant; -+ u8 cdd; -+ u8 rsvd; -+} __packed; -+ -+struct hostcmd_cmd_get_device_pwr_tbl_sc4 { -+ struct hostcmd_header cmd_hdr; -+ __le16 status; /* 0 = Found, 1 = Error */ -+ u8 region_code; -+ u8 number_of_channels; -+ __le32 current_channel_index; -+ /* Only for 1 channel, so, 1 channel at a time */ -+ struct channel_power_tbl_sc4 channel_pwr_tbl; -+} __packed; -+ -+/* HOSTCMD_CMD_QUIET_MODE */ -+struct hostcmd_cmd_quiet_mode { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le32 enable; -+ __le32 period; -+ __le32 duration; -+ __le32 next_offset; -+} __packed; -+ -+/* HOSTCMD_CMD_CORE_DUMP_DIAG_MODE */ -+struct hostcmd_cmd_core_dump_diag_mode { -+ struct hostcmd_header cmd_hdr; -+ __le16 status; -+} __packed; -+ -+/* HOSTCMD_CMD_GET_FW_CORE_DUMP */ -+#define MAX_CORE_REGIONS 20 -+#define MAX_CORE_SYMBOLS 30 -+#define MVL_COREDUMP_DIAG_MODE 0x00000001 -+#define MVL_COREDUMP_INCL_EXT 0x00000002 -+#define MAX_CORE_DUMP_BUFFER 2048 -+ -+struct core_region { -+ __le32 address; -+ __le32 length; -+} __packed; -+ -+struct core_symbol { -+ u8 name[16]; -+ __le32 address; -+ __le32 length; -+ __le16 entries; -+} __packed; -+ -+struct coredump { -+ u8 version_major; -+ u8 version_minor; -+ u8 version_patch; -+ u8 hdr_version; -+ u8 num_regions; -+ u8 num_symbols; -+ u8 fill[2]; -+ struct core_region region[MAX_CORE_REGIONS]; -+ struct core_symbol symbol[MAX_CORE_SYMBOLS]; -+ __le32 fill_end[40]; -+} __packed; -+ -+struct coredump_cmd { -+ __le32 context; -+ __le32 buffer; -+ __le32 buffer_len; -+ __le16 size_kb; -+ __le16 flags; -+} __packed; -+ -+struct debug_mem_cmd { -+ __le32 set; -+ __le32 type; -+ __le32 addr; -+ __le32 val; -+} __packed; -+ -+struct hostcmd_cmd_get_fw_core_dump { -+ struct hostcmd_header cmd_hdr; -+ union { -+ struct coredump_cmd coredump; -+ struct debug_mem_cmd debug_mem; -+ } cmd_data; -+ /*Buffer where F/W Copies the Core Dump*/ -+ char buffer[MAX_CORE_DUMP_BUFFER]; -+} __packed; -+ -+struct hostcmd_cmd_get_fw_core_dump_ { -+ struct hostcmd_header cmd_hdr; -+ union { -+ struct coredump_cmd coredump; -+ struct debug_mem_cmd debug_mem; -+ } cmd_data; -+} __packed; -+ -+/* HOSTCMD_CMD_802_11_SLOT_TIME */ -+struct hostcmd_cmd_802_11_slot_time { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ /* 0:long slot; 1:short slot */ -+ __le16 short_slot; -+} __packed; -+ -+/* HOSTCMD_CMD_EDMAC_CTRL */ -+struct hostcmd_cmd_edmac_ctrl { -+ struct hostcmd_header cmd_hdr; -+ __le16 action; -+ __le16 ed_ctrl_2g; -+ __le16 ed_offset_2g; -+ __le16 ed_ctrl_5g; -+ __le16 ed_offset_5g; -+ __le16 ed_bitmap_txq_lock; -+} __packed; -+ -+/* HOSTCMD_CMD_TXPWRLMT_CFG */ -+#define TXPWRLMT_CFG_VERSION_INFO_LEN 0x4 -+#define TXPWRLMT_CFG_MAX_SUBBAND_INFO 0x5 -+#define TXPWRLMT_CFG_SIG_LEN 0x4 -+#define TXPWRLMT_CFG_SIGNATURE 0xA1240E01 -+ -+struct hostcmd_cmd_txpwrlmt_cfg { -+ struct hostcmd_header cmd_hdr; -+ /* Action */ -+ __le16 action; -+ /*Sub band id*/ -+ u8 subband_id; -+ /* Cfg Complete Info*/ -+ u8 cfgComplete; -+ /* Data length */ -+ __le16 data_len; -+ /*number of entries*/ -+ __le16 num_entries; -+ /* Data */ -+ u8 data[1]; -+} __packed; -+ -+struct mwl_txpwrlmt_cfg_entry_hdr { -+ /* subband id */ -+ __le16 id; -+ /* length */ -+ __le16 len; -+ /* number of entries */ -+ __le16 num_entries; -+} __packed; -+ -+/* HOSTCMD_CMD_MCAST_CTS */ -+struct hostcmd_cmd_mcast_cts { -+ struct hostcmd_header cmd_hdr; -+ u8 enable; /* 1:enable, 0:disable */ -+} __packed; -+ -+#endif /* _HOSTCMD_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h -new file mode 100644 -index 000000000000..2c26bff80683 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/dev.h -@@ -0,0 +1,1032 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines device related information. */ -+ -+#ifndef _DEV_H_ -+#define _DEV_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define PCIE_DRV_NAME KBUILD_MODNAME -+#define PCIE_DRV_VERSION "10.3.8.0-20181210" -+ -+#define PCIE_MIN_BYTES_HEADROOM 64 -+#define PCIE_MIN_TX_HEADROOM_KF2 96 -+#define PCIE_NUM_OF_DESC_DATA SYSADPT_TOTAL_TX_QUEUES -+#define PCIE_AMPDU_QUEUES 4 -+#define PCIE_MAX_NUM_TX_DESC 256 -+#define PCIE_TX_QUEUE_LIMIT (3 * PCIE_MAX_NUM_TX_DESC) -+#define PCIE_TX_WAKE_Q_THRESHOLD (2 * PCIE_MAX_NUM_TX_DESC) -+#define PCIE_DELAY_FREE_Q_LIMIT PCIE_MAX_NUM_TX_DESC -+#define PCIE_MAX_NUM_RX_DESC 256 -+#define PCIE_RECEIVE_LIMIT 64 -+ -+enum { -+ IEEE_TYPE_MANAGEMENT = 0, -+ IEEE_TYPE_CONTROL, -+ IEEE_TYPE_DATA -+}; -+ -+#define MAC_REG_ADDR(offset) (offset) -+#define MAC_REG_ADDR_PCI(offset) ((pcie_priv->iobase1 + 0xA000) + offset) -+ -+#define MCU_CCA_CNT MAC_REG_ADDR(0x06A0) -+#define MCU_TXPE_CNT MAC_REG_ADDR(0x06A4) -+#define MCU_LAST_READ MAC_REG_ADDR(0x06A8) -+ -+/* Map to 0x80000000 (Bus control) on BAR0 */ -+#define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 /* (From host to ARM) */ -+#define MACREG_REG_H2A_INTERRUPT_CAUSE 0x00000C1C /* (From host to ARM) */ -+#define MACREG_REG_H2A_INTERRUPT_MASK 0x00000C20 /* (From host to ARM) */ -+#define MACREG_REG_H2A_INTERRUPT_CLEAR_SEL 0x00000C24 /* (From host to ARM) */ -+#define MACREG_REG_H2A_INTERRUPT_STATUS_MASK 0x00000C28 /* (From host to ARM) */ -+ -+#define MACREG_REG_A2H_INTERRUPT_EVENTS 0x00000C2C /* (From ARM to host) */ -+#define MACREG_REG_A2H_INTERRUPT_CAUSE 0x00000C30 /* (From ARM to host) */ -+#define MACREG_REG_A2H_INTERRUPT_MASK 0x00000C34 /* (From ARM to host) */ -+#define MACREG_REG_A2H_INTERRUPT_CLEAR_SEL 0x00000C38 /* (From ARM to host) */ -+#define MACREG_REG_A2H_INTERRUPT_STATUS_MASK 0x00000C3C /* (From ARM to host) */ -+ -+/* Map to 0x80000000 on BAR1 */ -+#define MACREG_REG_GEN_PTR 0x00000C10 -+#define MACREG_REG_INT_CODE 0x00000C14 -+ -+/* Bit definition for MACREG_REG_A2H_INTERRUPT_CAUSE (A2HRIC) */ -+#define MACREG_A2HRIC_BIT_TX_DONE BIT(0) -+#define MACREG_A2HRIC_BIT_RX_RDY BIT(1) -+#define MACREG_A2HRIC_BIT_OPC_DONE BIT(2) -+#define MACREG_A2HRIC_BIT_MAC_EVENT BIT(3) -+#define MACREG_A2HRIC_BIT_RX_PROBLEM BIT(4) -+#define MACREG_A2HRIC_BIT_RADIO_OFF BIT(5) -+#define MACREG_A2HRIC_BIT_RADIO_ON BIT(6) -+#define MACREG_A2HRIC_BIT_RADAR_DETECT BIT(7) -+#define MACREG_A2HRIC_BIT_ICV_ERROR BIT(8) -+#define MACREG_A2HRIC_BIT_WEAKIV_ERROR BIT(9) -+#define MACREG_A2HRIC_BIT_QUE_EMPTY BIT(10) -+#define MACREG_A2HRIC_BIT_QUE_FULL BIT(11) -+#define MACREG_A2HRIC_BIT_CHAN_SWITCH BIT(12) -+#define MACREG_A2HRIC_BIT_TX_WATCHDOG BIT(13) -+#define MACREG_A2HRIC_BA_WATCHDOG BIT(14) -+/* 15 taken by ISR_TXACK */ -+#define MACREG_A2HRIC_BIT_SSU_DONE BIT(16) -+#define MACREG_A2HRIC_CONSEC_TXFAIL BIT(17) -+ -+#define ISR_SRC_BITS (MACREG_A2HRIC_BIT_RX_RDY | \ -+ MACREG_A2HRIC_BIT_TX_DONE | \ -+ MACREG_A2HRIC_BIT_OPC_DONE | \ -+ MACREG_A2HRIC_BIT_MAC_EVENT | \ -+ MACREG_A2HRIC_BIT_WEAKIV_ERROR | \ -+ MACREG_A2HRIC_BIT_ICV_ERROR | \ -+ MACREG_A2HRIC_BIT_SSU_DONE | \ -+ MACREG_A2HRIC_BIT_RADAR_DETECT | \ -+ MACREG_A2HRIC_BIT_CHAN_SWITCH | \ -+ MACREG_A2HRIC_BIT_TX_WATCHDOG | \ -+ MACREG_A2HRIC_BIT_QUE_EMPTY | \ -+ MACREG_A2HRIC_BA_WATCHDOG | \ -+ MACREG_A2HRIC_CONSEC_TXFAIL) -+ -+#define MACREG_A2HRIC_BIT_MASK ISR_SRC_BITS -+ -+/* Bit definition for MACREG_REG_H2A_INTERRUPT_CAUSE (H2ARIC) */ -+#define MACREG_H2ARIC_BIT_PPA_READY BIT(0) -+#define MACREG_H2ARIC_BIT_DOOR_BELL BIT(1) -+#define MACREG_H2ARIC_BIT_PS BIT(2) -+#define MACREG_H2ARIC_BIT_PSPOLL BIT(3) -+#define ISR_RESET BIT(15) -+#define ISR_RESET_AP33 BIT(26) -+ -+/* Data descriptor related constants */ -+#define EAGLE_RXD_CTRL_DRIVER_OWN 0x00 -+#define EAGLE_RXD_CTRL_DMA_OWN 0x80 -+ -+#define EAGLE_RXD_STATUS_OK 0x01 -+ -+#define EAGLE_TXD_STATUS_IDLE 0x00000000 -+#define EAGLE_TXD_STATUS_OK 0x00000001 -+#define EAGLE_TXD_STATUS_FW_OWNED 0x80000000 -+ -+struct pcie_tx_desc { -+ u8 data_rate; -+ u8 tx_priority; -+ __le16 qos_ctrl; -+ __le32 pkt_ptr; -+ __le16 pkt_len; -+ u8 dest_addr[ETH_ALEN]; -+ __le32 pphys_next; -+ __le32 sap_pkt_info; -+ __le32 rate_info; -+ u8 type; -+ u8 xmit_control; /* bit 0: use rateinfo, bit 1: disable ampdu */ -+ __le16 reserved; -+ __le32 tcpack_sn; -+ __le32 tcpack_src_dst; -+ __le32 reserved1; -+ __le32 reserved2; -+ u8 reserved3[2]; -+ u8 packet_info; -+ u8 packet_id; -+ __le16 packet_len_and_retry; -+ __le16 packet_rate_info; -+ __le32 flags; -+ __le32 status; -+} __packed; -+ -+struct pcie_tx_hndl { -+ struct sk_buff *psk_buff; -+ struct pcie_tx_desc *pdesc; -+ struct pcie_tx_hndl *pnext; -+}; -+ -+/* Receive rate information constants */ -+#define RX_RATE_INFO_FORMAT_11A 0 -+#define RX_RATE_INFO_FORMAT_11B 1 -+#define RX_RATE_INFO_FORMAT_11N 2 -+#define RX_RATE_INFO_FORMAT_11AC 4 -+ -+#define RX_RATE_INFO_HT20 0 -+#define RX_RATE_INFO_HT40 1 -+#define RX_RATE_INFO_HT80 2 -+#define RX_RATE_INFO_HT160 3 -+ -+#define RX_RATE_INFO_LONG_INTERVAL 0 -+#define RX_RATE_INFO_SHORT_INTERVAL 1 -+ -+#define MWL_RX_RATE_FORMAT_MASK 0x0007 -+#define MWL_RX_RATE_NSS_MASK 0x0018 -+#define MWL_RX_RATE_NSS_SHIFT 3 -+#define MWL_RX_RATE_BW_MASK 0x0060 -+#define MWL_RX_RATE_BW_SHIFT 5 -+#define MWL_RX_RATE_GI_MASK 0x0080 -+#define MWL_RX_RATE_GI_SHIFT 7 -+#define MWL_RX_RATE_RT_MASK 0xFF00 -+#define MWL_RX_RATE_RT_SHIFT 8 -+ -+struct pcie_rx_desc { -+ __le16 pkt_len; /* total length of received data */ -+ __le16 rate; /* receive rate information */ -+ __le32 pphys_buff_data; /* physical address of payload data */ -+ __le32 pphys_next; /* physical address of next RX desc */ -+ __le16 qos_ctrl; /* received QosCtrl field variable */ -+ __le16 ht_sig2; /* like name states */ -+ __le32 hw_rssi_info; -+ __le32 hw_noise_floor_info; -+ u8 noise_floor; -+ u8 reserved[3]; -+ u8 rssi; /* received signal strengt indication */ -+ u8 status; /* status field containing USED bit */ -+ u8 channel; /* channel this pkt was received on */ -+ u8 rx_control; /* the control element of the desc */ -+ __le32 reserved1[3]; -+} __packed; -+ -+struct pcie_rx_hndl { -+ struct sk_buff *psk_buff; /* associated sk_buff for Linux */ -+ struct pcie_rx_desc *pdesc; -+ struct pcie_rx_hndl *pnext; -+}; -+ -+struct pcie_desc_data { -+ dma_addr_t pphys_tx_ring; /* ptr to first TX desc (phys.) */ -+ struct pcie_tx_desc *ptx_ring; /* ptr to first TX desc (virt.) */ -+ struct pcie_tx_hndl *tx_hndl; -+ struct pcie_tx_hndl *pnext_tx_hndl;/* next TX handle that can be used */ -+ struct pcie_tx_hndl *pstale_tx_hndl;/* the staled TX handle */ -+ dma_addr_t pphys_rx_ring; /* ptr to first RX desc (phys.) */ -+ struct pcie_rx_desc *prx_ring; /* ptr to first RX desc (virt.) */ -+ struct pcie_rx_hndl *rx_hndl; -+ struct pcie_rx_hndl *pnext_rx_hndl;/* next RX handle that can be used */ -+ u32 wcb_base; /* FW base offset for registers */ -+ u32 rx_desc_write; /* FW descriptor write position */ -+ u32 rx_desc_read; /* FW descriptor read position */ -+ u32 rx_buf_size; /* length of the RX buffers */ -+}; -+ -+/* DMA header used by firmware and hardware. */ -+struct pcie_dma_data { -+ __le16 fwlen; -+ struct ieee80211_hdr wh; -+ char data[0]; -+} __packed; -+ -+/* New Data Path */ -+#define MACREG_REG_SCRATCH3 0x00000C44 -+#define MACREG_REG_TXSENDHEAD 0x00000CD0 -+#define MACREG_REG_TXSEDNTAIL 0x00000CD4 -+#define MACREG_REG_TXDONEHEAD 0x00000CD8 -+#define MACREG_REG_TXDONETAIL 0x00000CDC -+#define MACREG_REG_RXDESCHEAD 0x00000CE0 -+#define MACREG_REG_RXDESCTAIL 0x00000CE4 -+#define MACREG_REG_RXDONEHEAD 0x00000CE8 -+#define MACREG_REG_RXDONETAIL 0x00000CEC -+#define MACREG_REG_ACNTHEAD 0x00000CF0 -+#define MACREG_REG_ACNTTAIL 0x00000CF4 -+ -+/* Buff removed from Tx Send Ring */ -+#define MACREG_A2HRIC_TX_DESC_TAIL_RDY (1<<9) -+/* Buff added to Tx Done Ring */ -+#define MACREG_A2HRIC_TX_DONE_HEAD_RDY (1<<10) -+/* Records added to Accounting Ring */ -+#define MACREG_A2HRIC_ACNT_HEAD_RDY (1<<12) -+/* Buff removed from Rx Desc Ring */ -+#define MACREG_A2HRIC_RX_DESC_TAIL_RDY (1<<17) -+/* Buff added to Rx Done Ring */ -+#define MACREG_A2HRIC_RX_DONE_HEAD_RDY (1<<18) -+#define MACREG_A2HRIC_NEWDP_DFS (1<<19) -+#define MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH (1<<20) -+ -+#define ISR_SRC_BITS_NDP ((MACREG_A2HRIC_ACNT_HEAD_RDY) | \ -+ (MACREG_A2HRIC_RX_DONE_HEAD_RDY) | \ -+ (MACREG_A2HRIC_NEWDP_DFS) | \ -+ (MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH)) -+ -+#define MACREG_A2HRIC_BIT_MASK_NDP ISR_SRC_BITS_NDP -+ -+#define MIN_BYTES_RX_HEADROOM (64 + 2) -+#define AMPDU_QUEUES_NDP (SYSADPT_MAX_STA_SC4 * \ -+ SYSADPT_MAX_TID) -+#define MAX_NUM_TX_DESC 1024 -+#define MAX_NUM_RX_DESC (1024 * 16) -+#define MAX_TX_RING_SEND_SIZE (4 * MAX_NUM_TX_DESC) -+#define MAX_TX_RING_DONE_SIZE MAX_NUM_TX_DESC -+#define MAX_RX_RING_SEND_SIZE MAX_NUM_RX_DESC -+#define MAX_RX_RING_DONE_SIZE MAX_NUM_RX_DESC -+#define DEFAULT_ACNT_RING_SIZE 0x10000 -+#define MAX_AGGR_SIZE 1900 -+#define TX_QUEUE_LIMIT MAX_NUM_TX_DESC -+#define TX_WAKE_Q_THRESHOLD (MAX_NUM_TX_DESC - 256) -+ -+/* RateCode usage notes: -+ * * General -+ * * No error checking is provided on RateCodes, so usage of invalid values -+ * or rates not supported by HW can result in undefined operation. -+ * * Some values are not allowed by Std, but are included to sanitize the -+ * table; -+ * * MaxPwr should only be used for rates that can be sent using Max Power, -+ * such as for TxEVM limits or regulatory. It is only valid for Host -+ * Generated frames, and not for DRA, etc. -+ * * VHT -+ * * Need to reconsile MU. -+ * * HT -+ * * MCS and SS are made to mimic 11ac, so MCS=mcs[2:0] and SS=mcs[4:3]; -+ * * MCS32 is selected by providing MCS=10; -+ * * Legacy -+ * * MCS0..7 = 6/9/12/18/24/36/48/54; -+ * * MCS8..15 = 1S/1L/2S/2L/5.5S/5.5L/11S/11L; -+ * * BW is used to request legacy duplicate modes; -+ */ -+#define RATECODE_DEFAULT 0xFFFF /* Don't override the Rate */ -+#define RATECODE_TYPE_MASK 0xC000 /* Mask to extract Type */ -+#define RATECODE_TYPE_SHIFT 14 /* Shift to extract Type */ -+#define RATECODE_TYPE_VHT 0x8000 /* Use VHT rates */ -+#define RATECODE_TYPE_HT 0x4000 /* Use HT rates */ -+#define RATECODE_TYPE_LEGACY 0x0000 /* Use Legacy (a/b/g) rates */ -+#define RATECODE_MAXPWR 0x2000 /* Send at Max Power / Off Channel */ -+#define RATECODE_RSVD 0x1000 /* Unused */ -+#define RATECODE_STBC 0x0800 /* Use Space Time Block Codes */ -+#define RATECODE_BFMR 0x0400 /* Use Beamforming */ -+#define RATECODE_SS_MASK 0x0300 /* Mask to extract nSS-1 */ -+#define RATECODE_SS_SHIFT 8 /* Shift to extract nSS-1 */ -+#define RATECODE_MCS_MASK 0x00F0 /* Mask to extract MCS rate */ -+#define RATECODE_MCS_SHIFT 4 /* Shift to extract MCS rate */ -+#define RATECODE_BW_MASK 0x000C /* Mask to extract Channel BW */ -+#define RATECODE_BW_SHIFT 2 /* Shift to extract Channel BW */ -+#define RATECODE_BW_160MHZ 0x000C /* Send 160M wide packet (or 80+80) */ -+#define RATECODE_BW_80MHZ 0x0008 /* Send 80M wide packet */ -+#define RATECODE_BW_40MHZ 0x0004 /* Send 40M wide packet */ -+#define RATECODE_BW_20MHZ 0x0000 /* Send 20M wide packet */ -+#define RATECODE_LDPC 0x0002 /* Use Low Density Parity Codes */ -+#define RATECODE_SGI 0x0001 /* Use Short Guard Interval */ -+ -+#define TXRING_CTRL_LEN_SHIFT 0 /* PCIe Payload size (Starts w/ SNAP) */ -+#define TXRING_CTRL_LEN_MASK 0x3FFF /* PCIe Payload size (Starts w/ SNAP) */ -+#define TXRING_CTRL_QID_SHIFT 14 /* Queue ID (STA*UP, Mcast, MC2UC, etc)*/ -+#define TXRING_CTRL_QID_MASK 0xFFF /* Queue ID (STA*UP, Mcast, MC2UC, etc)*/ -+#define TXRING_CTRL_TAG_SHIFT 26 /* Tags for special Processing */ -+#define TXRING_CTRL_TAG_MASK 0x3F /* Tags for special Processing */ -+#define TXRING_CTRL_TAG_MGMT 0x01 /* Has Host generated dot11 Header */ -+#define TXRING_CTRL_TAG_EAP 0x02 /* Tag for EAPOL frames */ -+#define TXRING_CTRL_TAG_TCP_ACK 0x4 -+#define TXRING_CTRL_TAG_RSVD 0x3C /* Unused */ -+ -+struct tx_info { /* Tx INFO used by MAC HW */ -+ __le32 reserved0[10]; -+ __le32 rate_info; -+ __le32 reserved1[14]; -+} __packed; -+ -+struct pcie_tx_desc_ndp { -+ union { /* Union for Tx DA/SA or Mgmt Overrides */ -+ struct { /* Fields for Data frames */ -+ u8 da[ETH_ALEN]; /* L2 Destination Address */ -+ u8 sa[ETH_ALEN]; /* L2 Source Address */ -+ }; -+ struct { /* Fields when marked as Mgmt */ -+ __le16 rate_code; /* Rate Code: Table + Index */ -+ u8 max_retry; -+ u8 pad[5]; /* Unused */ -+ __le32 call_back; /* Used for Packet returned to FW */ -+ }; -+ } u; -+ __le32 ctrl; /* Bit fields (TXRING_CTRL_*) */ -+ __le32 data; /* PCIe Payload Pointer (Starts w/ SNAP) */ -+ __le32 user; /* Value returned to Host when done */ -+ __le32 tcp_dst_src; -+ __le32 tcp_sn; -+} __packed; -+ -+struct tx_ring_done { -+ __le32 user; -+} __packed; -+ -+#define RXRING_CTRL_CASE_SHIFT 0 /* What is in the buffer(RXRING_CASE_*) */ -+#define RXRING_CTRL_CASE_MASK 0x1F /* What is in the buffer(RXRING_CASE_*) */ -+#define RXRING_CTRL_STA_SHIFT 5 /* STA information (or Mcast group) */ -+#define RXRING_CTRL_STA_MASK 0x1FF /* STA information (or Mcast group) */ -+#define RXRING_CTRL_STA_UNKNOWN 0x1FF /* STA Idx for packets from Unknown STA */ -+#define RXRING_CTRL_STA_FROMDS 0x1FE /* STA Idx for packets from DS */ -+#define RXRING_CTRL_TID_SHIFT 14 /* TID/UP for QoS Data frames */ -+#define RXRING_CTRL_TID_MASK 0xF /* TID/UP for QoS Data frames */ -+#define RXRING_CTRL_KEY_SHIFT 18 /* Key Type used (KEY_TYPE_*) */ -+#define RXRING_CTRL_KEY_MASK 0xF /* Key Type used (KEY_TYPE_*) */ -+#define RXRING_CTRL_TRUNC (1UL<<31) /* Packet Truncated */ -+ -+/* Rx Buffer Formats -+ * Each Case listed above will indicate the format used, and each format will -+ * carry their length in the packet buffer. Should the packet be too big for -+ * the buffer, it will be truncated, but the full length will still be -+ * indicated. Currently only a single, fixed size Rx Pool is envisioned. -+ * -+ * Fmt0 is used for Slow path, when some processing of dot11 headers may still -+ * be required, or for promiscuous mode captures. It is in the HW RxINFO -+ * (rx_info_t) format including dot11_t followed by Payload. The Length field in -+ * the dot11_t is updated to only include Payload bytes, and is in Little Endian -+ * format. If the frame is too big, it is truncated to the buffer size, and -+ * promiscuous packets may also be configured for truncation to reduce load. The -+ * mark field is replaced with software status, and the RSSI will be updated to -+ * apply Rx calibration. -+ * -+ * Fmt1 is used for fast path Data packets in the run state, where all rx -+ * processing of dot11 headers is performed from radio FW. It has an AMSDU -+ * centric format of DA/SA/Len followed by SNAP, with the Length in Big Endian -+ * Format. In most cases conversion to Ethernet format is accomplished by -+ * copying 12 bytes to drop 8 bytes in the middle. -+ * -+ * Fmt2 is used for fast path AMSDU packets that are malformed. They just -+ * contain the dot11 header (dot11_t) containing the residual Len (Little -+ * Endian) after any valid MSDU have been extracted. The header is followed by -+ * the first invalid MSDU which will be truncated to 64 bytes. -+ */ -+enum { /* What is in Rx Buffer and why it was delivered */ -+ /* Data for Assoc Clients in Run State on Channel [Fmt1] */ -+ RXRING_CASE_FAST_DATA, -+ RXRING_CASE_FAST_BAD_AMSDU, /* Fast Data with bad AMSDU Header [Fmt2] */ -+ /* Data for Assoc Clients using unconfigured queue [Fmt0] */ -+ RXRING_CASE_SLOW_NOQUEUE, -+ /* Data for Assoc Clients not matching Run State [Fmt0] */ -+ RXRING_CASE_SLOW_NORUN, -+ /* Data for filtered Multicast groups [Fmt0] */ -+ RXRING_CASE_SLOW_MCAST, -+ RXRING_CASE_SLOW_BAD_STA, /* Data for Unassoc Clients [Fmt0] */ -+ RXRING_CASE_SLOW_BAD_MIC, /* Decrypt failure [Fmt0] */ -+ RXRING_CASE_SLOW_BAD_PN, /* Decrypt PN replay [Fmt0] */ -+ RXRING_CASE_SLOW_MGMT, /* Mgmt traffic to this AP or Bcast [Fmt0]*/ -+ RXRING_CASE_SLOW_PROMISC, /* Packets captured promiscuously [Fmt0] */ -+ RXRING_CASE_SLOW_DEL_DONE, /* Client has been deleted [N/A] */ -+ RXRING_CASE_DROP, /* Buffer returned to Host [N/A] */ -+}; -+ -+enum { /* Type of Key */ -+ KEY_TYPE_NONE, /* Bypass (never stored in real keys) */ -+ KEY_TYPE_WEP40, /* WEP with 40 bit key + 24 bit IV = 64 */ -+ KEY_TYPE_WEP104, /* WEP with 104 bit key + 24 bit IV = 128 */ -+ KEY_TYPE_TKIP, /* TKIP */ -+ KEY_TYPE_CCMP128, /* CCMP with 128 bit Key */ -+ KEY_TYPE_CCMP256, /* CCMP with 256 bit Key + 16 byte MIC */ -+ KEY_TYPE_WAPI, /* WAPI */ -+ KEY_TYPE_UNKNOWN, /* Not known what key was used (Rx Only) */ -+ KEY_TYPE_GCMP128, /* GCMP with 128 bit Key */ -+ KEY_TYPE_GCMP256, /* GCMP with 256 bit Key + 16 byte MIC */ -+}; -+ -+#define RXINFO_RSSI_X_SHIFT 24 -+#define RXINFO_RSSI_X_MASK 0xFF -+#define RXINFO_HT_SIG1_SHIFT 0 -+#define RXINFO_HT_SIG1_MASK 0xFFFFFF -+#define RXINFO_HT_SIG2_SHIFT 0 -+#define RXINFO_HT_SIG2_MASK 0x3FFFF -+#define RXINFO_RATE_SHIFT 24 -+#define RXINFO_RATE_MASK 0xFF -+#define RXINFO_NF_A_SHIFT 12 -+#define RXINFO_NF_A_MASK 0xFFF -+#define RXINFO_NF_B_SHIFT 0 -+#define RXINFO_NF_B_MASK 0xFFF -+#define RXINFO_NF_C_SHIFT 12 -+#define RXINFO_NF_C_MASK 0xFFF -+#define RXINFO_NF_D_SHIFT 0 -+#define RXINFO_NF_D_MASK 0xFFF -+#define RXINFO_PARAM_SHIFT 0 -+#define RXINFO_PARAM_MASK 0xFFFFFF -+ -+struct rx_info { /* HW Rx buffer */ -+ __le32 reserved0[2]; -+ __le32 rssi_x; -+ __le32 reserved1[2]; -+ __le32 ht_sig1; -+ __le32 ht_sig2_rate; -+ __le32 reserved2[6]; -+ __le32 nf_a_b; -+ __le32 nf_c_d; -+ __le32 reserved3[6]; -+ __le32 param; -+ __le32 reserved4[2]; -+ __le32 hdr[0]; /* Len from HW includes rx_info w/ hdr */ -+} __packed; -+ -+struct pcie_rx_desc_ndp { /* ToNIC Rx Empty Buffer Ring Entry */ -+ __le32 data; /* PCIe Payload Pointer */ -+ __le32 user; /* Value returned to Host when done */ -+} __packed; -+ -+struct rx_ring_done { /* FromNIC Rx Done Ring Entry */ -+ __le32 user; /* Value returned to Host when done */ -+ __le32 tsf; /* Rx Radio Timestamp from MAC */ -+ __le32 ctrl; /* Bit fields (RXRING_CTRL_*) */ -+} __packed; -+ -+struct pcie_desc_data_ndp { -+ dma_addr_t pphys_tx_ring; /* ptr to first TX desc (phys.) */ -+ struct pcie_tx_desc_ndp *ptx_ring;/* ptr to first TX desc (virt.) */ -+ dma_addr_t pphys_rx_ring; /* ptr to first RX desc (phys.) */ -+ struct pcie_rx_desc_ndp *prx_ring;/* ptr to first RX desc (virt.) */ -+ u32 wcb_base; /* FW base offset for registers */ -+ u32 rx_buf_size; /* length of the RX buffers */ -+ u32 tx_sent_tail; /* index to the TX desc FW used */ -+ u32 tx_sent_head; /* index to next TX desc to be used*/ -+ u32 tx_done_tail; /* index to Tx Done queue tail */ -+ /* keept the skb owned by fw */ -+ dma_addr_t pphys_tx_buflist[MAX_TX_RING_SEND_SIZE]; -+ struct sk_buff *tx_vbuflist[MAX_TX_RING_SEND_SIZE]; -+ u32 tx_vbuflist_idx; /* idx to empty slot in tx_vbuflist*/ -+ struct sk_buff *rx_vbuflist[MAX_NUM_RX_DESC]; -+ struct tx_ring_done *ptx_ring_done; -+ dma_addr_t pphys_tx_ring_done; /* ptr to first TX done desc (phys.) */ -+ struct rx_ring_done *prx_ring_done; -+ dma_addr_t pphys_rx_ring_done; /* ptr to first RX done desc (phys.) */ -+ dma_addr_t pphys_acnt_ring; /* ptr to first account record (phys.)*/ -+ u8 *pacnt_ring; /* ptr to first accounting record */ -+ u32 tx_desc_busy_cnt; -+ u8 *pacnt_buf; -+ u32 acnt_ring_size; -+}; -+ -+struct ndp_rx_counter { -+ u32 fast_data_cnt; -+ u32 fast_bad_amsdu_cnt; -+ u32 slow_noqueue_cnt; -+ u32 slow_norun_cnt; -+ u32 slow_mcast_cnt; -+ u32 slow_bad_sta_cnt; -+ u32 slow_bad_mic_cnt; -+ u32 slow_bad_pn_cnt; -+ u32 slow_mgmt_cnt; -+ u32 slow_promisc_cnt; -+ u32 drop_cnt; -+ u32 offch_promisc_cnt; -+ u32 mu_pkt_cnt; -+}; -+ -+/* KF2 - 88W8997 */ -+#define PCIE_MAX_TXRX_BD 0x20 -+/* PCIE read data pointer for queue 0 and 1 */ -+#define PCIE_RD_DATA_PTR_Q0_Q1 0xC1A4 /* 0x8000C1A4 */ -+/* PCIE read data pointer for queue 2 and 3 */ -+#define PCIE_RD_DATA_PTR_Q2_Q3 0xC1A8 /* 0x8000C1A8 */ -+/* PCIE write data pointer for queue 0 and 1 */ -+#define PCIE_WR_DATA_PTR_Q0_Q1 0xC174 /* 0x8000C174 */ -+/* PCIE write data pointer for queue 2 and 3 */ -+#define PCIE_WR_DATA_PTR_Q2_Q3 0xC178 /* 0x8000C178 */ -+ -+/* TX buffer description read pointer */ -+#define REG_TXBD_RDPTR PCIE_RD_DATA_PTR_Q0_Q1 -+/* TX buffer description write pointer */ -+#define REG_TXBD_WRPTR PCIE_WR_DATA_PTR_Q0_Q1 -+ -+#define PCIE_TX_START_PTR 16 -+ -+#define PCIE_TXBD_MASK 0x0FFF0000 -+#define PCIE_TXBD_WRAP_MASK 0x1FFF0000 -+ -+#define PCIE_BD_FLAG_RX_ROLLOVER_IND BIT(12) -+#define PCIE_BD_FLAG_TX_START_PTR BIT(16) -+#define PCIE_BD_FLAG_TX_ROLLOVER_IND BIT(28) -+#define PCIE_BD_FLAG_TX2_START_PTR BIT(0) -+#define PCIE_BD_FLAG_TX2_ROLLOVER_IND BIT(12) -+ -+#define PCIE_BD_FLAG_FIRST_DESC BIT(0) -+#define PCIE_BD_FLAG_LAST_DESC BIT(1) -+ -+#define PCIE_TX_WCB_FLAGS_DONT_ENCRYPT 0x00000001 -+#define PCIE_TX_WCB_FLAGS_NO_CCK_RATE 0x00000002 -+ -+#define PCIE_TXBD_NOT_FULL(wrptr, rdptr) \ -+ (((wrptr & PCIE_TXBD_MASK) != (rdptr & PCIE_TXBD_MASK)) \ -+ || ((wrptr & PCIE_BD_FLAG_TX_ROLLOVER_IND) == \ -+ (rdptr & PCIE_BD_FLAG_TX_ROLLOVER_IND))) -+ -+struct pcie_data_buf { -+ /* Buffer descriptor flags */ -+ __le16 flags; -+ /* Offset of fragment/pkt to start of ip header */ -+ __le16 offset; -+ /* Fragment length of the buffer */ -+ __le16 frag_len; -+ /* Length of the buffer */ -+ __le16 len; -+ /* Physical address of the buffer */ -+ __le64 paddr; -+ /* Reserved */ -+ __le32 reserved; -+} __packed; -+ -+struct pcie_pfu_dma_data { -+ struct pcie_tx_desc tx_desc; -+ struct pcie_dma_data dma_data; -+} __packed; -+ -+struct pcie_priv { -+ struct mwl_priv *mwl_priv; -+ struct pci_dev *pdev; -+ void __iomem *iobase0; /* MEM Base Address Register 0 */ -+ void __iomem *iobase1; /* MEM Base Address Register 1 */ -+ u32 next_bar_num; -+ -+ struct sk_buff_head txq[PCIE_NUM_OF_DESC_DATA]; -+ -+ spinlock_t int_mask_lock ____cacheline_aligned_in_smp; -+ struct tasklet_struct tx_task; -+ struct tasklet_struct tx_done_task; -+ struct tasklet_struct rx_task; -+ struct tasklet_struct qe_task; -+ unsigned int tx_head_room; -+ int txq_limit; -+ int txq_wake_threshold; -+ bool is_tx_schedule; -+ bool is_tx_done_schedule; -+ int recv_limit; -+ bool is_rx_schedule; -+ bool is_qe_schedule; -+ u32 qe_trig_num; -+ unsigned long qe_trig_time; -+ -+ /* various descriptor data */ -+ /* for tx descriptor data */ -+ spinlock_t tx_desc_lock ____cacheline_aligned_in_smp; -+ struct pcie_desc_data desc_data[PCIE_NUM_OF_DESC_DATA]; -+ int delay_q_idx; -+ struct sk_buff *delay_q[PCIE_DELAY_FREE_Q_LIMIT]; -+ /* number of descriptors owned by fw at any one time */ -+ int fw_desc_cnt[PCIE_NUM_OF_DESC_DATA]; -+ -+ /* new data path */ -+ struct pcie_desc_data_ndp desc_data_ndp; -+ int tx_done_cnt; -+ struct ieee80211_sta *sta_link[SYSADPT_MAX_STA_SC4 + 1]; -+ struct sk_buff_head rx_skb_trace; -+ struct ndp_rx_counter rx_cnts; -+ u32 rx_skb_unlink_err; -+ u32 signature_err; -+ u32 recheck_rxringdone; -+ u32 acnt_busy; -+ u32 acnt_wrap; -+ u32 acnt_drop; -+ -+ /* KF2 - 88W8997 */ -+ struct firmware *cal_data; -+ /* Write pointer for TXBD ring */ -+ u32 txbd_wrptr; -+ /* Shadow copy of TXBD read pointer */ -+ u32 txbd_rdptr; -+ /* TXBD ring size */ -+ u32 txbd_ring_size; -+ /* Virtual base address of txbd_ring */ -+ u8 *txbd_ring_vbase; -+ /* Physical base address of txbd_ring */ -+ dma_addr_t txbd_ring_pbase; -+ /* Ring of buffer descriptors for TX */ -+ struct pcie_data_buf *txbd_ring[PCIE_MAX_TXRX_BD]; -+ struct sk_buff *tx_buf_list[PCIE_MAX_TXRX_BD]; -+}; -+ -+enum { /* Definition of accounting record codes */ -+ ACNT_CODE_BUSY = 0, /* Marked busy until filled in */ -+ ACNT_CODE_WRAP, /* Used to pad when wrapping */ -+ ACNT_CODE_DROP, /* Count of dropped records */ -+ ACNT_CODE_TX_ENQUEUE, /* TXINFO when added to TCQ (acnt_tx_s) */ -+ ACNT_CODE_RX_PPDU, /* RXINFO for each PPDu (acnt_rx_s) */ -+ ACNT_CODE_TX_FLUSH, /* Flush Tx Queue */ -+ ACNT_CODE_RX_RESET, /* Channel Change / Rx Reset */ -+ ACNT_CODE_TX_RESET, /* TCQ reset */ -+ ACNT_CODE_QUOTE_LEVEL,/* Quota Level changes */ -+ ACNT_CODE_TX_DONE, /* Tx status when done */ -+ ACNT_CODE_RA_STATS, /* rateinfo PER (acnt_ra_s) */ -+ ACNT_CODE_BA_STATS, /* BA stats (acnt_ba_s) */ -+ ACNT_CODE_BF_MIMO_CTRL,/* BF Mimo Ctrl Field Log (acnt_bf_mimo_ctrl_s)*/ -+}; -+ -+struct acnt_s { /* Baseline Accounting Record format */ -+ __le16 code; /* Unique code for each type */ -+ u8 len; /* Length in DWORDS, including header */ -+ u8 pad; /* Alignment for generic, but specific can reuse*/ -+ __le32 tsf; /* Timestamp for Entry (when len>1) */ -+} __packed; -+ -+struct acnt_tx_s { /* Accounting Record For Tx (at Enqueue time) */ -+ __le16 code; /* Unique code for each type */ -+ u8 len; /* Length in DWORDS, including header */ -+ u8 tcq; /* Which TCQ was used */ -+ __le32 tsf; /* Timestamp for Entry (when len>1) */ -+ __le64 bitmap; /* Map of SeqNr when AMPDU */ -+ __le16 air_time; /* Air Time used by PPDU */ -+ __le16 npkts; /* Number of Descriptors sent (AMPDU&AMSDU) */ -+ __le16 qid; /* Transmit Queue ID */ -+ __le16 latency; /* Latency of oldest frame in AMPDU (128us) */ -+ __le16 rate1; /* Rate Code for sending data */ -+ __le16 rate2; /* Rate Code for sending RTS/CTS protection */ -+ u8 rate_tbl_index; /* Rate table index for this TxInfo rate */ -+ u8 type; /* SU:0 or MU:1 */ -+ u8 pad[1]; /* Unused */ -+ u8 retries; /* Number of retries of oldest frame in AMPDU */ -+ __le32 tx_cnt; /* No. of pkt sent */ -+ struct tx_info tx_info;/* Transmit parameters used for 1st MPDU/AMPDU */ -+ struct pcie_dma_data hdr;/* Dot11 header used for 1st MPDU in AMPDU */ -+ u8 payload[0]; /* Variable Payload by use case */ -+} __packed; -+ -+struct acnt_rx_s { /* Accounting Record for Rx PPDU */ -+ __le16 code; /* Unique code for each type */ -+ u8 len; /* Length in DWORDS, including header */ -+ u8 flags; /* Flags (ACNTRX_*) */ -+ __le32 tsf; /* Timestamp for Entry (when len>1) */ -+ __le64 bitmap; /* Map of SeqNr when AMPDU */ -+ __le16 air_time; /* Air Time used by PPDU (no CSMA overhead) */ -+ __le16 rate; /* Rate Code for receiving data */ -+ struct rx_info rx_info;/* Receive parameters from 1st valid MPDU/AMPDU*/ -+} __packed; -+ -+struct acnt_ra_s { /* Accounting Record w/ rateinfo PER */ -+ __le16 code; /* Unique code for each type */ -+ u8 len; /* Length in DWORDS, including header */ -+ u8 per; /* PER for this rateinfo */ -+ __le32 tsf; /* Timestamp for Entry (when len>1) */ -+ __le16 stn_id; /* sta index this rateinfo is tied to */ -+ u8 type; /* SU:0 or MU:1 */ -+ u8 rate_tbl_index; /* ratetbl index */ -+ __le32 rate_info; /* rateinfo for this ratetbl index */ -+ __le32 tx_attempt_cnt;/* Total tx pkt during rate adapt interval */ -+} __packed; -+ -+struct acnt_ba_s { /* Accounting Record w/ rateinfo PER */ -+ __le16 code; /* Unique code for each type */ -+ u8 len; /* Length in DWORDS, including header */ -+ u8 ba_hole; /* Total missing pkt in a BA */ -+ __le32 tsf; /* Timestamp for Entry (when len>1) */ -+ __le16 stnid; /* sta index for this BA */ -+ u8 no_ba; /* No BA received */ -+ u8 ba_expected; /* Total expected pkt to be BA'd */ -+ u8 type; /* SU:0 or MU:1 */ -+ u8 pad[3]; /* Unused */ -+} __packed; -+ -+struct acnt_bf_mimo_ctrl_s {/* Accounting Record w/ BF MIMO Control Field Data*/ -+ __le16 code; /* Unique code for each type */ -+ u8 len; /* Length in DWORDS, including header */ -+ u8 type; /* SU:0, MU:1 */ -+ __le32 tsf; /* Timestamp for Entry (when len>1) */ -+ u8 rec_mac[6]; /* Received Packet Source MAC Address */ -+ __le16 pad; /* Padding */ -+ __le32 mimo_ctrl; /* BF MIMO Control Field Data */ -+ __le64 comp_bf_rep; /* First 8 bytes of Compressed BF Report */ -+} __packed; -+ -+static inline void pcie_tx_add_dma_header(struct mwl_priv *priv, -+ struct sk_buff *skb, -+ int head_pad, -+ int tail_pad) -+{ -+ struct ieee80211_hdr *wh; -+ int dma_hdrlen; -+ int hdrlen; -+ int reqd_hdrlen; -+ int needed_room; -+ struct pcie_dma_data *dma_data; -+ -+ dma_hdrlen = (priv->chip_type == MWL8997) ? -+ sizeof(struct pcie_pfu_dma_data) : -+ sizeof(struct pcie_dma_data); -+ -+ /* Add a firmware DMA header; the firmware requires that we -+ * present a 2-byte payload length followed by a 4-address -+ * header (without QoS field), followed (optionally) by any -+ * WEP/ExtIV header (but only filled in for CCMP). -+ */ -+ wh = (struct ieee80211_hdr *)skb->data; -+ -+ hdrlen = ieee80211_hdrlen(wh->frame_control); -+ -+ reqd_hdrlen = dma_hdrlen + head_pad; -+ -+ if (hdrlen != reqd_hdrlen) { -+ needed_room = reqd_hdrlen - hdrlen; -+ if (skb_headroom(skb) < needed_room) { -+ wiphy_debug(priv->hw->wiphy, "headroom is short: %d %d", -+ skb_headroom(skb), needed_room); -+ skb_cow(skb, needed_room); -+ } -+ skb_push(skb, needed_room); -+ } -+ -+ if (ieee80211_is_data_qos(wh->frame_control)) -+ hdrlen -= IEEE80211_QOS_CTL_LEN; -+ -+ if (priv->chip_type == MWL8997) -+ dma_data = &((struct pcie_pfu_dma_data *)skb->data)->dma_data; -+ else -+ dma_data = (struct pcie_dma_data *)skb->data; -+ -+ if (wh != &dma_data->wh) -+ memmove(&dma_data->wh, wh, hdrlen); -+ -+ if (hdrlen != sizeof(dma_data->wh)) -+ memset(((void *)&dma_data->wh) + hdrlen, 0, -+ sizeof(dma_data->wh) - hdrlen); -+ -+ /* Firmware length is the length of the fully formed "802.11 -+ * payload". That is, everything except for the 802.11 header. -+ * This includes all crypto material including the MIC. -+ */ -+ dma_data->fwlen = -+ cpu_to_le16(skb->len - dma_hdrlen + tail_pad); -+} -+ -+static inline void pcie_tx_encapsulate_frame(struct mwl_priv *priv, -+ struct sk_buff *skb, -+ struct ieee80211_key_conf *k_conf, -+ bool *ccmp) -+{ -+ int head_pad = 0; -+ int data_pad = 0; -+ -+ /* Make sure the packet header is in the DMA header format (4-address -+ * without QoS), and add head & tail padding when HW crypto is enabled. -+ * -+ * We have the following trailer padding requirements: -+ * - WEP: 4 trailer bytes (ICV) -+ * - TKIP: 12 trailer bytes (8 MIC + 4 ICV) -+ * - CCMP: 8 trailer bytes (MIC) -+ */ -+ -+ if (k_conf) { -+ head_pad = k_conf->iv_len; -+ -+ switch (k_conf->cipher) { -+ case WLAN_CIPHER_SUITE_WEP40: -+ case WLAN_CIPHER_SUITE_WEP104: -+ data_pad = 4; -+ break; -+ case WLAN_CIPHER_SUITE_TKIP: -+ data_pad = 12; -+ break; -+ case WLAN_CIPHER_SUITE_CCMP: -+ data_pad = 8; -+ if (ccmp) -+ *ccmp = true; -+ break; -+ } -+ } -+ -+ pcie_tx_add_dma_header(priv, skb, head_pad, data_pad); -+} -+ -+static inline void pcie_tx_prepare_info(struct mwl_priv *priv, u32 rate, -+ struct ieee80211_tx_info *info) -+{ -+ u32 format, bandwidth, short_gi, rate_id; -+ -+ ieee80211_tx_info_clear_status(info); -+ -+ info->status.rates[0].idx = -1; -+ info->status.rates[0].count = 0; -+ info->status.rates[0].flags = 0; -+ info->flags &= ~IEEE80211_TX_CTL_AMPDU; -+ info->flags |= IEEE80211_TX_STAT_ACK; -+ -+ if (rate) { -+ /* Prepare rate information */ -+ format = rate & MWL_TX_RATE_FORMAT_MASK; -+ bandwidth = -+ (rate & MWL_TX_RATE_BANDWIDTH_MASK) >> -+ MWL_TX_RATE_BANDWIDTH_SHIFT; -+ short_gi = (rate & MWL_TX_RATE_SHORTGI_MASK) >> -+ MWL_TX_RATE_SHORTGI_SHIFT; -+ rate_id = (rate & MWL_TX_RATE_RATEIDMCS_MASK) >> -+ MWL_TX_RATE_RATEIDMCS_SHIFT; -+ -+ info->status.rates[0].idx = rate_id; -+ if (format == TX_RATE_FORMAT_LEGACY) { -+ if (priv->hw->conf.chandef.chan->hw_value > -+ BAND_24_CHANNEL_NUM) { -+ info->status.rates[0].idx -= 5; -+ } -+ } -+ if (format == TX_RATE_FORMAT_11N) -+ info->status.rates[0].flags |= -+ IEEE80211_TX_RC_MCS; -+ if (format == TX_RATE_FORMAT_11AC) -+ info->status.rates[0].flags |= -+ IEEE80211_TX_RC_VHT_MCS; -+ if (bandwidth == TX_RATE_BANDWIDTH_40) -+ info->status.rates[0].flags |= -+ IEEE80211_TX_RC_40_MHZ_WIDTH; -+ if (bandwidth == TX_RATE_BANDWIDTH_80) -+ info->status.rates[0].flags |= -+ IEEE80211_TX_RC_80_MHZ_WIDTH; -+ if (bandwidth == TX_RATE_BANDWIDTH_160) -+ info->status.rates[0].flags |= -+ IEEE80211_TX_RC_160_MHZ_WIDTH; -+ if (short_gi == TX_RATE_INFO_SHORT_GI) -+ info->status.rates[0].flags |= -+ IEEE80211_TX_RC_SHORT_GI; -+ info->status.rates[0].count = 1; -+ info->status.rates[1].idx = -1; -+ } -+} -+ -+static inline void pcie_tx_count_packet(struct ieee80211_sta *sta, u8 tid) -+{ -+ struct mwl_sta *sta_info; -+ struct mwl_tx_info *tx_stats; -+ -+ if (WARN_ON(tid >= SYSADPT_MAX_TID)) -+ return; -+ -+ sta_info = mwl_dev_get_sta(sta); -+ -+ tx_stats = &sta_info->tx_stats[tid]; -+ -+ if (tx_stats->start_time == 0) -+ tx_stats->start_time = jiffies; -+ -+ /* reset the packet count after each second elapses. If the number of -+ * packets ever exceeds the ampdu_min_traffic threshold, we will allow -+ * an ampdu stream to be started. -+ */ -+ if (jiffies - tx_stats->start_time > HZ) { -+ tx_stats->pkts = 0; -+ tx_stats->start_time = jiffies; -+ } else { -+ tx_stats->pkts++; -+ } -+} -+ -+static inline void pcie_rx_prepare_status(struct mwl_priv *priv, u16 format, -+ u16 nss, u16 bw, u16 gi, u16 rate, -+ struct ieee80211_rx_status *status) -+{ -+#ifdef RX_ENC_FLAG_STBC_SHIFT -+ switch (format) { -+ case RX_RATE_INFO_FORMAT_11N: -+ status->encoding = RX_ENC_HT; -+ status->bw = RATE_INFO_BW_20; -+ if (bw == RX_RATE_INFO_HT40) -+ status->bw = RATE_INFO_BW_40; -+ if (gi == RX_RATE_INFO_SHORT_INTERVAL) -+ status->enc_flags |= RX_ENC_FLAG_SHORT_GI; -+ break; -+ case RX_RATE_INFO_FORMAT_11AC: -+ status->encoding = RX_ENC_VHT; -+ status->bw = RATE_INFO_BW_20; -+ if (bw == RX_RATE_INFO_HT40) -+ status->bw = RATE_INFO_BW_40; -+ if (bw == RX_RATE_INFO_HT80) -+ status->bw = RATE_INFO_BW_80; -+ if (bw == RX_RATE_INFO_HT160) -+ status->bw = RATE_INFO_BW_160; -+ if (gi == RX_RATE_INFO_SHORT_INTERVAL) -+ status->enc_flags |= RX_ENC_FLAG_SHORT_GI; -+ status->nss = (nss + 1); -+ break; -+ } -+#else -+ switch (format) { -+ case RX_RATE_INFO_FORMAT_11N: -+ status->flag |= RX_FLAG_HT; -+ if (bw == RX_RATE_INFO_HT40) -+ status->flag |= RX_FLAG_40MHZ; -+ if (gi == RX_RATE_INFO_SHORT_INTERVAL) -+ status->flag |= RX_FLAG_SHORT_GI; -+ break; -+ case RX_RATE_INFO_FORMAT_11AC: -+ status->flag |= RX_FLAG_VHT; -+ if (bw == RX_RATE_INFO_HT40) -+ status->flag |= RX_FLAG_40MHZ; -+ if (bw == RX_RATE_INFO_HT80) -+ status->vht_flag |= RX_VHT_FLAG_80MHZ; -+ if (bw == RX_RATE_INFO_HT160) -+ status->vht_flag |= RX_VHT_FLAG_160MHZ; -+ if (gi == RX_RATE_INFO_SHORT_INTERVAL) -+ status->flag |= RX_FLAG_SHORT_GI; -+ status->vht_nss = (nss + 1); -+ break; -+ } -+#endif -+ status->rate_idx = rate; -+ -+ if (priv->hw->conf.chandef.chan->hw_value > -+ BAND_24_CHANNEL_NUM) { -+ status->band = NL80211_BAND_5GHZ; -+#ifdef RX_ENC_FLAG_STBC_SHIFT -+ if ((!(status->encoding == RX_ENC_HT)) && -+ (!(status->encoding == RX_ENC_VHT))) { -+#else -+ if ((!(status->flag & RX_FLAG_HT)) && -+ (!(status->flag & RX_FLAG_VHT))) { -+#endif -+ status->rate_idx -= 5; -+ if (status->rate_idx >= BAND_50_RATE_NUM) -+ status->rate_idx = BAND_50_RATE_NUM - 1; -+ } -+ } else { -+ status->band = NL80211_BAND_2GHZ; -+#ifdef RX_ENC_FLAG_STBC_SHIFT -+ if ((!(status->encoding == RX_ENC_HT)) && -+ (!(status->encoding == RX_ENC_VHT))) { -+#else -+ if ((!(status->flag & RX_FLAG_HT)) && -+ (!(status->flag & RX_FLAG_VHT))) { -+#endif -+ if (status->rate_idx >= BAND_24_RATE_NUM) -+ status->rate_idx = BAND_24_RATE_NUM - 1; -+ } -+ } -+} -+ -+static inline void pcie_rx_remove_dma_header(struct sk_buff *skb, __le16 qos) -+{ -+ struct pcie_dma_data *dma_data; -+ int hdrlen; -+ -+ dma_data = (struct pcie_dma_data *)skb->data; -+ hdrlen = ieee80211_hdrlen(dma_data->wh.frame_control); -+ -+ if (hdrlen != sizeof(dma_data->wh)) { -+ if (ieee80211_is_data_qos(dma_data->wh.frame_control)) { -+ memmove(dma_data->data - hdrlen, -+ &dma_data->wh, hdrlen - 2); -+ *((__le16 *)(dma_data->data - 2)) = qos; -+ } else { -+ memmove(dma_data->data - hdrlen, &dma_data->wh, hdrlen); -+ } -+ } -+ -+ if (hdrlen != sizeof(*dma_data)) -+ skb_pull(skb, sizeof(*dma_data) - hdrlen); -+} -+ -+static inline void pcie_mask_int(struct pcie_priv *pcie_priv, -+ u32 mask_bit, bool set) -+{ -+ unsigned long flags; -+ void __iomem *int_status_mask; -+ u32 status; -+ -+ spin_lock_irqsave(&pcie_priv->int_mask_lock, flags); -+ int_status_mask = pcie_priv->iobase1 + -+ MACREG_REG_A2H_INTERRUPT_STATUS_MASK; -+ status = readl(int_status_mask); -+ if (set) -+ writel((status | mask_bit), int_status_mask); -+ else -+ writel((status & ~mask_bit), int_status_mask); -+ spin_unlock_irqrestore(&pcie_priv->int_mask_lock, flags); -+} -+ -+#endif /* _DEV_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c -new file mode 100644 -index 000000000000..939ed54133c7 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.c -@@ -0,0 +1,274 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements firmware download related -+ * functions. -+ */ -+ -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "hif/fwcmd.h" -+#include "hif/pcie/dev.h" -+#include "hif/pcie/sc4_ddr.h" -+#include "hif/pcie/fwdl.h" -+ -+#define FW_DOWNLOAD_BLOCK_SIZE 256 -+#define FW_CHECK_MSECS 3 -+ -+#define FW_MAX_NUM_CHECKS 0xffff -+ -+static void pcie_trigger_pcicmd_bootcode(struct pcie_priv *pcie_priv) -+{ -+ writel(pcie_priv->mwl_priv->pphys_cmd_buf, -+ pcie_priv->iobase1 + MACREG_REG_GEN_PTR); -+ writel(0x00, pcie_priv->iobase1 + MACREG_REG_INT_CODE); -+ writel(MACREG_H2ARIC_BIT_DOOR_BELL, -+ pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); -+} -+ -+static bool pcie_download_ddr_init(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = (struct pcie_priv *)priv->hif.priv; -+ u32 size_ddr_init = sizeof(sc4_ddr_init); -+ u8 *p = (u8 *)&sc4_ddr_init[0]; -+ u32 curr_iteration = 0; -+ u32 size_ddr_init_downloaded = 0; -+ u32 int_code = 0; -+ u32 len = 0; -+ -+ /* download ddr init code */ -+ wiphy_debug(priv->hw->wiphy, "ddr init: download start\n"); -+ -+ while (size_ddr_init_downloaded < size_ddr_init) { -+ len = readl(pcie_priv->iobase1 + 0xc40); -+ -+ if (!len) -+ break; -+ -+ /* this copies the next chunk of fw binary to be delivered */ -+ memcpy((char *)&priv->pcmd_buf[0], p, len); -+ /* this is arbitrary per your platform; we use 0xffff */ -+ curr_iteration = (FW_MAX_NUM_CHECKS * 500); -+ /* this function writes pdata to c10, then write 2 to c18 */ -+ pcie_trigger_pcicmd_bootcode(pcie_priv); -+ -+ /* NOTE: the following back to back checks on C1C is time -+ * sensitive, hence may need to be tweaked dependent on host -+ * processor. Time for SC2 to go from the write of event 2 to -+ * C1C == 2 is ~1300 nSec. Hence the checkings on host has to -+ * consider how efficient your code can be to meet this timing, -+ * or you can alternatively tweak this routines to fit your -+ * platform -+ */ -+ do { -+ int_code = readl(pcie_priv->iobase1 + 0xc1c); -+ if (int_code != 0) -+ break; -+ cond_resched(); -+ curr_iteration--; -+ } while (curr_iteration); -+ -+ do { -+ int_code = readl(pcie_priv->iobase1 + 0xc1c); -+ if ((int_code & MACREG_H2ARIC_BIT_DOOR_BELL) != -+ MACREG_H2ARIC_BIT_DOOR_BELL) -+ break; -+ cond_resched(); -+ curr_iteration--; -+ } while (curr_iteration); -+ -+ if (curr_iteration == 0) { -+ /* This limited loop check allows you to exit gracefully -+ * without locking up your entire system just because fw -+ * download failed -+ */ -+ wiphy_err(priv->hw->wiphy, -+ "Exhausted curr_iteration during download\n"); -+ return false; -+ } -+ -+ p += len; -+ size_ddr_init_downloaded += len; -+ } -+ -+ wiphy_debug(priv->hw->wiphy, "ddr init: download complete\n"); -+ -+ return true; -+} -+ -+void pcie_reset(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ u32 regval; -+ -+ regval = readl(pcie_priv->iobase1 + MACREG_REG_INT_CODE); -+ if (regval == 0xffffffff) { -+ wiphy_err(priv->hw->wiphy, "adapter does not exist\n"); -+ return; -+ } -+ -+ writel(ISR_RESET, pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); -+} -+ -+int pcie_download_firmware(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ const struct firmware *fw = priv->fw_ucode; -+ u32 curr_iteration = 0; -+ u32 size_fw_downloaded = 0; -+ u32 int_code = 0; -+ u32 len = 0; -+ u32 fwreadysignature = HOSTCMD_SOFTAP_FWRDY_SIGNATURE; -+ -+ pcie_reset(hw); -+ -+ /* FW before jumping to boot rom, it will enable PCIe transaction retry, -+ * wait for boot code to stop it. -+ */ -+ usleep_range(FW_CHECK_MSECS * 1000, FW_CHECK_MSECS * 2000); -+ -+ if (priv->chip_type == MWL8964) { -+ writel(MACREG_A2HRIC_BIT_MASK_NDP, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CLEAR_SEL); -+ } else { -+ writel(MACREG_A2HRIC_BIT_MASK, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CLEAR_SEL); -+ } -+ writel(0x00, pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); -+ writel(0x00, pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); -+ if (priv->chip_type == MWL8964) { -+ writel(MACREG_A2HRIC_BIT_MASK_NDP, -+ pcie_priv->iobase1 + -+ MACREG_REG_A2H_INTERRUPT_STATUS_MASK); -+ } else { -+ writel(MACREG_A2HRIC_BIT_MASK, -+ pcie_priv->iobase1 + -+ MACREG_REG_A2H_INTERRUPT_STATUS_MASK); -+ } -+ -+ /* this routine interacts with SC2 bootrom to download firmware binary -+ * to the device. After DMA'd to SC2, the firmware could be deflated to -+ * reside on its respective blocks such as ITCM, DTCM, SQRAM, -+ * (or even DDR, AFTER DDR is init'd before fw download -+ */ -+ wiphy_debug(hw->wiphy, "fw download start\n"); -+ -+ if (priv->chip_type != MWL8997) -+ /* Disable PFU before FWDL */ -+ writel(0x100, pcie_priv->iobase1 + 0xE0E4); -+ -+ /* make sure SCRATCH2 C40 is clear, in case we are too quick */ -+ while (readl(pcie_priv->iobase1 + 0xc40) == 0) -+ cond_resched(); -+ -+ if (priv->chip_type == MWL8964) { -+ if (!pcie_download_ddr_init(priv)) { -+ wiphy_err(hw->wiphy, -+ "ddr init: code download failed\n"); -+ goto err_download; -+ } -+ } -+ -+ while (size_fw_downloaded < fw->size) { -+ len = readl(pcie_priv->iobase1 + 0xc40); -+ -+ if (!len) -+ break; -+ -+ /* this copies the next chunk of fw binary to be delivered */ -+ memcpy((char *)&priv->pcmd_buf[0], -+ (fw->data + size_fw_downloaded), len); -+ -+ /* this function writes pdata to c10, then write 2 to c18 */ -+ pcie_trigger_pcicmd_bootcode(pcie_priv); -+ -+ /* this is arbitrary per your platform; we use 0xffff */ -+ curr_iteration = FW_MAX_NUM_CHECKS; -+ -+ /* NOTE: the following back to back checks on C1C is time -+ * sensitive, hence may need to be tweaked dependent on host -+ * processor. Time for SC2 to go from the write of event 2 to -+ * C1C == 2 is ~1300 nSec. Hence the checkings on host has to -+ * consider how efficient your code can be to meet this timing, -+ * or you can alternatively tweak this routines to fit your -+ * platform -+ */ -+ do { -+ int_code = readl(pcie_priv->iobase1 + 0xc1c); -+ if ((int_code & MACREG_H2ARIC_BIT_DOOR_BELL) != -+ MACREG_H2ARIC_BIT_DOOR_BELL) -+ break; -+ cond_resched(); -+ curr_iteration--; -+ } while (curr_iteration); -+ -+ if (curr_iteration == 0) { -+ /* This limited loop check allows you to exit gracefully -+ * without locking up your entire system just because fw -+ * download failed -+ */ -+ wiphy_err(hw->wiphy, -+ "Exhausted curr_iteration for fw download\n"); -+ goto err_download; -+ } -+ -+ size_fw_downloaded += len; -+ } -+ -+ wiphy_debug(hw->wiphy, -+ "FwSize = %d downloaded Size = %d curr_iteration %d\n", -+ (int)fw->size, size_fw_downloaded, curr_iteration); -+ -+ /* Now firware is downloaded successfully, so this part is to check -+ * whether fw can properly execute to an extent that write back -+ * signature to indicate its readiness to the host. NOTE: if your -+ * downloaded fw crashes, this signature checking will fail. This -+ * part is similar as SC1 -+ */ -+ *((u32 *)&priv->pcmd_buf[1]) = 0; -+ pcie_trigger_pcicmd_bootcode(pcie_priv); -+ curr_iteration = FW_MAX_NUM_CHECKS; -+ do { -+ curr_iteration--; -+ writel(HOSTCMD_SOFTAP_MODE, -+ pcie_priv->iobase1 + MACREG_REG_GEN_PTR); -+ usleep_range(FW_CHECK_MSECS * 1000, FW_CHECK_MSECS * 2000); -+ int_code = readl(pcie_priv->iobase1 + MACREG_REG_INT_CODE); -+ if (!(curr_iteration % 0xff) && (int_code != 0)) -+ wiphy_err(hw->wiphy, "%x;", int_code); -+ } while ((curr_iteration) && -+ (int_code != fwreadysignature)); -+ -+ if (curr_iteration == 0) { -+ wiphy_err(hw->wiphy, -+ "Exhausted curr_iteration for fw signature\n"); -+ goto err_download; -+ } -+ -+ wiphy_debug(hw->wiphy, "fw download complete\n"); -+ writel(0x00, pcie_priv->iobase1 + MACREG_REG_INT_CODE); -+ -+ return 0; -+ -+err_download: -+ -+ pcie_reset(hw); -+ -+ return -EIO; -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h -new file mode 100644 -index 000000000000..36a3311aa678 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/fwdl.h -@@ -0,0 +1,24 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines firmware download related functions. */ -+ -+#ifndef _FWDL_H_ -+#define _FWDL_H_ -+ -+void pcie_reset(struct ieee80211_hw *hw); -+int pcie_download_firmware(struct ieee80211_hw *hw); -+ -+#endif /* _FWDL_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c -new file mode 100644 -index 000000000000..da55913c0570 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/pcie.c -@@ -0,0 +1,1645 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements functions needed for PCIe module. */ -+ -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "vendor_cmd.h" -+#include "hif/fwcmd.h" -+#include "hif/pcie/dev.h" -+#include "hif/pcie/fwdl.h" -+#include "hif/pcie/tx.h" -+#include "hif/pcie/rx.h" -+#include "hif/pcie/tx_ndp.h" -+#include "hif/pcie/rx_ndp.h" -+ -+#define PCIE_DRV_DESC "Marvell Mac80211 Wireless PCIE Network Driver" -+#define PCIE_DEV_NAME "Marvell 802.11ac PCIE Adapter" -+ -+#define MAX_WAIT_FW_COMPLETE_ITERATIONS 2000 -+#define CHECK_BA_TRAFFIC_TIME 300 /* msec */ -+#define CHECK_TX_DONE_TIME 50 /* msec */ -+ -+static struct pci_device_id pcie_id_tbl[] = { -+ { PCI_VDEVICE(MARVELL, 0x2a55), .driver_data = MWL8864, }, -+ { PCI_VDEVICE(MARVELL, 0x2b38), .driver_data = MWL8897, }, -+ { PCI_VDEVICE(MARVELL, 0x2b40), .driver_data = MWL8964, }, -+ { PCI_VDEVICE(MARVELL_EXT, 0x2b42), .driver_data = MWL8997, }, -+ { }, -+}; -+ -+static struct mwl_chip_info pcie_chip_tbl[] = { -+ [MWL8864] = { -+ .part_name = "88W8864", -+ .fw_image = "mwlwifi/88W8864.bin", -+ .cal_file = NULL, -+ .txpwrlmt_file = NULL, -+ .antenna_tx = ANTENNA_TX_4_AUTO, -+ .antenna_rx = ANTENNA_RX_4_AUTO, -+ }, -+ [MWL8897] = { -+ .part_name = "88W8897", -+ .fw_image = "mwlwifi/88W8897.bin", -+ .cal_file = NULL, -+ .txpwrlmt_file = NULL, -+ .antenna_tx = ANTENNA_TX_2, -+ .antenna_rx = ANTENNA_RX_2, -+ }, -+ [MWL8964] = { -+ .part_name = "88W8964", -+ .fw_image = "mwlwifi/88W8964.bin", -+ .cal_file = NULL, -+ .txpwrlmt_file = NULL, -+ .antenna_tx = ANTENNA_TX_4_AUTO, -+ .antenna_rx = ANTENNA_RX_4_AUTO, -+ }, -+ [MWL8997] = { -+ .part_name = "88W8997", -+ .fw_image = "mwlwifi/88W8997.bin", -+ .cal_file = "mwlwifi/WlanCalData_ext.conf", -+ .txpwrlmt_file = "mwlwifi/txpwrlmt_cfg.conf", -+ .antenna_tx = ANTENNA_TX_2, -+ .antenna_rx = ANTENNA_RX_2, -+ }, -+}; -+ -+static int pcie_alloc_resource(struct pcie_priv *pcie_priv) -+{ -+ struct pci_dev *pdev = pcie_priv->pdev; -+ struct device *dev = &pdev->dev; -+ void __iomem *addr; -+ -+ pcie_priv->next_bar_num = 1; /* 32-bit */ -+ if (pci_resource_flags(pdev, 0) & 0x04) -+ pcie_priv->next_bar_num = 2; /* 64-bit */ -+ -+ addr = devm_ioremap_resource(dev, &pdev->resource[0]); -+ if (IS_ERR(addr)) { -+ pr_err("%s: cannot reserve PCI memory region 0\n", -+ PCIE_DRV_NAME); -+ goto err; -+ } -+ pcie_priv->iobase0 = addr; -+ pr_debug("iobase0 = %p\n", pcie_priv->iobase0); -+ -+ addr = devm_ioremap_resource(dev, -+ &pdev->resource[pcie_priv->next_bar_num]); -+ if (IS_ERR(addr)) { -+ pr_err("%s: cannot reserve PCI memory region 1\n", -+ PCIE_DRV_NAME); -+ goto err; -+ } -+ pcie_priv->iobase1 = addr; -+ pr_debug("iobase1 = %p\n", pcie_priv->iobase1); -+ -+ return 0; -+ -+err: -+ pr_err("pci alloc fail\n"); -+ -+ return -EIO; -+} -+ -+static u32 pcie_read_mac_reg(struct pcie_priv *pcie_priv, u32 offset) -+{ -+ struct mwl_priv *priv = pcie_priv->mwl_priv; -+ -+ if (priv->chip_type == MWL8964) { -+ u32 *addr_val = kmalloc(64 * sizeof(u32), GFP_ATOMIC); -+ u32 val; -+ -+ if (addr_val) { -+ mwl_fwcmd_get_addr_value(priv->hw, -+ 0x8000a000 + offset, 4, -+ addr_val, 0); -+ val = addr_val[0]; -+ kfree(addr_val); -+ return val; -+ } -+ return 0; -+ } else -+ return le32_to_cpu(*(__le32 *) -+ (MAC_REG_ADDR_PCI(offset))); -+} -+ -+static bool pcie_chk_adapter(struct pcie_priv *pcie_priv) -+{ -+ struct mwl_priv *priv = pcie_priv->mwl_priv; -+ u32 regval; -+ -+ regval = readl(pcie_priv->iobase1 + MACREG_REG_INT_CODE); -+ -+ if (regval == 0xffffffff) { -+ wiphy_err(priv->hw->wiphy, "adapter does not exist\n"); -+ return false; -+ } -+ -+ if (priv->cmd_timeout) -+ wiphy_debug(priv->hw->wiphy, "MACREG_REG_INT_CODE: 0x%04x\n", -+ regval); -+ -+ return true; -+} -+ -+static void pcie_send_cmd(struct pcie_priv *pcie_priv) -+{ -+ writel(pcie_priv->mwl_priv->pphys_cmd_buf, -+ pcie_priv->iobase1 + MACREG_REG_GEN_PTR); -+ writel(MACREG_H2ARIC_BIT_DOOR_BELL, -+ pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); -+} -+ -+static int pcie_wait_complete(struct mwl_priv *priv, unsigned short cmd) -+{ -+ unsigned int curr_iteration = MAX_WAIT_FW_COMPLETE_ITERATIONS; -+ unsigned short int_code = 0; -+ -+ do { -+ int_code = le16_to_cpu(*((__le16 *)&priv->pcmd_buf[0])); -+ usleep_range(1000, 2000); -+ } while ((int_code != cmd) && (--curr_iteration) && !priv->rmmod); -+ -+ if (curr_iteration == 0) { -+ wiphy_err(priv->hw->wiphy, "cmd 0x%04x=%s timed out\n", -+ cmd, mwl_fwcmd_get_cmd_string(cmd)); -+ wiphy_err(priv->hw->wiphy, "return code: 0x%04x\n", int_code); -+ return -EIO; -+ } -+ -+ if (priv->chip_type != MWL8997) -+ usleep_range(3000, 5000); -+ -+ return 0; -+} -+ -+static int pcie_init(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ const struct hostcmd_get_hw_spec *get_hw_spec; -+ struct hostcmd_set_hw_spec set_hw_spec; -+ int rc, i; -+ -+ spin_lock_init(&pcie_priv->int_mask_lock); -+ tasklet_init(&pcie_priv->tx_task, -+ (void *)pcie_tx_skbs, (unsigned long)hw); -+ tasklet_disable(&pcie_priv->tx_task); -+ tasklet_init(&pcie_priv->tx_done_task, -+ (void *)pcie_tx_done, (unsigned long)hw); -+ tasklet_disable(&pcie_priv->tx_done_task); -+ spin_lock_init(&pcie_priv->tx_desc_lock); -+ tasklet_init(&pcie_priv->rx_task, -+ (void *)pcie_rx_recv, (unsigned long)hw); -+ tasklet_disable(&pcie_priv->rx_task); -+ tasklet_init(&pcie_priv->qe_task, -+ (void *)pcie_tx_flush_amsdu, (unsigned long)hw); -+ tasklet_disable(&pcie_priv->qe_task); -+ pcie_priv->txq_limit = PCIE_TX_QUEUE_LIMIT; -+ pcie_priv->txq_wake_threshold = PCIE_TX_WAKE_Q_THRESHOLD; -+ pcie_priv->is_tx_done_schedule = false; -+ pcie_priv->recv_limit = PCIE_RECEIVE_LIMIT; -+ pcie_priv->is_rx_schedule = false; -+ pcie_priv->is_qe_schedule = false; -+ pcie_priv->qe_trig_num = 0; -+ pcie_priv->qe_trig_time = jiffies; -+ -+ rc = pcie_tx_init(hw); -+ if (rc) { -+ wiphy_err(hw->wiphy, "%s: fail to initialize TX\n", -+ PCIE_DRV_NAME); -+ goto err_mwl_tx_init; -+ } -+ -+ rc = pcie_rx_init(hw); -+ if (rc) { -+ wiphy_err(hw->wiphy, "%s: fail to initialize RX\n", -+ PCIE_DRV_NAME); -+ goto err_mwl_rx_init; -+ } -+ -+ /* get and prepare HW specifications */ -+ get_hw_spec = mwl_fwcmd_get_hw_specs(hw); -+ if (!get_hw_spec) { -+ wiphy_err(hw->wiphy, "%s: fail to get HW specifications\n", -+ PCIE_DRV_NAME); -+ goto err_get_hw_specs; -+ } -+ ether_addr_copy(&priv->hw_data.mac_addr[0], -+ get_hw_spec->permanent_addr); -+ pcie_priv->desc_data[0].wcb_base = -+ le32_to_cpu(get_hw_spec->wcb_base0) & 0x0000ffff; -+ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++) -+ pcie_priv->desc_data[i].wcb_base = -+ le32_to_cpu(get_hw_spec->wcb_base[i - 1]) & 0x0000ffff; -+ pcie_priv->desc_data[0].rx_desc_read = -+ le32_to_cpu(get_hw_spec->rxpd_rd_ptr) & 0x0000ffff; -+ pcie_priv->desc_data[0].rx_desc_write = -+ le32_to_cpu(get_hw_spec->rxpd_wr_ptr) & 0x0000ffff; -+ priv->hw_data.fw_release_num = le32_to_cpu(get_hw_spec->fw_release_num); -+ priv->hw_data.hw_version = get_hw_spec->version; -+ if (priv->chip_type != MWL8997) { -+ writel(pcie_priv->desc_data[0].pphys_tx_ring, -+ pcie_priv->iobase0 + pcie_priv->desc_data[0].wcb_base); -+ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++) -+ writel(pcie_priv->desc_data[i].pphys_tx_ring, -+ pcie_priv->iobase0 + -+ pcie_priv->desc_data[i].wcb_base); -+ } -+ writel(pcie_priv->desc_data[0].pphys_rx_ring, -+ pcie_priv->iobase0 + pcie_priv->desc_data[0].rx_desc_read); -+ writel(pcie_priv->desc_data[0].pphys_rx_ring, -+ pcie_priv->iobase0 + pcie_priv->desc_data[0].rx_desc_write); -+ -+ /* prepare and set HW specifications */ -+ memset(&set_hw_spec, 0, sizeof(set_hw_spec)); -+ if (priv->chip_type == MWL8997) { -+ set_hw_spec.wcb_base[0] = -+ cpu_to_le32(pcie_priv->txbd_ring_pbase); -+ set_hw_spec.tx_wcb_num_per_queue = -+ cpu_to_le32(PCIE_MAX_TXRX_BD); -+ set_hw_spec.num_tx_queues = cpu_to_le32(1); -+ set_hw_spec.features |= HW_SET_PARMS_FEATURES_HOST_PROBE_RESP; -+ } else { -+ set_hw_spec.wcb_base[0] = -+ cpu_to_le32(pcie_priv->desc_data[0].pphys_tx_ring); -+ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++) -+ set_hw_spec.wcb_base[i] = cpu_to_le32( -+ pcie_priv->desc_data[i].pphys_tx_ring); -+ set_hw_spec.tx_wcb_num_per_queue = -+ cpu_to_le32(PCIE_MAX_NUM_TX_DESC); -+ set_hw_spec.num_tx_queues = cpu_to_le32(PCIE_NUM_OF_DESC_DATA); -+ } -+ set_hw_spec.total_rx_wcb = cpu_to_le32(PCIE_MAX_NUM_RX_DESC); -+ set_hw_spec.rxpd_wr_ptr = -+ cpu_to_le32(pcie_priv->desc_data[0].pphys_rx_ring); -+ rc = mwl_fwcmd_set_hw_specs(hw, &set_hw_spec); -+ if (rc) { -+ wiphy_err(hw->wiphy, "%s: fail to set HW specifications\n", -+ PCIE_DRV_NAME); -+ goto err_set_hw_specs; -+ } -+ -+ return rc; -+ -+err_set_hw_specs: -+err_get_hw_specs: -+ -+ pcie_rx_deinit(hw); -+ -+err_mwl_rx_init: -+ -+ pcie_tx_deinit(hw); -+ -+err_mwl_tx_init: -+ -+ wiphy_err(hw->wiphy, "%s: init fail\n", PCIE_DRV_NAME); -+ -+ return rc; -+} -+ -+static void pcie_deinit(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ pcie_rx_deinit(hw); -+ pcie_tx_deinit(hw); -+ tasklet_kill(&pcie_priv->qe_task); -+ tasklet_kill(&pcie_priv->rx_task); -+ tasklet_kill(&pcie_priv->tx_done_task); -+ tasklet_kill(&pcie_priv->tx_task); -+ pcie_reset(hw); -+} -+ -+static int pcie_get_info(struct ieee80211_hw *hw, char *buf, size_t size) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ char *p = buf; -+ int len = 0; -+ -+ len += scnprintf(p + len, size - len, "iobase0: %p\n", -+ pcie_priv->iobase0); -+ len += scnprintf(p + len, size - len, "iobase1: %p\n", -+ pcie_priv->iobase1); -+ len += scnprintf(p + len, size - len, -+ "tx limit: %d\n", pcie_priv->txq_limit); -+ len += scnprintf(p + len, size - len, -+ "rx limit: %d\n", pcie_priv->recv_limit); -+ len += scnprintf(p + len, size - len, -+ "qe trigger number: %d\n", pcie_priv->qe_trig_num); -+ return len; -+} -+ -+static void pcie_enable_data_tasks(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ tasklet_enable(&pcie_priv->tx_task); -+ tasklet_enable(&pcie_priv->tx_done_task); -+ tasklet_enable(&pcie_priv->rx_task); -+ tasklet_enable(&pcie_priv->qe_task); -+} -+ -+static void pcie_disable_data_tasks(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ tasklet_disable(&pcie_priv->tx_task); -+ tasklet_disable(&pcie_priv->tx_done_task); -+ tasklet_disable(&pcie_priv->rx_task); -+ tasklet_disable(&pcie_priv->qe_task); -+} -+ -+static int pcie_exec_cmd(struct ieee80211_hw *hw, unsigned short cmd) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ bool busy = false; -+ -+ might_sleep(); -+ -+ if (!pcie_chk_adapter(pcie_priv)) { -+ wiphy_err(priv->hw->wiphy, "adapter does not exist\n"); -+ priv->in_send_cmd = false; -+ return -EIO; -+ } -+ -+ if (!priv->in_send_cmd && !priv->rmmod) { -+ priv->in_send_cmd = true; -+ if (priv->dump_hostcmd) -+ wiphy_debug(priv->hw->wiphy, "send cmd 0x%04x=%s\n", -+ cmd, mwl_fwcmd_get_cmd_string(cmd)); -+ pcie_send_cmd(pcie_priv); -+ if (pcie_wait_complete(priv, 0x8000 | cmd)) { -+ wiphy_err(priv->hw->wiphy, "timeout: 0x%04x\n", cmd); -+ priv->in_send_cmd = false; -+ priv->cmd_timeout = true; -+ if (priv->heartbeat) -+ vendor_cmd_basic_event( -+ hw->wiphy, -+ MWL_VENDOR_EVENT_CMD_TIMEOUT); -+ return -EIO; -+ } -+ } else { -+ wiphy_warn(priv->hw->wiphy, -+ "previous command is running or module removed\n"); -+ busy = true; -+ } -+ -+ if (!busy) -+ priv->in_send_cmd = false; -+ -+ return 0; -+} -+ -+static int pcie_get_irq_num(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ return pcie_priv->pdev->irq; -+} -+ -+static irqreturn_t pcie_isr(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ u32 int_status; -+ -+ int_status = readl(pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); -+ -+ if (int_status == 0x00000000) -+ return IRQ_NONE; -+ -+ if (int_status == 0xffffffff) { -+ wiphy_warn(hw->wiphy, "card unplugged?\n"); -+ } else { -+ writel(~int_status, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); -+ -+ if (int_status & MACREG_A2HRIC_BIT_TX_DONE) { -+ if (!pcie_priv->is_tx_done_schedule) { -+ pcie_mask_int(pcie_priv, -+ MACREG_A2HRIC_BIT_TX_DONE, false); -+ tasklet_schedule(&pcie_priv->tx_done_task); -+ pcie_priv->is_tx_done_schedule = true; -+ } -+ } -+ -+ if (int_status & MACREG_A2HRIC_BIT_RX_RDY) { -+ if (!pcie_priv->is_rx_schedule) { -+ pcie_mask_int(pcie_priv, -+ MACREG_A2HRIC_BIT_RX_RDY, false); -+ tasklet_schedule(&pcie_priv->rx_task); -+ pcie_priv->is_rx_schedule = true; -+ } -+ } -+ -+ if (int_status & MACREG_A2HRIC_BIT_RADAR_DETECT) { -+ wiphy_info(hw->wiphy, "radar detected by firmware\n"); -+ ieee80211_radar_detected(hw); -+ } -+ -+ if (int_status & MACREG_A2HRIC_BIT_QUE_EMPTY) { -+ if (!pcie_priv->is_qe_schedule) { -+ if (time_after(jiffies, -+ (pcie_priv->qe_trig_time + 1))) { -+ pcie_mask_int(pcie_priv, -+ MACREG_A2HRIC_BIT_QUE_EMPTY, -+ false); -+ tasklet_schedule(&pcie_priv->qe_task); -+ pcie_priv->qe_trig_num++; -+ pcie_priv->is_qe_schedule = true; -+ pcie_priv->qe_trig_time = jiffies; -+ } -+ } -+ } -+ -+ if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH) -+ ieee80211_queue_work(hw, &priv->chnl_switch_handle); -+ -+ if (int_status & MACREG_A2HRIC_BA_WATCHDOG) -+ ieee80211_queue_work(hw, &priv->watchdog_ba_handle); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static void pcie_irq_enable(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ if (pcie_chk_adapter(pcie_priv)) { -+ writel(0x00, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); -+ writel(MACREG_A2HRIC_BIT_MASK, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); -+ } -+} -+ -+static void pcie_irq_disable(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ if (pcie_chk_adapter(pcie_priv)) -+ writel(0x00, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); -+} -+ -+static void pcie_timer_routine(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ static int cnt; -+ struct mwl_ampdu_stream *stream; -+ struct mwl_sta *sta_info; -+ struct mwl_tx_info *tx_stats; -+ struct mwl_ampdu_stream *rm_stream = NULL; -+ u32 rm_pkts = 0; -+ bool ba_full = true; -+ int i; -+ -+ if ((++cnt * SYSADPT_TIMER_WAKEUP_TIME) < CHECK_BA_TRAFFIC_TIME) -+ return; -+ cnt = 0; -+ spin_lock_bh(&priv->stream_lock); -+ for (i = 0; i < priv->ampdu_num; i++) { -+ stream = &priv->ampdu[i]; -+ -+ if (stream->state == AMPDU_STREAM_ACTIVE) { -+ sta_info = mwl_dev_get_sta(stream->sta); -+ tx_stats = &sta_info->tx_stats[stream->tid]; -+ -+ if ((jiffies - tx_stats->start_time > HZ) && -+ (tx_stats->pkts < SYSADPT_AMPDU_PACKET_THRESHOLD)) { -+ if (rm_pkts) { -+ if (tx_stats->pkts < rm_pkts) { -+ rm_stream = stream; -+ rm_pkts = tx_stats->pkts; -+ } -+ } else { -+ rm_stream = stream; -+ rm_pkts = tx_stats->pkts; -+ } -+ } -+ -+ if (jiffies - tx_stats->start_time > HZ) { -+ tx_stats->pkts = 0; -+ tx_stats->start_time = jiffies; -+ } -+ } else -+ ba_full = false; -+ } -+ if (ba_full && rm_stream) { -+ ieee80211_stop_tx_ba_session(rm_stream->sta, -+ rm_stream->tid); -+ wiphy_debug(hw->wiphy, "Stop BA %pM\n", rm_stream->sta->addr); -+ } -+ spin_unlock_bh(&priv->stream_lock); -+} -+ -+static void pcie_tx_return_pkts(struct ieee80211_hw *hw) -+{ -+ pcie_tx_done((unsigned long)hw); -+} -+ -+static struct device_node *pcie_get_device_node(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct device_node *dev_node; -+ -+ dev_node = pci_bus_to_OF_node(pcie_priv->pdev->bus); -+ wiphy_info(priv->hw->wiphy, "device node: %s\n", dev_node->full_name); -+ -+ return dev_node; -+} -+ -+static void pcie_get_survey(struct ieee80211_hw *hw, -+ struct mwl_survey_info *survey_info) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ survey_info->filled = SURVEY_INFO_TIME | -+ SURVEY_INFO_TIME_BUSY | -+ SURVEY_INFO_TIME_TX | -+ SURVEY_INFO_NOISE_DBM; -+ survey_info->time_period += pcie_read_mac_reg(pcie_priv, MCU_LAST_READ); -+ survey_info->time_busy += pcie_read_mac_reg(pcie_priv, MCU_CCA_CNT); -+ survey_info->time_tx += pcie_read_mac_reg(pcie_priv, MCU_TXPE_CNT); -+ survey_info->noise = priv->noise; -+} -+ -+static int pcie_reg_access(struct ieee80211_hw *hw, bool write) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ u8 set; -+ u32 *addr_val; -+ int ret = 0; -+ -+ set = write ? WL_SET : WL_GET; -+ -+ switch (priv->reg_type) { -+ case MWL_ACCESS_RF: -+ ret = mwl_fwcmd_reg_rf(hw, set, priv->reg_offset, -+ &priv->reg_value); -+ break; -+ case MWL_ACCESS_BBP: -+ ret = mwl_fwcmd_reg_bb(hw, set, priv->reg_offset, -+ &priv->reg_value); -+ break; -+ case MWL_ACCESS_CAU: -+ ret = mwl_fwcmd_reg_cau(hw, set, priv->reg_offset, -+ &priv->reg_value); -+ break; -+ case MWL_ACCESS_ADDR0: -+ if (set == WL_GET) -+ priv->reg_value = -+ readl(pcie_priv->iobase0 + priv->reg_offset); -+ else -+ writel(priv->reg_value, -+ pcie_priv->iobase0 + priv->reg_offset); -+ break; -+ case MWL_ACCESS_ADDR1: -+ if (set == WL_GET) -+ priv->reg_value = -+ readl(pcie_priv->iobase1 + priv->reg_offset); -+ else -+ writel(priv->reg_value, -+ pcie_priv->iobase1 + priv->reg_offset); -+ break; -+ case MWL_ACCESS_ADDR: -+ addr_val = kzalloc(64 * sizeof(u32), GFP_KERNEL); -+ if (addr_val) { -+ addr_val[0] = priv->reg_value; -+ ret = mwl_fwcmd_get_addr_value(hw, priv->reg_offset, -+ 4, addr_val, set); -+ if ((!ret) && (set == WL_GET)) -+ priv->reg_value = addr_val[0]; -+ kfree(addr_val); -+ } else { -+ ret = -ENOMEM; -+ } -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ return ret; -+} -+ -+static struct mwl_hif_ops pcie_hif_ops = { -+ .driver_name = PCIE_DRV_NAME, -+ .driver_version = PCIE_DRV_VERSION, -+ .tx_head_room = PCIE_MIN_BYTES_HEADROOM, -+ .ampdu_num = PCIE_AMPDU_QUEUES, -+ .reset = pcie_reset, -+ .init = pcie_init, -+ .deinit = pcie_deinit, -+ .get_info = pcie_get_info, -+ .enable_data_tasks = pcie_enable_data_tasks, -+ .disable_data_tasks = pcie_disable_data_tasks, -+ .exec_cmd = pcie_exec_cmd, -+ .get_irq_num = pcie_get_irq_num, -+ .irq_handler = pcie_isr, -+ .irq_enable = pcie_irq_enable, -+ .irq_disable = pcie_irq_disable, -+ .download_firmware = pcie_download_firmware, -+ .timer_routine = pcie_timer_routine, -+ .tx_xmit = pcie_tx_xmit, -+ .tx_del_pkts_via_vif = pcie_tx_del_pkts_via_vif, -+ .tx_del_pkts_via_sta = pcie_tx_del_pkts_via_sta, -+ .tx_del_ampdu_pkts = pcie_tx_del_ampdu_pkts, -+ .tx_del_sta_amsdu_pkts = pcie_tx_del_sta_amsdu_pkts, -+ .tx_return_pkts = pcie_tx_return_pkts, -+ .get_device_node = pcie_get_device_node, -+ .get_survey = pcie_get_survey, -+ .reg_access = pcie_reg_access, -+}; -+ -+static int pcie_init_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ const struct hostcmd_get_hw_spec *get_hw_spec; -+ struct hostcmd_set_hw_spec set_hw_spec; -+ int rc; -+ -+ spin_lock_init(&pcie_priv->int_mask_lock); -+ tasklet_init(&pcie_priv->tx_task, -+ (void *)pcie_tx_skbs_ndp, (unsigned long)hw); -+ tasklet_disable(&pcie_priv->tx_task); -+ spin_lock_init(&pcie_priv->tx_desc_lock); -+ tasklet_init(&pcie_priv->rx_task, -+ (void *)pcie_rx_recv_ndp, (unsigned long)hw); -+ tasklet_disable(&pcie_priv->rx_task); -+ pcie_priv->txq_limit = TX_QUEUE_LIMIT; -+ pcie_priv->txq_wake_threshold = TX_WAKE_Q_THRESHOLD; -+ pcie_priv->is_tx_schedule = false; -+ pcie_priv->recv_limit = MAX_NUM_RX_DESC; -+ pcie_priv->is_rx_schedule = false; -+ -+ rc = pcie_tx_init_ndp(hw); -+ if (rc) { -+ wiphy_err(hw->wiphy, "%s: fail to initialize TX\n", -+ PCIE_DRV_NAME); -+ goto err_mwl_tx_init; -+ } -+ -+ rc = pcie_rx_init_ndp(hw); -+ if (rc) { -+ wiphy_err(hw->wiphy, "%s: fail to initialize RX\n", -+ PCIE_DRV_NAME); -+ goto err_mwl_rx_init; -+ } -+ -+ /* get and prepare HW specifications */ -+ get_hw_spec = mwl_fwcmd_get_hw_specs(hw); -+ if (!get_hw_spec) { -+ wiphy_err(hw->wiphy, "%s: fail to get HW specifications\n", -+ PCIE_DRV_NAME); -+ goto err_get_hw_specs; -+ } -+ ether_addr_copy(&priv->hw_data.mac_addr[0], -+ get_hw_spec->permanent_addr); -+ priv->hw_data.fw_release_num = le32_to_cpu(get_hw_spec->fw_release_num); -+ priv->hw_data.hw_version = get_hw_spec->version; -+ -+ /* prepare and set HW specifications */ -+ memset(&set_hw_spec, 0, sizeof(set_hw_spec)); -+ set_hw_spec.wcb_base[0] = -+ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_tx_ring); -+ set_hw_spec.wcb_base[1] = -+ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_tx_ring_done); -+ set_hw_spec.wcb_base[2] = -+ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_rx_ring); -+ set_hw_spec.wcb_base[3] = -+ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_rx_ring_done); -+ set_hw_spec.acnt_base_addr = -+ cpu_to_le32(pcie_priv->desc_data_ndp.pphys_acnt_ring); -+ set_hw_spec.acnt_buf_size = -+ cpu_to_le32(pcie_priv->desc_data_ndp.acnt_ring_size); -+ rc = mwl_fwcmd_set_hw_specs(hw, &set_hw_spec); -+ if (rc) { -+ wiphy_err(hw->wiphy, "%s: fail to set HW specifications\n", -+ PCIE_DRV_NAME); -+ goto err_set_hw_specs; -+ } -+ -+ return rc; -+ -+err_set_hw_specs: -+err_get_hw_specs: -+ -+ pcie_rx_deinit_ndp(hw); -+ -+err_mwl_rx_init: -+ -+ pcie_tx_deinit_ndp(hw); -+ -+err_mwl_tx_init: -+ -+ wiphy_err(hw->wiphy, "%s: init fail\n", PCIE_DRV_NAME); -+ -+ return rc; -+} -+ -+static void pcie_deinit_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ pcie_rx_deinit_ndp(hw); -+ pcie_tx_deinit_ndp(hw); -+ tasklet_kill(&pcie_priv->rx_task); -+ tasklet_kill(&pcie_priv->tx_task); -+ pcie_reset(hw); -+} -+ -+static int pcie_get_info_ndp(struct ieee80211_hw *hw, char *buf, size_t size) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ char *p = buf; -+ int len = 0; -+ -+ len += scnprintf(p + len, size - len, "iobase0: %p\n", -+ pcie_priv->iobase0); -+ len += scnprintf(p + len, size - len, "iobase1: %p\n", -+ pcie_priv->iobase1); -+ len += scnprintf(p + len, size - len, -+ "tx limit: %d\n", pcie_priv->txq_limit); -+ len += scnprintf(p + len, size - len, -+ "rx limit: %d\n", pcie_priv->recv_limit); -+ return len; -+} -+ -+static int pcie_get_tx_status_ndp(struct ieee80211_hw *hw, char *buf, -+ size_t size) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ char *p = buf; -+ int len = 0; -+ -+ len += scnprintf(p + len, size - len, "tx_done_cnt: %d\n", -+ pcie_priv->tx_done_cnt); -+ len += scnprintf(p + len, size - len, "tx_desc_busy_cnt: %d\n", -+ pcie_priv->desc_data_ndp.tx_desc_busy_cnt); -+ len += scnprintf(p + len, size - len, "tx_sent_head: %d\n", -+ pcie_priv->desc_data_ndp.tx_sent_head); -+ len += scnprintf(p + len, size - len, "tx_sent_tail: %d\n", -+ pcie_priv->desc_data_ndp.tx_sent_tail); -+ len += scnprintf(p + len, size - len, "tx_done_head: %d\n", -+ readl(pcie_priv->iobase1 + MACREG_REG_TXDONEHEAD)); -+ len += scnprintf(p + len, size - len, "tx_done_tail: %d\n", -+ pcie_priv->desc_data_ndp.tx_done_tail); -+ len += scnprintf(p + len, size - len, "tx_vbuflist_idx: %d\n", -+ pcie_priv->desc_data_ndp.tx_vbuflist_idx); -+ return len; -+} -+ -+static int pcie_get_rx_status_ndp(struct ieee80211_hw *hw, char *buf, -+ size_t size) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ char *p = buf; -+ int len = 0; -+ -+ len += scnprintf(p + len, size - len, "rx_done_head: %d\n", -+ readl(pcie_priv->iobase1 + MACREG_REG_RXDONEHEAD)); -+ len += scnprintf(p + len, size - len, "rx_done_tail: %d\n", -+ readl(pcie_priv->iobase1 + MACREG_REG_RXDONETAIL)); -+ len += scnprintf(p + len, size - len, "rx_desc_head: %d\n", -+ readl(pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD)); -+ len += scnprintf(p + len, size - len, "rx_skb_trace: %d\n", -+ skb_queue_len(&pcie_priv->rx_skb_trace)); -+ len += scnprintf(p + len, size - len, "rx_skb_unlink_err: %d\n", -+ pcie_priv->rx_skb_unlink_err); -+ len += scnprintf(p + len, size - len, "signature_err: %d\n", -+ pcie_priv->signature_err); -+ len += scnprintf(p + len, size - len, "recheck_rxringdone: %d\n", -+ pcie_priv->recheck_rxringdone); -+ len += scnprintf(p + len, size - len, "fast_data_cnt: %d\n", -+ pcie_priv->rx_cnts.fast_data_cnt); -+ len += scnprintf(p + len, size - len, "fast_bad_amsdu_cnt: %d\n", -+ pcie_priv->rx_cnts.fast_bad_amsdu_cnt); -+ len += scnprintf(p + len, size - len, "slow_noqueue_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_noqueue_cnt); -+ len += scnprintf(p + len, size - len, "slow_norun_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_norun_cnt); -+ len += scnprintf(p + len, size - len, "slow_mcast_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_mcast_cnt); -+ len += scnprintf(p + len, size - len, "slow_bad_sta_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_bad_sta_cnt); -+ len += scnprintf(p + len, size - len, "slow_bad_mic_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_bad_mic_cnt); -+ len += scnprintf(p + len, size - len, "slow_bad_pn_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_bad_pn_cnt); -+ len += scnprintf(p + len, size - len, "slow_mgmt_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_mgmt_cnt); -+ len += scnprintf(p + len, size - len, "slow_promisc_cnt: %d\n", -+ pcie_priv->rx_cnts.slow_promisc_cnt); -+ len += scnprintf(p + len, size - len, "drop_cnt: %d\n", -+ pcie_priv->rx_cnts.drop_cnt); -+ len += scnprintf(p + len, size - len, "offch_promisc_cnt: %d\n", -+ pcie_priv->rx_cnts.offch_promisc_cnt); -+ len += scnprintf(p + len, size - len, "mu_pkt_cnt: %d\n", -+ pcie_priv->rx_cnts.mu_pkt_cnt); -+ return len; -+} -+ -+static void pcie_enable_data_tasks_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ tasklet_enable(&pcie_priv->tx_task); -+ tasklet_enable(&pcie_priv->rx_task); -+} -+ -+static void pcie_disable_data_tasks_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ tasklet_disable(&pcie_priv->tx_task); -+ tasklet_disable(&pcie_priv->rx_task); -+} -+ -+static irqreturn_t pcie_isr_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ u32 int_status; -+ -+ int_status = readl(pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); -+ -+ if (int_status == 0x00000000) -+ return IRQ_NONE; -+ -+ if (int_status == 0xffffffff) { -+ wiphy_warn(hw->wiphy, "card unplugged?\n"); -+ } else { -+ writel(~int_status, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); -+ -+ if (int_status & MACREG_A2HRIC_ACNT_HEAD_RDY) -+ ieee80211_queue_work(hw, &priv->account_handle); -+ -+ if (int_status & MACREG_A2HRIC_RX_DONE_HEAD_RDY) { -+ if (!pcie_priv->is_rx_schedule) { -+ pcie_mask_int(pcie_priv, -+ MACREG_A2HRIC_RX_DONE_HEAD_RDY, -+ false); -+ tasklet_schedule(&pcie_priv->rx_task); -+ pcie_priv->is_rx_schedule = true; -+ } -+ } -+ -+ if (int_status & MACREG_A2HRIC_NEWDP_DFS) { -+ wiphy_info(hw->wiphy, "radar detected by firmware\n"); -+ ieee80211_radar_detected(hw); -+ } -+ -+ if (int_status & MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH) -+ ieee80211_queue_work(hw, &priv->chnl_switch_handle); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static void pcie_irq_enable_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ if (pcie_chk_adapter(pcie_priv)) { -+ writel(0x00, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); -+ writel(MACREG_A2HRIC_BIT_MASK_NDP, -+ pcie_priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); -+ } -+} -+ -+static void pcie_timer_routine_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num = SYSADPT_TX_WMM_QUEUES; -+ static int cnt; -+ -+ if (!pcie_priv->is_tx_schedule) { -+ while (num--) { -+ if (skb_queue_len(&pcie_priv->txq[num]) > 0) { -+ tasklet_schedule(&pcie_priv->tx_task); -+ pcie_priv->is_tx_schedule = true; -+ break; -+ } -+ } -+ } -+ -+ if ((++cnt * SYSADPT_TIMER_WAKEUP_TIME) >= CHECK_TX_DONE_TIME) { -+ pcie_tx_done_ndp(hw); -+ cnt = 0; -+ } -+} -+ -+static void pcie_tx_return_pkts_ndp(struct ieee80211_hw *hw) -+{ -+ pcie_tx_done_ndp(hw); -+} -+ -+static void pcie_set_sta_id(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, -+ bool sta_mode, bool set) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct mwl_sta *sta_info; -+ u16 stnid; -+ -+ sta_info = mwl_dev_get_sta(sta); -+ stnid = sta_mode ? 0 : sta_info->stnid; -+ pcie_priv->sta_link[stnid] = set ? sta : NULL; -+} -+ -+static void pcie_tx_account(struct mwl_priv *priv, -+ struct mwl_sta *sta_info, -+ struct acnt_tx_s *acnt_tx) -+{ -+ u32 rate_info, tx_cnt; -+ u8 index, type, rate_ac, format, bw, gi, mcs, nss; -+ u16 ratemask; -+ u8 i, found; -+ struct mwl_tx_hist *tx_hist; -+ struct mwl_tx_hist_data *tx_hist_data; -+ -+ rate_info = le32_to_cpu(acnt_tx->tx_info.rate_info); -+ tx_cnt = le32_to_cpu(acnt_tx->tx_cnt); -+ index = acnt_tx->rate_tbl_index; -+ type = acnt_tx->type; -+ -+ if (!rate_info) -+ return; -+ sta_info->tx_rate_info = rate_info; -+ -+ tx_hist = &sta_info->tx_hist; -+ if (!tx_hist || (type >= SU_MU_TYPE_CNT)) -+ return; -+ -+ format = rate_info & MWL_TX_RATE_FORMAT_MASK; -+ bw = (rate_info & MWL_TX_RATE_BANDWIDTH_MASK) >> -+ MWL_TX_RATE_BANDWIDTH_SHIFT; -+ gi = (rate_info & MWL_TX_RATE_SHORTGI_MASK) >> -+ MWL_TX_RATE_SHORTGI_SHIFT; -+ mcs = (rate_info & MWL_TX_RATE_RATEIDMCS_MASK) >> -+ MWL_TX_RATE_RATEIDMCS_SHIFT; -+ -+ tx_hist->cur_rate_info[type] = rate_info; -+ -+ /* Rate table index is valid */ -+ if (index != 0xff) { -+ if (type == MU_MIMO) { -+ rate_ac = mcs & 0xf; -+ nss = mcs >> 4; -+ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { -+ tx_hist_data = -+ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; -+ tx_hist_data->rateinfo = rate_info; -+ tx_hist_data->cnt++; -+ tx_hist->total_tx_cnt[type] += tx_cnt; -+ } -+ } else { -+ /* If legacy, skip legacy preamble bit 15 */ -+ if (format == TX_RATE_FORMAT_LEGACY) -+ ratemask = 0xfff; -+ else -+ ratemask = 0xffff; -+ tx_hist_data = &tx_hist->su_rate[0]; -+ if ((tx_hist_data[index].rateinfo & ratemask) == -+ (rate_info & ratemask)) { -+ tx_hist_data[index].cnt++; -+ tx_hist->total_tx_cnt[type] += tx_cnt; -+ } -+ } -+ } else { -+ if (type == MU_MIMO) { -+ rate_ac = mcs & 0xf; -+ nss = mcs >> 4; -+ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { -+ tx_hist_data = -+ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; -+ tx_hist_data->rateinfo = rate_info; -+ tx_hist_data->cnt++; -+ tx_hist->total_tx_cnt[type] += tx_cnt; -+ } -+ } else { -+ /* If legacy, skip legacy preamble bit 15 */ -+ if (format == TX_RATE_FORMAT_LEGACY) -+ ratemask = 0xfff; -+ else -+ ratemask = 0xffff; -+ tx_hist_data = &tx_hist->custom_rate[0]; -+ /* Go through non rate table buffer to see if any has -+ * been used. If all used up, recycle by using index 0 -+ */ -+ for (i = 0; i < TX_RATE_HISTO_CUSTOM_CNT; i++) { -+ if (!tx_hist_data[i].rateinfo || -+ ((tx_hist_data[i].rateinfo & ratemask) == -+ (rate_info & ratemask))) { -+ found = 1; -+ break; -+ } -+ } -+ if (found) -+ index = i; -+ else -+ index = 0; /* reuse index 0 buffer */ -+ tx_hist_data[index].rateinfo = rate_info; -+ tx_hist_data[index].cnt++; -+ tx_hist->total_tx_cnt[type] += tx_cnt; -+ } -+ } -+} -+ -+static void pcie_rx_account(struct mwl_priv *priv, -+ struct mwl_sta *sta_info, -+ struct acnt_rx_s *acnt_rx) -+{ -+ u32 sig1, sig2, rate, param; -+ u16 format, nss, bw, gi, rate_mcs; -+ -+ sig1 = (le32_to_cpu(acnt_rx->rx_info.ht_sig1) >> -+ RXINFO_HT_SIG1_SHIFT) & RXINFO_HT_SIG1_MASK; -+ sig2 = (le32_to_cpu(acnt_rx->rx_info.ht_sig2_rate) >> -+ RXINFO_HT_SIG2_SHIFT) & RXINFO_HT_SIG2_MASK; -+ rate = (le32_to_cpu(acnt_rx->rx_info.ht_sig2_rate) >> -+ RXINFO_RATE_SHIFT) & RXINFO_RATE_MASK; -+ param = (le32_to_cpu(acnt_rx->rx_info.param) >> -+ RXINFO_PARAM_SHIFT) & RXINFO_PARAM_MASK; -+ -+ format = (param >> 3) & 0x7; -+ nss = 0; -+ bw = RX_RATE_INFO_HT20; -+ switch (format) { -+ case RX_RATE_INFO_FORMAT_11A: -+ rate_mcs = rate & 0xF; -+ if (rate_mcs == 10) -+ rate_mcs = 7; /* 12 Mbps */ -+ else -+ rate_mcs = utils_get_rate_id(rate_mcs); -+ gi = RX_RATE_INFO_SHORT_INTERVAL; -+ if ((rate_mcs == 5) || (rate_mcs == 7) || (rate_mcs == 9)) -+ return; -+ break; -+ case RX_RATE_INFO_FORMAT_11B: -+ rate_mcs = utils_get_rate_id(rate & 0xF); -+ gi = RX_RATE_INFO_LONG_INTERVAL; -+ if ((rate_mcs == 0) || (rate_mcs == 1)) -+ return; -+ break; -+ case RX_RATE_INFO_FORMAT_11N: -+ if ((sig1 & 0x3f) >= 16) -+ return; -+ bw = (sig1 >> 7) & 0x1; -+ gi = (sig2 >> 7) & 0x1; -+ rate_mcs = sig1 & 0x3F; -+ if (rate_mcs > 76) -+ return; -+ break; -+ case RX_RATE_INFO_FORMAT_11AC: -+ if (((sig2 >> 4) & 0xf) >= 10) -+ return; -+ nss = (sig1 >> 10) & 0x3; -+ if (!nss) -+ return; -+ bw = sig1 & 0x3; -+ gi = sig2 & 0x1; -+ rate_mcs = (sig2 >> 4) & 0xF; -+ if (rate_mcs > 9) -+ return; -+ break; -+ default: -+ return; -+ } -+ -+ sta_info->rx_format = format; -+ sta_info->rx_nss = nss; -+ sta_info->rx_bw = bw; -+ sta_info->rx_gi = gi; -+ sta_info->rx_rate_mcs = rate_mcs; -+ sta_info->rx_signal = ((le32_to_cpu(acnt_rx->rx_info.rssi_x) >> -+ RXINFO_RSSI_X_SHIFT) & RXINFO_RSSI_X_MASK); -+} -+ -+static void pcie_tx_per(struct mwl_priv *priv, struct mwl_sta *sta_info, -+ struct acnt_ra_s *acnt_ra) -+{ -+ u32 rate_info; -+ u8 index, per, type, rate_ac, per_index, format, bw, gi, mcs, nss; -+ u16 ratemask; -+ u8 i, found; -+ struct mwl_tx_hist *tx_hist; -+ struct mwl_tx_hist_data *tx_hist_data; -+ -+ rate_info = le32_to_cpu(acnt_ra->rate_info); -+ index = acnt_ra->rate_tbl_index; -+ per = acnt_ra->per; -+ type = acnt_ra->type; -+ -+ tx_hist = &sta_info->tx_hist; -+ -+ if (!tx_hist || !rate_info || (type >= SU_MU_TYPE_CNT)) -+ return; -+ -+ if ((type == SU_MIMO) && (index >= MAX_SUPPORTED_RATES) && -+ (index != 0xFF)) -+ return; -+ -+ if (per >= TX_HISTO_PER_THRES[3]) -+ per_index = 4; -+ else if (per >= TX_HISTO_PER_THRES[2]) -+ per_index = 3; -+ else if (per >= TX_HISTO_PER_THRES[1]) -+ per_index = 2; -+ else if (per >= TX_HISTO_PER_THRES[0]) -+ per_index = 1; -+ else -+ per_index = 0; -+ -+ format = rate_info & MWL_TX_RATE_FORMAT_MASK; -+ bw = (rate_info & MWL_TX_RATE_BANDWIDTH_MASK) >> -+ MWL_TX_RATE_BANDWIDTH_SHIFT; -+ gi = (rate_info & MWL_TX_RATE_SHORTGI_MASK) >> -+ MWL_TX_RATE_SHORTGI_SHIFT; -+ mcs = (rate_info & MWL_TX_RATE_RATEIDMCS_MASK) >> -+ MWL_TX_RATE_RATEIDMCS_SHIFT; -+ -+ /* Rate table index is valid */ -+ if (index != 0xff) { -+ if (type == MU_MIMO) { -+ rate_ac = mcs & 0xf; -+ nss = mcs >> 4; -+ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { -+ tx_hist_data = -+ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; -+ tx_hist_data->rateinfo = rate_info; -+ tx_hist_data->per[per_index]++; -+ } -+ } else { -+ /* If legacy, skip legacy preamble bit 15 */ -+ if (format == TX_RATE_FORMAT_LEGACY) -+ ratemask = 0xfff; -+ else -+ ratemask = 0xffff; -+ tx_hist_data = &tx_hist->su_rate[0]; -+ if ((tx_hist_data[index].rateinfo & ratemask) == -+ (rate_info & ratemask)) -+ tx_hist_data[index].per[per_index]++; -+ } -+ } else { -+ if (type == MU_MIMO) { -+ rate_ac = mcs & 0xf; -+ nss = mcs >> 4; -+ if (nss < (QS_NUM_SUPPORTED_11AC_NSS - 1)) { -+ tx_hist_data = -+ &tx_hist->mu_rate[nss][bw][gi][rate_ac]; -+ tx_hist_data->rateinfo = rate_info; -+ tx_hist_data->per[per_index]++; -+ } -+ } else { -+ /* If legacy, skip legacy preamble bit 15 */ -+ if (format == TX_RATE_FORMAT_LEGACY) -+ ratemask = 0xfff; -+ else -+ ratemask = 0xffff; -+ tx_hist_data = &tx_hist->custom_rate[0]; -+ /* Go through non rate table buffer to see if any has -+ * been used. If all used up, recycle by using index 0 -+ */ -+ for (i = 0; i < TX_RATE_HISTO_CUSTOM_CNT; i++) { -+ if (!tx_hist_data[i].rateinfo || -+ ((tx_hist_data[i].rateinfo & ratemask) == -+ (rate_info & ratemask))) { -+ found = 1; -+ break; -+ } -+ } -+ if (found) -+ index = i; -+ else -+ index = 0; /* reuse index 0 buffer */ -+ tx_hist_data[index].rateinfo = rate_info; -+ tx_hist_data[index].per[per_index]++; -+ } -+ } -+} -+ -+static void pcie_ba_account(struct mwl_priv *priv, -+ struct mwl_sta *sta_info, -+ struct acnt_ba_s *acnt_ba) -+{ -+ struct mwl_tx_ba_hist *ba_hist = &sta_info->ba_hist; -+ -+ if (sta_info->stnid != le16_to_cpu(acnt_ba->stnid)) -+ return; -+ -+ if (ba_hist->enable && ba_hist->ba_stats && -+ (ba_hist->index < ACNT_BA_SIZE)) { -+ ba_hist->type = acnt_ba->type; -+ ba_hist->ba_stats[ba_hist->index].ba_hole = acnt_ba->ba_hole; -+ ba_hist->ba_stats[ba_hist->index].ba_expected = -+ acnt_ba->ba_expected; -+ ba_hist->ba_stats[ba_hist->index].no_ba = acnt_ba->no_ba; -+ ba_hist->index++; -+ if (ba_hist->index == ACNT_BA_SIZE) -+ wiphy_info(priv->hw->wiphy, -+ "Aid:%d BA histo collection done\n", -+ priv->ba_aid); -+ } -+} -+ -+static void pcie_bf_mimo_ctrl_decode(struct mwl_priv *priv, -+ struct acnt_bf_mimo_ctrl_s *bf_mimo_ctrl) -+{ -+ struct file *fp_data = NULL; -+ const char filename[] = "/tmp/BF_MIMO_Ctrl_Field_Output.txt"; -+ char str_buf[256]; -+ char *buf = &str_buf[0]; -+ mm_segment_t oldfs; -+ -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ buf += sprintf(buf, "\nMAC: %pM\n", bf_mimo_ctrl->rec_mac); -+ buf += sprintf(buf, "SU_0_MU_1: %d\n", bf_mimo_ctrl->type); -+ buf += sprintf(buf, "MIMO_Ctrl_Field: 0x%x\n", -+ le32_to_cpu(bf_mimo_ctrl->mimo_ctrl)); -+ buf += sprintf(buf, "Comp_BF_Report_First_8Bytes: 0x%llx\n", -+ le64_to_cpu(bf_mimo_ctrl->comp_bf_rep)); -+ -+ fp_data = filp_open(filename, O_RDWR | O_CREAT | O_TRUNC, 0); -+ -+ if (!IS_ERR(fp_data)) { -+ __kernel_write(fp_data, str_buf, strlen(str_buf), -+ &fp_data->f_pos); -+ filp_close(fp_data, current->files); -+ } else { -+ wiphy_err(priv->hw->wiphy, "Error opening %s! %x\n", -+ filename, (unsigned int)fp_data); -+ } -+ -+ set_fs(oldfs); -+} -+ -+static void pcie_process_account(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ u32 acnt_head, acnt_tail; -+ u32 read_size; -+ u8 *acnt_recds; -+ u8 *pstart, *pend; -+ struct acnt_s *acnt; -+ struct acnt_tx_s *acnt_tx; -+ struct acnt_rx_s *acnt_rx; -+ struct acnt_ra_s *acnt_ra; -+ struct acnt_ba_s *acnt_ba; -+ struct acnt_bf_mimo_ctrl_s *acnt_bf_mimo_ctrl; -+ struct pcie_dma_data *dma_data; -+ struct mwl_sta *sta_info; -+ u16 nf_a, nf_b, nf_c, nf_d; -+ u16 stnid; -+ u8 type; -+ -+ acnt_head = readl(pcie_priv->iobase1 + MACREG_REG_ACNTHEAD); -+ acnt_tail = readl(pcie_priv->iobase1 + MACREG_REG_ACNTTAIL); -+ -+ if (acnt_tail == acnt_head) -+ return; -+ -+ if (acnt_tail > acnt_head) { -+ read_size = desc->acnt_ring_size - acnt_tail + acnt_head; -+ if (read_size > desc->acnt_ring_size) { -+ wiphy_err(hw->wiphy, -+ "account size overflow (%d %d %d)\n", -+ acnt_head, acnt_tail, read_size); -+ goto process_next; -+ } -+ memset(desc->pacnt_buf, 0, desc->acnt_ring_size); -+ memcpy(desc->pacnt_buf, desc->pacnt_ring + acnt_tail, -+ desc->acnt_ring_size - acnt_tail); -+ memcpy(desc->pacnt_buf + desc->acnt_ring_size - acnt_tail, -+ desc->pacnt_ring, acnt_head); -+ acnt_recds = desc->pacnt_buf; -+ } else { -+ read_size = acnt_head - acnt_tail; -+ if (read_size > desc->acnt_ring_size) { -+ wiphy_err(hw->wiphy, -+ "account size overflow (%d %d %d)\n", -+ acnt_head, acnt_tail, read_size); -+ goto process_next; -+ } -+ acnt_recds = desc->pacnt_ring + acnt_tail; -+ } -+ -+ pstart = acnt_recds; -+ pend = pstart + read_size; -+ while (pstart < pend) { -+ acnt = (struct acnt_s *)pstart; -+ -+ switch (le16_to_cpu(acnt->code)) { -+ case ACNT_CODE_BUSY: -+ pcie_priv->acnt_busy++; -+ break; -+ case ACNT_CODE_WRAP: -+ pcie_priv->acnt_wrap++; -+ break; -+ case ACNT_CODE_DROP: -+ pcie_priv->acnt_drop++; -+ break; -+ case ACNT_CODE_TX_ENQUEUE: -+ acnt_tx = (struct acnt_tx_s *)pstart; -+ sta_info = utils_find_sta(priv, acnt_tx->hdr.wh.addr1); -+ if (sta_info) { -+ spin_lock_bh(&priv->sta_lock); -+ pcie_tx_account(priv, sta_info, acnt_tx); -+ spin_unlock_bh(&priv->sta_lock); -+ } -+ break; -+ case ACNT_CODE_RX_PPDU: -+ acnt_rx = (struct acnt_rx_s *)pstart; -+ nf_a = (le32_to_cpu(acnt_rx->rx_info.nf_a_b) >> -+ RXINFO_NF_A_SHIFT) & RXINFO_NF_A_MASK; -+ nf_b = (le32_to_cpu(acnt_rx->rx_info.nf_a_b) >> -+ RXINFO_NF_B_SHIFT) & RXINFO_NF_B_MASK; -+ nf_c = (le32_to_cpu(acnt_rx->rx_info.nf_c_d) >> -+ RXINFO_NF_C_SHIFT) & RXINFO_NF_C_MASK; -+ nf_d = (le32_to_cpu(acnt_rx->rx_info.nf_c_d) >> -+ RXINFO_NF_D_SHIFT) & RXINFO_NF_D_MASK; -+ if ((nf_a >= 2048) && (nf_b >= 2048) && -+ (nf_c >= 2048) && (nf_d >= 2048)) { -+ nf_a = ((4096 - nf_a) >> 4); -+ nf_b = ((4096 - nf_b) >> 4); -+ nf_c = ((4096 - nf_c) >> 4); -+ nf_d = ((4096 - nf_d) >> 4); -+ priv->noise = -+ -((nf_a + nf_b + nf_c + nf_d) / 4); -+ } -+ dma_data = (struct pcie_dma_data *) -+ &acnt_rx->rx_info.hdr[0]; -+ sta_info = utils_find_sta(priv, dma_data->wh.addr2); -+ if (sta_info) { -+ spin_lock_bh(&priv->sta_lock); -+ pcie_rx_account(priv, sta_info, acnt_rx); -+ spin_unlock_bh(&priv->sta_lock); -+ } -+ break; -+ case ACNT_CODE_RA_STATS: -+ acnt_ra = (struct acnt_ra_s *)pstart; -+ stnid = le16_to_cpu(acnt_ra->stn_id); -+ if ((stnid > 0) && (stnid <= priv->stnid_num)) { -+ type = acnt_ra->type; -+ if (type < 2) { -+ if (acnt_ra->tx_attempt_cnt >= 250) -+ priv->ra_tx_attempt[type][5]++; -+ else if (acnt_ra->tx_attempt_cnt >= 100) -+ priv->ra_tx_attempt[type][4]++; -+ else if (acnt_ra->tx_attempt_cnt >= 50) -+ priv->ra_tx_attempt[type][3]++; -+ else if (acnt_ra->tx_attempt_cnt >= 15) -+ priv->ra_tx_attempt[type][2]++; -+ else if (acnt_ra->tx_attempt_cnt >= 4) -+ priv->ra_tx_attempt[type][1]++; -+ else -+ priv->ra_tx_attempt[type][0]++; -+ } -+ sta_info = utils_find_sta_by_id(priv, stnid); -+ if (sta_info) { -+ spin_lock_bh(&priv->sta_lock); -+ pcie_tx_per(priv, sta_info, acnt_ra); -+ spin_unlock_bh(&priv->sta_lock); -+ } -+ } -+ break; -+ case ACNT_CODE_BA_STATS: -+ acnt_ba = (struct acnt_ba_s *)pstart; -+ if (priv->ba_aid) { -+ sta_info = utils_find_sta_by_aid(priv, -+ priv->ba_aid); -+ if (sta_info) { -+ spin_lock_bh(&priv->sta_lock); -+ pcie_ba_account(priv, sta_info, -+ acnt_ba); -+ spin_unlock_bh(&priv->sta_lock); -+ } -+ } -+ break; -+ case ACNT_CODE_BF_MIMO_CTRL: -+ acnt_bf_mimo_ctrl = -+ (struct acnt_bf_mimo_ctrl_s *)pstart; -+ pcie_bf_mimo_ctrl_decode(priv, acnt_bf_mimo_ctrl); -+ break; -+ default: -+ break; -+ } -+ -+ if (acnt->len) -+ pstart += acnt->len * 4; -+ else -+ goto process_next; -+ } -+process_next: -+ acnt_tail = acnt_head; -+ writel(acnt_tail, pcie_priv->iobase1 + MACREG_REG_ACNTTAIL); -+} -+ -+static int pcie_mcast_cts(struct ieee80211_hw *hw, bool enable) -+{ -+ return mwl_fwcmd_mcast_cts(hw, enable ? 1 : 0); -+} -+ -+static const struct mwl_hif_ops pcie_hif_ops_ndp = { -+ .driver_name = PCIE_DRV_NAME, -+ .driver_version = PCIE_DRV_VERSION, -+ .tx_head_room = PCIE_MIN_BYTES_HEADROOM, -+ .ampdu_num = AMPDU_QUEUES_NDP, -+ .reset = pcie_reset, -+ .init = pcie_init_ndp, -+ .deinit = pcie_deinit_ndp, -+ .get_info = pcie_get_info_ndp, -+ .get_tx_status = pcie_get_tx_status_ndp, -+ .get_rx_status = pcie_get_rx_status_ndp, -+ .enable_data_tasks = pcie_enable_data_tasks_ndp, -+ .disable_data_tasks = pcie_disable_data_tasks_ndp, -+ .exec_cmd = pcie_exec_cmd, -+ .get_irq_num = pcie_get_irq_num, -+ .irq_handler = pcie_isr_ndp, -+ .irq_enable = pcie_irq_enable_ndp, -+ .irq_disable = pcie_irq_disable, -+ .download_firmware = pcie_download_firmware, -+ .timer_routine = pcie_timer_routine_ndp, -+ .tx_xmit = pcie_tx_xmit_ndp, -+ .tx_return_pkts = pcie_tx_return_pkts_ndp, -+ .get_device_node = pcie_get_device_node, -+ .get_survey = pcie_get_survey, -+ .reg_access = pcie_reg_access, -+ .set_sta_id = pcie_set_sta_id, -+ .process_account = pcie_process_account, -+ .mcast_cts = pcie_mcast_cts, -+}; -+ -+static int pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) -+{ -+ static bool printed_version; -+ struct ieee80211_hw *hw; -+ struct mwl_priv *priv; -+ struct pcie_priv *pcie_priv; -+ const struct mwl_hif_ops *hif_ops; -+ int rc = 0; -+ -+ if (id->driver_data >= MWLUNKNOWN) -+ return -ENODEV; -+ -+ if (!printed_version) { -+ pr_info("<<%s version %s>>\n", -+ PCIE_DRV_DESC, PCIE_DRV_VERSION); -+ printed_version = true; -+ } -+ -+ rc = pci_enable_device(pdev); -+ if (rc) { -+ pr_err("%s: cannot enable new PCI device\n", -+ PCIE_DRV_NAME); -+ return rc; -+ } -+ -+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); -+ if (rc) { -+ pr_err("%s: 32-bit PCI DMA not supported\n", -+ PCIE_DRV_NAME); -+ goto err_pci_disable_device; -+ } -+ -+ pci_set_master(pdev); -+ -+ if (id->driver_data == MWL8964) -+ hif_ops = &pcie_hif_ops_ndp; -+ else -+ hif_ops = &pcie_hif_ops; -+ hw = mwl_alloc_hw(MWL_BUS_PCIE, id->driver_data, &pdev->dev, -+ hif_ops, sizeof(*pcie_priv)); -+ if (!hw) { -+ pr_err("%s: mwlwifi hw alloc failed\n", -+ PCIE_DRV_NAME); -+ rc = -ENOMEM; -+ goto err_pci_disable_device; -+ } -+ -+ pci_set_drvdata(pdev, hw); -+ -+ priv = hw->priv; -+ priv->antenna_tx = pcie_chip_tbl[priv->chip_type].antenna_tx; -+ priv->antenna_rx = pcie_chip_tbl[priv->chip_type].antenna_rx; -+ pcie_priv = priv->hif.priv; -+ pcie_priv->mwl_priv = priv; -+ pcie_priv->pdev = pdev; -+ if (id->driver_data != MWL8964) { -+ pcie_priv->tx_head_room = PCIE_MIN_BYTES_HEADROOM; -+ if (id->driver_data == MWL8997) { -+ if (NET_SKB_PAD < PCIE_MIN_TX_HEADROOM_KF2) { -+ pcie_priv->tx_head_room = -+ PCIE_MIN_TX_HEADROOM_KF2; -+ pcie_hif_ops.tx_head_room = -+ PCIE_MIN_TX_HEADROOM_KF2; -+ } -+ } -+ } -+ -+ rc = pcie_alloc_resource(pcie_priv); -+ if (rc) -+ goto err_alloc_pci_resource; -+ -+ rc = mwl_init_hw(hw, pcie_chip_tbl[priv->chip_type].fw_image, -+ pcie_chip_tbl[priv->chip_type].cal_file, -+ pcie_chip_tbl[priv->chip_type].txpwrlmt_file); -+ if (rc) -+ goto err_wl_init; -+ -+ vendor_cmd_basic_event(hw->wiphy, MWL_VENDOR_EVENT_DRIVER_READY); -+ -+ return rc; -+ -+err_wl_init: -+ -+ pcie_reset(hw); -+ -+err_alloc_pci_resource: -+ -+ pci_set_drvdata(pdev, NULL); -+ mwl_free_hw(hw); -+ -+err_pci_disable_device: -+ -+ pci_disable_device(pdev); -+ -+ return rc; -+} -+ -+static void pcie_remove(struct pci_dev *pdev) -+{ -+ struct ieee80211_hw *hw = pci_get_drvdata(pdev); -+ struct mwl_priv *priv = hw->priv; -+ -+ priv->rmmod = true; -+ while (priv->in_send_cmd) -+ usleep_range(1000, 2000); -+ vendor_cmd_basic_event(hw->wiphy, MWL_VENDOR_EVENT_DRIVER_START_REMOVE); -+ mwl_deinit_hw(hw); -+ pci_set_drvdata(pdev, NULL); -+ mwl_free_hw(hw); -+ pci_disable_device(pdev); -+} -+ -+static struct pci_driver mwl_pcie_driver = { -+ .name = PCIE_DRV_NAME, -+ .id_table = pcie_id_tbl, -+ .probe = pcie_probe, -+ .remove = pcie_remove -+}; -+ -+module_pci_driver(mwl_pcie_driver); -+ -+MODULE_DESCRIPTION(PCIE_DRV_DESC); -+MODULE_VERSION(PCIE_DRV_VERSION); -+MODULE_AUTHOR("Marvell Semiconductor, Inc."); -+MODULE_LICENSE("GPL v2"); -+MODULE_SUPPORTED_DEVICE(PCIE_DEV_NAME); -+MODULE_DEVICE_TABLE(pci, pcie_id_tbl); -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c -new file mode 100644 -index 000000000000..25076c6d66df ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.c -@@ -0,0 +1,540 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements receive related functions. */ -+ -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "hif/pcie/dev.h" -+#include "hif/pcie/rx.h" -+ -+#define MAX_NUM_RX_RING_BYTES (PCIE_MAX_NUM_RX_DESC * \ -+ sizeof(struct pcie_rx_desc)) -+ -+#define MAX_NUM_RX_HNDL_BYTES (PCIE_MAX_NUM_RX_DESC * \ -+ sizeof(struct pcie_rx_hndl)) -+ -+#define DECRYPT_ERR_MASK 0x80 -+#define GENERAL_DECRYPT_ERR 0xFF -+#define TKIP_DECRYPT_MIC_ERR 0x02 -+#define WEP_DECRYPT_ICV_ERR 0x04 -+#define TKIP_DECRYPT_ICV_ERR 0x08 -+ -+#define W836X_RSSI_OFFSET 8 -+ -+static int pcie_rx_ring_alloc(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data *desc; -+ -+ desc = &pcie_priv->desc_data[0]; -+ -+ desc->prx_ring = (struct pcie_rx_desc *) -+ dma_alloc_coherent(priv->dev, -+ MAX_NUM_RX_RING_BYTES, -+ &desc->pphys_rx_ring, -+ GFP_KERNEL); -+ -+ if (!desc->prx_ring) { -+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); -+ return -ENOMEM; -+ } -+ -+ memset(desc->prx_ring, 0x00, MAX_NUM_RX_RING_BYTES); -+ -+ desc->rx_hndl = kzalloc(MAX_NUM_RX_HNDL_BYTES, GFP_KERNEL); -+ -+ if (!desc->rx_hndl) { -+ dma_free_coherent(priv->dev, -+ MAX_NUM_RX_RING_BYTES, -+ desc->prx_ring, -+ desc->pphys_rx_ring); -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static int pcie_rx_ring_init(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data *desc; -+ int i; -+ struct pcie_rx_hndl *rx_hndl; -+ dma_addr_t dma; -+ u32 val; -+ -+ desc = &pcie_priv->desc_data[0]; -+ -+ if (desc->prx_ring) { -+ desc->rx_buf_size = SYSADPT_MAX_AGGR_SIZE; -+ -+ for (i = 0; i < PCIE_MAX_NUM_RX_DESC; i++) { -+ rx_hndl = &desc->rx_hndl[i]; -+ rx_hndl->psk_buff = -+ dev_alloc_skb(desc->rx_buf_size); -+ -+ if (!rx_hndl->psk_buff) { -+ wiphy_err(priv->hw->wiphy, -+ "rxdesc %i: no skbuff available\n", -+ i); -+ return -ENOMEM; -+ } -+ -+ skb_reserve(rx_hndl->psk_buff, -+ PCIE_MIN_BYTES_HEADROOM); -+ desc->prx_ring[i].rx_control = -+ EAGLE_RXD_CTRL_DRIVER_OWN; -+ desc->prx_ring[i].status = EAGLE_RXD_STATUS_OK; -+ desc->prx_ring[i].qos_ctrl = 0x0000; -+ desc->prx_ring[i].channel = 0x00; -+ desc->prx_ring[i].rssi = 0x00; -+ desc->prx_ring[i].pkt_len = -+ cpu_to_le16(SYSADPT_MAX_AGGR_SIZE); -+ dma = pci_map_single(pcie_priv->pdev, -+ rx_hndl->psk_buff->data, -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { -+ wiphy_err(priv->hw->wiphy, -+ "failed to map pci memory!\n"); -+ return -ENOMEM; -+ } -+ desc->prx_ring[i].pphys_buff_data = cpu_to_le32(dma); -+ val = (u32)desc->pphys_rx_ring + -+ ((i + 1) * sizeof(struct pcie_rx_desc)); -+ desc->prx_ring[i].pphys_next = cpu_to_le32(val); -+ rx_hndl->pdesc = &desc->prx_ring[i]; -+ if (i < (PCIE_MAX_NUM_RX_DESC - 1)) -+ rx_hndl->pnext = &desc->rx_hndl[i + 1]; -+ } -+ desc->prx_ring[PCIE_MAX_NUM_RX_DESC - 1].pphys_next = -+ cpu_to_le32((u32)desc->pphys_rx_ring); -+ desc->rx_hndl[PCIE_MAX_NUM_RX_DESC - 1].pnext = -+ &desc->rx_hndl[0]; -+ desc->pnext_rx_hndl = &desc->rx_hndl[0]; -+ -+ return 0; -+ } -+ -+ wiphy_err(priv->hw->wiphy, "no valid RX mem\n"); -+ -+ return -ENOMEM; -+} -+ -+static void pcie_rx_ring_cleanup(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data *desc; -+ int i; -+ struct pcie_rx_hndl *rx_hndl; -+ -+ desc = &pcie_priv->desc_data[0]; -+ -+ if (desc->prx_ring) { -+ for (i = 0; i < PCIE_MAX_NUM_RX_DESC; i++) { -+ rx_hndl = &desc->rx_hndl[i]; -+ if (!rx_hndl->psk_buff) -+ continue; -+ -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu -+ (rx_hndl->pdesc->pphys_buff_data), -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ -+ dev_kfree_skb_any(rx_hndl->psk_buff); -+ -+ wiphy_debug(priv->hw->wiphy, -+ "unmapped+free'd %i 0x%p 0x%x %i\n", -+ i, rx_hndl->psk_buff->data, -+ le32_to_cpu( -+ rx_hndl->pdesc->pphys_buff_data), -+ desc->rx_buf_size); -+ -+ rx_hndl->psk_buff = NULL; -+ } -+ } -+} -+ -+static void pcie_rx_ring_free(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data *desc; -+ -+ desc = &pcie_priv->desc_data[0]; -+ -+ if (desc->prx_ring) { -+ pcie_rx_ring_cleanup(priv); -+ -+ dma_free_coherent(priv->dev, -+ MAX_NUM_RX_RING_BYTES, -+ desc->prx_ring, -+ desc->pphys_rx_ring); -+ -+ desc->prx_ring = NULL; -+ } -+ -+ kfree(desc->rx_hndl); -+ -+ desc->pnext_rx_hndl = NULL; -+} -+ -+static inline void pcie_rx_status(struct mwl_priv *priv, -+ struct pcie_rx_desc *pdesc, -+ struct ieee80211_rx_status *status) -+{ -+ u16 rx_rate; -+ -+ memset(status, 0, sizeof(*status)); -+ -+ if (priv->chip_type == MWL8997) -+ status->signal = (s8)pdesc->rssi; -+ else -+ status->signal = -(pdesc->rssi + W836X_RSSI_OFFSET); -+ -+ rx_rate = le16_to_cpu(pdesc->rate); -+ pcie_rx_prepare_status(priv, -+ rx_rate & MWL_RX_RATE_FORMAT_MASK, -+ (rx_rate & MWL_RX_RATE_NSS_MASK) >> -+ MWL_RX_RATE_NSS_SHIFT, -+ (rx_rate & MWL_RX_RATE_BW_MASK) >> -+ MWL_RX_RATE_BW_SHIFT, -+ (rx_rate & MWL_RX_RATE_GI_MASK) >> -+ MWL_RX_RATE_GI_SHIFT, -+ (rx_rate & MWL_RX_RATE_RT_MASK) >> -+ MWL_RX_RATE_RT_SHIFT, -+ status); -+ -+ status->freq = ieee80211_channel_to_frequency(pdesc->channel, -+ status->band); -+ -+ /* check if status has a specific error bit (bit 7) set or indicates -+ * a general decrypt error -+ */ -+ if ((pdesc->status == GENERAL_DECRYPT_ERR) || -+ (pdesc->status & DECRYPT_ERR_MASK)) { -+ /* check if status is not equal to 0xFF -+ * the 0xFF check is for backward compatibility -+ */ -+ if (pdesc->status != GENERAL_DECRYPT_ERR) { -+ if (((pdesc->status & (~DECRYPT_ERR_MASK)) & -+ TKIP_DECRYPT_MIC_ERR) && !((pdesc->status & -+ (WEP_DECRYPT_ICV_ERR | TKIP_DECRYPT_ICV_ERR)))) { -+ status->flag |= RX_FLAG_MMIC_ERROR; -+ } -+ } -+ } -+} -+ -+static inline bool pcie_rx_process_mesh_amsdu(struct mwl_priv *priv, -+ struct sk_buff *skb, -+ struct ieee80211_rx_status *status) -+{ -+ struct ieee80211_hdr *wh; -+ struct mwl_sta *sta_info; -+ struct ieee80211_sta *sta; -+ u8 *qc; -+ int wh_len; -+ int len; -+ u8 pad; -+ u8 *data; -+ u16 frame_len; -+ struct sk_buff *newskb; -+ -+ wh = (struct ieee80211_hdr *)skb->data; -+ -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ sta = container_of((void *)sta_info, struct ieee80211_sta, -+ drv_priv[0]); -+ if (ether_addr_equal(sta->addr, wh->addr2)) { -+ if (!sta_info->is_mesh_node) { -+ spin_unlock_bh(&priv->sta_lock); -+ return false; -+ } -+ } -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ qc = ieee80211_get_qos_ctl(wh); -+ *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; -+ -+ wh_len = ieee80211_hdrlen(wh->frame_control); -+ len = wh_len; -+ data = skb->data; -+ -+ while (len < skb->len) { -+ frame_len = *(u8 *)(data + len + ETH_HLEN - 1) | -+ (*(u8 *)(data + len + ETH_HLEN - 2) << 8); -+ -+ if ((len + ETH_HLEN + frame_len) > skb->len) -+ break; -+ -+ newskb = dev_alloc_skb(wh_len + frame_len); -+ if (!newskb) -+ break; -+ -+ ether_addr_copy(wh->addr3, data + len); -+ ether_addr_copy(wh->addr4, data + len + ETH_ALEN); -+ memcpy(newskb->data, wh, wh_len); -+ memcpy(newskb->data + wh_len, data + len + ETH_HLEN, frame_len); -+ skb_put(newskb, wh_len + frame_len); -+ -+ pad = ((ETH_HLEN + frame_len) % 4) ? -+ (4 - (ETH_HLEN + frame_len) % 4) : 0; -+ len += (ETH_HLEN + frame_len + pad); -+ if (len < skb->len) -+ status->flag |= RX_FLAG_AMSDU_MORE; -+ else -+ status->flag &= ~RX_FLAG_AMSDU_MORE; -+ memcpy(IEEE80211_SKB_RXCB(newskb), status, sizeof(*status)); -+ ieee80211_rx(priv->hw, newskb); -+ } -+ -+ dev_kfree_skb_any(skb); -+ -+ return true; -+} -+ -+static inline int pcie_rx_refill(struct mwl_priv *priv, -+ struct pcie_rx_hndl *rx_hndl) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data *desc; -+ dma_addr_t dma; -+ -+ desc = &pcie_priv->desc_data[0]; -+ -+ rx_hndl->psk_buff = dev_alloc_skb(desc->rx_buf_size); -+ -+ if (!rx_hndl->psk_buff) -+ return -ENOMEM; -+ -+ skb_reserve(rx_hndl->psk_buff, PCIE_MIN_BYTES_HEADROOM); -+ -+ rx_hndl->pdesc->status = EAGLE_RXD_STATUS_OK; -+ rx_hndl->pdesc->qos_ctrl = 0x0000; -+ rx_hndl->pdesc->channel = 0x00; -+ rx_hndl->pdesc->rssi = 0x00; -+ rx_hndl->pdesc->pkt_len = cpu_to_le16(desc->rx_buf_size); -+ -+ dma = pci_map_single(pcie_priv->pdev, -+ rx_hndl->psk_buff->data, -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { -+ dev_kfree_skb_any(rx_hndl->psk_buff); -+ wiphy_err(priv->hw->wiphy, -+ "failed to map pci memory!\n"); -+ return -ENOMEM; -+ } -+ -+ rx_hndl->pdesc->pphys_buff_data = cpu_to_le32(dma); -+ -+ return 0; -+} -+ -+int pcie_rx_init(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ int rc; -+ -+ rc = pcie_rx_ring_alloc(priv); -+ if (rc) { -+ wiphy_err(hw->wiphy, "allocating RX ring failed\n"); -+ return rc; -+ } -+ -+ rc = pcie_rx_ring_init(priv); -+ if (rc) { -+ pcie_rx_ring_free(priv); -+ wiphy_err(hw->wiphy, -+ "initializing RX ring failed\n"); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+void pcie_rx_deinit(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ pcie_rx_ring_cleanup(priv); -+ pcie_rx_ring_free(priv); -+} -+ -+void pcie_rx_recv(unsigned long data) -+{ -+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data *desc; -+ struct pcie_rx_hndl *curr_hndl; -+ int work_done = 0; -+ struct sk_buff *prx_skb = NULL; -+ int pkt_len; -+ struct ieee80211_rx_status *status; -+ struct mwl_vif *mwl_vif = NULL; -+ struct ieee80211_hdr *wh; -+ -+ desc = &pcie_priv->desc_data[0]; -+ curr_hndl = desc->pnext_rx_hndl; -+ -+ if (!curr_hndl) { -+ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_RX_RDY, true); -+ pcie_priv->is_rx_schedule = false; -+ wiphy_warn(hw->wiphy, "busy or no receiving packets\n"); -+ return; -+ } -+ -+ while ((curr_hndl->pdesc->rx_control == EAGLE_RXD_CTRL_DMA_OWN) && -+ (work_done < pcie_priv->recv_limit)) { -+ prx_skb = curr_hndl->psk_buff; -+ if (!prx_skb) -+ goto out; -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu(curr_hndl->pdesc->pphys_buff_data), -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ pkt_len = le16_to_cpu(curr_hndl->pdesc->pkt_len); -+ -+ if (skb_tailroom(prx_skb) < pkt_len) { -+ dev_kfree_skb_any(prx_skb); -+ goto out; -+ } -+ -+ if (curr_hndl->pdesc->channel != -+ hw->conf.chandef.chan->hw_value) { -+ dev_kfree_skb_any(prx_skb); -+ goto out; -+ } -+ -+ status = IEEE80211_SKB_RXCB(prx_skb); -+ pcie_rx_status(priv, curr_hndl->pdesc, status); -+ -+ if (priv->chip_type == MWL8997) { -+ priv->noise = (s8)curr_hndl->pdesc->noise_floor; -+ if (priv->noise > 0) -+ priv->noise = -priv->noise; -+ } else -+ priv->noise = -curr_hndl->pdesc->noise_floor; -+ -+ wh = &((struct pcie_dma_data *)prx_skb->data)->wh; -+ -+ if (ieee80211_has_protected(wh->frame_control)) { -+ /* Check if hw crypto has been enabled for -+ * this bss. If yes, set the status flags -+ * accordingly -+ */ -+ if (ieee80211_has_tods(wh->frame_control)) { -+ mwl_vif = utils_find_vif_bss(priv, wh->addr1); -+ if (!mwl_vif && -+ ieee80211_has_a4(wh->frame_control)) -+ mwl_vif = -+ utils_find_vif_bss(priv, -+ wh->addr2); -+ } else { -+ mwl_vif = utils_find_vif_bss(priv, wh->addr2); -+ } -+ -+ if ((mwl_vif && mwl_vif->is_hw_crypto_enabled) || -+ is_multicast_ether_addr(wh->addr1) || -+ (ieee80211_is_mgmt(wh->frame_control) && -+ !is_multicast_ether_addr(wh->addr1))) { -+ /* When MMIC ERROR is encountered -+ * by the firmware, payload is -+ * dropped and only 32 bytes of -+ * mwlwifi Firmware header is sent -+ * to the host. -+ * -+ * We need to add four bytes of -+ * key information. In it -+ * MAC80211 expects keyidx set to -+ * 0 for triggering Counter -+ * Measure of MMIC failure. -+ */ -+ if (status->flag & RX_FLAG_MMIC_ERROR) { -+ struct pcie_dma_data *dma_data; -+ -+ dma_data = (struct pcie_dma_data *) -+ prx_skb->data; -+ memset((void *)&dma_data->data, 0, 4); -+ pkt_len += 4; -+ } -+ -+ if (!ieee80211_is_auth(wh->frame_control)) { -+ if (priv->chip_type != MWL8997) -+ status->flag |= -+ RX_FLAG_IV_STRIPPED | -+ RX_FLAG_DECRYPTED | -+ RX_FLAG_MMIC_STRIPPED; -+ else -+ status->flag |= -+ RX_FLAG_DECRYPTED | -+ RX_FLAG_MMIC_STRIPPED; -+ } -+ } -+ } -+ -+ skb_put(prx_skb, pkt_len); -+ pcie_rx_remove_dma_header(prx_skb, curr_hndl->pdesc->qos_ctrl); -+ -+ wh = (struct ieee80211_hdr *)prx_skb->data; -+ -+ if (ieee80211_is_data_qos(wh->frame_control)) { -+ const u8 eapol[] = {0x88, 0x8e}; -+ u8 *qc = ieee80211_get_qos_ctl(wh); -+ u8 *data; -+ -+ data = prx_skb->data + -+ ieee80211_hdrlen(wh->frame_control) + 6; -+ -+ if (!memcmp(data, eapol, sizeof(eapol))) -+ *qc |= 7; -+ } -+ -+ if (ieee80211_is_data_qos(wh->frame_control) && -+ ieee80211_has_a4(wh->frame_control)) { -+ u8 *qc = ieee80211_get_qos_ctl(wh); -+ -+ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) -+ if (pcie_rx_process_mesh_amsdu(priv, prx_skb, -+ status)) -+ goto out; -+ } -+ -+ if (ieee80211_is_probe_req(wh->frame_control) && -+ priv->dump_probe) -+ wiphy_info(hw->wiphy, "Probe Req: %pM\n", wh->addr2); -+ -+ ieee80211_rx(hw, prx_skb); -+out: -+ pcie_rx_refill(priv, curr_hndl); -+ curr_hndl->pdesc->rx_control = EAGLE_RXD_CTRL_DRIVER_OWN; -+ curr_hndl->pdesc->qos_ctrl = 0; -+ curr_hndl = curr_hndl->pnext; -+ work_done++; -+ } -+ -+ desc->pnext_rx_hndl = curr_hndl; -+ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_RX_RDY, true); -+ pcie_priv->is_rx_schedule = false; -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h -new file mode 100644 -index 000000000000..d2b580fceb0a ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx.h -@@ -0,0 +1,25 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines receive related functions. */ -+ -+#ifndef _RX_H_ -+#define _RX_H_ -+ -+int pcie_rx_init(struct ieee80211_hw *hw); -+void pcie_rx_deinit(struct ieee80211_hw *hw); -+void pcie_rx_recv(unsigned long data); -+ -+#endif /* _RX_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c -new file mode 100644 -index 000000000000..d1ede588b4c1 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.c -@@ -0,0 +1,612 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements receive related functions for new data -+ * path. -+ */ -+ -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "hif/pcie/dev.h" -+#include "hif/pcie/rx_ndp.h" -+ -+#define MAX_NUM_RX_RING_BYTES (MAX_NUM_RX_DESC * \ -+ sizeof(struct pcie_rx_desc_ndp)) -+#define MAX_NUM_RX_RING_DONE_BYTES (MAX_NUM_RX_DESC * \ -+ sizeof(struct rx_ring_done)) -+ -+static int pcie_rx_ring_alloc_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ -+ desc->prx_ring = (struct pcie_rx_desc_ndp *) -+ dma_alloc_coherent(priv->dev, -+ MAX_NUM_RX_RING_BYTES, -+ &desc->pphys_rx_ring, -+ GFP_KERNEL); -+ if (!desc->prx_ring) -+ goto err_no_mem; -+ memset(desc->prx_ring, 0x00, MAX_NUM_RX_RING_BYTES); -+ -+ desc->prx_ring_done = (struct rx_ring_done *) -+ dma_alloc_coherent(priv->dev, -+ MAX_NUM_RX_RING_DONE_BYTES, -+ &desc->pphys_rx_ring_done, -+ GFP_KERNEL); -+ if (!desc->prx_ring_done) -+ goto err_no_mem; -+ memset(desc->prx_ring_done, 0x00, MAX_NUM_RX_RING_DONE_BYTES); -+ return 0; -+ -+err_no_mem: -+ -+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); -+ return -ENOMEM; -+} -+ -+static int pcie_rx_ring_init_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ int i; -+ struct sk_buff *psk_buff; -+ dma_addr_t dma; -+ -+ skb_queue_head_init(&pcie_priv->rx_skb_trace); -+ if (desc->prx_ring) { -+ desc->rx_buf_size = MAX_AGGR_SIZE; -+ -+ for (i = 0; i < MAX_NUM_RX_DESC; i++) { -+ psk_buff = __alloc_skb(desc->rx_buf_size + NET_SKB_PAD, -+ GFP_ATOMIC, SKB_ALLOC_RX, -+ NUMA_NO_NODE); -+ skb_reserve(psk_buff, NET_SKB_PAD); -+ if (!psk_buff) { -+ wiphy_err(priv->hw->wiphy, -+ "rxdesc %i: no skbuff available\n", -+ i); -+ return -ENOMEM; -+ } -+ skb_reserve(psk_buff, MIN_BYTES_RX_HEADROOM); -+ -+ dma = pci_map_single(pcie_priv->pdev, -+ psk_buff->data, -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { -+ wiphy_err(priv->hw->wiphy, -+ "failed to map pci memory!\n"); -+ return -ENOMEM; -+ } -+ -+ desc->rx_vbuflist[i] = psk_buff; -+ desc->prx_ring[i].user = cpu_to_le32(i); -+ desc->prx_ring[i].data = cpu_to_le32(dma); -+ *((u32 *)&psk_buff->cb[16]) = 0xdeadbeef; -+ skb_queue_tail(&pcie_priv->rx_skb_trace, psk_buff); -+ } -+ -+ writel(1023, pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD); -+ return 0; -+ } -+ -+ wiphy_err(priv->hw->wiphy, "no valid RX mem\n"); -+ return -ENOMEM; -+} -+ -+static void pcie_rx_ring_cleanup_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ int i; -+ -+ if (desc->prx_ring) { -+ for (i = 0; i < MAX_NUM_RX_DESC; i++) { -+ if (desc->rx_vbuflist[i]) { -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu( -+ desc->prx_ring[i].data), -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ desc->rx_vbuflist[i] = NULL; -+ } -+ } -+ skb_queue_purge(&pcie_priv->rx_skb_trace); -+ } -+} -+ -+static void pcie_rx_ring_free_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ -+ if (desc->prx_ring) { -+ pcie_rx_ring_cleanup_ndp(priv); -+ dma_free_coherent(priv->dev, -+ MAX_NUM_RX_RING_BYTES, -+ desc->prx_ring, -+ desc->pphys_rx_ring); -+ desc->prx_ring = NULL; -+ } -+ -+ if (desc->prx_ring_done) { -+ dma_free_coherent(priv->dev, -+ MAX_NUM_RX_RING_DONE_BYTES, -+ desc->prx_ring_done, -+ desc->pphys_rx_ring_done); -+ desc->prx_ring_done = NULL; -+ } -+} -+ -+static inline void pcie_rx_update_ndp_cnts(struct mwl_priv *priv, u32 ctrl) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ -+ switch (ctrl) { -+ case RXRING_CASE_DROP: -+ pcie_priv->rx_cnts.drop_cnt++; -+ break; -+ case RXRING_CASE_FAST_BAD_AMSDU: -+ pcie_priv->rx_cnts.fast_bad_amsdu_cnt++; -+ break; -+ case RXRING_CASE_FAST_DATA: -+ pcie_priv->rx_cnts.fast_data_cnt++; -+ break; -+ case RXRING_CASE_SLOW_BAD_MIC: -+ pcie_priv->rx_cnts.slow_bad_mic_cnt++; -+ break; -+ case RXRING_CASE_SLOW_BAD_PN: -+ pcie_priv->rx_cnts.slow_bad_pn_cnt++; -+ break; -+ case RXRING_CASE_SLOW_BAD_STA: -+ pcie_priv->rx_cnts.slow_bad_sta_cnt++; -+ break; -+ case RXRING_CASE_SLOW_MCAST: -+ pcie_priv->rx_cnts.slow_mcast_cnt++; -+ break; -+ case RXRING_CASE_SLOW_MGMT: -+ pcie_priv->rx_cnts.slow_mgmt_cnt++; -+ break; -+ case RXRING_CASE_SLOW_NOQUEUE: -+ pcie_priv->rx_cnts.slow_noqueue_cnt++; -+ break; -+ case RXRING_CASE_SLOW_NORUN: -+ pcie_priv->rx_cnts.slow_norun_cnt++; -+ break; -+ case RXRING_CASE_SLOW_PROMISC: -+ pcie_priv->rx_cnts.slow_promisc_cnt++; -+ break; -+ } -+} -+ -+static void pcie_rx_status_ndp(struct mwl_priv *priv, -+ struct mwl_sta *sta_info, -+ struct ieee80211_rx_status *status) -+{ -+ memset(status, 0, sizeof(*status)); -+ pcie_rx_prepare_status(priv, -+ sta_info->rx_format, -+ sta_info->rx_nss, -+ sta_info->rx_bw, -+ sta_info->rx_gi, -+ sta_info->rx_rate_mcs, -+ status); -+ status->signal = -sta_info->rx_signal; -+ status->band = priv->hw->conf.chandef.chan->band; -+ status->freq = ieee80211_channel_to_frequency( -+ priv->hw->conf.chandef.chan->hw_value, status->band); -+} -+ -+static inline void pcie_rx_process_fast_data(struct mwl_priv *priv, -+ struct sk_buff *skb, -+ u16 stnid) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct ieee80211_sta *sta; -+ struct mwl_sta *sta_info; -+ struct mwl_vif *mwl_vif; -+ struct ieee80211_hdr hdr; -+ u16 hdrlen, ethertype; -+ __le16 fc; -+ struct ieee80211_rx_status *status; -+ -+ if (stnid == RXRING_CTRL_STA_FROMDS) -+ stnid = 0; -+ -+ if (stnid > SYSADPT_MAX_STA_SC4) -+ goto drop_packet; -+ -+ sta = pcie_priv->sta_link[stnid]; -+ if (!sta) -+ goto drop_packet; -+ -+ sta_info = mwl_dev_get_sta(sta); -+ mwl_vif = sta_info->mwl_vif; -+ if (!mwl_vif) -+ goto drop_packet; -+ -+ ethertype = (skb->data[20] << 8) | skb->data[21]; -+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); -+ -+ memset(&hdr, 0, sizeof(hdr)); -+ switch (mwl_vif->type) { -+ case NL80211_IFTYPE_AP: -+ if (sta_info->wds) { -+ fc |= (cpu_to_le16(IEEE80211_FCTL_TODS) | -+ cpu_to_le16(IEEE80211_FCTL_FROMDS)); -+ /* RA TA DA SA */ -+ ether_addr_copy(hdr.addr1, mwl_vif->bssid); -+ ether_addr_copy(hdr.addr2, sta->addr); -+ ether_addr_copy(hdr.addr3, skb->data); -+ ether_addr_copy(hdr.addr4, skb->data + ETH_ALEN); -+ hdrlen = 30; -+ } else { -+ fc |= cpu_to_le16(IEEE80211_FCTL_TODS); -+ /* BSSID SA DA */ -+ ether_addr_copy(hdr.addr1, mwl_vif->bssid); -+ ether_addr_copy(hdr.addr2, skb->data + ETH_ALEN); -+ ether_addr_copy(hdr.addr3, skb->data); -+ hdrlen = 24; -+ } -+ break; -+ case NL80211_IFTYPE_STATION: -+ if (sta_info->wds) { -+ fc |= (cpu_to_le16(IEEE80211_FCTL_TODS) | -+ cpu_to_le16(IEEE80211_FCTL_FROMDS)); -+ /* RA TA DA SA */ -+ ether_addr_copy(hdr.addr1, mwl_vif->sta_mac); -+ ether_addr_copy(hdr.addr2, mwl_vif->bssid); -+ ether_addr_copy(hdr.addr3, skb->data); -+ ether_addr_copy(hdr.addr4, skb->data + ETH_ALEN); -+ hdrlen = 30; -+ } else { -+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); -+ /* DA BSSID SA */ -+ ether_addr_copy(hdr.addr1, skb->data); -+ ether_addr_copy(hdr.addr2, mwl_vif->bssid); -+ ether_addr_copy(hdr.addr3, skb->data + ETH_ALEN); -+ hdrlen = 24; -+ } -+ break; -+ default: -+ goto drop_packet; -+ } -+ -+ if (sta->wme) { -+ fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); -+ hdrlen += 2; -+ } -+ -+ status = IEEE80211_SKB_RXCB(skb); -+ pcie_rx_status_ndp(priv, sta_info, status); -+ if (mwl_vif->is_hw_crypto_enabled) { -+ fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); -+ status->flag |= RX_FLAG_IV_STRIPPED | -+ RX_FLAG_DECRYPTED | -+ RX_FLAG_MMIC_STRIPPED; -+ } -+ -+ hdr.frame_control = fc; -+ hdr.duration_id = 0; -+ -+ skb_pull(skb, ETH_HLEN); -+ -+ if (ieee80211_is_data_qos(fc)) { -+ __le16 *qos_control; -+ -+ qos_control = (__le16 *)skb_push(skb, 2); -+ memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2); -+ if (ethertype == ETH_P_PAE) -+ *qos_control = cpu_to_le16( -+ IEEE80211_QOS_CTL_ACK_POLICY_NOACK | 7); -+ else -+ *qos_control = cpu_to_le16( -+ IEEE80211_QOS_CTL_ACK_POLICY_NOACK); -+ } else -+ memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); -+ -+ status->flag |= RX_FLAG_DUP_VALIDATED; -+ ieee80211_rx(priv->hw, skb); -+ -+ return; -+drop_packet: -+ -+ dev_kfree_skb_any(skb); -+} -+ -+static inline void pcie_rx_process_slow_data(struct mwl_priv *priv, -+ struct sk_buff *skb, -+ bool bad_mic, u8 signal) -+{ -+ struct ieee80211_rx_status *status; -+ struct ieee80211_hdr *wh; -+ struct mwl_vif *mwl_vif = NULL; -+ -+ pcie_rx_remove_dma_header(skb, 0); -+ status = IEEE80211_SKB_RXCB(skb); -+ memset(status, 0, sizeof(*status)); -+ status->signal = -signal; -+ status->band = priv->hw->conf.chandef.chan->band; -+ status->freq = ieee80211_channel_to_frequency( -+ priv->hw->conf.chandef.chan->hw_value, status->band); -+ -+ if (bad_mic) -+ status->flag |= RX_FLAG_MMIC_ERROR; -+ else { -+ wh = (struct ieee80211_hdr *)skb->data; -+ -+ if (ieee80211_has_protected(wh->frame_control)) { -+ if (ieee80211_has_tods(wh->frame_control)) { -+ mwl_vif = utils_find_vif_bss(priv, wh->addr1); -+ if (!mwl_vif && -+ ieee80211_has_a4(wh->frame_control)) -+ mwl_vif = -+ utils_find_vif_bss(priv, -+ wh->addr2); -+ } else { -+ mwl_vif = utils_find_vif_bss(priv, wh->addr2); -+ } -+ -+ if ((mwl_vif && mwl_vif->is_hw_crypto_enabled) || -+ is_multicast_ether_addr(wh->addr1) || -+ (ieee80211_is_mgmt(wh->frame_control) && -+ !is_multicast_ether_addr(wh->addr1))) { -+ if (!ieee80211_is_auth(wh->frame_control)) -+ status->flag |= RX_FLAG_IV_STRIPPED | -+ RX_FLAG_DECRYPTED | -+ RX_FLAG_MMIC_STRIPPED; -+ } -+ } -+ -+ if (ieee80211_has_a4(wh->frame_control) && !priv->wds_check) { -+ ether_addr_copy(priv->wds_check_sta, wh->addr2); -+ ieee80211_queue_work(priv->hw, &priv->wds_check_handle); -+ priv->wds_check = true; -+ } -+ } -+ -+ status->flag |= RX_FLAG_DUP_VALIDATED; -+ ieee80211_rx(priv->hw, skb); -+} -+ -+static inline int pcie_rx_refill_ndp(struct mwl_priv *priv, u32 buf_idx) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ struct sk_buff *psk_buff; -+ dma_addr_t dma; -+ -+ psk_buff = __alloc_skb(desc->rx_buf_size + NET_SKB_PAD, GFP_ATOMIC, -+ SKB_ALLOC_RX, NUMA_NO_NODE); -+ skb_reserve(psk_buff, NET_SKB_PAD); -+ if (!psk_buff) -+ return -ENOMEM; -+ skb_reserve(psk_buff, MIN_BYTES_RX_HEADROOM); -+ -+ dma = pci_map_single(pcie_priv->pdev, -+ psk_buff->data, -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { -+ wiphy_err(priv->hw->wiphy, -+ "refill: failed to map pci memory!\n"); -+ return -ENOMEM; -+ } -+ -+ desc->rx_vbuflist[buf_idx] = psk_buff; -+ desc->prx_ring[buf_idx].data = cpu_to_le32(dma); -+ *((u32 *)&psk_buff->cb[16]) = 0xdeadbeef; -+ skb_queue_tail(&pcie_priv->rx_skb_trace, psk_buff); -+ -+ return 0; -+} -+ -+int pcie_rx_init_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ int rc; -+ -+ rc = pcie_rx_ring_alloc_ndp(priv); -+ if (rc) { -+ pcie_rx_ring_free_ndp(priv); -+ wiphy_err(hw->wiphy, "allocating RX ring failed\n"); -+ return rc; -+ } -+ -+ rc = pcie_rx_ring_init_ndp(priv); -+ if (rc) { -+ pcie_rx_ring_free_ndp(priv); -+ wiphy_err(hw->wiphy, -+ "initializing RX ring failed\n"); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+void pcie_rx_deinit_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ pcie_rx_ring_cleanup_ndp(priv); -+ pcie_rx_ring_free_ndp(priv); -+} -+ -+void pcie_rx_recv_ndp(unsigned long data) -+{ -+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ struct rx_ring_done *prx_ring_done; -+ struct pcie_rx_desc_ndp *prx_desc; -+ u32 rx_done_head; -+ u32 rx_done_tail; -+ u32 rx_desc_head; -+ struct sk_buff *psk_buff; -+ u32 buf_idx; -+ u32 rx_cnt; -+ u32 ctrl, ctrl_case; -+ bool bad_mic; -+ u16 stnid; -+ u16 pktlen; -+ struct rx_info *rx_info; -+ struct pcie_dma_data *dma_data; -+ u8 signal; -+ -+ rx_done_head = readl(pcie_priv->iobase1 + MACREG_REG_RXDONEHEAD); -+ rx_done_tail = readl(pcie_priv->iobase1 + MACREG_REG_RXDONETAIL); -+ rx_desc_head = readl(pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD); -+ rx_cnt = 0; -+ -+ while ((rx_done_tail != rx_done_head) && -+ (rx_cnt < pcie_priv->recv_limit)) { -+recheck: -+ prx_ring_done = &desc->prx_ring_done[rx_done_tail]; -+ wmb(); /*Data Memory Barrier*/ -+ if (le32_to_cpu(prx_ring_done->user) == 0xdeadbeef) { -+ pcie_priv->recheck_rxringdone++; -+ udelay(1); -+ goto recheck; -+ } -+ buf_idx = le32_to_cpu(prx_ring_done->user) & 0x3fff; -+ prx_ring_done->user = cpu_to_le32(0xdeadbeef); -+ rx_done_tail++; -+ prx_desc = &desc->prx_ring[buf_idx]; -+ if (!prx_desc->data) -+ wiphy_err(hw->wiphy, "RX desc data is NULL\n"); -+ psk_buff = desc->rx_vbuflist[buf_idx]; -+ if (!psk_buff) { -+ wiphy_err(hw->wiphy, "RX socket buffer is NULL\n"); -+ goto out; -+ } -+ if (*((u32 *)&psk_buff->cb[16]) != 0xdeadbeef) { -+ pcie_priv->signature_err++; -+ break; -+ } -+ if (psk_buff->next && psk_buff->prev) { -+ skb_unlink(psk_buff, &pcie_priv->rx_skb_trace); -+ *((u32 *)&psk_buff->cb[16]) = 0xbeefdead; -+ } else { -+ pcie_priv->rx_skb_unlink_err++; -+ break; -+ } -+ -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu(prx_desc->data), -+ desc->rx_buf_size, -+ PCI_DMA_FROMDEVICE); -+ -+ bad_mic = false; -+ ctrl = le32_to_cpu(prx_ring_done->ctrl); -+ ctrl_case = ctrl & RXRING_CTRL_CASE_MASK; -+ stnid = (ctrl >> RXRING_CTRL_STA_SHIFT) & RXRING_CTRL_STA_MASK; -+ pcie_rx_update_ndp_cnts(priv, ctrl_case); -+ -+ switch (ctrl_case) { -+ case RXRING_CASE_FAST_DATA: -+ if (stnid == RXRING_CTRL_STA_UNKNOWN) { -+ dev_kfree_skb_any(psk_buff); -+ break; -+ } -+ pktlen = psk_buff->data[12] << 8 | psk_buff->data[13]; -+ pktlen += ETH_HLEN; -+ -+ if (skb_tailroom(psk_buff) >= pktlen) { -+ skb_put(psk_buff, pktlen); -+ pcie_rx_process_fast_data(priv, psk_buff, -+ stnid); -+ } else { -+ wiphy_err(hw->wiphy, -+ "fast: space %d(%d) is not enough\n", -+ skb_tailroom(psk_buff), pktlen); -+ dev_kfree_skb_any(psk_buff); -+ } -+ break; -+ case RXRING_CASE_FAST_BAD_AMSDU: -+ case RXRING_CASE_SLOW_BAD_STA: -+ case RXRING_CASE_SLOW_DEL_DONE: -+ case RXRING_CASE_DROP: -+ case RXRING_CASE_SLOW_BAD_PN: -+ if (ctrl_case == RXRING_CASE_SLOW_DEL_DONE) { -+ wiphy_debug(hw->wiphy, -+ "staid %d deleted\n", -+ stnid); -+ utils_free_stnid(priv, stnid); -+ } -+ dev_kfree_skb_any(psk_buff); -+ break; -+ case RXRING_CASE_SLOW_BAD_MIC: -+ bad_mic = true; -+ case RXRING_CASE_SLOW_NOQUEUE: -+ case RXRING_CASE_SLOW_NORUN: -+ case RXRING_CASE_SLOW_MGMT: -+ case RXRING_CASE_SLOW_MCAST: -+ case RXRING_CASE_SLOW_PROMISC: -+ rx_info = (struct rx_info *)psk_buff->data; -+ dma_data = (struct pcie_dma_data *)&rx_info->hdr[0]; -+ pktlen = le16_to_cpu(dma_data->fwlen); -+ pktlen += sizeof(*rx_info); -+ pktlen += sizeof(struct pcie_dma_data); -+ if (bad_mic) { -+ memset((void *)&dma_data->data, 0, 4); -+ pktlen += 4; -+ } -+ if (skb_tailroom(psk_buff) >= pktlen) { -+ skb_put(psk_buff, pktlen); -+ skb_pull(psk_buff, sizeof(*rx_info)); -+ signal = ((le32_to_cpu(rx_info->rssi_x) >> -+ RXINFO_RSSI_X_SHIFT) & -+ RXINFO_RSSI_X_MASK); -+ pcie_rx_process_slow_data(priv, psk_buff, -+ bad_mic, signal); -+ } else { -+ wiphy_err(hw->wiphy, -+ "slow: space %d(%d) is not enough\n", -+ skb_tailroom(psk_buff), pktlen); -+ dev_kfree_skb_any(psk_buff); -+ } -+ break; -+ default: -+ wiphy_err(hw->wiphy, "unknown control case: %d\n", -+ ctrl_case); -+ dev_kfree_skb_any(psk_buff); -+ break; -+ } -+out: -+ pcie_rx_refill_ndp(priv, buf_idx); -+ -+ if (rx_done_tail >= MAX_RX_RING_DONE_SIZE) -+ rx_done_tail = 0; -+ -+ rx_done_head = -+ readl(pcie_priv->iobase1 + MACREG_REG_RXDONEHEAD); -+ rx_cnt++; -+ } -+ -+ rx_desc_head += rx_cnt; -+ if (rx_desc_head >= MAX_RX_RING_SEND_SIZE) -+ rx_desc_head = rx_desc_head - MAX_RX_RING_SEND_SIZE; -+ writel(rx_done_tail, pcie_priv->iobase1 + MACREG_REG_RXDONETAIL); -+ writel(rx_desc_head, pcie_priv->iobase1 + MACREG_REG_RXDESCHEAD); -+ -+ pcie_mask_int(pcie_priv, MACREG_A2HRIC_RX_DONE_HEAD_RDY, true); -+ pcie_priv->is_rx_schedule = false; -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h -new file mode 100644 -index 000000000000..7e83cedf4351 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/rx_ndp.h -@@ -0,0 +1,26 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines receive related functions for new data path. -+ */ -+ -+#ifndef _RX_NDP_H_ -+#define _RX_NDP_H_ -+ -+int pcie_rx_init_ndp(struct ieee80211_hw *hw); -+void pcie_rx_deinit_ndp(struct ieee80211_hw *hw); -+void pcie_rx_recv_ndp(unsigned long data); -+ -+#endif /* _RX_NDP_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h -new file mode 100644 -index 000000000000..2da0257accba ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/sc4_ddr.h -@@ -0,0 +1,965 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+static unsigned char sc4_ddr_init[] = { -+0x05, -+0x00, -+0x00, -+0x00, -+0x00, -+0x00, -+0x00, -+0x00, -+0xa4, -+0x03, -+0x00, -+0x00, -+0x2a, -+0xbe, -+0xad, -+0x7e, -+0x00, -+0x00, -+0x00, -+0xa8, -+0x01, -+0x00, -+0x00, -+0x00, -+0x00, -+0x00, -+0x00, -+0x00, -+0x5c, -+0x48, -+0x5b, -+0x49, -+0x04, -+0x00, -+0x00, -+0x00, -+0x10, -+0xb5, -+0xc0, -+0xf8, -+0x08, -+0x00, -+0x00, -+0x00, -+0xe0, -+0x12, -+0xef, -+0x21, -+0x0c, -+0x00, -+0x00, -+0x00, -+0xc0, -+0xf8, -+0x20, -+0x13, -+0x10, -+0x00, -+0x00, -+0x00, -+0x02, -+0x21, -+0xc0, -+0xf8, -+0x14, -+0x00, -+0x00, -+0x00, -+0x24, -+0x13, -+0x02, -+0x01, -+0x18, -+0x00, -+0x00, -+0x00, -+0x57, -+0x49, -+0x0a, -+0x60, -+0x1c, -+0x00, -+0x00, -+0x00, -+0x00, -+0x21, -+0x56, -+0x4b, -+0x20, -+0x00, -+0x00, -+0x00, -+0xc0, -+0x3b, -+0x19, -+0x60, -+0x24, -+0x00, -+0x00, -+0x00, -+0x54, -+0x4b, -+0x1b, -+0x1d, -+0x28, -+0x00, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x53, -+0x4b, -+0x2c, -+0x00, -+0x00, -+0x00, -+0xbc, -+0x3b, -+0x19, -+0x60, -+0x30, -+0x00, -+0x00, -+0x00, -+0x51, -+0x4b, -+0x08, -+0x33, -+0x34, -+0x00, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x50, -+0x4b, -+0x38, -+0x00, -+0x00, -+0x00, -+0xb8, -+0x3b, -+0x19, -+0x60, -+0x3c, -+0x00, -+0x00, -+0x00, -+0x4e, -+0x4b, -+0x0c, -+0x33, -+0x40, -+0x00, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x4d, -+0x4b, -+0x44, -+0x00, -+0x00, -+0x00, -+0xb4, -+0x3b, -+0x19, -+0x60, -+0x48, -+0x00, -+0x00, -+0x00, -+0x4b, -+0x4b, -+0x10, -+0x33, -+0x4c, -+0x00, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x4a, -+0x4b, -+0x50, -+0x00, -+0x00, -+0x00, -+0xb0, -+0x3b, -+0x19, -+0x60, -+0x54, -+0x00, -+0x00, -+0x00, -+0x48, -+0x4b, -+0x80, -+0x33, -+0x58, -+0x00, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x47, -+0x4b, -+0x5c, -+0x00, -+0x00, -+0x00, -+0x40, -+0x3b, -+0x19, -+0x60, -+0x60, -+0x00, -+0x00, -+0x00, -+0x47, -+0x4c, -+0x46, -+0x4b, -+0x64, -+0x00, -+0x00, -+0x00, -+0x23, -+0x60, -+0x24, -+0x1d, -+0x68, -+0x00, -+0x00, -+0x00, -+0x23, -+0x60, -+0x24, -+0x1d, -+0x6c, -+0x00, -+0x00, -+0x00, -+0x23, -+0x60, -+0x44, -+0x4b, -+0x70, -+0x00, -+0x00, -+0x00, -+0x40, -+0x33, -+0x19, -+0x60, -+0x74, -+0x00, -+0x00, -+0x00, -+0x1b, -+0x1d, -+0x19, -+0x60, -+0x78, -+0x00, -+0x00, -+0x00, -+0x1b, -+0x1d, -+0x19, -+0x60, -+0x7c, -+0x00, -+0x00, -+0x00, -+0x41, -+0x4b, -+0xc0, -+0xf8, -+0x80, -+0x00, -+0x00, -+0x00, -+0xe0, -+0x31, -+0x41, -+0x4b, -+0x84, -+0x00, -+0x00, -+0x00, -+0xc0, -+0xf8, -+0xf0, -+0x31, -+0x88, -+0x00, -+0x00, -+0x00, -+0x03, -+0x04, -+0xc0, -+0xf8, -+0x8c, -+0x00, -+0x00, -+0x00, -+0xf0, -+0x32, -+0x40, -+0xf2, -+0x90, -+0x00, -+0x00, -+0x00, -+0x55, -+0x13, -+0xc0, -+0xf8, -+0x94, -+0x00, -+0x00, -+0x00, -+0x60, -+0x33, -+0x3d, -+0x4b, -+0x98, -+0x00, -+0x00, -+0x00, -+0xc0, -+0xf8, -+0x64, -+0x33, -+0x9c, -+0x00, -+0x00, -+0x00, -+0x13, -+0x1d, -+0xc0, -+0xf8, -+0xa0, -+0x00, -+0x00, -+0x00, -+0x68, -+0x33, -+0x3b, -+0x4b, -+0xa4, -+0x00, -+0x00, -+0x00, -+0xc0, -+0xf8, -+0x6c, -+0x33, -+0xa8, -+0x00, -+0x00, -+0x00, -+0x3a, -+0x4b, -+0xc0, -+0xf8, -+0xac, -+0x00, -+0x00, -+0x00, -+0x70, -+0x33, -+0x3a, -+0x4b, -+0xb0, -+0x00, -+0x00, -+0x00, -+0xc0, -+0xf8, -+0x74, -+0x33, -+0xb4, -+0x00, -+0x00, -+0x00, -+0x39, -+0x4b, -+0xc0, -+0xf8, -+0xb8, -+0x00, -+0x00, -+0x00, -+0x78, -+0x33, -+0xc4, -+0x23, -+0xbc, -+0x00, -+0x00, -+0x00, -+0xc0, -+0xf8, -+0x7c, -+0x33, -+0xc0, -+0x00, -+0x00, -+0x00, -+0x37, -+0x4b, -+0xc0, -+0xf8, -+0xc4, -+0x00, -+0x00, -+0x00, -+0x80, -+0x33, -+0x37, -+0x4b, -+0xc8, -+0x00, -+0x00, -+0x00, -+0xc0, -+0xf8, -+0x84, -+0x33, -+0xcc, -+0x00, -+0x00, -+0x00, -+0x36, -+0x4b, -+0xc0, -+0xf8, -+0xd0, -+0x00, -+0x00, -+0x00, -+0x88, -+0x33, -+0x42, -+0xf2, -+0xd4, -+0x00, -+0x00, -+0x00, -+0x44, -+0x23, -+0xc0, -+0xf8, -+0xd8, -+0x00, -+0x00, -+0x00, -+0x8c, -+0x33, -+0xc0, -+0xf8, -+0xdc, -+0x00, -+0x00, -+0x00, -+0x90, -+0x13, -+0x4f, -+0xf4, -+0xe0, -+0x00, -+0x00, -+0x00, -+0x60, -+0x43, -+0xc0, -+0xf8, -+0xe4, -+0x00, -+0x00, -+0x00, -+0xa0, -+0x32, -+0xc0, -+0xf8, -+0xe8, -+0x00, -+0x00, -+0x00, -+0xa4, -+0x22, -+0xc0, -+0xf8, -+0xec, -+0x00, -+0x00, -+0x00, -+0xa8, -+0x12, -+0x44, -+0xf2, -+0xf0, -+0x00, -+0x00, -+0x00, -+0x40, -+0x02, -+0x2e, -+0x4b, -+0xf4, -+0x00, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x1b, -+0x1d, -+0xf8, -+0x00, -+0x00, -+0x00, -+0x2d, -+0x4a, -+0x1a, -+0x60, -+0xfc, -+0x00, -+0x00, -+0x00, -+0x1b, -+0x1d, -+0x2d, -+0x4a, -+0x00, -+0x01, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x1b, -+0x1d, -+0x04, -+0x01, -+0x00, -+0x00, -+0x2c, -+0x4a, -+0x1a, -+0x60, -+0x08, -+0x01, -+0x00, -+0x00, -+0x1b, -+0x1d, -+0x2c, -+0x4a, -+0x0c, -+0x01, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x1b, -+0x1d, -+0x10, -+0x01, -+0x00, -+0x00, -+0x4f, -+0xf4, -+0x60, -+0x12, -+0x14, -+0x01, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x25, -+0x4a, -+0x18, -+0x01, -+0x00, -+0x00, -+0x28, -+0x32, -+0x11, -+0x60, -+0x1c, -+0x01, -+0x00, -+0x00, -+0x23, -+0x4a, -+0x30, -+0x32, -+0x20, -+0x01, -+0x00, -+0x00, -+0x11, -+0x60, -+0x12, -+0x1d, -+0x24, -+0x01, -+0x00, -+0x00, -+0x11, -+0x60, -+0x03, -+0x22, -+0x28, -+0x01, -+0x00, -+0x00, -+0x20, -+0x4b, -+0x38, -+0x33, -+0x2c, -+0x01, -+0x00, -+0x00, -+0x1a, -+0x60, -+0x20, -+0x22, -+0x30, -+0x01, -+0x00, -+0x00, -+0x1b, -+0x1d, -+0x1a, -+0x60, -+0x34, -+0x01, -+0x00, -+0x00, -+0x1b, -+0x1d, -+0x4f, -+0xf0, -+0x38, -+0x01, -+0x00, -+0x00, -+0x04, -+0x22, -+0x1a, -+0x60, -+0x3c, -+0x01, -+0x00, -+0x00, -+0x03, -+0x06, -+0x1b, -+0x4a, -+0x40, -+0x01, -+0x00, -+0x00, -+0x20, -+0x32, -+0x13, -+0x60, -+0x44, -+0x01, -+0x00, -+0x00, -+0x43, -+0x06, -+0x13, -+0x60, -+0x48, -+0x01, -+0x00, -+0x00, -+0x83, -+0x06, -+0x13, -+0x60, -+0x4c, -+0x01, -+0x00, -+0x00, -+0x0c, -+0x4b, -+0x1c, -+0x4a, -+0x50, -+0x01, -+0x00, -+0x00, -+0x70, -+0x33, -+0x1a, -+0x60, -+0x54, -+0x01, -+0x00, -+0x00, -+0x41, -+0x62, -+0x4f, -+0xf4, -+0x58, -+0x01, -+0x00, -+0x00, -+0x7f, -+0x22, -+0x82, -+0x62, -+0x5c, -+0x01, -+0x00, -+0x00, -+0x19, -+0x4a, -+0x82, -+0x66, -+0x60, -+0x01, -+0x00, -+0x00, -+0xc1, -+0x66, -+0x40, -+0xf2, -+0x64, -+0x01, -+0x00, -+0x00, -+0x63, -+0x42, -+0x42, -+0x63, -+0x68, -+0x01, -+0x00, -+0x00, -+0x01, -+0x64, -+0x17, -+0x49, -+0x6c, -+0x01, -+0x00, -+0x00, -+0x01, -+0x60, -+0x10, -+0xbd, -+0x70, -+0x01, -+0x00, -+0x00, -+0x22, -+0x76, -+0x30, -+0x00, -+0x74, -+0x01, -+0x00, -+0x00, -+0x20, -+0x00, -+0x00, -+0xf0, -+0x78, -+0x01, -+0x00, -+0x00, -+0x40, -+0x06, -+0x00, -+0xf0, -+0x7c, -+0x01, -+0x00, -+0x00, -+0x04, -+0x00, -+0x15, -+0x15, -+0x80, -+0x01, -+0x00, -+0x00, -+0x00, -+0x05, -+0x00, -+0xf0, -+0x84, -+0x01, -+0x00, -+0x00, -+0x01, -+0x00, -+0x0d, -+0x00, -+0x88, -+0x01, -+0x00, -+0x00, -+0x32, -+0x05, -+0x00, -+0x04, -+0x8c, -+0x01, -+0x00, -+0x00, -+0xa9, -+0x02, -+0xb8, -+0x00, -+0x90, -+0x01, -+0x00, -+0x00, -+0x00, -+0x01, -+0x40, -+0x00, -+0x94, -+0x01, -+0x00, -+0x00, -+0xeb, -+0x06, -+0x77, -+0x00, -+0x98, -+0x01, -+0x00, -+0x00, -+0x00, -+0x52, -+0x7b, -+0x50, -+0x9c, -+0x01, -+0x00, -+0x00, -+0x0b, -+0x06, -+0x04, -+0x10, -+0xa0, -+0x01, -+0x00, -+0x00, -+0x10, -+0x07, -+0x17, -+0x13, -+0xa4, -+0x01, -+0x00, -+0x00, -+0x07, -+0x74, -+0x70, -+0x00, -+0xa8, -+0x01, -+0x00, -+0x00, -+0x40, -+0x00, -+0x70, -+0x50, -+0xac, -+0x01, -+0x00, -+0x00, -+0x00, -+0x04, -+0x00, -+0xf0, -+0xb0, -+0x01, -+0x00, -+0x00, -+0x79, -+0x07, -+0x70, -+0x17, -+0xb4, -+0x01, -+0x00, -+0x00, -+0x70, -+0x07, -+0xf0, -+0x0f, -+0xb8, -+0x01, -+0x00, -+0x00, -+0x77, -+0xfc, -+0x03, -+0x3f, -+0xbc, -+0x01, -+0x00, -+0x00, -+0x00, -+0x31, -+0x10, -+0x00, -+0xc0, -+0x01, -+0x00, -+0x00, -+0x01, -+0x00, -+0x00, -+0xc0, -+0xc4, -+0x01, -+0x00, -+0x00, -+0x66, -+0x66, -+0x66, -+0x00, -+0xc8, -+0x01, -+0x00, -+0x00, -+0x01, -+0x00, -+0x00, -+0x11, -+0x37, -+0x3e, -+0xfc, -+0xdc, -+}; -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c -new file mode 100644 -index 000000000000..dd77589ef5a6 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.c -@@ -0,0 +1,1396 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements transmit related functions. */ -+ -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "hif/fwcmd.h" -+#include "hif/pcie/dev.h" -+#include "hif/pcie/tx.h" -+ -+#define MAX_NUM_TX_RING_BYTES (PCIE_MAX_NUM_TX_DESC * \ -+ sizeof(struct pcie_tx_desc)) -+ -+#define MAX_NUM_TX_HNDL_BYTES (PCIE_MAX_NUM_TX_DESC * \ -+ sizeof(struct pcie_tx_hndl)) -+ -+#define TOTAL_HW_QUEUES (SYSADPT_TX_WMM_QUEUES + \ -+ PCIE_AMPDU_QUEUES) -+ -+#define EAGLE_TXD_XMITCTRL_USE_MC_RATE 0x8 /* Use multicast data rate */ -+ -+#define MWL_QOS_ACK_POLICY_MASK 0x0060 -+#define MWL_QOS_ACK_POLICY_NORMAL 0x0000 -+#define MWL_QOS_ACK_POLICY_BLOCKACK 0x0060 -+ -+#define EXT_IV 0x20 -+#define INCREASE_IV(iv16, iv32) \ -+{ \ -+ (iv16)++; \ -+ if ((iv16) == 0) \ -+ (iv32)++; \ -+} -+ -+/* Transmission information to transmit a socket buffer. */ -+struct pcie_tx_ctrl { -+ void *vif; -+ void *sta; -+ void *k_conf; -+ void *amsdu_pkts; -+ u8 tx_priority; -+ u8 type; -+ u16 qos_ctrl; -+ u8 xmit_control; -+}; -+ -+struct ccmp_hdr { -+ __le16 iv16; -+ u8 rsvd; -+ u8 key_id; -+ __le32 iv32; -+} __packed; -+ -+static int pcie_tx_ring_alloc(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data *desc; -+ int num; -+ u8 *mem; -+ -+ desc = &pcie_priv->desc_data[0]; -+ -+ mem = dma_alloc_coherent(priv->dev, -+ MAX_NUM_TX_RING_BYTES * -+ PCIE_NUM_OF_DESC_DATA, -+ &desc->pphys_tx_ring, -+ GFP_KERNEL); -+ -+ if (!mem) { -+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); -+ return -ENOMEM; -+ } -+ -+ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { -+ desc = &pcie_priv->desc_data[num]; -+ -+ desc->ptx_ring = (struct pcie_tx_desc *) -+ (mem + num * MAX_NUM_TX_RING_BYTES); -+ -+ desc->pphys_tx_ring = (dma_addr_t) -+ ((u32)pcie_priv->desc_data[0].pphys_tx_ring + -+ num * MAX_NUM_TX_RING_BYTES); -+ -+ memset(desc->ptx_ring, 0x00, -+ MAX_NUM_TX_RING_BYTES); -+ } -+ -+ mem = kzalloc(MAX_NUM_TX_HNDL_BYTES * PCIE_NUM_OF_DESC_DATA, -+ GFP_KERNEL); -+ -+ if (!mem) { -+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); -+ dma_free_coherent(priv->dev, -+ MAX_NUM_TX_RING_BYTES * -+ PCIE_NUM_OF_DESC_DATA, -+ pcie_priv->desc_data[0].ptx_ring, -+ pcie_priv->desc_data[0].pphys_tx_ring); -+ return -ENOMEM; -+ } -+ -+ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { -+ desc = &pcie_priv->desc_data[num]; -+ -+ desc->tx_hndl = (struct pcie_tx_hndl *) -+ (mem + num * MAX_NUM_TX_HNDL_BYTES); -+ } -+ -+ return 0; -+} -+ -+static int pcie_txbd_ring_create(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num; -+ u8 *mem; -+ -+ /* driver maintaines the write pointer and firmware maintaines the read -+ * pointer. -+ */ -+ pcie_priv->txbd_wrptr = 0; -+ pcie_priv->txbd_rdptr = 0; -+ -+ /* allocate shared memory for the BD ring and divide the same in to -+ * several descriptors -+ */ -+ pcie_priv->txbd_ring_size = -+ sizeof(struct pcie_data_buf) * PCIE_MAX_TXRX_BD; -+ wiphy_info(priv->hw->wiphy, "TX ring: allocating %d bytes\n", -+ pcie_priv->txbd_ring_size); -+ -+ mem = dma_alloc_coherent(priv->dev, -+ pcie_priv->txbd_ring_size, -+ &pcie_priv->txbd_ring_pbase, -+ GFP_KERNEL); -+ -+ if (!mem) { -+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); -+ return -ENOMEM; -+ } -+ pcie_priv->txbd_ring_vbase = mem; -+ wiphy_info(priv->hw->wiphy, -+ "TX ring: - base: %p, pbase: 0x%x, len: %d\n", -+ pcie_priv->txbd_ring_vbase, -+ pcie_priv->txbd_ring_pbase, -+ pcie_priv->txbd_ring_size); -+ -+ for (num = 0; num < PCIE_MAX_TXRX_BD; num++) { -+ pcie_priv->txbd_ring[num] = -+ (struct pcie_data_buf *)(pcie_priv->txbd_ring_vbase + -+ (sizeof(struct pcie_data_buf) * num)); -+ pcie_priv->txbd_ring[num]->flags = 0; -+ pcie_priv->txbd_ring[num]->offset = 0; -+ pcie_priv->txbd_ring[num]->frag_len = 0; -+ pcie_priv->txbd_ring[num]->len = 0; -+ pcie_priv->txbd_ring[num]->paddr = 0; -+ pcie_priv->tx_buf_list[num] = NULL; -+ } -+ -+ return 0; -+} -+ -+static int pcie_tx_ring_init(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num, i; -+ struct pcie_desc_data *desc; -+ -+ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { -+ skb_queue_head_init(&pcie_priv->txq[num]); -+ pcie_priv->fw_desc_cnt[num] = 0; -+ -+ if (priv->chip_type == MWL8997) -+ continue; -+ -+ desc = &pcie_priv->desc_data[num]; -+ -+ if (desc->ptx_ring) { -+ for (i = 0; i < PCIE_MAX_NUM_TX_DESC; i++) { -+ desc->ptx_ring[i].status = -+ cpu_to_le32(EAGLE_TXD_STATUS_IDLE); -+ desc->ptx_ring[i].pphys_next = -+ cpu_to_le32((u32)desc->pphys_tx_ring + -+ ((i + 1) * -+ sizeof(struct pcie_tx_desc))); -+ desc->tx_hndl[i].pdesc = -+ &desc->ptx_ring[i]; -+ if (i < PCIE_MAX_NUM_TX_DESC - 1) -+ desc->tx_hndl[i].pnext = -+ &desc->tx_hndl[i + 1]; -+ } -+ desc->ptx_ring[PCIE_MAX_NUM_TX_DESC - 1].pphys_next = -+ cpu_to_le32((u32)desc->pphys_tx_ring); -+ desc->tx_hndl[PCIE_MAX_NUM_TX_DESC - 1].pnext = -+ &desc->tx_hndl[0]; -+ -+ desc->pstale_tx_hndl = &desc->tx_hndl[0]; -+ desc->pnext_tx_hndl = &desc->tx_hndl[0]; -+ } else { -+ wiphy_err(priv->hw->wiphy, "no valid TX mem\n"); -+ return -ENOMEM; -+ } -+ } -+ -+ return 0; -+} -+ -+static void pcie_tx_ring_cleanup(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int cleaned_tx_desc = 0; -+ int num, i; -+ struct pcie_desc_data *desc; -+ -+ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { -+ skb_queue_purge(&pcie_priv->txq[num]); -+ pcie_priv->fw_desc_cnt[num] = 0; -+ -+ if (priv->chip_type == MWL8997) -+ continue; -+ -+ desc = &pcie_priv->desc_data[num]; -+ -+ if (desc->ptx_ring) { -+ for (i = 0; i < PCIE_MAX_NUM_TX_DESC; i++) { -+ if (!desc->tx_hndl[i].psk_buff) -+ continue; -+ -+ wiphy_debug(priv->hw->wiphy, -+ "unmapped and free'd %i %p %x\n", -+ i, -+ desc->tx_hndl[i].psk_buff->data, -+ le32_to_cpu( -+ desc->ptx_ring[i].pkt_ptr)); -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu( -+ desc->ptx_ring[i].pkt_ptr), -+ desc->tx_hndl[i].psk_buff->len, -+ PCI_DMA_TODEVICE); -+ dev_kfree_skb_any(desc->tx_hndl[i].psk_buff); -+ desc->ptx_ring[i].status = -+ cpu_to_le32(EAGLE_TXD_STATUS_IDLE); -+ desc->ptx_ring[i].pkt_ptr = 0; -+ desc->ptx_ring[i].pkt_len = 0; -+ desc->tx_hndl[i].psk_buff = NULL; -+ cleaned_tx_desc++; -+ } -+ } -+ } -+ -+ wiphy_info(priv->hw->wiphy, "cleaned %i TX descr\n", cleaned_tx_desc); -+} -+ -+static void pcie_tx_ring_free(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num; -+ -+ if (pcie_priv->desc_data[0].ptx_ring) { -+ dma_free_coherent(priv->dev, -+ MAX_NUM_TX_RING_BYTES * -+ PCIE_NUM_OF_DESC_DATA, -+ pcie_priv->desc_data[0].ptx_ring, -+ pcie_priv->desc_data[0].pphys_tx_ring); -+ } -+ -+ for (num = 0; num < PCIE_NUM_OF_DESC_DATA; num++) { -+ if (pcie_priv->desc_data[num].ptx_ring) -+ pcie_priv->desc_data[num].ptx_ring = NULL; -+ pcie_priv->desc_data[num].pstale_tx_hndl = NULL; -+ pcie_priv->desc_data[num].pnext_tx_hndl = NULL; -+ } -+ -+ kfree(pcie_priv->desc_data[0].tx_hndl); -+} -+ -+static void pcie_txbd_ring_delete(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct sk_buff *skb; -+ struct pcie_tx_desc *tx_desc; -+ int num; -+ -+ if (pcie_priv->txbd_ring_vbase) { -+ dma_free_coherent(priv->dev, -+ pcie_priv->txbd_ring_size, -+ pcie_priv->txbd_ring_vbase, -+ pcie_priv->txbd_ring_pbase); -+ } -+ -+ for (num = 0; num < PCIE_MAX_TXRX_BD; num++) { -+ pcie_priv->txbd_ring[num] = NULL; -+ if (pcie_priv->tx_buf_list[num]) { -+ skb = pcie_priv->tx_buf_list[num]; -+ tx_desc = (struct pcie_tx_desc *)skb->data; -+ -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu(tx_desc->pkt_ptr), -+ skb->len, -+ PCI_DMA_TODEVICE); -+ dev_kfree_skb_any(skb); -+ } -+ pcie_priv->tx_buf_list[num] = NULL; -+ } -+ -+ pcie_priv->txbd_wrptr = 0; -+ pcie_priv->txbd_rdptr = 0; -+ pcie_priv->txbd_ring_size = 0; -+ pcie_priv->txbd_ring_vbase = NULL; -+ pcie_priv->txbd_ring_pbase = 0; -+} -+ -+static inline void pcie_tx_add_ccmp_hdr(u8 *pccmp_hdr, -+ u8 key_id, u16 iv16, u32 iv32) -+{ -+ struct ccmp_hdr *ccmp_h = (struct ccmp_hdr *)pccmp_hdr; -+ -+ ccmp_h->iv16 = cpu_to_le16(iv16); -+ ccmp_h->rsvd = 0; -+ ccmp_h->key_id = EXT_IV | (key_id << 6); -+ ccmp_h->iv32 = cpu_to_le32(iv32); -+} -+ -+static inline bool pcie_tx_available(struct mwl_priv *priv, int desc_num) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_tx_hndl *tx_hndl; -+ -+ if (priv->chip_type == MWL8997) -+ return PCIE_TXBD_NOT_FULL(pcie_priv->txbd_wrptr, -+ pcie_priv->txbd_rdptr); -+ -+ tx_hndl = pcie_priv->desc_data[desc_num].pnext_tx_hndl; -+ -+ if (!tx_hndl->pdesc) -+ return false; -+ -+ if (tx_hndl->pdesc->status != EAGLE_TXD_STATUS_IDLE) { -+ /* Interrupt F/W anyway */ -+ if (tx_hndl->pdesc->status & -+ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)) -+ writel(MACREG_H2ARIC_BIT_PPA_READY, -+ pcie_priv->iobase1 + -+ MACREG_REG_H2A_INTERRUPT_EVENTS); -+ return false; -+ } -+ -+ return true; -+} -+ -+static inline void pcie_tx_skb(struct mwl_priv *priv, int desc_num, -+ struct sk_buff *tx_skb) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct pcie_tx_hndl *tx_hndl = NULL; -+ struct pcie_tx_desc *tx_desc; -+ struct ieee80211_sta *sta; -+ struct ieee80211_vif *vif; -+ struct mwl_vif *mwl_vif; -+ struct ieee80211_key_conf *k_conf; -+ bool ccmp = false; -+ struct pcie_pfu_dma_data *pfu_dma_data; -+ struct pcie_dma_data *dma_data; -+ struct ieee80211_hdr *wh; -+ dma_addr_t dma; -+ -+ if (WARN_ON(!tx_skb)) -+ return; -+ -+ tx_info = IEEE80211_SKB_CB(tx_skb); -+ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ sta = (struct ieee80211_sta *)tx_ctrl->sta; -+ vif = (struct ieee80211_vif *)tx_ctrl->vif; -+ mwl_vif = mwl_dev_get_vif(vif); -+ k_conf = (struct ieee80211_key_conf *)tx_ctrl->k_conf; -+ -+ pcie_tx_encapsulate_frame(priv, tx_skb, k_conf, &ccmp); -+ -+ if (priv->chip_type == MWL8997) { -+ pfu_dma_data = (struct pcie_pfu_dma_data *)tx_skb->data; -+ tx_desc = &pfu_dma_data->tx_desc; -+ dma_data = &pfu_dma_data->dma_data; -+ } else { -+ tx_hndl = pcie_priv->desc_data[desc_num].pnext_tx_hndl; -+ tx_hndl->psk_buff = tx_skb; -+ tx_desc = tx_hndl->pdesc; -+ dma_data = (struct pcie_dma_data *)tx_skb->data; -+ } -+ wh = &dma_data->wh; -+ -+ if (ieee80211_is_probe_resp(wh->frame_control) && -+ priv->dump_probe) -+ wiphy_info(priv->hw->wiphy, -+ "Probe Resp: %pM\n", wh->addr1); -+ -+ if (ieee80211_is_data(wh->frame_control) || -+ (ieee80211_is_mgmt(wh->frame_control) && -+ ieee80211_has_protected(wh->frame_control) && -+ !is_multicast_ether_addr(wh->addr1))) { -+ if (is_multicast_ether_addr(wh->addr1)) { -+ if (ccmp) { -+ pcie_tx_add_ccmp_hdr(dma_data->data, -+ mwl_vif->keyidx, -+ mwl_vif->iv16, -+ mwl_vif->iv32); -+ INCREASE_IV(mwl_vif->iv16, mwl_vif->iv32); -+ } -+ } else { -+ if (ccmp) { -+ if (vif->type == NL80211_IFTYPE_STATION) { -+ pcie_tx_add_ccmp_hdr(dma_data->data, -+ mwl_vif->keyidx, -+ mwl_vif->iv16, -+ mwl_vif->iv32); -+ INCREASE_IV(mwl_vif->iv16, -+ mwl_vif->iv32); -+ } else { -+ struct mwl_sta *sta_info; -+ -+ sta_info = mwl_dev_get_sta(sta); -+ -+ pcie_tx_add_ccmp_hdr(dma_data->data, -+ 0, -+ sta_info->iv16, -+ sta_info->iv32); -+ INCREASE_IV(sta_info->iv16, -+ sta_info->iv32); -+ } -+ } -+ } -+ } -+ -+ if (tx_info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT) -+ tx_desc->flags |= PCIE_TX_WCB_FLAGS_DONT_ENCRYPT; -+ if (tx_info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) -+ tx_desc->flags |= PCIE_TX_WCB_FLAGS_NO_CCK_RATE; -+ tx_desc->tx_priority = tx_ctrl->tx_priority; -+ tx_desc->qos_ctrl = cpu_to_le16(tx_ctrl->qos_ctrl); -+ tx_desc->pkt_len = cpu_to_le16(tx_skb->len); -+ tx_desc->packet_info = 0; -+ tx_desc->data_rate = 0; -+ tx_desc->type = tx_ctrl->type; -+ tx_desc->xmit_control = tx_ctrl->xmit_control; -+ tx_desc->sap_pkt_info = 0; -+ dma = pci_map_single(pcie_priv->pdev, tx_skb->data, -+ tx_skb->len, PCI_DMA_TODEVICE); -+ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { -+ dev_kfree_skb_any(tx_skb); -+ wiphy_err(priv->hw->wiphy, -+ "failed to map pci memory!\n"); -+ return; -+ } -+ if (priv->chip_type == MWL8997) -+ tx_desc->pkt_ptr = cpu_to_le32(sizeof(struct pcie_tx_desc)); -+ else -+ tx_desc->pkt_ptr = cpu_to_le32(dma); -+ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED); -+ /* make sure all the memory transactions done by cpu were completed */ -+ wmb(); /*Data Memory Barrier*/ -+ -+ if (priv->chip_type == MWL8997) { -+ u32 wrindx; -+ struct pcie_data_buf *data_buf; -+ const u32 num_tx_buffs = PCIE_MAX_TXRX_BD << PCIE_TX_START_PTR; -+ -+ wrindx = (pcie_priv->txbd_wrptr & PCIE_TXBD_MASK) >> -+ PCIE_TX_START_PTR; -+ pcie_priv->tx_buf_list[wrindx] = tx_skb; -+ data_buf = pcie_priv->txbd_ring[wrindx]; -+ data_buf->paddr = cpu_to_le64(dma); -+ data_buf->len = cpu_to_le16(tx_skb->len); -+ data_buf->flags = cpu_to_le16(PCIE_BD_FLAG_FIRST_DESC | -+ PCIE_BD_FLAG_LAST_DESC); -+ data_buf->frag_len = cpu_to_le16(tx_skb->len); -+ data_buf->offset = 0; -+ pcie_priv->txbd_wrptr += PCIE_BD_FLAG_TX_START_PTR; -+ -+ if ((pcie_priv->txbd_wrptr & PCIE_TXBD_MASK) == num_tx_buffs) -+ pcie_priv->txbd_wrptr = ((pcie_priv->txbd_wrptr & -+ PCIE_BD_FLAG_TX_ROLLOVER_IND) ^ -+ PCIE_BD_FLAG_TX_ROLLOVER_IND); -+ -+ /* Write the TX ring write pointer in to REG_TXBD_WRPTR */ -+ writel(pcie_priv->txbd_wrptr, -+ pcie_priv->iobase1 + REG_TXBD_WRPTR); -+ } else { -+ writel(MACREG_H2ARIC_BIT_PPA_READY, -+ pcie_priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); -+ pcie_priv->desc_data[desc_num].pnext_tx_hndl = tx_hndl->pnext; -+ pcie_priv->fw_desc_cnt[desc_num]++; -+ } -+} -+ -+static inline -+struct sk_buff *pcie_tx_do_amsdu(struct mwl_priv *priv, -+ int desc_num, -+ struct sk_buff *tx_skb, -+ struct ieee80211_tx_info *tx_info) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct ieee80211_sta *sta; -+ struct mwl_sta *sta_info; -+ struct pcie_tx_ctrl *tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ struct ieee80211_tx_info *amsdu_info; -+ struct sk_buff_head *amsdu_pkts; -+ struct mwl_amsdu_frag *amsdu; -+ int amsdu_allow_size; -+ struct ieee80211_hdr *wh; -+ int wh_len; -+ u16 len; -+ u8 *data; -+ -+ sta = (struct ieee80211_sta *)tx_ctrl->sta; -+ sta_info = mwl_dev_get_sta(sta); -+ -+ if (!sta_info->is_amsdu_allowed) -+ return tx_skb; -+ -+ wh = (struct ieee80211_hdr *)tx_skb->data; -+ if (sta_info->is_mesh_node && is_multicast_ether_addr(wh->addr3)) -+ return tx_skb; -+ -+ if (ieee80211_is_qos_nullfunc(wh->frame_control)) -+ return tx_skb; -+ -+ if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_4K) -+ amsdu_allow_size = SYSADPT_AMSDU_4K_MAX_SIZE; -+ else if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_8K) -+ amsdu_allow_size = SYSADPT_AMSDU_8K_MAX_SIZE; -+ else -+ return tx_skb; -+ -+ spin_lock_bh(&sta_info->amsdu_lock); -+ amsdu = &sta_info->amsdu_ctrl.frag[desc_num]; -+ -+ if ((tx_skb->len > SYSADPT_AMSDU_ALLOW_SIZE) || -+ utils_is_non_amsdu_packet(tx_skb->data, true)) { -+ if (amsdu->num) { -+ pcie_tx_skb(priv, desc_num, amsdu->skb); -+ amsdu->num = 0; -+ amsdu->cur_pos = NULL; -+ } -+ spin_unlock_bh(&sta_info->amsdu_lock); -+ return tx_skb; -+ } -+ -+ /* potential amsdu size, should add amsdu header 14 bytes + -+ * maximum padding 3. -+ */ -+ wh_len = ieee80211_hdrlen(wh->frame_control); -+ len = tx_skb->len - wh_len + 17; -+ -+ if (amsdu->num) { -+ if ((amsdu->skb->len + len) > amsdu_allow_size) { -+ pcie_tx_skb(priv, desc_num, amsdu->skb); -+ amsdu->num = 0; -+ amsdu->cur_pos = NULL; -+ } -+ } -+ -+ amsdu->jiffies = jiffies; -+ len = tx_skb->len - wh_len; -+ -+ if (amsdu->num == 0) { -+ struct sk_buff *newskb; -+ int headroom; -+ -+ amsdu_pkts = (struct sk_buff_head *) -+ kmalloc(sizeof(*amsdu_pkts), GFP_ATOMIC); -+ if (!amsdu_pkts) { -+ spin_unlock_bh(&sta_info->amsdu_lock); -+ return tx_skb; -+ } -+ newskb = dev_alloc_skb(amsdu_allow_size + -+ pcie_priv->tx_head_room); -+ if (!newskb) { -+ spin_unlock_bh(&sta_info->amsdu_lock); -+ kfree(amsdu_pkts); -+ return tx_skb; -+ } -+ -+ headroom = skb_headroom(newskb); -+ if (headroom < pcie_priv->tx_head_room) -+ skb_reserve(newskb, -+ (pcie_priv->tx_head_room - headroom)); -+ -+ data = newskb->data; -+ memcpy(data, tx_skb->data, wh_len); -+ if (sta_info->is_mesh_node) { -+ ether_addr_copy(data + wh_len, wh->addr3); -+ ether_addr_copy(data + wh_len + ETH_ALEN, wh->addr4); -+ } else { -+ ether_addr_copy(data + wh_len, -+ ieee80211_get_DA(wh)); -+ ether_addr_copy(data + wh_len + ETH_ALEN, -+ ieee80211_get_SA(wh)); -+ } -+ *(u8 *)(data + wh_len + ETH_HLEN - 1) = len & 0xff; -+ *(u8 *)(data + wh_len + ETH_HLEN - 2) = (len >> 8) & 0xff; -+ memcpy(data + wh_len + ETH_HLEN, tx_skb->data + wh_len, len); -+ -+ skb_put(newskb, tx_skb->len + ETH_HLEN); -+ tx_ctrl->qos_ctrl |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; -+ amsdu_info = IEEE80211_SKB_CB(newskb); -+ memcpy(amsdu_info, tx_info, sizeof(*tx_info)); -+ skb_queue_head_init(amsdu_pkts); -+ ((struct pcie_tx_ctrl *)&amsdu_info->status)->amsdu_pkts = -+ (void *)amsdu_pkts; -+ amsdu->skb = newskb; -+ } else { -+ amsdu->cur_pos += amsdu->pad; -+ data = amsdu->cur_pos; -+ -+ if (sta_info->is_mesh_node) { -+ ether_addr_copy(data, wh->addr3); -+ ether_addr_copy(data + ETH_ALEN, wh->addr4); -+ } else { -+ ether_addr_copy(data, ieee80211_get_DA(wh)); -+ ether_addr_copy(data + ETH_ALEN, ieee80211_get_SA(wh)); -+ } -+ *(u8 *)(data + ETH_HLEN - 1) = len & 0xff; -+ *(u8 *)(data + ETH_HLEN - 2) = (len >> 8) & 0xff; -+ memcpy(data + ETH_HLEN, tx_skb->data + wh_len, len); -+ -+ skb_put(amsdu->skb, len + ETH_HLEN + amsdu->pad); -+ amsdu_info = IEEE80211_SKB_CB(amsdu->skb); -+ amsdu_pkts = (struct sk_buff_head *) -+ ((struct pcie_tx_ctrl *) -+ &amsdu_info->status)->amsdu_pkts; -+ } -+ -+ amsdu->num++; -+ amsdu->pad = ((len + ETH_HLEN) % 4) ? (4 - (len + ETH_HLEN) % 4) : 0; -+ amsdu->cur_pos = amsdu->skb->data + amsdu->skb->len; -+ skb_queue_tail(amsdu_pkts, tx_skb); -+ -+ if (amsdu->num > SYSADPT_AMSDU_PACKET_THRESHOLD) { -+ amsdu->num = 0; -+ amsdu->cur_pos = NULL; -+ spin_unlock_bh(&sta_info->amsdu_lock); -+ return amsdu->skb; -+ } -+ -+ spin_unlock_bh(&sta_info->amsdu_lock); -+ return NULL; -+} -+ -+static inline void pcie_tx_ack_amsdu_pkts(struct ieee80211_hw *hw, u32 rate, -+ struct sk_buff_head *amsdu_pkts) -+{ -+ struct sk_buff *amsdu_pkt; -+ struct ieee80211_tx_info *info; -+ -+ while (skb_queue_len(amsdu_pkts) > 0) { -+ amsdu_pkt = skb_dequeue(amsdu_pkts); -+ info = IEEE80211_SKB_CB(amsdu_pkt); -+ pcie_tx_prepare_info(hw->priv, rate, info); -+ ieee80211_tx_status(hw, amsdu_pkt); -+ } -+ -+ kfree(amsdu_pkts); -+} -+ -+static void pcie_pfu_tx_done(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ u32 wrdoneidx, rdptr; -+ const u32 num_tx_buffs = PCIE_MAX_TXRX_BD << PCIE_TX_START_PTR; -+ struct pcie_data_buf *data_buf; -+ struct sk_buff *done_skb; -+ struct pcie_pfu_dma_data *pfu_dma; -+ struct pcie_tx_desc *tx_desc; -+ struct pcie_dma_data *dma_data; -+ struct ieee80211_hdr *wh; -+ struct ieee80211_tx_info *info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct ieee80211_sta *sta; -+ struct mwl_sta *sta_info; -+ u32 rate = 0; -+ struct sk_buff_head *amsdu_pkts; -+ int hdrlen; -+ -+ spin_lock_bh(&pcie_priv->tx_desc_lock); -+ /* Read the TX ring read pointer set by firmware */ -+ rdptr = readl(pcie_priv->iobase1 + REG_TXBD_RDPTR); -+ /* free from previous txbd_rdptr to current txbd_rdptr */ -+ while (((pcie_priv->txbd_rdptr & PCIE_TXBD_MASK) != -+ (rdptr & PCIE_TXBD_MASK)) || -+ ((pcie_priv->txbd_rdptr & PCIE_BD_FLAG_TX_ROLLOVER_IND) != -+ (rdptr & PCIE_BD_FLAG_TX_ROLLOVER_IND))) { -+ wrdoneidx = pcie_priv->txbd_rdptr & PCIE_TXBD_MASK; -+ wrdoneidx >>= PCIE_TX_START_PTR; -+ -+ data_buf = pcie_priv->txbd_ring[wrdoneidx]; -+ done_skb = pcie_priv->tx_buf_list[wrdoneidx]; -+ if (done_skb) { -+ pfu_dma = (struct pcie_pfu_dma_data *)done_skb->data; -+ tx_desc = &pfu_dma->tx_desc; -+ dma_data = &pfu_dma->dma_data; -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu(data_buf->paddr), -+ le16_to_cpu(data_buf->len), -+ PCI_DMA_TODEVICE); -+ tx_desc->pkt_ptr = 0; -+ tx_desc->pkt_len = 0; -+ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_IDLE); -+ wmb(); /* memory barrier */ -+ -+ wh = &dma_data->wh; -+ if (ieee80211_is_nullfunc(wh->frame_control) || -+ ieee80211_is_qos_nullfunc(wh->frame_control)) { -+ dev_kfree_skb_any(done_skb); -+ done_skb = NULL; -+ goto next; -+ } -+ -+ info = IEEE80211_SKB_CB(done_skb); -+ tx_ctrl = (struct pcie_tx_ctrl *)&info->status; -+ sta = (struct ieee80211_sta *)tx_ctrl->sta; -+ if (sta) { -+ sta_info = mwl_dev_get_sta(sta); -+ rate = sta_info->tx_rate_info; -+ } -+ -+ if (ieee80211_is_data(wh->frame_control) || -+ ieee80211_is_data_qos(wh->frame_control)) { -+ amsdu_pkts = (struct sk_buff_head *) -+ tx_ctrl->amsdu_pkts; -+ if (amsdu_pkts) { -+ pcie_tx_ack_amsdu_pkts(priv->hw, rate, -+ amsdu_pkts); -+ dev_kfree_skb_any(done_skb); -+ done_skb = NULL; -+ } else { -+ pcie_tx_prepare_info(priv, rate, info); -+ } -+ } else { -+ pcie_tx_prepare_info(priv, 0, info); -+ } -+ -+ if (done_skb) { -+ /* Remove H/W dma header */ -+ hdrlen = ieee80211_hdrlen( -+ dma_data->wh.frame_control); -+ memmove(dma_data->data - hdrlen, -+ &dma_data->wh, hdrlen); -+ skb_pull(done_skb, sizeof(*pfu_dma) - hdrlen); -+ ieee80211_tx_status(priv->hw, done_skb); -+ } -+ } -+next: -+ memset(data_buf, 0, sizeof(*data_buf)); -+ pcie_priv->tx_buf_list[wrdoneidx] = NULL; -+ -+ pcie_priv->txbd_rdptr += PCIE_BD_FLAG_TX_START_PTR; -+ if ((pcie_priv->txbd_rdptr & PCIE_TXBD_MASK) == num_tx_buffs) -+ pcie_priv->txbd_rdptr = ((pcie_priv->txbd_rdptr & -+ PCIE_BD_FLAG_TX_ROLLOVER_IND) ^ -+ PCIE_BD_FLAG_TX_ROLLOVER_IND); -+ } -+ spin_unlock_bh(&pcie_priv->tx_desc_lock); -+ -+ if (pcie_priv->is_tx_done_schedule) { -+ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_TX_DONE, true); -+ tasklet_schedule(&pcie_priv->tx_task); -+ pcie_priv->is_tx_done_schedule = false; -+ } -+} -+ -+static void pcie_non_pfu_tx_done(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num; -+ struct pcie_desc_data *desc; -+ struct pcie_tx_hndl *tx_hndl; -+ struct pcie_tx_desc *tx_desc; -+ struct sk_buff *done_skb; -+ int idx; -+ u32 rate; -+ struct pcie_dma_data *dma_data; -+ struct ieee80211_hdr *wh; -+ struct ieee80211_tx_info *info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct sk_buff_head *amsdu_pkts; -+ int hdrlen; -+ -+ spin_lock_bh(&pcie_priv->tx_desc_lock); -+ for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) { -+ desc = &pcie_priv->desc_data[num]; -+ tx_hndl = desc->pstale_tx_hndl; -+ tx_desc = tx_hndl->pdesc; -+ -+ if ((tx_desc->status & -+ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)) && -+ (tx_hndl->pnext->pdesc->status & -+ cpu_to_le32(EAGLE_TXD_STATUS_OK))) -+ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_OK); -+ -+ while (tx_hndl && -+ (tx_desc->status & cpu_to_le32(EAGLE_TXD_STATUS_OK)) && -+ (!(tx_desc->status & -+ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)))) { -+ pci_unmap_single(pcie_priv->pdev, -+ le32_to_cpu(tx_desc->pkt_ptr), -+ le16_to_cpu(tx_desc->pkt_len), -+ PCI_DMA_TODEVICE); -+ done_skb = tx_hndl->psk_buff; -+ rate = le32_to_cpu(tx_desc->rate_info); -+ tx_desc->pkt_ptr = 0; -+ tx_desc->pkt_len = 0; -+ tx_desc->status = -+ cpu_to_le32(EAGLE_TXD_STATUS_IDLE); -+ tx_hndl->psk_buff = NULL; -+ wmb(); /*Data Memory Barrier*/ -+ -+ skb_get(done_skb); -+ idx = pcie_priv->delay_q_idx; -+ if (pcie_priv->delay_q[idx]) -+ dev_kfree_skb_any(pcie_priv->delay_q[idx]); -+ pcie_priv->delay_q[idx] = done_skb; -+ idx++; -+ if (idx >= PCIE_DELAY_FREE_Q_LIMIT) -+ idx = 0; -+ pcie_priv->delay_q_idx = idx; -+ -+ dma_data = (struct pcie_dma_data *)done_skb->data; -+ wh = &dma_data->wh; -+ if (ieee80211_is_nullfunc(wh->frame_control) || -+ ieee80211_is_qos_nullfunc(wh->frame_control)) { -+ dev_kfree_skb_any(done_skb); -+ done_skb = NULL; -+ goto next; -+ } -+ -+ info = IEEE80211_SKB_CB(done_skb); -+ if (ieee80211_is_data(wh->frame_control) || -+ ieee80211_is_data_qos(wh->frame_control)) { -+ tx_ctrl = (struct pcie_tx_ctrl *)&info->status; -+ amsdu_pkts = (struct sk_buff_head *) -+ tx_ctrl->amsdu_pkts; -+ if (amsdu_pkts) { -+ pcie_tx_ack_amsdu_pkts(priv->hw, rate, -+ amsdu_pkts); -+ dev_kfree_skb_any(done_skb); -+ done_skb = NULL; -+ } else { -+ pcie_tx_prepare_info(priv, rate, info); -+ } -+ } else { -+ pcie_tx_prepare_info(priv, 0, info); -+ } -+ -+ if (done_skb) { -+ /* Remove H/W dma header */ -+ hdrlen = ieee80211_hdrlen( -+ dma_data->wh.frame_control); -+ memmove(dma_data->data - hdrlen, -+ &dma_data->wh, hdrlen); -+ skb_pull(done_skb, sizeof(*dma_data) - hdrlen); -+ ieee80211_tx_status(priv->hw, done_skb); -+ } -+next: -+ tx_hndl = tx_hndl->pnext; -+ tx_desc = tx_hndl->pdesc; -+ pcie_priv->fw_desc_cnt[num]--; -+ } -+ -+ desc->pstale_tx_hndl = tx_hndl; -+ } -+ spin_unlock_bh(&pcie_priv->tx_desc_lock); -+ -+ if (pcie_priv->is_tx_done_schedule) { -+ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_TX_DONE, true); -+ tasklet_schedule(&pcie_priv->tx_task); -+ pcie_priv->is_tx_done_schedule = false; -+ } -+} -+ -+int pcie_tx_init(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int rc; -+ int i; -+ -+ if (priv->chip_type == MWL8997) -+ rc = pcie_txbd_ring_create(priv); -+ else -+ rc = pcie_tx_ring_alloc(priv); -+ -+ if (rc) { -+ wiphy_err(hw->wiphy, "allocating TX ring failed\n"); -+ return rc; -+ } -+ -+ rc = pcie_tx_ring_init(priv); -+ if (rc) { -+ pcie_tx_ring_free(priv); -+ wiphy_err(hw->wiphy, "initializing TX ring failed\n"); -+ return rc; -+ } -+ -+ pcie_priv->delay_q_idx = 0; -+ for (i = 0; i < PCIE_DELAY_FREE_Q_LIMIT; i++) -+ pcie_priv->delay_q[i] = NULL; -+ -+ return 0; -+} -+ -+void pcie_tx_deinit(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int i; -+ -+ for (i = 0; i < PCIE_DELAY_FREE_Q_LIMIT; i++) -+ if (pcie_priv->delay_q[i]) -+ dev_kfree_skb_any(pcie_priv->delay_q[i]); -+ -+ pcie_tx_ring_cleanup(priv); -+ -+ if (priv->chip_type == MWL8997) -+ pcie_txbd_ring_delete(priv); -+ else -+ pcie_tx_ring_free(priv); -+} -+ -+void pcie_tx_skbs(unsigned long data) -+{ -+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num = SYSADPT_TX_WMM_QUEUES; -+ struct sk_buff *tx_skb; -+ -+ spin_lock_bh(&pcie_priv->tx_desc_lock); -+ while (num--) { -+ while (skb_queue_len(&pcie_priv->txq[num]) > 0) { -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ -+ if (!pcie_tx_available(priv, num)) -+ break; -+ -+ tx_skb = skb_dequeue(&pcie_priv->txq[num]); -+ if (!tx_skb) -+ continue; -+ tx_info = IEEE80211_SKB_CB(tx_skb); -+ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ -+ if (tx_ctrl->tx_priority >= SYSADPT_TX_WMM_QUEUES) -+ tx_skb = pcie_tx_do_amsdu(priv, num, -+ tx_skb, tx_info); -+ -+ if (tx_skb) { -+ if (pcie_tx_available(priv, num)) -+ pcie_tx_skb(priv, num, tx_skb); -+ else -+ skb_queue_head(&pcie_priv->txq[num], -+ tx_skb); -+ } -+ } -+ -+ if (skb_queue_len(&pcie_priv->txq[num]) < -+ pcie_priv->txq_wake_threshold) { -+ int queue; -+ -+ queue = SYSADPT_TX_WMM_QUEUES - num - 1; -+ if (ieee80211_queue_stopped(hw, queue)) -+ ieee80211_wake_queue(hw, queue); -+ } -+ } -+ spin_unlock_bh(&pcie_priv->tx_desc_lock); -+} -+ -+void pcie_tx_flush_amsdu(unsigned long data) -+{ -+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct mwl_sta *sta_info; -+ int i; -+ struct mwl_amsdu_frag *amsdu_frag; -+ -+ spin_lock(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ spin_lock(&pcie_priv->tx_desc_lock); -+ spin_lock(&sta_info->amsdu_lock); -+ for (i = 0; i < SYSADPT_TX_WMM_QUEUES; i++) { -+ amsdu_frag = &sta_info->amsdu_ctrl.frag[i]; -+ if (amsdu_frag->num) { -+ if (time_after(jiffies, -+ (amsdu_frag->jiffies + 1))) { -+ if (pcie_tx_available(priv, i)) { -+ pcie_tx_skb(priv, i, -+ amsdu_frag->skb); -+ amsdu_frag->num = 0; -+ amsdu_frag->cur_pos = NULL; -+ } -+ } -+ } -+ } -+ spin_unlock(&sta_info->amsdu_lock); -+ spin_unlock(&pcie_priv->tx_desc_lock); -+ } -+ spin_unlock(&priv->sta_lock); -+ -+ pcie_mask_int(pcie_priv, MACREG_A2HRIC_BIT_QUE_EMPTY, true); -+ pcie_priv->is_qe_schedule = false; -+} -+ -+void pcie_tx_done(unsigned long data) -+{ -+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; -+ struct mwl_priv *priv = hw->priv; -+ -+ if (priv->chip_type == MWL8997) -+ pcie_pfu_tx_done(priv); -+ else -+ pcie_non_pfu_tx_done(priv); -+} -+ -+void pcie_tx_xmit(struct ieee80211_hw *hw, -+ struct ieee80211_tx_control *control, -+ struct sk_buff *skb) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int index; -+ struct ieee80211_sta *sta; -+ struct ieee80211_tx_info *tx_info; -+ struct mwl_vif *mwl_vif; -+ struct ieee80211_hdr *wh; -+ u8 xmitcontrol; -+ u16 qos; -+ int txpriority; -+ u8 tid = 0; -+ struct mwl_ampdu_stream *stream = NULL; -+ bool start_ba_session = false; -+ bool mgmtframe = false; -+ struct ieee80211_mgmt *mgmt; -+ bool eapol_frame = false; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct ieee80211_key_conf *k_conf = NULL; -+ int rc; -+ -+ index = skb_get_queue_mapping(skb); -+ sta = control->sta; -+ -+ wh = (struct ieee80211_hdr *)skb->data; -+ tx_info = IEEE80211_SKB_CB(skb); -+ mwl_vif = mwl_dev_get_vif(tx_info->control.vif); -+ -+ if (ieee80211_is_data_qos(wh->frame_control)) -+ qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); -+ else -+ qos = 0; -+ -+ if (ieee80211_is_mgmt(wh->frame_control)) { -+ mgmtframe = true; -+ mgmt = (struct ieee80211_mgmt *)skb->data; -+ } else { -+ u16 pkt_type; -+ struct mwl_sta *sta_info; -+ -+ pkt_type = be16_to_cpu(*((__be16 *) -+ &skb->data[ieee80211_hdrlen(wh->frame_control) + 6])); -+ if (pkt_type == ETH_P_PAE) { -+ index = IEEE80211_AC_VO; -+ eapol_frame = true; -+ } -+ if (sta) { -+ if (mwl_vif->is_hw_crypto_enabled) { -+ sta_info = mwl_dev_get_sta(sta); -+ if (!sta_info->is_key_set && !eapol_frame) { -+ dev_kfree_skb_any(skb); -+ return; -+ } -+ } -+ } -+ } -+ -+ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { -+ wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); -+ wh->seq_ctrl |= cpu_to_le16(mwl_vif->seqno); -+ mwl_vif->seqno += 0x10; -+ } -+ -+ /* Setup firmware control bit fields for each frame type. */ -+ xmitcontrol = 0; -+ -+ if (mgmtframe || ieee80211_is_ctl(wh->frame_control)) { -+ qos = 0; -+ } else if (ieee80211_is_data(wh->frame_control)) { -+ qos &= ~MWL_QOS_ACK_POLICY_MASK; -+ -+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { -+ xmitcontrol &= 0xfb; -+ qos |= MWL_QOS_ACK_POLICY_BLOCKACK; -+ } else { -+ xmitcontrol |= 0x4; -+ qos |= MWL_QOS_ACK_POLICY_NORMAL; -+ } -+ -+ if (is_multicast_ether_addr(wh->addr1) || eapol_frame) -+ xmitcontrol |= EAGLE_TXD_XMITCTRL_USE_MC_RATE; -+ } -+ -+ k_conf = tx_info->control.hw_key; -+ -+ /* Queue ADDBA request in the respective data queue. While setting up -+ * the ampdu stream, mac80211 queues further packets for that -+ * particular ra/tid pair. However, packets piled up in the hardware -+ * for that ra/tid pair will still go out. ADDBA request and the -+ * related data packets going out from different queues asynchronously -+ * will cause a shift in the receiver window which might result in -+ * ampdu packets getting dropped at the receiver after the stream has -+ * been setup. -+ */ -+ if (mgmtframe) { -+ u16 capab; -+ -+ if (unlikely(ieee80211_is_action(wh->frame_control) && -+ mgmt->u.action.category == WLAN_CATEGORY_BACK && -+ mgmt->u.action.u.addba_req.action_code == -+ WLAN_ACTION_ADDBA_REQ)) { -+ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); -+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; -+ index = utils_tid_to_ac(tid); -+ } -+ -+ if (unlikely(ieee80211_is_assoc_req(wh->frame_control))) -+ utils_add_basic_rates(hw->conf.chandef.chan->band, skb); -+ } -+ -+ index = SYSADPT_TX_WMM_QUEUES - index - 1; -+ txpriority = index; -+ -+ if (sta && sta->ht_cap.ht_supported && !eapol_frame && -+ ieee80211_is_data_qos(wh->frame_control)) { -+ tid = qos & 0xf; -+ pcie_tx_count_packet(sta, tid); -+ -+ spin_lock_bh(&priv->stream_lock); -+ stream = mwl_fwcmd_lookup_stream(hw, sta, tid); -+ -+ if (stream) { -+ if (stream->state == AMPDU_STREAM_ACTIVE) { -+ if (WARN_ON(!(qos & -+ MWL_QOS_ACK_POLICY_BLOCKACK))) { -+ spin_unlock_bh(&priv->stream_lock); -+ dev_kfree_skb_any(skb); -+ return; -+ } -+ -+ txpriority = -+ (SYSADPT_TX_WMM_QUEUES + stream->idx) % -+ TOTAL_HW_QUEUES; -+ } else if (stream->state == AMPDU_STREAM_NEW) { -+ /* We get here if the driver sends us packets -+ * after we've initiated a stream, but before -+ * our ampdu_action routine has been called -+ * with IEEE80211_AMPDU_TX_START to get the SSN -+ * for the ADDBA request. So this packet can -+ * go out with no risk of sequence number -+ * mismatch. No special handling is required. -+ */ -+ } else { -+ /* Drop packets that would go out after the -+ * ADDBA request was sent but before the ADDBA -+ * response is received. If we don't do this, -+ * the recipient would probably receive it -+ * after the ADDBA request with SSN 0. This -+ * will cause the recipient's BA receive window -+ * to shift, which would cause the subsequent -+ * packets in the BA stream to be discarded. -+ * mac80211 queues our packets for us in this -+ * case, so this is really just a safety check. -+ */ -+ wiphy_warn(hw->wiphy, -+ "can't send packet during ADDBA\n"); -+ spin_unlock_bh(&priv->stream_lock); -+ dev_kfree_skb_any(skb); -+ return; -+ } -+ } else { -+ if (mwl_fwcmd_ampdu_allowed(sta, tid)) { -+ stream = mwl_fwcmd_add_stream(hw, sta, tid); -+ -+ if (stream) -+ start_ba_session = true; -+ } -+ } -+ -+ spin_unlock_bh(&priv->stream_lock); -+ } else { -+ qos &= ~MWL_QOS_ACK_POLICY_MASK; -+ qos |= MWL_QOS_ACK_POLICY_NORMAL; -+ } -+ -+ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ tx_ctrl->vif = (void *)tx_info->control.vif; -+ tx_ctrl->sta = (void *)sta; -+ tx_ctrl->k_conf = (void *)k_conf; -+ tx_ctrl->amsdu_pkts = NULL; -+ tx_ctrl->tx_priority = txpriority; -+ tx_ctrl->type = (mgmtframe ? IEEE_TYPE_MANAGEMENT : IEEE_TYPE_DATA); -+ tx_ctrl->qos_ctrl = qos; -+ tx_ctrl->xmit_control = xmitcontrol; -+ -+ if (skb_queue_len(&pcie_priv->txq[index]) > pcie_priv->txq_limit) -+ ieee80211_stop_queue(hw, SYSADPT_TX_WMM_QUEUES - index - 1); -+ -+ skb_queue_tail(&pcie_priv->txq[index], skb); -+ -+ tasklet_schedule(&pcie_priv->tx_task); -+ -+ /* Initiate the ampdu session here */ -+ if (start_ba_session) { -+ spin_lock_bh(&priv->stream_lock); -+ rc = mwl_fwcmd_start_stream(hw, stream); -+ if (rc) -+ mwl_fwcmd_remove_stream(hw, stream); -+ else -+ wiphy_debug(hw->wiphy, "Mac80211 start BA %pM\n", -+ stream->sta->addr); -+ spin_unlock_bh(&priv->stream_lock); -+ } -+} -+ -+void pcie_tx_del_pkts_via_vif(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num; -+ struct sk_buff *skb, *tmp; -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct sk_buff_head *amsdu_pkts; -+ unsigned long flags; -+ -+ for (num = 1; num < PCIE_NUM_OF_DESC_DATA; num++) { -+ spin_lock_irqsave(&pcie_priv->txq[num].lock, flags); -+ skb_queue_walk_safe(&pcie_priv->txq[num], skb, tmp) { -+ tx_info = IEEE80211_SKB_CB(skb); -+ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ if (tx_ctrl->vif == vif) { -+ amsdu_pkts = (struct sk_buff_head *) -+ tx_ctrl->amsdu_pkts; -+ if (amsdu_pkts) { -+ skb_queue_purge(amsdu_pkts); -+ kfree(amsdu_pkts); -+ } -+ __skb_unlink(skb, &pcie_priv->txq[num]); -+ dev_kfree_skb_any(skb); -+ } -+ } -+ spin_unlock_irqrestore(&pcie_priv->txq[num].lock, flags); -+ } -+} -+ -+void pcie_tx_del_pkts_via_sta(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num; -+ struct sk_buff *skb, *tmp; -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct sk_buff_head *amsdu_pkts; -+ unsigned long flags; -+ -+ for (num = 1; num < PCIE_NUM_OF_DESC_DATA; num++) { -+ spin_lock_irqsave(&pcie_priv->txq[num].lock, flags); -+ skb_queue_walk_safe(&pcie_priv->txq[num], skb, tmp) { -+ tx_info = IEEE80211_SKB_CB(skb); -+ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ if (tx_ctrl->sta == sta) { -+ amsdu_pkts = (struct sk_buff_head *) -+ tx_ctrl->amsdu_pkts; -+ if (amsdu_pkts) { -+ skb_queue_purge(amsdu_pkts); -+ kfree(amsdu_pkts); -+ } -+ __skb_unlink(skb, &pcie_priv->txq[num]); -+ dev_kfree_skb_any(skb); -+ } -+ } -+ spin_unlock_irqrestore(&pcie_priv->txq[num].lock, flags); -+ } -+} -+ -+void pcie_tx_del_ampdu_pkts(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, u8 tid) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); -+ int ac, desc_num; -+ struct mwl_amsdu_frag *amsdu_frag; -+ struct sk_buff *skb, *tmp; -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct sk_buff_head *amsdu_pkts; -+ unsigned long flags; -+ -+ ac = utils_tid_to_ac(tid); -+ desc_num = SYSADPT_TX_WMM_QUEUES - ac - 1; -+ spin_lock_irqsave(&pcie_priv->txq[desc_num].lock, flags); -+ skb_queue_walk_safe(&pcie_priv->txq[desc_num], skb, tmp) { -+ tx_info = IEEE80211_SKB_CB(skb); -+ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ if (tx_ctrl->sta == sta) { -+ amsdu_pkts = (struct sk_buff_head *) -+ tx_ctrl->amsdu_pkts; -+ if (amsdu_pkts) { -+ skb_queue_purge(amsdu_pkts); -+ kfree(amsdu_pkts); -+ } -+ __skb_unlink(skb, &pcie_priv->txq[desc_num]); -+ dev_kfree_skb_any(skb); -+ } -+ } -+ spin_unlock_irqrestore(&pcie_priv->txq[desc_num].lock, flags); -+ -+ spin_lock_bh(&sta_info->amsdu_lock); -+ amsdu_frag = &sta_info->amsdu_ctrl.frag[desc_num]; -+ if (amsdu_frag->num) { -+ amsdu_frag->num = 0; -+ amsdu_frag->cur_pos = NULL; -+ if (amsdu_frag->skb) { -+ tx_info = IEEE80211_SKB_CB(amsdu_frag->skb); -+ tx_ctrl = (struct pcie_tx_ctrl *)&tx_info->status; -+ amsdu_pkts = (struct sk_buff_head *) -+ tx_ctrl->amsdu_pkts; -+ if (amsdu_pkts) { -+ skb_queue_purge(amsdu_pkts); -+ kfree(amsdu_pkts); -+ } -+ dev_kfree_skb_any(amsdu_frag->skb); -+ } -+ } -+ spin_unlock_bh(&sta_info->amsdu_lock); -+} -+ -+void pcie_tx_del_sta_amsdu_pkts(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); -+ int num; -+ struct mwl_amsdu_frag *amsdu_frag; -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl *tx_ctrl; -+ struct sk_buff_head *amsdu_pkts; -+ -+ spin_lock_bh(&sta_info->amsdu_lock); -+ for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) { -+ amsdu_frag = &sta_info->amsdu_ctrl.frag[num]; -+ if (amsdu_frag->num) { -+ amsdu_frag->num = 0; -+ amsdu_frag->cur_pos = NULL; -+ if (amsdu_frag->skb) { -+ tx_info = IEEE80211_SKB_CB(amsdu_frag->skb); -+ tx_ctrl = (struct pcie_tx_ctrl *) -+ &tx_info->status; -+ amsdu_pkts = (struct sk_buff_head *) -+ tx_ctrl->amsdu_pkts; -+ if (amsdu_pkts) { -+ skb_queue_purge(amsdu_pkts); -+ kfree(amsdu_pkts); -+ } -+ dev_kfree_skb_any(amsdu_frag->skb); -+ } -+ } -+ } -+ spin_unlock_bh(&sta_info->amsdu_lock); -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h -new file mode 100644 -index 000000000000..c233ba1aaa9d ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx.h -@@ -0,0 +1,38 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines transmit related functions. */ -+ -+#ifndef _TX_H_ -+#define _TX_H_ -+ -+int pcie_tx_init(struct ieee80211_hw *hw); -+void pcie_tx_deinit(struct ieee80211_hw *hw); -+void pcie_tx_skbs(unsigned long data); -+void pcie_tx_done(unsigned long data); -+void pcie_tx_flush_amsdu(unsigned long data); -+void pcie_tx_xmit(struct ieee80211_hw *hw, -+ struct ieee80211_tx_control *control, -+ struct sk_buff *skb); -+void pcie_tx_del_pkts_via_vif(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif); -+void pcie_tx_del_pkts_via_sta(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta); -+void pcie_tx_del_ampdu_pkts(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta, u8 tid); -+void pcie_tx_del_sta_amsdu_pkts(struct ieee80211_hw *hw, -+ struct ieee80211_sta *sta); -+ -+#endif /* _TX_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c -new file mode 100644 -index 000000000000..6758cde363c6 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.c -@@ -0,0 +1,693 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements transmit related functions for new data -+ * path. -+ */ -+ -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "hif/fwcmd.h" -+#include "hif/pcie/dev.h" -+#include "hif/pcie/tx_ndp.h" -+ -+#define MAX_NUM_TX_RING_BYTES (MAX_NUM_TX_DESC * \ -+ sizeof(struct pcie_tx_desc_ndp)) -+#define MAX_NUM_TX_RING_DONE_BYTES (MAX_NUM_TX_DESC * \ -+ sizeof(struct tx_ring_done)) -+#define QUEUE_STAOFFSET ((SYSADPT_NUM_OF_AP - 1) + \ -+ SYSADPT_NUM_OF_CLIENT) -+#define PROBE_RESPONSE_TXQNUM ((SYSADPT_MAX_STA_SC4 + SYSADPT_NUM_OF_AP + \ -+ SYSADPT_NUM_OF_CLIENT) * SYSADPT_MAX_TID) -+#define MGMT_TXQNUM ((PROBE_RESPONSE_TXQNUM + 1)) -+#define TXDONE_THRESHOLD 4 -+ -+#define TX_CTRL_TYPE_DATA BIT(0) -+#define TX_CTRL_EAPOL BIT(1) -+#define TX_CTRL_TCP_ACK BIT(2) -+ -+/* Transmission information to transmit a socket buffer. -+ */ -+struct pcie_tx_ctrl_ndp { -+ u16 tx_que_priority; -+ u8 hdrlen; -+ u8 flags; -+ u32 rate; -+ u32 tcp_dst_src; -+ u32 tcp_sn; -+} __packed; -+ -+static int pcie_tx_ring_alloc_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ u8 *mem; -+ -+ mem = dma_alloc_coherent(priv->dev, -+ MAX_NUM_TX_RING_BYTES, -+ &desc->pphys_tx_ring, -+ GFP_KERNEL); -+ if (!mem) -+ goto err_no_mem; -+ desc->ptx_ring = (struct pcie_tx_desc_ndp *)mem; -+ memset(desc->ptx_ring, 0x00, MAX_NUM_TX_RING_BYTES); -+ -+ mem = dma_alloc_coherent(priv->dev, -+ MAX_NUM_TX_RING_DONE_BYTES, -+ &desc->pphys_tx_ring_done, -+ GFP_KERNEL); -+ if (!mem) -+ goto err_no_mem; -+ desc->ptx_ring_done = (struct tx_ring_done *)mem; -+ memset(desc->ptx_ring_done, 0x00, MAX_NUM_TX_RING_DONE_BYTES); -+ -+ mem = dma_alloc_coherent(priv->dev, -+ DEFAULT_ACNT_RING_SIZE, -+ &desc->pphys_acnt_ring, -+ GFP_KERNEL); -+ if (!mem) -+ goto err_no_mem; -+ desc->pacnt_ring = (u8 *)mem; -+ memset(desc->pacnt_ring, 0x00, DEFAULT_ACNT_RING_SIZE); -+ -+ desc->pacnt_buf = kzalloc(DEFAULT_ACNT_RING_SIZE, GFP_KERNEL); -+ if (!desc->pacnt_buf) -+ goto err_no_mem; -+ desc->acnt_ring_size = DEFAULT_ACNT_RING_SIZE; -+ -+ return 0; -+ -+err_no_mem: -+ -+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n"); -+ -+ return -ENOMEM; -+} -+ -+static int pcie_tx_ring_init_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ int i; -+ -+ for (i = 0; i < PCIE_NUM_OF_DESC_DATA; i++) -+ skb_queue_head_init(&pcie_priv->txq[i]); -+ -+ if (!desc->ptx_ring) { -+ for (i = 0; i < MAX_NUM_TX_DESC; i++) -+ desc->ptx_ring[i].user = cpu_to_le32(i); -+ desc->tx_desc_busy_cnt = 0; -+ } -+ -+ return 0; -+} -+ -+static void pcie_tx_ring_cleanup_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ struct sk_buff *tx_skb; -+ int i; -+ -+ for (i = 0; i < PCIE_NUM_OF_DESC_DATA; i++) -+ skb_queue_purge(&pcie_priv->txq[i]); -+ -+ for (i = 0; i < MAX_TX_RING_SEND_SIZE; i++) { -+ tx_skb = desc->tx_vbuflist[i]; -+ if (tx_skb) { -+ pci_unmap_single(pcie_priv->pdev, -+ desc->pphys_tx_buflist[i], -+ tx_skb->len, -+ PCI_DMA_TODEVICE); -+ dev_kfree_skb_any(tx_skb); -+ desc->pphys_tx_buflist[i] = 0; -+ desc->tx_vbuflist[i] = NULL; -+ } -+ } -+ desc->tx_sent_tail = 0; -+ desc->tx_sent_head = 0; -+ desc->tx_done_tail = 0; -+ desc->tx_vbuflist_idx = 0; -+ desc->tx_desc_busy_cnt = 0; -+} -+ -+static void pcie_tx_ring_free_ndp(struct mwl_priv *priv) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ -+ if (desc->ptx_ring) { -+ dma_free_coherent(priv->dev, -+ MAX_NUM_TX_RING_BYTES, -+ desc->ptx_ring, -+ desc->pphys_tx_ring); -+ desc->ptx_ring = NULL; -+ } -+ -+ if (desc->ptx_ring_done) { -+ dma_free_coherent(priv->dev, -+ MAX_NUM_TX_RING_DONE_BYTES, -+ desc->ptx_ring_done, -+ desc->pphys_tx_ring_done); -+ desc->prx_ring_done = NULL; -+ } -+ -+ if (desc->pacnt_ring) { -+ dma_free_coherent(priv->dev, -+ DEFAULT_ACNT_RING_SIZE, -+ desc->pacnt_ring, -+ desc->pphys_acnt_ring); -+ desc->pacnt_ring = NULL; -+ } -+ -+ kfree(desc->pacnt_buf); -+} -+ -+static inline u32 pcie_tx_set_skb(struct mwl_priv *priv, struct sk_buff *skb, -+ dma_addr_t dma) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ u32 index = desc->tx_vbuflist_idx; -+ -+ while (desc->tx_vbuflist[index]) -+ index = (index + 1) % MAX_TX_RING_SEND_SIZE; -+ -+ desc->tx_vbuflist_idx = (index + 1) % MAX_TX_RING_SEND_SIZE; -+ desc->pphys_tx_buflist[index] = dma; -+ desc->tx_vbuflist[index] = skb; -+ -+ return index; -+} -+ -+static inline int pcie_tx_skb_ndp(struct mwl_priv *priv, -+ struct sk_buff *tx_skb) -+{ -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ u32 tx_send_tail; -+ u32 tx_send_head_new; -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl_ndp *tx_ctrl; -+ struct pcie_tx_desc_ndp *pnext_tx_desc; -+ struct ieee80211_hdr *wh; -+ u32 ctrl = 0; -+ dma_addr_t dma; -+ -+ spin_lock_bh(&pcie_priv->tx_desc_lock); -+ -+ tx_send_tail = desc->tx_sent_tail; -+ tx_send_head_new = desc->tx_sent_head; -+ -+ if (((tx_send_head_new + 1) & (MAX_NUM_TX_DESC-1)) == tx_send_tail) { -+ /* Update the tx_send_tail */ -+ tx_send_tail = readl(pcie_priv->iobase1 + -+ MACREG_REG_TXSEDNTAIL); -+ desc->tx_sent_tail = tx_send_tail; -+ -+ if (((tx_send_head_new + 1) & (MAX_NUM_TX_DESC-1)) == -+ tx_send_tail) { -+ spin_unlock_bh(&pcie_priv->tx_desc_lock); -+ return -EAGAIN; -+ } -+ } -+ -+ tx_info = IEEE80211_SKB_CB(tx_skb); -+ tx_ctrl = (struct pcie_tx_ctrl_ndp *)tx_info->status.status_driver_data; -+ pnext_tx_desc = &desc->ptx_ring[tx_send_head_new]; -+ -+ if (tx_ctrl->flags & TX_CTRL_TYPE_DATA) { -+ wh = (struct ieee80211_hdr *)tx_skb->data; -+ -+ skb_pull(tx_skb, tx_ctrl->hdrlen); -+ ether_addr_copy(pnext_tx_desc->u.sa, -+ ieee80211_get_SA(wh)); -+ ether_addr_copy(pnext_tx_desc->u.da, -+ ieee80211_get_DA(wh)); -+ -+ if (tx_ctrl->flags & TX_CTRL_EAPOL) -+ ctrl = TXRING_CTRL_TAG_EAP << TXRING_CTRL_TAG_SHIFT; -+ if (tx_ctrl->flags & TX_CTRL_TCP_ACK) { -+ pnext_tx_desc->tcp_dst_src = -+ cpu_to_le32(tx_ctrl->tcp_dst_src); -+ pnext_tx_desc->tcp_sn = cpu_to_le32(tx_ctrl->tcp_sn); -+ ctrl = TXRING_CTRL_TAG_TCP_ACK << TXRING_CTRL_TAG_SHIFT; -+ } -+ ctrl |= (((tx_ctrl->tx_que_priority & TXRING_CTRL_QID_MASK) << -+ TXRING_CTRL_QID_SHIFT) | -+ ((tx_skb->len & TXRING_CTRL_LEN_MASK) << -+ TXRING_CTRL_LEN_SHIFT)); -+ } else { -+ /* Assigning rate code; use legacy 6mbps rate. */ -+ pnext_tx_desc->u.rate_code = cpu_to_le16(RATECODE_TYPE_LEGACY + -+ (0 << RATECODE_MCS_SHIFT) + RATECODE_BW_20MHZ); -+ pnext_tx_desc->u.max_retry = 5; -+ -+ ctrl = (((tx_ctrl->tx_que_priority & TXRING_CTRL_QID_MASK) << -+ TXRING_CTRL_QID_SHIFT) | -+ (((tx_skb->len - sizeof(struct pcie_dma_data)) & -+ TXRING_CTRL_LEN_MASK) << TXRING_CTRL_LEN_SHIFT) | -+ (TXRING_CTRL_TAG_MGMT << TXRING_CTRL_TAG_SHIFT)); -+ } -+ -+ dma = pci_map_single(pcie_priv->pdev, tx_skb->data, -+ tx_skb->len, PCI_DMA_TODEVICE); -+ if (pci_dma_mapping_error(pcie_priv->pdev, dma)) { -+ dev_kfree_skb_any(tx_skb); -+ wiphy_err(priv->hw->wiphy, -+ "failed to map pci memory!\n"); -+ spin_unlock_bh(&pcie_priv->tx_desc_lock); -+ return -EIO; -+ } -+ -+ pnext_tx_desc->data = cpu_to_le32(dma); -+ pnext_tx_desc->ctrl = cpu_to_le32(ctrl); -+ pnext_tx_desc->user = cpu_to_le32(pcie_tx_set_skb(priv, tx_skb, dma)); -+ -+ if ((tx_ctrl->flags & TX_CTRL_TYPE_DATA) && -+ (tx_ctrl->rate != 0)) { -+ skb_push(tx_skb, tx_ctrl->hdrlen); -+ skb_get(tx_skb); -+ pcie_tx_prepare_info(priv, tx_ctrl->rate, tx_info); -+ tx_ctrl->flags |= TX_CTRL_TYPE_DATA; -+ ieee80211_tx_status(priv->hw, tx_skb); -+ } -+ -+ if (++tx_send_head_new >= MAX_NUM_TX_DESC) -+ tx_send_head_new = 0; -+ desc->tx_sent_head = tx_send_head_new; -+ wmb(); /*Data Memory Barrier*/ -+ writel(tx_send_head_new, pcie_priv->iobase1 + MACREG_REG_TXSENDHEAD); -+ desc->tx_desc_busy_cnt++; -+ -+ spin_unlock_bh(&pcie_priv->tx_desc_lock); -+ -+ return 0; -+} -+ -+static inline void pcie_tx_check_tcp_ack(struct sk_buff *tx_skb, -+ struct pcie_tx_ctrl_ndp *tx_ctrl) -+{ -+ struct iphdr *iph; -+ struct tcphdr *tcph; -+ -+ if (tx_ctrl->flags & TX_CTRL_TYPE_DATA) { -+ iph = (struct iphdr *)(tx_skb->data + tx_ctrl->hdrlen + 8); -+ tcph = (struct tcphdr *)((u8 *)iph + (iph->ihl * 4)); -+ if ((iph->protocol == IPPROTO_TCP) && -+ (tx_skb->protocol == htons(ETH_P_IP))) { -+ if ((tcph->ack == 1) && (ntohs(iph->tot_len) == -+ (iph->ihl * 4 + tcph->doff * 4))) { -+ if (tcph->syn || tcph->fin) -+ return; -+ -+ tx_ctrl->flags |= TX_CTRL_TCP_ACK; -+ tx_ctrl->tcp_dst_src = ntohs(tcph->source) | -+ (ntohs(tcph->dest) << 16); -+ tx_ctrl->tcp_sn = ntohl(tcph->ack_seq); -+ } -+ } -+ } -+} -+ -+int pcie_tx_init_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct sk_buff skb; -+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(&skb); -+ int rc; -+ -+ if (sizeof(struct pcie_tx_ctrl_ndp) > -+ sizeof(tx_info->status.status_driver_data)) { -+ wiphy_err(hw->wiphy, "driver data is not enough: %d (%d)\n", -+ sizeof(struct pcie_tx_ctrl_ndp), -+ sizeof(tx_info->status.status_driver_data)); -+ return -ENOMEM; -+ } -+ -+ rc = pcie_tx_ring_alloc_ndp(priv); -+ if (rc) { -+ pcie_tx_ring_free_ndp(priv); -+ wiphy_err(hw->wiphy, "allocating TX ring failed\n"); -+ return rc; -+ } -+ -+ rc = pcie_tx_ring_init_ndp(priv); -+ if (rc) { -+ pcie_tx_ring_free_ndp(priv); -+ wiphy_err(hw->wiphy, "initializing TX ring failed\n"); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+void pcie_tx_deinit_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ pcie_tx_ring_cleanup_ndp(priv); -+ pcie_tx_ring_free_ndp(priv); -+} -+ -+void pcie_tx_skbs_ndp(unsigned long data) -+{ -+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data; -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ int num = SYSADPT_TX_WMM_QUEUES; -+ struct sk_buff *tx_skb; -+ int rc; -+ -+ while (num--) { -+ while (skb_queue_len(&pcie_priv->txq[num]) > 0) { -+ if (pcie_priv->desc_data_ndp.tx_desc_busy_cnt >= -+ (MAX_TX_RING_SEND_SIZE - 1)) { -+ pcie_tx_done_ndp(hw); -+ break; -+ } -+ -+ tx_skb = skb_dequeue(&pcie_priv->txq[num]); -+ -+ rc = pcie_tx_skb_ndp(priv, tx_skb); -+ if (rc) { -+ pcie_tx_done_ndp(hw); -+ if (rc == -EAGAIN) -+ skb_queue_head(&pcie_priv->txq[num], -+ tx_skb); -+ break; -+ } -+ -+ if (++pcie_priv->tx_done_cnt > TXDONE_THRESHOLD) { -+ pcie_tx_done_ndp(hw); -+ pcie_priv->tx_done_cnt = 0; -+ } -+ } -+ -+ if (skb_queue_len(&pcie_priv->txq[num]) < -+ pcie_priv->txq_wake_threshold) { -+ int queue; -+ -+ queue = SYSADPT_TX_WMM_QUEUES - num - 1; -+ if (ieee80211_queue_stopped(hw, queue)) -+ ieee80211_wake_queue(hw, queue); -+ } -+ } -+ -+ pcie_priv->is_tx_schedule = false; -+} -+ -+void pcie_tx_done_ndp(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct pcie_desc_data_ndp *desc = &pcie_priv->desc_data_ndp; -+ u32 tx_done_head, tx_done_tail; -+ struct tx_ring_done *ptx_ring_done; -+ u32 index; -+ struct sk_buff *skb; -+ struct ieee80211_tx_info *tx_info; -+ struct pcie_tx_ctrl_ndp *tx_ctrl; -+ struct pcie_dma_data *dma_data; -+ u16 hdrlen; -+ -+ spin_lock_bh(&pcie_priv->tx_desc_lock); -+ -+ tx_done_head = readl(pcie_priv->iobase1 + -+ MACREG_REG_TXDONEHEAD); -+ tx_done_tail = desc->tx_done_tail & (MAX_TX_RING_DONE_SIZE - 1); -+ tx_done_head &= (MAX_TX_RING_DONE_SIZE - 1); -+ -+ while (tx_done_head != tx_done_tail) { -+ ptx_ring_done = &desc->ptx_ring_done[tx_done_tail]; -+ -+ index = le32_to_cpu(ptx_ring_done->user); -+ ptx_ring_done->user = 0; -+ if (index >= MAX_TX_RING_SEND_SIZE) { -+ wiphy_err(hw->wiphy, -+ "corruption for index of buffer\n"); -+ break; -+ } -+ skb = desc->tx_vbuflist[index]; -+ if (!skb) { -+ wiphy_err(hw->wiphy, -+ "buffer is NULL for tx done ring\n"); -+ break; -+ } -+ pci_unmap_single(pcie_priv->pdev, -+ desc->pphys_tx_buflist[index], -+ skb->len, -+ PCI_DMA_TODEVICE); -+ desc->pphys_tx_buflist[index] = 0; -+ desc->tx_vbuflist[index] = NULL; -+ -+ tx_info = IEEE80211_SKB_CB(skb); -+ tx_ctrl = (struct pcie_tx_ctrl_ndp *) -+ tx_info->status.status_driver_data; -+ -+ if (tx_ctrl->flags & TX_CTRL_TYPE_DATA) { -+ dev_kfree_skb_any(skb); -+ goto bypass_ack; -+ } else { -+ /* Remove H/W dma header */ -+ dma_data = (struct pcie_dma_data *)skb->data; -+ -+ if (ieee80211_is_assoc_resp( -+ dma_data->wh.frame_control) || -+ ieee80211_is_reassoc_resp( -+ dma_data->wh.frame_control)) { -+ dev_kfree_skb_any(skb); -+ goto bypass_ack; -+ } -+ hdrlen = ieee80211_hdrlen( -+ dma_data->wh.frame_control); -+ memmove(dma_data->data - hdrlen, -+ &dma_data->wh, hdrlen); -+ skb_pull(skb, sizeof(*dma_data) - hdrlen); -+ } -+ -+ pcie_tx_prepare_info(priv, 0, tx_info); -+ ieee80211_tx_status(hw, skb); -+ -+bypass_ack: -+ if (++tx_done_tail >= MAX_TX_RING_DONE_SIZE) -+ tx_done_tail = 0; -+ desc->tx_desc_busy_cnt--; -+ } -+ -+ writel(tx_done_tail, pcie_priv->iobase1 + -+ MACREG_REG_TXDONETAIL); -+ desc->tx_done_tail = tx_done_tail; -+ -+ spin_unlock_bh(&pcie_priv->tx_desc_lock); -+} -+ -+void pcie_tx_xmit_ndp(struct ieee80211_hw *hw, -+ struct ieee80211_tx_control *control, -+ struct sk_buff *skb) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct pcie_priv *pcie_priv = priv->hif.priv; -+ struct ieee80211_tx_info *tx_info; -+ struct ieee80211_key_conf *k_conf; -+ struct mwl_vif *mwl_vif; -+ int index; -+ struct ieee80211_sta *sta; -+ struct mwl_sta *sta_info; -+ struct ieee80211_hdr *wh; -+ u8 *da; -+ u16 qos; -+ u8 tid = 0; -+ struct mwl_ampdu_stream *stream = NULL; -+ u16 tx_que_priority; -+ bool mgmtframe = false; -+ struct ieee80211_mgmt *mgmt; -+ bool eapol_frame = false; -+ bool start_ba_session = false; -+ struct pcie_tx_ctrl_ndp *tx_ctrl; -+ -+ tx_info = IEEE80211_SKB_CB(skb); -+ k_conf = tx_info->control.hw_key; -+ mwl_vif = mwl_dev_get_vif(tx_info->control.vif); -+ index = skb_get_queue_mapping(skb); -+ sta = control->sta; -+ sta_info = sta ? mwl_dev_get_sta(sta) : NULL; -+ -+ wh = (struct ieee80211_hdr *)skb->data; -+ -+ if (ieee80211_is_data_qos(wh->frame_control)) -+ qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); -+ else -+ qos = 0xFFFF; -+ -+ if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { -+ index = IEEE80211_AC_VO; -+ eapol_frame = true; -+ } -+ -+ if (ieee80211_is_mgmt(wh->frame_control)) { -+ mgmtframe = true; -+ mgmt = (struct ieee80211_mgmt *)skb->data; -+ } -+ -+ if (mgmtframe) { -+ u16 capab; -+ -+ if (unlikely(ieee80211_is_action(wh->frame_control) && -+ mgmt->u.action.category == WLAN_CATEGORY_BACK && -+ mgmt->u.action.u.addba_req.action_code == -+ WLAN_ACTION_ADDBA_REQ)) { -+ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); -+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; -+ index = utils_tid_to_ac(tid); -+ } -+ -+ if (unlikely(ieee80211_is_assoc_req(wh->frame_control))) -+ utils_add_basic_rates(hw->conf.chandef.chan->band, skb); -+ -+ if (ieee80211_is_probe_req(wh->frame_control) || -+ ieee80211_is_probe_resp(wh->frame_control)) -+ tx_que_priority = PROBE_RESPONSE_TXQNUM; -+ else { -+ if (( -+ (mwl_vif->macid == SYSADPT_NUM_OF_AP) && -+ (!ieee80211_has_protected(wh->frame_control) || -+ (ieee80211_has_protected(wh->frame_control) && -+ ieee80211_is_auth(wh->frame_control))) -+ ) || -+ !sta || -+ ieee80211_is_auth(wh->frame_control) || -+ ieee80211_is_assoc_req(wh->frame_control) || -+ ieee80211_is_assoc_resp(wh->frame_control)) -+ tx_que_priority = MGMT_TXQNUM; -+ else { -+ if (is_multicast_ether_addr(wh->addr1) && -+ (mwl_vif->macid != SYSADPT_NUM_OF_AP)) -+ tx_que_priority = mwl_vif->macid * -+ SYSADPT_MAX_TID; -+ else -+ tx_que_priority = SYSADPT_MAX_TID * -+ (sta_info->stnid + -+ QUEUE_STAOFFSET) + 6; -+ } -+ } -+ -+ if (ieee80211_is_assoc_resp(wh->frame_control) || -+ ieee80211_is_reassoc_resp(wh->frame_control)) { -+ struct sk_buff *ack_skb; -+ struct ieee80211_tx_info *ack_info; -+ -+ ack_skb = skb_copy(skb, GFP_ATOMIC); -+ ack_info = IEEE80211_SKB_CB(ack_skb); -+ pcie_tx_prepare_info(priv, 0, ack_info); -+ ieee80211_tx_status(hw, ack_skb); -+ } -+ -+ pcie_tx_encapsulate_frame(priv, skb, k_conf, NULL); -+ } else { -+ tid = qos & 0x7; -+ if (sta && sta->ht_cap.ht_supported && !eapol_frame && -+ qos != 0xFFFF) { -+ pcie_tx_count_packet(sta, tid); -+ spin_lock_bh(&priv->stream_lock); -+ stream = mwl_fwcmd_lookup_stream(hw, sta, tid); -+ if (!stream || -+ stream->state == AMPDU_STREAM_IN_PROGRESS) { -+ wiphy_warn(hw->wiphy, -+ "can't send packet during ADDBA\n"); -+ spin_unlock_bh(&priv->stream_lock); -+ dev_kfree_skb_any(skb); -+ return; -+ } -+ if ((stream->state == AMPDU_NO_STREAM) && -+ mwl_fwcmd_ampdu_allowed(sta, tid)) { -+ stream = mwl_fwcmd_add_stream(hw, sta, tid); -+ if (stream) -+ start_ba_session = true; -+ } -+ spin_unlock_bh(&priv->stream_lock); -+ } -+ -+ da = ieee80211_get_DA(wh); -+ -+ if (is_multicast_ether_addr(da) -+ && (mwl_vif->macid != SYSADPT_NUM_OF_AP)) { -+ -+ tx_que_priority = mwl_vif->macid * SYSADPT_MAX_TID; -+ -+ if (da[ETH_ALEN - 1] == 0xff) -+ tx_que_priority += 7; -+ -+ if (ieee80211_has_a4(wh->frame_control)) { -+ if (sta && sta_info->wds) -+ tx_que_priority = SYSADPT_MAX_TID * -+ (sta_info->stnid + -+ QUEUE_STAOFFSET) + 6; -+ } -+ } else { -+ if (sta) { -+ if (!eapol_frame) -+ tx_que_priority = SYSADPT_MAX_TID * -+ (sta_info->stnid + -+ QUEUE_STAOFFSET) + -+ ((qos == 0xFFFF) ? 0 : tid); -+ else -+ tx_que_priority = SYSADPT_MAX_TID * -+ (sta_info->stnid + -+ QUEUE_STAOFFSET) + -+ ((qos == 0xFFFF) ? 0 : 6); -+ } else -+ tx_que_priority = 0; -+ } -+ } -+ -+ index = SYSADPT_TX_WMM_QUEUES - index - 1; -+ -+ tx_ctrl = (struct pcie_tx_ctrl_ndp *)tx_info->status.status_driver_data; -+ tx_ctrl->tx_que_priority = tx_que_priority; -+ tx_ctrl->hdrlen = ieee80211_hdrlen(wh->frame_control); -+ tx_ctrl->flags = 0; -+ if (!mgmtframe) -+ tx_ctrl->flags |= TX_CTRL_TYPE_DATA; -+ if (eapol_frame) -+ tx_ctrl->flags |= TX_CTRL_EAPOL; -+ tx_ctrl->rate = sta ? sta_info->tx_rate_info : 0; -+ if (ieee80211_is_nullfunc(wh->frame_control) || -+ ieee80211_is_qos_nullfunc(wh->frame_control)) -+ tx_ctrl->rate = 0; -+ pcie_tx_check_tcp_ack(skb, tx_ctrl); -+ -+ if (skb_queue_len(&pcie_priv->txq[index]) > pcie_priv->txq_limit) -+ ieee80211_stop_queue(hw, SYSADPT_TX_WMM_QUEUES - index - 1); -+ -+ skb_queue_tail(&pcie_priv->txq[index], skb); -+ -+ if (!pcie_priv->is_tx_schedule) { -+ tasklet_schedule(&pcie_priv->tx_task); -+ pcie_priv->is_tx_schedule = true; -+ } -+ -+ /* Initiate the ampdu session here */ -+ if (start_ba_session) { -+ spin_lock_bh(&priv->stream_lock); -+ if (mwl_fwcmd_start_stream(hw, stream)) -+ mwl_fwcmd_remove_stream(hw, stream); -+ spin_unlock_bh(&priv->stream_lock); -+ } -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h -new file mode 100644 -index 000000000000..2ad5f381b9ee ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hif/pcie/tx_ndp.h -@@ -0,0 +1,30 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines transmit related functions for new data path. -+ */ -+ -+#ifndef _TX_NDP_H_ -+#define _TX_NDP_H_ -+ -+int pcie_tx_init_ndp(struct ieee80211_hw *hw); -+void pcie_tx_deinit_ndp(struct ieee80211_hw *hw); -+void pcie_tx_skbs_ndp(unsigned long data); -+void pcie_tx_done_ndp(struct ieee80211_hw *hw); -+void pcie_tx_xmit_ndp(struct ieee80211_hw *hw, -+ struct ieee80211_tx_control *control, -+ struct sk_buff *skb); -+ -+#endif /* _TX_NDP_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels b/drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels -new file mode 100644 -index 000000000000..adadd2e4d8d4 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hostapd/700-interoperability-workaround-for-80+80-and-160-MHz-channels -@@ -0,0 +1,32 @@ -+diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c -+index 3236016..e923094 100644 -+--- a/src/ap/ieee802_11_vht.c -++++ b/src/ap/ieee802_11_vht.c -+@@ -82,6 +82,27 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) -+ -+ oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; -+ -++ if (hapd->iconf->vht_oper_chwidth == 2) { -++ /* -++ * Convert 160 MHz channel width to new style as interop -++ * workaround. -++ */ -++ oper->vht_op_info_chwidth = 1; -++ oper->vht_op_info_chan_center_freq_seg1_idx = -++ oper->vht_op_info_chan_center_freq_seg0_idx; -++ if (hapd->iconf->channel < -++ hapd->iconf->vht_oper_centr_freq_seg0_idx) -++ oper->vht_op_info_chan_center_freq_seg0_idx -= 8; -++ else -++ oper->vht_op_info_chan_center_freq_seg0_idx += 8; -++ } else if (hapd->iconf->vht_oper_chwidth == 3) { -++ /* -++ * Convert 80+80 MHz channel width to new style as interop -++ * workaround. -++ */ -++ oper->vht_op_info_chwidth = 1; -++ } -++ -+ /* VHT Basic MCS set comes from hw */ -+ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ -+ oper->vht_basic_mcs_set = host_to_le16(0xfffc); -diff --git a/drivers/net/wireless/marvell/mwlwifi/hostapd/README b/drivers/net/wireless/marvell/mwlwifi/hostapd/README -new file mode 100644 -index 000000000000..a5fb2b68d3d3 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/hostapd/README -@@ -0,0 +1,26 @@ -+700-interoperability-workaround-for-80+80-and-160-MHz-channels: -+ -+patch for OpenWrt hostapd package 2016-01-15 for following commit -+(move it to package/network/services/hostapd/patches). -+ -+Note: After hostapd package 2016-06-15, this commit is already included. -+ -+commit 03a72eacda5d9a1837a74387081596a0d5466ec1 -+Author: Jouni Malinen -+Date: Thu Dec 17 18:39:19 2015 +0200 -+ -+ VHT: Add an interoperability workaround for 80+80 and 160 MHz channels -+ -+ Number of deployed 80 MHz capable VHT stations that do not support 80+80 -+ and 160 MHz bandwidths seem to misbehave when trying to connect to an AP -+ that advertises 80+80 or 160 MHz channel bandwidth in the VHT Operation -+ element. To avoid such issues with deployed devices, modify the design -+ based on newly proposed IEEE 802.11 standard changes. -+ -+ This allows poorly implemented VHT 80 MHz stations to connect with the -+ AP in 80 MHz mode. 80+80 and 160 MHz capable stations need to support -+ the new workaround mechanism to allow full bandwidth to be used. -+ However, there are more or less no impacted station with 80+80/160 -+ capability deployed. -+ -+ Signed-off-by: Jouni Malinen jouni@qca.qualcomm.com -diff --git a/drivers/net/wireless/marvell/mwlwifi/mac80211.c b/drivers/net/wireless/marvell/mwlwifi/mac80211.c -new file mode 100644 -index 000000000000..725dec0f604b ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/mac80211.c -@@ -0,0 +1,933 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements mac80211 related functions. */ -+ -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "hif/fwcmd.h" -+#include "hif/hif-ops.h" -+ -+#define MAX_AMPDU_ATTEMPTS 5 -+ -+static const struct ieee80211_rate mwl_rates_24[] = { -+ { .bitrate = 10, .hw_value = 2, }, -+ { .bitrate = 20, .hw_value = 4, }, -+ { .bitrate = 55, .hw_value = 11, }, -+ { .bitrate = 110, .hw_value = 22, }, -+ { .bitrate = 220, .hw_value = 44, }, -+ { .bitrate = 60, .hw_value = 12, }, -+ { .bitrate = 90, .hw_value = 18, }, -+ { .bitrate = 120, .hw_value = 24, }, -+ { .bitrate = 180, .hw_value = 36, }, -+ { .bitrate = 240, .hw_value = 48, }, -+ { .bitrate = 360, .hw_value = 72, }, -+ { .bitrate = 480, .hw_value = 96, }, -+ { .bitrate = 540, .hw_value = 108, }, -+}; -+ -+static const struct ieee80211_rate mwl_rates_50[] = { -+ { .bitrate = 60, .hw_value = 12, }, -+ { .bitrate = 90, .hw_value = 18, }, -+ { .bitrate = 120, .hw_value = 24, }, -+ { .bitrate = 180, .hw_value = 36, }, -+ { .bitrate = 240, .hw_value = 48, }, -+ { .bitrate = 360, .hw_value = 72, }, -+ { .bitrate = 480, .hw_value = 96, }, -+ { .bitrate = 540, .hw_value = 108, }, -+}; -+ -+static void mwl_get_rateinfo(struct mwl_priv *priv, u8 *addr, -+ struct mwl_sta *sta_info) -+{ -+ int table_size = (sizeof(__le32) * 2 * SYSADPT_MAX_RATE_ADAPT_RATES); -+ u8 *rate_table, *rate_idx; -+ u32 rate_info; -+ struct mwl_tx_hist_data *tx_hist_data; -+ int ret, idx; -+ -+ rate_table = kzalloc(table_size, GFP_KERNEL); -+ if (!rate_table) -+ return; -+ -+ ret = mwl_fwcmd_get_ratetable(priv->hw, addr, rate_table, -+ table_size, 0); -+ if (ret) { -+ kfree(rate_table); -+ return; -+ } -+ -+ idx = 0; -+ rate_idx = rate_table; -+ rate_info = le32_to_cpu(*(__le32 *)rate_idx); -+ tx_hist_data = &sta_info->tx_hist.su_rate[0]; -+ while (rate_info) { -+ if (idx < SYSADPT_MAX_RATE_ADAPT_RATES) -+ tx_hist_data[idx].rateinfo = rate_info; -+ idx++; -+ rate_idx += (2 * sizeof(__le32)); -+ rate_info = le32_to_cpu(*(__le32 *)rate_idx); -+ } -+ -+ kfree(rate_table); -+} -+ -+static void mwl_mac80211_tx(struct ieee80211_hw *hw, -+ struct ieee80211_tx_control *control, -+ struct sk_buff *skb) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if (!priv->radio_on) { -+ wiphy_warn(hw->wiphy, -+ "dropped TX frame since radio is disabled\n"); -+ dev_kfree_skb_any(skb); -+ return; -+ } -+ -+ mwl_hif_tx_xmit(hw, control, skb); -+} -+ -+static int mwl_mac80211_start(struct ieee80211_hw *hw) -+{ -+ struct mwl_priv *priv = hw->priv; -+ int rc; -+ -+ /* Enable TX and RX tasklets. */ -+ mwl_hif_enable_data_tasks(hw); -+ -+ /* Enable interrupts */ -+ mwl_hif_irq_enable(hw); -+ -+ rc = mwl_fwcmd_radio_enable(hw); -+ if (rc) -+ goto fwcmd_fail; -+ rc = mwl_fwcmd_set_rate_adapt_mode(hw, 0); -+ if (rc) -+ goto fwcmd_fail; -+ rc = mwl_fwcmd_set_wmm_mode(hw, true); -+ if (rc) -+ goto fwcmd_fail; -+ rc = mwl_fwcmd_ht_guard_interval(hw, GUARD_INTERVAL_AUTO); -+ if (rc) -+ goto fwcmd_fail; -+ rc = mwl_fwcmd_set_dwds_stamode(hw, true); -+ if (rc) -+ goto fwcmd_fail; -+ rc = mwl_fwcmd_set_fw_flush_timer(hw, SYSADPT_AMSDU_FLUSH_TIME); -+ if (rc) -+ goto fwcmd_fail; -+ rc = mwl_fwcmd_set_optimization_level(hw, 1); -+ if (rc) -+ goto fwcmd_fail; -+ if (priv->chip_type == MWL8997) { -+ rc = mwl_fwcmd_config_EDMACCtrl(hw, 0); -+ if (rc) -+ goto fwcmd_fail; -+ } -+ if (priv->chip_type == MWL8964) { -+ rc = mwl_fwcmd_newdp_dmathread_start(hw); -+ if (rc) -+ goto fwcmd_fail; -+ rc = mwl_fwcmd_set_bftype(hw, priv->bf_type); -+ if (rc) -+ goto fwcmd_fail; -+ } -+ -+ ieee80211_wake_queues(hw); -+ return 0; -+ -+fwcmd_fail: -+ mwl_hif_irq_disable(hw); -+ mwl_hif_disable_data_tasks(hw); -+ -+ return rc; -+} -+ -+static void mwl_mac80211_stop(struct ieee80211_hw *hw) -+{ -+ mwl_fwcmd_radio_disable(hw); -+ -+ ieee80211_stop_queues(hw); -+ -+ /* Disable interrupts */ -+ mwl_hif_irq_disable(hw); -+ -+ /* Disable TX and RX tasklets. */ -+ mwl_hif_disable_data_tasks(hw); -+ -+ /* Return all skbs to mac80211 */ -+ mwl_hif_tx_return_pkts(hw); -+} -+ -+static int mwl_mac80211_add_interface(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ u32 macids_supported; -+ int macid; -+ -+ switch (vif->type) { -+ case NL80211_IFTYPE_AP: -+ case NL80211_IFTYPE_MESH_POINT: -+ if (vif->type == NL80211_IFTYPE_MESH_POINT) -+ if (priv->chip_type != MWL8997) -+ return -EPERM; -+ macids_supported = priv->ap_macids_supported; -+ break; -+ case NL80211_IFTYPE_STATION: -+ macids_supported = priv->sta_macids_supported; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ macid = ffs(macids_supported & ~priv->macids_used); -+ -+ if (!macid) { -+ wiphy_warn(hw->wiphy, "no macid can be allocated\n"); -+ return -EBUSY; -+ } -+ macid--; -+ -+ /* Setup driver private area. */ -+ mwl_vif = mwl_dev_get_vif(vif); -+ memset(mwl_vif, 0, sizeof(*mwl_vif)); -+ mwl_vif->type = vif->type; -+ mwl_vif->macid = macid; -+ mwl_vif->seqno = 0; -+ mwl_vif->is_hw_crypto_enabled = false; -+ mwl_vif->beacon_info.valid = false; -+ mwl_vif->set_beacon = false; -+ mwl_vif->basic_rate_idx = 0; -+ mwl_vif->broadcast_ssid = 0xFF; -+ mwl_vif->iv16 = 1; -+ mwl_vif->iv32 = 0; -+ mwl_vif->keyidx = 0; -+ -+ switch (vif->type) { -+ case NL80211_IFTYPE_AP: -+ ether_addr_copy(mwl_vif->bssid, vif->addr); -+ mwl_fwcmd_set_new_stn_add_self(hw, vif); -+ if (priv->chip_type == MWL8964) { -+ /* allow firmware to really set channel */ -+ mwl_fwcmd_bss_start(hw, vif, true); -+ mwl_fwcmd_bss_start(hw, vif, false); -+ } -+ break; -+ case NL80211_IFTYPE_MESH_POINT: -+ ether_addr_copy(mwl_vif->bssid, vif->addr); -+ mwl_fwcmd_set_new_stn_add_self(hw, vif); -+ break; -+ case NL80211_IFTYPE_STATION: -+ ether_addr_copy(mwl_vif->sta_mac, vif->addr); -+ mwl_fwcmd_bss_start(hw, vif, true); -+ mwl_fwcmd_set_infra_mode(hw, vif); -+ mwl_fwcmd_set_mac_addr_client(hw, vif, vif->addr); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ priv->macids_used |= 1 << mwl_vif->macid; -+ spin_lock_bh(&priv->vif_lock); -+ list_add_tail(&mwl_vif->list, &priv->vif_list); -+ spin_unlock_bh(&priv->vif_lock); -+ -+ return 0; -+} -+ -+static void mwl_mac80211_remove_vif(struct mwl_priv *priv, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_vif *mwl_vif = mwl_dev_get_vif(vif); -+ -+ if (!priv->macids_used) -+ return; -+ -+ mwl_hif_tx_del_pkts_via_vif(priv->hw, vif); -+ -+ priv->macids_used &= ~(1 << mwl_vif->macid); -+ spin_lock_bh(&priv->vif_lock); -+ list_del(&mwl_vif->list); -+ spin_unlock_bh(&priv->vif_lock); -+} -+ -+static void mwl_mac80211_remove_interface(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ switch (vif->type) { -+ case NL80211_IFTYPE_AP: -+ case NL80211_IFTYPE_MESH_POINT: -+ mwl_fwcmd_set_new_stn_del(hw, vif, vif->addr); -+ break; -+ case NL80211_IFTYPE_STATION: -+ mwl_fwcmd_remove_mac_addr(hw, vif, vif->addr); -+ break; -+ default: -+ break; -+ } -+ -+ mwl_mac80211_remove_vif(priv, vif); -+} -+ -+static int mwl_mac80211_config(struct ieee80211_hw *hw, -+ u32 changed) -+{ -+ struct ieee80211_conf *conf = &hw->conf; -+ int rc; -+ -+ wiphy_debug(hw->wiphy, "change: 0x%x\n", changed); -+ -+ if (conf->flags & IEEE80211_CONF_IDLE) -+ rc = mwl_fwcmd_radio_disable(hw); -+ else -+ rc = mwl_fwcmd_radio_enable(hw); -+ -+ if (rc) -+ goto out; -+ -+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { -+ int rate = 0; -+ -+ if (conf->chandef.chan->band == NL80211_BAND_2GHZ) { -+ mwl_fwcmd_set_apmode(hw, AP_MODE_2_4GHZ_11AC_MIXED); -+ mwl_fwcmd_set_linkadapt_cs_mode(hw, -+ LINK_CS_STATE_CONSERV); -+ rate = mwl_rates_24[0].hw_value; -+ } else if (conf->chandef.chan->band == NL80211_BAND_5GHZ) { -+ mwl_fwcmd_set_apmode(hw, AP_MODE_11AC); -+ mwl_fwcmd_set_linkadapt_cs_mode(hw, -+ LINK_CS_STATE_AUTO); -+ rate = mwl_rates_50[0].hw_value; -+ -+ if (conf->radar_enabled) -+ mwl_fwcmd_set_radar_detect(hw, MONITOR_START); -+ else -+ mwl_fwcmd_set_radar_detect(hw, -+ STOP_DETECT_RADAR); -+ } -+ -+ rc = mwl_fwcmd_set_rf_channel(hw, conf); -+ if (rc) -+ goto out; -+ rc = mwl_fwcmd_use_fixed_rate(hw, rate, rate); -+ if (rc) -+ goto out; -+ rc = mwl_fwcmd_max_tx_power(hw, conf, 0); -+ if (rc) -+ goto out; -+ rc = mwl_fwcmd_tx_power(hw, conf, 0); -+ if (rc) -+ goto out; -+ rc = mwl_fwcmd_set_cdd(hw); -+ } -+ -+out: -+ -+ return rc; -+} -+ -+static void mwl_mac80211_bss_info_changed_sta(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_bss_conf *info, -+ u32 changed) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ if ((changed & BSS_CHANGED_ERP_SLOT) && (priv->chip_type == MWL8997)) { -+ if (priv->use_short_slot != vif->bss_conf.use_short_slot) { -+ mwl_fwcmd_set_slot_time(hw, -+ vif->bss_conf.use_short_slot); -+ priv->use_short_slot = vif->bss_conf.use_short_slot; -+ } -+ } -+ -+ if (changed & BSS_CHANGED_ERP_PREAMBLE) { -+ if (priv->use_short_preamble != -+ vif->bss_conf.use_short_preamble) { -+ mwl_fwcmd_set_radio_preamble( -+ hw, vif->bss_conf.use_short_preamble); -+ priv->use_short_preamble = -+ vif->bss_conf.use_short_preamble; -+ } -+ } -+ -+ if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) -+ mwl_fwcmd_set_aid(hw, vif, (u8 *)vif->bss_conf.bssid, -+ vif->bss_conf.aid); -+} -+ -+static void mwl_mac80211_bss_info_changed_ap(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_bss_conf *info, -+ u32 changed) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_vif *mwl_vif; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ -+ if ((changed & BSS_CHANGED_ERP_SLOT) && (priv->chip_type == MWL8997)) { -+ if (priv->use_short_slot != vif->bss_conf.use_short_slot) { -+ mwl_fwcmd_set_slot_time(hw, -+ vif->bss_conf.use_short_slot); -+ priv->use_short_slot = vif->bss_conf.use_short_slot; -+ } -+ } -+ -+ if (changed & BSS_CHANGED_ERP_PREAMBLE) { -+ if (priv->use_short_preamble != -+ vif->bss_conf.use_short_preamble) { -+ mwl_fwcmd_set_radio_preamble( -+ hw, vif->bss_conf.use_short_preamble); -+ priv->use_short_preamble = -+ vif->bss_conf.use_short_preamble; -+ } -+ } -+ -+ if (changed & BSS_CHANGED_BASIC_RATES) { -+ int idx; -+ int rate; -+ -+ /* Use lowest supported basic rate for multicasts -+ * and management frames (such as probe responses -- -+ * beacons will always go out at 1 Mb/s). -+ */ -+ idx = ffs(vif->bss_conf.basic_rates); -+ if (idx) -+ idx--; -+ if (mwl_vif->basic_rate_idx != idx) { -+ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) -+ rate = mwl_rates_24[idx].hw_value; -+ else -+ rate = mwl_rates_50[idx].hw_value; -+ -+ mwl_fwcmd_use_fixed_rate(hw, rate, rate); -+ mwl_vif->basic_rate_idx = idx; -+ } -+ } -+ -+ if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { -+ struct sk_buff *skb; -+ -+ if ((info->ssid[0] != '\0') && -+ (info->ssid_len != 0) && -+ (!info->hidden_ssid)) { -+ if (mwl_vif->broadcast_ssid != true) { -+ mwl_fwcmd_broadcast_ssid_enable(hw, vif, true); -+ mwl_vif->broadcast_ssid = true; -+ } -+ } else { -+ if (mwl_vif->broadcast_ssid != false) { -+ mwl_fwcmd_broadcast_ssid_enable(hw, vif, false); -+ mwl_vif->broadcast_ssid = false; -+ } -+ } -+ -+ if (!mwl_vif->set_beacon) { -+ skb = ieee80211_beacon_get(hw, vif); -+ -+ if (skb) { -+ mwl_fwcmd_set_beacon(hw, vif, skb->data, skb->len); -+ dev_kfree_skb_any(skb); -+ } -+ mwl_vif->set_beacon = true; -+ } -+ } -+ -+ if (changed & BSS_CHANGED_BEACON_ENABLED) -+ mwl_fwcmd_bss_start(hw, vif, info->enable_beacon); -+} -+ -+static void mwl_mac80211_bss_info_changed(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_bss_conf *info, -+ u32 changed) -+{ -+ switch (vif->type) { -+ case NL80211_IFTYPE_AP: -+ case NL80211_IFTYPE_MESH_POINT: -+ mwl_mac80211_bss_info_changed_ap(hw, vif, info, changed); -+ break; -+ case NL80211_IFTYPE_STATION: -+ mwl_mac80211_bss_info_changed_sta(hw, vif, info, changed); -+ break; -+ default: -+ break; -+ } -+} -+ -+static void mwl_mac80211_configure_filter(struct ieee80211_hw *hw, -+ unsigned int changed_flags, -+ unsigned int *total_flags, -+ u64 multicast) -+{ -+ /* AP firmware doesn't allow fine-grained control over -+ * the receive filter. -+ */ -+ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; -+} -+ -+static int mwl_mac80211_set_key(struct ieee80211_hw *hw, -+ enum set_key_cmd cmd_param, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta, -+ struct ieee80211_key_conf *key) -+{ -+ struct mwl_vif *mwl_vif; -+ struct mwl_sta *sta_info; -+ int rc = 0; -+ u8 encr_type; -+ u8 *addr; -+ -+ mwl_vif = mwl_dev_get_vif(vif); -+ addr = sta ? sta->addr : vif->addr; -+ -+ if (cmd_param == SET_KEY) { -+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) || -+ (key->cipher == WLAN_CIPHER_SUITE_WEP104)) { -+ encr_type = ENCR_TYPE_WEP; -+ } else if (key->cipher == WLAN_CIPHER_SUITE_CCMP) { -+ encr_type = ENCR_TYPE_AES; -+ if ((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) == 0) { -+ if (vif->type != NL80211_IFTYPE_STATION) -+ mwl_vif->keyidx = key->keyidx; -+ } -+ } else if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { -+ encr_type = ENCR_TYPE_TKIP; -+ } else { -+ encr_type = ENCR_TYPE_DISABLE; -+ } -+ -+ rc = mwl_fwcmd_update_encryption_enable(hw, vif, addr, -+ encr_type); -+ if (rc) -+ goto out; -+ rc = mwl_fwcmd_encryption_set_key(hw, vif, addr, key); -+ if (rc) -+ goto out; -+ -+ mwl_vif->is_hw_crypto_enabled = true; -+ if (sta) { -+ sta_info = mwl_dev_get_sta(sta); -+ sta_info->is_key_set = true; -+ } -+ } else { -+ rc = mwl_fwcmd_encryption_remove_key(hw, vif, addr, key); -+ if (rc) -+ goto out; -+ } -+ -+out: -+ -+ return rc; -+} -+ -+static int mwl_mac80211_set_rts_threshold(struct ieee80211_hw *hw, -+ u32 value) -+{ -+ return mwl_fwcmd_set_rts_threshold(hw, value); -+} -+ -+static int mwl_mac80211_sta_add(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_priv *priv = hw->priv; -+ u16 stnid, sta_stnid = 0; -+ struct mwl_vif *mwl_vif; -+ struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); -+ bool use_4addr = wdev->use_4addr; -+ struct mwl_sta *sta_info; -+ struct ieee80211_key_conf *key; -+ int rc; -+ int i; -+ -+ if (vif->type == NL80211_IFTYPE_STATION) -+ sta->aid = 1; -+ mwl_vif = mwl_dev_get_vif(vif); -+ stnid = utils_assign_stnid(priv, mwl_vif->macid, sta->aid); -+ if (!stnid) -+ return -EPERM; -+ if (vif->type == NL80211_IFTYPE_STATION) { -+ sta_stnid = utils_assign_stnid(priv, mwl_vif->macid, -+ sta->aid + 1); -+ if (!sta_stnid) { -+ utils_free_stnid(priv, stnid); -+ return -EPERM; -+ } -+ ether_addr_copy(mwl_vif->bssid, sta->addr); -+ } -+ sta_info = mwl_dev_get_sta(sta); -+ memset(sta_info, 0, sizeof(*sta_info)); -+ -+ if (vif->type == NL80211_IFTYPE_MESH_POINT) -+ sta_info->is_mesh_node = true; -+ -+ if (sta->ht_cap.ht_supported) { -+ sta_info->is_ampdu_allowed = true; -+ sta_info->is_amsdu_allowed = false; -+ if (sta->ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) -+ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_8K; -+ else -+ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_4K; -+ if ((sta->tdls) && (!sta->wme)) -+ sta->wme = true; -+ } -+ sta_info->mwl_vif = mwl_vif; -+ sta_info->stnid = stnid; -+ if (vif->type == NL80211_IFTYPE_STATION) -+ sta_info->sta_stnid = sta_stnid; -+ sta_info->tx_rate_info = utils_get_init_tx_rate(priv, &hw->conf, sta); -+ sta_info->iv16 = 1; -+ sta_info->iv32 = 0; -+ spin_lock_init(&sta_info->amsdu_lock); -+ spin_lock_bh(&priv->sta_lock); -+ list_add_tail(&sta_info->list, &priv->sta_list); -+ spin_unlock_bh(&priv->sta_lock); -+ -+ if (vif->type == NL80211_IFTYPE_STATION) -+ mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr); -+ -+ if (priv->chip_type == MWL8964) { -+ if (use_4addr) { -+ sta_info->wds = true; -+ rc = mwl_fwcmd_set_new_stn_add_sc4(hw, vif, sta, -+ WDS_MODE); -+ } else -+ rc = mwl_fwcmd_set_new_stn_add_sc4(hw, vif, sta, 0); -+ } else -+ rc = mwl_fwcmd_set_new_stn_add(hw, vif, sta); -+ -+ if ((vif->type == NL80211_IFTYPE_STATION) && !use_4addr) -+ mwl_hif_set_sta_id(hw, sta, true, true); -+ else -+ mwl_hif_set_sta_id(hw, sta, false, true); -+ -+ for (i = 0; i < NUM_WEP_KEYS; i++) { -+ key = (struct ieee80211_key_conf *)mwl_vif->wep_key_conf[i].key; -+ -+ if (mwl_vif->wep_key_conf[i].enabled) -+ mwl_mac80211_set_key(hw, SET_KEY, vif, sta, key); -+ } -+ -+ mwl_get_rateinfo(priv, sta->addr, sta_info); -+ -+ return rc; -+} -+ -+static int mwl_mac80211_sta_remove(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_sta *sta) -+{ -+ struct mwl_priv *priv = hw->priv; -+ int rc; -+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta); -+ -+ mwl_hif_tx_del_sta_amsdu_pkts(hw, sta); -+ mwl_fwcmd_del_sta_streams(hw, sta); -+ mwl_hif_tx_del_pkts_via_sta(hw, sta); -+ -+ rc = mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr); -+ -+ if (vif->type == NL80211_IFTYPE_STATION) -+ mwl_hif_set_sta_id(hw, sta, true, false); -+ else -+ mwl_hif_set_sta_id(hw, sta, false, false); -+ -+ if (priv->chip_type != MWL8964) -+ utils_free_stnid(priv, sta_info->stnid); -+ if (vif->type == NL80211_IFTYPE_STATION) -+ utils_free_stnid(priv, sta_info->sta_stnid); -+ -+ spin_lock_bh(&priv->sta_lock); -+ list_del(&sta_info->list); -+ spin_unlock_bh(&priv->sta_lock); -+ -+ return rc; -+} -+ -+static int mwl_mac80211_conf_tx(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ u16 queue, -+ const struct ieee80211_tx_queue_params *params) -+{ -+ struct mwl_priv *priv = hw->priv; -+ int rc = 0; -+ -+ if (WARN_ON(queue > SYSADPT_TX_WMM_QUEUES - 1)) -+ return -EINVAL; -+ -+ memcpy(&priv->wmm_params[queue], params, sizeof(*params)); -+ -+ if (!priv->wmm_enabled) { -+ rc = mwl_fwcmd_set_wmm_mode(hw, true); -+ priv->wmm_enabled = true; -+ } -+ -+ if (!rc) { -+ int q = SYSADPT_TX_WMM_QUEUES - 1 - queue; -+ -+ rc = mwl_fwcmd_set_edca_params(hw, q, -+ params->cw_min, params->cw_max, -+ params->aifs, params->txop); -+ } -+ -+ return rc; -+} -+ -+static int mwl_mac80211_get_stats(struct ieee80211_hw *hw, -+ struct ieee80211_low_level_stats *stats) -+{ -+ return mwl_fwcmd_get_stat(hw, stats); -+} -+ -+static int mwl_mac80211_get_survey(struct ieee80211_hw *hw, -+ int idx, -+ struct survey_info *survey) -+{ -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_survey_info *survey_info; -+ -+ if (priv->survey_info_idx) { -+ if (idx >= priv->survey_info_idx) { -+ priv->survey_info_idx = 0; -+ return -ENOENT; -+ } -+ survey_info = &priv->survey_info[idx]; -+ } else { -+ if (idx != 0) -+ return -ENOENT; -+ mwl_fwcmd_get_survey(hw, 0); -+ survey_info = &priv->cur_survey_info; -+ if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) -+ survey->filled |= SURVEY_INFO_IN_USE; -+ } -+ -+ survey->channel = &survey_info->channel; -+ survey->filled |= survey_info->filled; -+ survey->time = survey_info->time_period / 1000; -+ survey->time_busy = survey_info->time_busy / 1000; -+ survey->time_tx = survey_info->time_tx / 1000; -+ survey->noise = survey_info->noise; -+ -+ return 0; -+} -+ -+static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_ampdu_params *params) -+{ -+ int rc = 0; -+ struct mwl_priv *priv = hw->priv; -+ struct mwl_ampdu_stream *stream; -+ enum ieee80211_ampdu_mlme_action action = params->action; -+ struct ieee80211_sta *sta = params->sta; -+ u16 tid = params->tid; -+ u8 buf_size = params->buf_size; -+ u8 *addr = sta->addr; -+ struct mwl_sta *sta_info; -+ -+ sta_info = mwl_dev_get_sta(sta); -+ -+ spin_lock_bh(&priv->stream_lock); -+ -+ stream = mwl_fwcmd_lookup_stream(hw, sta, tid); -+ -+ switch (action) { -+ case IEEE80211_AMPDU_RX_START: -+ if (priv->chip_type == MWL8964) { -+ struct mwl_ampdu_stream tmp; -+ -+ tmp.sta = sta; -+ tmp.tid = tid; -+ spin_unlock_bh(&priv->stream_lock); -+ mwl_fwcmd_create_ba(hw, &tmp, vif, -+ BA_FLAG_DIRECTION_DOWN, -+ buf_size, params->ssn, -+ params->amsdu); -+ spin_lock_bh(&priv->stream_lock); -+ break; -+ } -+ case IEEE80211_AMPDU_RX_STOP: -+ if (priv->chip_type == MWL8964) { -+ struct mwl_ampdu_stream tmp; -+ -+ tmp.sta = sta; -+ tmp.tid = tid; -+ spin_unlock_bh(&priv->stream_lock); -+ mwl_fwcmd_destroy_ba(hw, &tmp, -+ BA_FLAG_DIRECTION_DOWN); -+ spin_lock_bh(&priv->stream_lock); -+ } -+ break; -+ case IEEE80211_AMPDU_TX_START: -+ if (!sta_info->is_ampdu_allowed) { -+ wiphy_warn(hw->wiphy, "ampdu not allowed\n"); -+ rc = -EPERM; -+ break; -+ } -+ -+ if (!stream) { -+ stream = mwl_fwcmd_add_stream(hw, sta, tid); -+ if (!stream) { -+ wiphy_warn(hw->wiphy, "no stream found\n"); -+ rc = -EPERM; -+ break; -+ } -+ } -+ -+ if (priv->chip_type != MWL8964) { -+ spin_unlock_bh(&priv->stream_lock); -+ rc = mwl_fwcmd_check_ba(hw, stream, vif, -+ BA_FLAG_DIRECTION_UP); -+ spin_lock_bh(&priv->stream_lock); -+ if (rc) { -+ mwl_fwcmd_remove_stream(hw, stream); -+ sta_info->check_ba_failed[tid]++; -+ break; -+ } -+ } -+ stream->state = AMPDU_STREAM_IN_PROGRESS; -+ spin_unlock_bh(&priv->stream_lock); -+ rc = mwl_fwcmd_get_seqno(hw, stream, ¶ms->ssn); -+ spin_lock_bh(&priv->stream_lock); -+ if (rc) -+ break; -+ ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); -+ break; -+ case IEEE80211_AMPDU_TX_STOP_CONT: -+ case IEEE80211_AMPDU_TX_STOP_FLUSH: -+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: -+ if (stream) { -+ if (stream->state == AMPDU_STREAM_ACTIVE) { -+ stream->state = AMPDU_STREAM_IN_PROGRESS; -+ mwl_hif_tx_del_ampdu_pkts(hw, sta, tid); -+ spin_unlock_bh(&priv->stream_lock); -+ mwl_fwcmd_destroy_ba(hw, stream, -+ BA_FLAG_DIRECTION_UP); -+ spin_lock_bh(&priv->stream_lock); -+ sta_info->is_amsdu_allowed = false; -+ } -+ -+ mwl_fwcmd_remove_stream(hw, stream); -+ ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); -+ } else { -+ rc = -EPERM; -+ } -+ break; -+ case IEEE80211_AMPDU_TX_OPERATIONAL: -+ if (stream) { -+ if (WARN_ON(stream->state != -+ AMPDU_STREAM_IN_PROGRESS)) { -+ rc = -EPERM; -+ break; -+ } -+ spin_unlock_bh(&priv->stream_lock); -+ rc = mwl_fwcmd_create_ba(hw, stream, vif, -+ BA_FLAG_DIRECTION_UP, -+ buf_size, params->ssn, -+ params->amsdu); -+ spin_lock_bh(&priv->stream_lock); -+ -+ if (!rc) { -+ stream->state = AMPDU_STREAM_ACTIVE; -+ sta_info->check_ba_failed[tid] = 0; -+ if (priv->tx_amsdu) -+ sta_info->is_amsdu_allowed = -+ params->amsdu; -+ else -+ sta_info->is_amsdu_allowed = false; -+ } else { -+ spin_unlock_bh(&priv->stream_lock); -+ mwl_fwcmd_destroy_ba(hw, stream, -+ BA_FLAG_DIRECTION_UP); -+ spin_lock_bh(&priv->stream_lock); -+ mwl_fwcmd_remove_stream(hw, stream); -+ wiphy_err(hw->wiphy, -+ "ampdu operation error code: %d\n", -+ rc); -+ } -+ } else { -+ rc = -EPERM; -+ } -+ break; -+ default: -+ rc = -ENOTSUPP; -+ break; -+ } -+ -+ spin_unlock_bh(&priv->stream_lock); -+ -+ return rc; -+} -+ -+static int mwl_mac80211_chnl_switch(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ struct ieee80211_channel_switch *ch_switch) -+{ -+ int rc = 0; -+ -+ rc = mwl_fwcmd_set_switch_channel(hw, ch_switch); -+ -+ return rc; -+} -+ -+static void mwl_mac80211_sw_scan_start(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif, -+ const u8 *mac_addr) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ priv->sw_scanning = true; -+ priv->survey_info_idx = 0; -+} -+ -+static void mwl_mac80211_sw_scan_complete(struct ieee80211_hw *hw, -+ struct ieee80211_vif *vif) -+{ -+ struct mwl_priv *priv = hw->priv; -+ -+ priv->sw_scanning = false; -+} -+ -+const struct ieee80211_ops mwl_mac80211_ops = { -+ .tx = mwl_mac80211_tx, -+ .start = mwl_mac80211_start, -+ .stop = mwl_mac80211_stop, -+ .add_interface = mwl_mac80211_add_interface, -+ .remove_interface = mwl_mac80211_remove_interface, -+ .config = mwl_mac80211_config, -+ .bss_info_changed = mwl_mac80211_bss_info_changed, -+ .configure_filter = mwl_mac80211_configure_filter, -+ .set_key = mwl_mac80211_set_key, -+ .set_rts_threshold = mwl_mac80211_set_rts_threshold, -+ .sta_add = mwl_mac80211_sta_add, -+ .sta_remove = mwl_mac80211_sta_remove, -+ .conf_tx = mwl_mac80211_conf_tx, -+ .get_stats = mwl_mac80211_get_stats, -+ .get_survey = mwl_mac80211_get_survey, -+ .ampdu_action = mwl_mac80211_ampdu_action, -+ .pre_channel_switch = mwl_mac80211_chnl_switch, -+ .sw_scan_start = mwl_mac80211_sw_scan_start, -+ .sw_scan_complete = mwl_mac80211_sw_scan_complete, -+}; -diff --git a/drivers/net/wireless/marvell/mwlwifi/mu_mimo.c b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.c -new file mode 100644 -index 000000000000..74ab054f947e ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.c -@@ -0,0 +1,21 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements MU-MIMO functions. */ -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "mu_mimo.h" -+ -diff --git a/drivers/net/wireless/marvell/mwlwifi/mu_mimo.h b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.h -new file mode 100644 -index 000000000000..24179f404774 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/mu_mimo.h -@@ -0,0 +1,23 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines MU-MIMO functions. */ -+ -+#ifndef _MU_MIMO_H_ -+#define _MU_MIMO_H_ -+ -+ -+ -+#endif /* _MU_MIMO_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/sysadpt.h b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h -new file mode 100644 -index 000000000000..1194e5271870 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h -@@ -0,0 +1,86 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines system adaptation related information. */ -+ -+#ifndef _SYSADPT_H_ -+#define _SYSADPT_H_ -+ -+#define SYSADPT_MAX_STA 64 -+ -+#define SYSADPT_MAX_STA_SC4 300 -+ -+#define SYSADPT_MAX_NUM_CHANNELS 64 -+ -+#define SYSADPT_MAX_DATA_RATES_G 14 -+ -+#define SYSADPT_MAX_MCS_RATES 24 -+ -+#define SYSADPT_MAX_11AC_RATES 20 -+ -+#define SYSADPT_MAX_RATE_ADAPT_RATES (SYSADPT_MAX_DATA_RATES_G + \ -+ SYSADPT_MAX_MCS_RATES + \ -+ SYSADPT_MAX_11AC_RATES) -+ -+#define SYSADPT_TX_POWER_LEVEL_TOTAL 16 /* SC3 */ -+ -+#define SYSADPT_TX_GRP_PWR_LEVEL_TOTAL 28 /* KF2 */ -+ -+#define SYSADPT_TX_PWR_LEVEL_TOTAL_SC4 32 /* SC4 */ -+ -+#define SYSADPT_TX_WMM_QUEUES 4 -+ -+#define SYSADPT_NUM_OF_CLIENT 1 -+ -+#define SYSADPT_NUM_OF_AP 16 -+ -+#define SYSADPT_NUM_OF_MESH 1 -+ -+#define SYSADPT_TOTAL_TX_QUEUES (SYSADPT_TX_WMM_QUEUES + \ -+ SYSADPT_NUM_OF_AP) -+ -+#define SYSADPT_MAX_AGGR_SIZE 4096 -+ -+#define SYSADPT_AMPDU_PACKET_THRESHOLD 64 -+ -+#define SYSADPT_AMSDU_FW_MAX_SIZE 3300 -+ -+#define SYSADPT_AMSDU_4K_MAX_SIZE SYSADPT_AMSDU_FW_MAX_SIZE -+ -+#define SYSADPT_AMSDU_8K_MAX_SIZE SYSADPT_AMSDU_FW_MAX_SIZE -+ -+#define SYSADPT_AMSDU_ALLOW_SIZE 1600 -+ -+#define SYSADPT_AMSDU_FLUSH_TIME 500 -+ -+#define SYSADPT_AMSDU_PACKET_THRESHOLD 10 -+ -+#define SYSADPT_MAX_TID 8 -+ -+#define SYSADPT_QUIET_PERIOD_DEFAULT 100 -+ -+#define SYSADPT_QUIET_PERIOD_MIN 25 -+ -+#define SYSADPT_QUIET_START_OFFSET 10 -+ -+#define SYSADPT_THERMAL_THROTTLE_MAX 100 -+ -+#define SYSADPT_TIMER_WAKEUP_TIME 10 /* ms */ -+ -+#define SYSADPT_OTP_BUF_SIZE (256*8) /* 258 lines * 8 bytes */ -+ -+#define SYSADPT_TXPWRLMT_CFG_BUF_SIZE (3650) -+ -+#endif /* _SYSADPT_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/thermal.c b/drivers/net/wireless/marvell/mwlwifi/thermal.c -new file mode 100644 -index 000000000000..7c59def51e7f ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/thermal.c -@@ -0,0 +1,182 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements thermal framework related functions. */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "hif/fwcmd.h" -+#include "thermal.h" -+ -+static int -+mwl_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, -+ unsigned long *state) -+{ -+ *state = SYSADPT_THERMAL_THROTTLE_MAX; -+ -+ return 0; -+} -+ -+static int -+mwl_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, -+ unsigned long *state) -+{ -+ struct mwl_priv *priv = cdev->devdata; -+ -+ *state = priv->throttle_state; -+ -+ return 0; -+} -+ -+static int -+mwl_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, -+ unsigned long throttle_state) -+{ -+ struct mwl_priv *priv = cdev->devdata; -+ -+ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) { -+ wiphy_warn(priv->hw->wiphy, -+ "throttle state %ld is exceeding the limit %d\n", -+ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX); -+ return -EINVAL; -+ } -+ priv->throttle_state = throttle_state; -+ mwl_thermal_set_throttling(priv); -+ -+ return 0; -+} -+ -+static struct thermal_cooling_device_ops mwl_thermal_ops = { -+ .get_max_state = mwl_thermal_get_max_throttle_state, -+ .get_cur_state = mwl_thermal_get_cur_throttle_state, -+ .set_cur_state = mwl_thermal_set_cur_throttle_state, -+}; -+ -+static ssize_t mwl_thermal_show_temp(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct mwl_priv *priv = dev_get_drvdata(dev); -+ int ret, temperature; -+ -+ ret = mwl_fwcmd_get_temp(priv->hw, &priv->temperature); -+ if (ret) { -+ wiphy_warn(priv->hw->wiphy, "failed: can't get temperature\n"); -+ goto out; -+ } -+ -+ temperature = priv->temperature; -+ -+ /* display in millidegree celcius */ -+ ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); -+out: -+ return ret; -+} -+ -+static SENSOR_DEVICE_ATTR(temp1_input, 0444, mwl_thermal_show_temp, -+ NULL, 0); -+ -+static struct attribute *mwl_hwmon_attrs[] = { -+ &sensor_dev_attr_temp1_input.dev_attr.attr, -+ NULL, -+}; -+ATTRIBUTE_GROUPS(mwl_hwmon); -+ -+void mwl_thermal_set_throttling(struct mwl_priv *priv) -+{ -+ u32 period, duration, enabled; -+ int ret; -+ -+ period = priv->quiet_period; -+ duration = (period * priv->throttle_state) / 100; -+ enabled = duration ? 1 : 0; -+ -+ ret = mwl_fwcmd_quiet_mode(priv->hw, enabled, period, -+ duration, SYSADPT_QUIET_START_OFFSET); -+ if (ret) { -+ wiphy_warn(priv->hw->wiphy, -+ "failed: period %u duarion %u enabled %u ret %d\n", -+ period, duration, enabled, ret); -+ } -+} -+ -+int mwl_thermal_register(struct mwl_priv *priv) -+{ -+ struct thermal_cooling_device *cdev; -+ struct device *hwmon_dev; -+ int ret; -+ -+ if (priv->chip_type != MWL8897) -+ return 0; -+ -+ cdev = thermal_cooling_device_register("mwlwifi_thermal", priv, -+ &mwl_thermal_ops); -+ if (IS_ERR(cdev)) { -+ wiphy_err(priv->hw->wiphy, -+ "failed to setup thermal device result: %ld\n", -+ PTR_ERR(cdev)); -+ return -EINVAL; -+ } -+ -+ ret = sysfs_create_link(&priv->dev->kobj, &cdev->device.kobj, -+ "cooling_device"); -+ if (ret) { -+ wiphy_err(priv->hw->wiphy, -+ "failed to create cooling device symlink\n"); -+ goto err_cooling_destroy; -+ } -+ -+ priv->cdev = cdev; -+ priv->quiet_period = SYSADPT_QUIET_PERIOD_DEFAULT; -+ -+ if (!IS_ENABLED(CONFIG_HWMON)) -+ return 0; -+ -+ hwmon_dev = -+ devm_hwmon_device_register_with_groups(priv->dev, -+ "mwlwifi_hwmon", priv, -+ mwl_hwmon_groups); -+ if (IS_ERR(hwmon_dev)) { -+ wiphy_err(priv->hw->wiphy, -+ "failed to register hwmon device: %ld\n", -+ PTR_ERR(hwmon_dev)); -+ ret = -EINVAL; -+ goto err_remove_link; -+ } -+ -+ return 0; -+ -+err_remove_link: -+ sysfs_remove_link(&priv->dev->kobj, "cooling_device"); -+err_cooling_destroy: -+ thermal_cooling_device_unregister(cdev); -+ -+ return ret; -+} -+ -+void mwl_thermal_unregister(struct mwl_priv *priv) -+{ -+ if (priv->chip_type != MWL8897) -+ return; -+ -+ sysfs_remove_link(&priv->dev->kobj, "cooling_device"); -+ thermal_cooling_device_unregister(priv->cdev); -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/thermal.h b/drivers/net/wireless/marvell/mwlwifi/thermal.h -new file mode 100644 -index 000000000000..c7f0ad2b87eb ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/thermal.h -@@ -0,0 +1,42 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines Linux thermal framework related functions. */ -+ -+#ifndef _THERMAL_H_ -+#define _THERMAL_H_ -+ -+#include -+ -+#if IS_ENABLED(CONFIG_THERMAL) -+int mwl_thermal_register(struct mwl_priv *priv); -+void mwl_thermal_unregister(struct mwl_priv *priv); -+void mwl_thermal_set_throttling(struct mwl_priv *priv); -+#else -+static inline int mwl_thermal_register(struct mwl_priv *priv) -+{ -+ return 0; -+} -+ -+static inline void mwl_thermal_unregister(struct mwl_priv *priv) -+{ -+} -+ -+static inline void mwl_thermal_set_throttling(struct mwl_priv *priv) -+{ -+} -+#endif -+ -+#endif /* _THERMAL_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/utils.c b/drivers/net/wireless/marvell/mwlwifi/utils.c -new file mode 100644 -index 000000000000..b73054a3f55e ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/utils.c -@@ -0,0 +1,576 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements common utility functions. */ -+ -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+ -+static unsigned short phy_rate[][5] = { -+ {2, 13, 15, 27, 30}, /* 0 */ -+ {4, 26, 29, 54, 60}, /* 1 */ -+ {11, 39, 43, 81, 90}, /* 2 */ -+ {22, 52, 58, 108, 120}, /* 3 */ -+ {44, 78, 87, 162, 180}, /* 4 */ -+ {12, 104, 115, 216, 240}, /* 5 */ -+ {18, 117, 130, 243, 270}, /* 6 */ -+ {24, 130, 144, 270, 300}, /* 7 */ -+ {36, 26, 29, 54, 60}, /* 8 */ -+ {48, 52, 58, 108, 120}, /* 9 */ -+ {72, 78, 87, 162, 180}, /* 10 */ -+ {96, 104, 116, 216, 240}, /* 11 */ -+ {108, 156, 173, 324, 360}, /* 12 */ -+ {0, 208, 231, 432, 480}, /* 13 */ -+ {0, 234, 260, 486, 540}, /* 14 */ -+ {0, 260, 289, 540, 600}, /* 15 */ -+ {0, 39, 43, 81, 90}, /* 16 */ -+ {0, 78, 87, 162, 180}, /* 17 */ -+ {0, 117, 130, 243, 270}, /* 18 */ -+ {0, 156, 173, 324, 360}, /* 19 */ -+ {0, 234, 260, 486, 540}, /* 20 */ -+ {0, 312, 347, 648, 720}, /* 21 */ -+ {0, 351, 390, 729, 810}, /* 22 */ -+ {0, 390, 433, 810, 900}, /* 23 */ -+}; -+ -+/* 20Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ -+static unsigned short phy_rate_11ac20M[][6] = { -+ {13, 15, 26, 29, 39, 44}, /* 0 */ -+ {26, 29, 52, 58, 78, 87}, /* 1 */ -+ {39, 44, 78, 87, 117, 130}, /* 2 */ -+ {52, 58, 104, 116, 156, 174}, /* 3 */ -+ {78, 87, 156, 174, 234, 260}, /* 4 */ -+ {104, 116, 208, 231, 312, 347}, /* 5 */ -+ {117, 130, 234, 260, 351, 390}, /* 6 */ -+ {130, 145, 260, 289, 390, 434}, /* 7 */ -+ {156, 174, 312, 347, 468, 520}, /* 8 */ -+ /* Nss 1 and Nss 2 mcs9 not valid */ -+ {2, 2, 2, 2, 520, 578}, /* 9 */ -+}; -+ -+/* 40Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ -+static unsigned short phy_rate_11ac40M[][6] = { -+ {27, 30, 54, 60, 81, 90}, /* 0 */ -+ {54, 60, 108, 120, 162, 180}, /* 1 */ -+ {81, 90, 162, 180, 243, 270}, /* 2 */ -+ {108, 120, 216, 240, 324, 360}, /* 3 */ -+ {162, 180, 324, 360, 486, 540}, /* 4 */ -+ {216, 240, 432, 480, 648, 720}, /* 5 */ -+ {243, 270, 486, 540, 729, 810}, /* 6 */ -+ {270, 300, 540, 600, 810, 900}, /* 7 */ -+ {324, 360, 648, 720, 972, 1080}, /* 8 */ -+ {360, 400, 720, 800, 1080, 1200}, /* 9 */ -+}; -+ -+/* 80Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ -+static unsigned short phy_rate_11ac80M[][6] = { -+ {59, 65, 117, 130, 175, 195}, /* 0 */ -+ {117, 130, 234, 260, 351, 390}, /* 1 */ -+ {175, 195, 351, 390, 527, 585}, /* 2 */ -+ {234, 260, 468, 520, 702, 780}, /* 3 */ -+ {351, 390, 702, 780, 1053, 1170}, /* 4 */ -+ {468, 520, 936, 1040, 1404, 1560}, /* 5 */ -+ {527, 585, 1053, 1170, 2, 2}, /* 6, Nss 3 mcs6 not valid */ -+ {585, 650, 1170, 1300, 1755, 1950}, /* 7 */ -+ {702, 780, 1404, 1560, 2106, 2340}, /* 8 */ -+ {780, 867, 1560, 1733, 2340, 2600}, /* 9 */ -+}; -+ -+/* 160Mhz: Nss1_LGI, Nss1_SGI, Nss2_LGI, Nss2_SGI, Nss3_LGI, Nss3_SGI */ -+static unsigned short phy_rate_11ac160M[][6] = { -+ {117, 130, 234, 260, 351, 390}, /* 0 */ -+ {234, 260, 468, 520, 702, 780}, /* 1 */ -+ {351, 390, 702, 780, 1053, 1170}, /* 2 */ -+ {468, 520, 936, 1040, 1404, 1560}, /* 3 */ -+ {702, 780, 1404, 1560, 2106, 2340}, /* 4 */ -+ {936, 1040, 1872, 2080, 2808, 3120}, /* 5 */ -+ {1053, 1170, 2106, 2340, 3159, 3510}, /* 6 */ -+ {1170, 1300, 2340, 2600, 3510, 3900}, /* 7 */ -+ {1404, 1560, 2808, 3120, 4212, 4680}, /* 8 */ -+ {1560, 1733, 2130, 3467, 4680, 5200}, /* 9 */ -+}; -+ -+int utils_get_phy_rate(u8 format, u8 bandwidth, u8 short_gi, u8 mcs_id) -+{ -+ u8 index = 0; -+ u8 nss_11ac = 0; -+ u8 rate_11ac = 0; -+ -+ if (format == TX_RATE_FORMAT_11N) { -+ index = (bandwidth << 1) | short_gi; -+ index++; -+ } else if (format == TX_RATE_FORMAT_11AC) { -+ rate_11ac = mcs_id & 0xf; /* 11ac, mcs_id[3:0]: rate */ -+ nss_11ac = mcs_id >> 4; /* 11ac, mcs_id[6:4]: nss code */ -+ index = (nss_11ac << 1) | short_gi; -+ } -+ -+ if (format != TX_RATE_FORMAT_11AC) -+ return (phy_rate[mcs_id][index] / 2); -+ -+ if (bandwidth == TX_RATE_BANDWIDTH_20) -+ return (phy_rate_11ac20M[rate_11ac][index] / 2); -+ else if (bandwidth == TX_RATE_BANDWIDTH_40) -+ return (phy_rate_11ac40M[rate_11ac][index] / 2); -+ else if (bandwidth == TX_RATE_BANDWIDTH_80) -+ return (phy_rate_11ac80M[rate_11ac][index] / 2); -+ else -+ return (phy_rate_11ac160M[rate_11ac][index] / 2); -+} -+ -+u8 utils_get_rate_id(u8 rate) -+{ -+ switch (rate) { -+ case 10: /* 1 Mbit/s or 12 Mbit/s */ -+ return 0; -+ case 20: /* 2 Mbit/s */ -+ return 1; -+ case 55: /* 5.5 Mbit/s */ -+ return 2; -+ case 110: /* 11 Mbit/s */ -+ return 3; -+ case 220: /* 22 Mbit/s */ -+ return 4; -+ case 0xb: /* 6 Mbit/s */ -+ return 5; -+ case 0xf: /* 9 Mbit/s */ -+ return 6; -+ case 0xe: /* 18 Mbit/s */ -+ return 8; -+ case 0x9: /* 24 Mbit/s */ -+ return 9; -+ case 0xd: /* 36 Mbit/s */ -+ return 10; -+ case 0x8: /* 48 Mbit/s */ -+ return 11; -+ case 0xc: /* 54 Mbit/s */ -+ return 12; -+ case 0x7: /* 72 Mbit/s */ -+ return 13; -+ } -+ -+ return 0; -+} -+ -+u32 utils_get_init_tx_rate(struct mwl_priv *priv, struct ieee80211_conf *conf, -+ struct ieee80211_sta *sta) -+{ -+ u32 tx_rate; -+ u16 format, nss, bw, rate_mcs; -+ -+ if (sta->vht_cap.vht_supported) -+ format = TX_RATE_FORMAT_11AC; -+ else if (sta->ht_cap.ht_supported) -+ format = TX_RATE_FORMAT_11N; -+ else -+ format = TX_RATE_FORMAT_LEGACY; -+ -+ switch (priv->antenna_tx) { -+ case ANTENNA_TX_1: -+ nss = 1; -+ break; -+ case ANTENNA_TX_2: -+ nss = 2; -+ break; -+ case ANTENNA_TX_3: -+ case ANTENNA_TX_4_AUTO: -+ nss = 3; -+ break; -+ default: -+ nss = sta->rx_nss; -+ break; -+ } -+ if (nss > sta->rx_nss) -+ nss = sta->rx_nss; -+ -+ switch (conf->chandef.width) { -+ case NL80211_CHAN_WIDTH_20_NOHT: -+ case NL80211_CHAN_WIDTH_20: -+ bw = TX_RATE_BANDWIDTH_20; -+ break; -+ case NL80211_CHAN_WIDTH_40: -+ bw = TX_RATE_BANDWIDTH_40; -+ break; -+ case NL80211_CHAN_WIDTH_80: -+ bw = TX_RATE_BANDWIDTH_80; -+ break; -+ case NL80211_CHAN_WIDTH_160: -+ bw = TX_RATE_BANDWIDTH_160; -+ break; -+ default: -+ bw = sta->bandwidth; -+ break; -+ } -+ if (bw > sta->bandwidth) -+ bw = sta->bandwidth; -+ -+ switch (format) { -+ case TX_RATE_FORMAT_LEGACY: -+ rate_mcs = 12; /* ignore 11b */ -+ break; -+ case TX_RATE_FORMAT_11N: -+ rate_mcs = (nss * 8) - 1; -+ break; -+ default: -+ rate_mcs = ((nss - 1) << 4) | 8; -+ break; -+ } -+ -+ tx_rate = (format | (bw << MWL_TX_RATE_BANDWIDTH_SHIFT) | -+ (TX_RATE_INFO_SHORT_GI << MWL_TX_RATE_SHORTGI_SHIFT) | -+ (rate_mcs << MWL_TX_RATE_RATEIDMCS_SHIFT)); -+ -+ return tx_rate; -+} -+ -+struct mwl_vif *utils_find_vif_bss(struct mwl_priv *priv, u8 *bssid) -+{ -+ struct mwl_vif *mwl_vif; -+ -+ spin_lock_bh(&priv->vif_lock); -+ list_for_each_entry(mwl_vif, &priv->vif_list, list) { -+ if (ether_addr_equal(bssid, mwl_vif->bssid)) { -+ spin_unlock_bh(&priv->vif_lock); -+ return mwl_vif; -+ } -+ } -+ spin_unlock_bh(&priv->vif_lock); -+ -+ return NULL; -+} -+ -+struct mwl_sta *utils_find_sta(struct mwl_priv *priv, u8 *addr) -+{ -+ struct mwl_sta *sta_info; -+ struct ieee80211_sta *sta; -+ -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ sta = container_of((void *)sta_info, struct ieee80211_sta, -+ drv_priv); -+ if (ether_addr_equal(addr, sta->addr)) { -+ spin_unlock_bh(&priv->sta_lock); -+ return sta_info; -+ } -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ return NULL; -+} -+ -+struct mwl_sta *utils_find_sta_by_aid(struct mwl_priv *priv, u16 aid) -+{ -+ struct mwl_sta *sta_info; -+ struct ieee80211_sta *sta; -+ -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ sta = container_of((void *)sta_info, struct ieee80211_sta, -+ drv_priv); -+ if (sta->aid == aid) { -+ spin_unlock_bh(&priv->sta_lock); -+ return sta_info; -+ } -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ return NULL; -+} -+ -+struct mwl_sta *utils_find_sta_by_id(struct mwl_priv *priv, u16 stnid) -+{ -+ struct mwl_sta *sta_info; -+ -+ spin_lock_bh(&priv->sta_lock); -+ list_for_each_entry(sta_info, &priv->sta_list, list) { -+ if (sta_info->stnid == stnid) { -+ spin_unlock_bh(&priv->sta_lock); -+ return sta_info; -+ } -+ } -+ spin_unlock_bh(&priv->sta_lock); -+ -+ return NULL; -+} -+ -+void utils_dump_data_info(const char *prefix_str, const void *buf, size_t len) -+{ -+ print_hex_dump(KERN_INFO, prefix_str, DUMP_PREFIX_OFFSET, -+ 16, 1, buf, len, true); -+} -+ -+void utils_dump_data_debug(const char *prefix_str, const void *buf, size_t len) -+{ -+ print_hex_dump(KERN_DEBUG, prefix_str, DUMP_PREFIX_OFFSET, -+ 16, 1, buf, len, true); -+} -+ -+bool utils_is_non_amsdu_packet(const void *packet, bool mac80211) -+{ -+ const u8 *data = packet; -+ struct ieee80211_hdr *wh; -+ __be16 *protocol; -+ struct iphdr *iph; -+ struct udphdr *udph; -+ -+ if (mac80211) { -+ /* mac80211 packet */ -+ wh = (struct ieee80211_hdr *)data; -+ data += ieee80211_hdrlen(wh->frame_control) + 6; -+ protocol = (__be16 *)data; -+ } else { -+ /* mac802.3 packet */ -+ data += (2 * ETH_ALEN); -+ protocol = (__be16 *)data; -+ } -+ -+ if (*protocol == cpu_to_be16(ETH_P_PAE)) -+ return true; -+ -+ if (*protocol == htons(ETH_P_ARP)) -+ return true; -+ -+ if (*protocol == htons(ETH_P_IP)) { -+ data += sizeof(__be16); -+ iph = (struct iphdr *)data; -+ if (iph->protocol == IPPROTO_ICMP) -+ return true; -+ if (iph->protocol == IPPROTO_UDP) { -+ data += (iph->ihl * 4); -+ udph = (struct udphdr *)data; -+ if (((udph->source == htons(68)) && -+ (udph->dest == htons(67))) || -+ ((udph->source == htons(67)) && -+ (udph->dest == htons(68)))) -+ return true; -+ } -+ } -+ -+ return false; -+} -+ -+bool utils_is_arp(const void *packet, bool mac80211, u16 *arp_op) -+{ -+ const u8 *data = packet; -+ struct ieee80211_hdr *wh; -+ __be16 *protocol; -+ struct arphdr *arph; -+ -+ if (mac80211) { -+ /* mac80211 packet */ -+ wh = (struct ieee80211_hdr *)data; -+ data += ieee80211_hdrlen(wh->frame_control) + 6; -+ protocol = (__be16 *)data; -+ } else { -+ /* mac802.3 packet */ -+ data += (2 * ETH_ALEN); -+ protocol = (__be16 *)data; -+ } -+ -+ if (*protocol == htons(ETH_P_ARP)) { -+ data += sizeof(__be16); -+ arph = (struct arphdr *)data; -+ *arp_op = ntohs(arph->ar_op); -+ return true; -+ } -+ -+ return false; -+} -+ -+bool utils_is_icmp_echo(const void *packet, bool mac80211, u8 *type) -+{ -+ const u8 *data = packet; -+ struct ieee80211_hdr *wh; -+ __be16 *protocol; -+ struct iphdr *iph; -+ struct icmphdr *icmph; -+ -+ if (mac80211) { -+ /* mac80211 packet */ -+ wh = (struct ieee80211_hdr *)data; -+ data += ieee80211_hdrlen(wh->frame_control) + 6; -+ protocol = (__be16 *)data; -+ } else { -+ /* mac802.3 packet */ -+ data += (2 * ETH_ALEN); -+ protocol = (__be16 *)data; -+ } -+ -+ if (*protocol == htons(ETH_P_IP)) { -+ data += sizeof(__be16); -+ iph = (struct iphdr *)data; -+ if (iph->protocol == IPPROTO_ICMP) { -+ data += (iph->ihl * 4); -+ icmph = (struct icmphdr *)data; -+ *type = icmph->type; -+ return true; -+ } -+ } -+ -+ return false; -+} -+ -+bool utils_is_dhcp(const void *packet, bool mac80211, u8 *op, u8 *dhcp_client) -+{ -+ const u8 *data = packet; -+ struct ieee80211_hdr *wh; -+ __be16 *protocol; -+ struct iphdr *iph; -+ struct udphdr *udph; -+ -+ if (mac80211) { -+ /* mac80211 packet */ -+ wh = (struct ieee80211_hdr *)data; -+ data += ieee80211_hdrlen(wh->frame_control) + 6; -+ protocol = (__be16 *)data; -+ } else { -+ /* mac802.3 packet */ -+ data += (2 * ETH_ALEN); -+ protocol = (__be16 *)data; -+ } -+ -+ if (*protocol == htons(ETH_P_IP)) { -+ data += sizeof(__be16); -+ iph = (struct iphdr *)data; -+ if (iph->protocol == IPPROTO_UDP) { -+ data += (iph->ihl * 4); -+ udph = (struct udphdr *)data; -+ if (((udph->source == htons(68)) && -+ (udph->dest == htons(67))) || -+ ((udph->source == htons(67)) && -+ (udph->dest == htons(68)))) { -+ data += sizeof(struct udphdr); -+ *op = *data; -+ ether_addr_copy(dhcp_client, data + 28); -+ return true; -+ } -+ } -+ } -+ -+ return false; -+} -+ -+void utils_dump_arp(const void *packet, bool mac80211, size_t len) -+{ -+ const u8 *data = packet; -+ struct ieee80211_hdr *wh; -+ __be16 *protocol; -+ struct arphdr *arph; -+ -+ if (mac80211) { -+ /* mac80211 packet */ -+ wh = (struct ieee80211_hdr *)data; -+ data += ieee80211_hdrlen(wh->frame_control) + 6; -+ protocol = (__be16 *)data; -+ } else { -+ /* mac802.3 packet */ -+ data += (2 * ETH_ALEN); -+ protocol = (__be16 *)data; -+ } -+ -+ if (*protocol == htons(ETH_P_ARP)) { -+ data += sizeof(__be16); -+ arph = (struct arphdr *)data; -+ if (arph->ar_op == htons(ARPOP_REQUEST)) -+ utils_dump_data_info("ARP REQUEST: ", packet, len); -+ else if (arph->ar_op == htons(ARPOP_REPLY)) -+ utils_dump_data_info("ARP REPLY: ", packet, len); -+ } -+} -+ -+void utils_dump_icmp_echo(const void *packet, bool mac80211, size_t len) -+{ -+ const u8 *data = packet; -+ struct ieee80211_hdr *wh; -+ __be16 *protocol; -+ struct iphdr *iph; -+ struct icmphdr *icmph; -+ -+ if (mac80211) { -+ /* mac80211 packet */ -+ wh = (struct ieee80211_hdr *)data; -+ data += ieee80211_hdrlen(wh->frame_control) + 6; -+ protocol = (__be16 *)data; -+ } else { -+ /* mac802.3 packet */ -+ data += (2 * ETH_ALEN); -+ protocol = (__be16 *)data; -+ } -+ -+ if (*protocol == htons(ETH_P_IP)) { -+ data += sizeof(__be16); -+ iph = (struct iphdr *)data; -+ if (iph->protocol == IPPROTO_ICMP) { -+ data += (iph->ihl * 4); -+ icmph = (struct icmphdr *)data; -+ if (icmph->type == ICMP_ECHO) -+ utils_dump_data_info("ECHO REQUEST: ", -+ packet, len); -+ else if (icmph->type == ICMP_ECHOREPLY) -+ utils_dump_data_info("ECHO REPLY: ", -+ packet, len); -+ } -+ } -+} -+ -+void utils_dump_dhcp(const void *packet, bool mac80211, size_t len) -+{ -+ const u8 *data = packet; -+ struct ieee80211_hdr *wh; -+ __be16 *protocol; -+ struct iphdr *iph; -+ struct udphdr *udph; -+ const char *dhcp_op[8] = { -+ "DHCPDISCOVER", -+ "DHCPOFFER", -+ "DHCPREQUEST", -+ "DHCPDECLINE", -+ "DHCPACK", -+ "DHCPNAK", -+ "DHCPRELEASE", -+ "DHCPINFORM" -+ }; -+ -+ if (mac80211) { -+ /* mac80211 packet */ -+ wh = (struct ieee80211_hdr *)data; -+ data += ieee80211_hdrlen(wh->frame_control) + 6; -+ protocol = (__be16 *)data; -+ } else { -+ /* mac802.3 packet */ -+ data += (2 * ETH_ALEN); -+ protocol = (__be16 *)data; -+ } -+ -+ if (*protocol == htons(ETH_P_IP)) { -+ data += sizeof(__be16); -+ iph = (struct iphdr *)data; -+ if (iph->protocol == IPPROTO_UDP) { -+ data += (iph->ihl * 4); -+ udph = (struct udphdr *)data; -+ if (((udph->source == htons(68)) && -+ (udph->dest == htons(67))) || -+ ((udph->source == htons(67)) && -+ (udph->dest == htons(68)))) { -+ data += sizeof(struct udphdr); -+ utils_dump_data_info(dhcp_op[*data - 1], -+ packet, len); -+ } -+ } -+ } -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/utils.h b/drivers/net/wireless/marvell/mwlwifi/utils.h -new file mode 100644 -index 000000000000..4a292e990412 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/utils.h -@@ -0,0 +1,158 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines common utility functions. */ -+ -+#ifndef _UTILS_H_ -+#define _UTILS_H_ -+ -+#include -+#include -+#include -+#include -+ -+/* DHCP message types */ -+#define DHCPDISCOVER 1 -+#define DHCPOFFER 2 -+#define DHCPREQUEST 3 -+#define DHCPDECLINE 4 -+#define DHCPACK 5 -+#define DHCPNAK 6 -+#define DHCPRELEASE 7 -+#define DHCPINFORM 8 -+ -+static inline int utils_tid_to_ac(u8 tid) -+{ -+ switch (tid) { -+ case 0: -+ case 3: -+ return IEEE80211_AC_BE; -+ case 1: -+ case 2: -+ return IEEE80211_AC_BK; -+ case 4: -+ case 5: -+ return IEEE80211_AC_VI; -+ case 6: -+ case 7: -+ return IEEE80211_AC_VO; -+ default: -+ break; -+ } -+ -+ return -1; -+} -+ -+static inline void utils_add_basic_rates(int band, struct sk_buff *skb) -+{ -+ struct ieee80211_mgmt *mgmt; -+ int len; -+ u8 *pos; -+ -+ mgmt = (struct ieee80211_mgmt *)skb->data; -+ len = skb->len - ieee80211_hdrlen(mgmt->frame_control); -+ len -= 4; -+ pos = (u8 *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, -+ mgmt->u.assoc_req.variable, -+ len); -+ if (pos) { -+ pos++; -+ len = *pos++; -+ while (len) { -+ if (band == NL80211_BAND_2GHZ) { -+ if ((*pos == 2) || (*pos == 4) || -+ (*pos == 11) || (*pos == 22)) -+ *pos |= 0x80; -+ } else { -+ if ((*pos == 12) || (*pos == 24) || -+ (*pos == 48)) -+ *pos |= 0x80; -+ } -+ pos++; -+ len--; -+ } -+ } -+} -+ -+static inline int utils_assign_stnid(struct mwl_priv *priv, int macid, u16 aid) -+{ -+ int stnid; -+ int i; -+ -+ spin_lock_bh(&priv->stnid_lock); -+ stnid = priv->available_stnid; -+ if (stnid >= priv->stnid_num) { -+ spin_unlock_bh(&priv->stnid_lock); -+ return 0; -+ } -+ priv->stnid[stnid].macid = macid; -+ priv->stnid[stnid].aid = aid; -+ stnid++; -+ for (i = stnid; i < priv->stnid_num; i++) { -+ if (!priv->stnid[i].aid) -+ break; -+ } -+ priv->available_stnid = i; -+ spin_unlock_bh(&priv->stnid_lock); -+ return stnid; -+} -+ -+static inline void utils_free_stnid(struct mwl_priv *priv, u16 stnid) -+{ -+ spin_lock_bh(&priv->stnid_lock); -+ if (stnid && (stnid <= priv->stnid_num)) { -+ stnid--; -+ priv->stnid[stnid].macid = 0; -+ priv->stnid[stnid].aid = 0; -+ if (priv->available_stnid > stnid) -+ priv->available_stnid = stnid; -+ } -+ spin_unlock_bh(&priv->stnid_lock); -+} -+ -+int utils_get_phy_rate(u8 format, u8 bandwidth, u8 short_gi, u8 mcs_id); -+ -+u8 utils_get_rate_id(u8 rate); -+ -+u32 utils_get_init_tx_rate(struct mwl_priv *priv, struct ieee80211_conf *conf, -+ struct ieee80211_sta *sta); -+ -+struct mwl_vif *utils_find_vif_bss(struct mwl_priv *priv, u8 *bssid); -+ -+struct mwl_sta *utils_find_sta(struct mwl_priv *priv, u8 *addr); -+ -+struct mwl_sta *utils_find_sta_by_aid(struct mwl_priv *priv, u16 aid); -+ -+struct mwl_sta *utils_find_sta_by_id(struct mwl_priv *priv, u16 stnid); -+ -+void utils_dump_data_info(const char *prefix_str, const void *buf, size_t len); -+ -+void utils_dump_data_debug(const char *prefix_str, const void *buf, size_t len); -+ -+bool utils_is_non_amsdu_packet(const void *packet, bool mac80211); -+ -+bool utils_is_arp(const void *packet, bool mac80211, u16 *arp_op); -+ -+bool utils_is_icmp_echo(const void *packet, bool mac80211, u8 *type); -+ -+bool utils_is_dhcp(const void *packet, bool mac80211, u8 *op, u8 *dhcp_client); -+ -+void utils_dump_arp(const void *packet, bool mac80211, size_t len); -+ -+void utils_dump_icmp_echo(const void *packet, bool mac80211, size_t len); -+ -+void utils_dump_dhcp(const void *packet, bool mac80211, size_t len); -+ -+#endif /* _UTILS_H_ */ -diff --git a/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c -new file mode 100644 -index 000000000000..3e26fc42c225 ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.c -@@ -0,0 +1,136 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file implements vendor spcific functions. */ -+ -+#include -+#include -+#include -+ -+#include "sysadpt.h" -+#include "core.h" -+#include "utils.h" -+#include "hif/fwcmd.h" -+#include "vendor_cmd.h" -+ -+static const struct nla_policy mwl_vendor_attr_policy[NUM_MWL_VENDOR_ATTR] = { -+ [MWL_VENDOR_ATTR_BF_TYPE] = { .type = NLA_U8 }, -+}; -+ -+static int mwl_vendor_cmd_set_bf_type(struct wiphy *wiphy, -+ struct wireless_dev *wdev, -+ const void *data, int data_len) -+{ -+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); -+ struct mwl_priv *priv = hw->priv; -+ struct nlattr *tb[NUM_MWL_VENDOR_ATTR]; -+ int rc; -+ u8 val; -+ -+ if (priv->chip_type != MWL8964) -+ return -EPERM; -+ -+ rc = nla_parse(tb, MWL_VENDOR_ATTR_MAX, data, data_len, -+ mwl_vendor_attr_policy -+#if (defined(LINUX_BACKPORT) || (LINUX_VERSION_CODE >=KERNEL_VERSION(4,12,0))) -+ , NULL -+#endif -+ ); -+ if (rc) -+ return rc; -+ -+ if (!tb[MWL_VENDOR_ATTR_BF_TYPE]) -+ return -EINVAL; -+ -+ val = nla_get_u8(tb[MWL_VENDOR_ATTR_BF_TYPE]); -+ if ((val < TXBF_MODE_OFF) || (val > TXBF_MODE_BFMER_AUTO)) -+ return -EINVAL; -+ wiphy_debug(wiphy, "set bf_type: 0x%x\n", val); -+ -+ rc = mwl_fwcmd_set_bftype(hw, val); -+ if (!rc) -+ priv->bf_type = val; -+ -+ return rc; -+} -+ -+static int mwl_vendor_cmd_get_bf_type(struct wiphy *wiphy, -+ struct wireless_dev *wdev, -+ const void *data, int data_len) -+{ -+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); -+ struct mwl_priv *priv = hw->priv; -+ struct sk_buff *skb; -+ -+ if (priv->chip_type != MWL8964) -+ return -EPERM; -+ -+ skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 8); -+ if (!skb) -+ return -ENOMEM; -+ -+ nla_put_u8(skb, MWL_VENDOR_ATTR_BF_TYPE, priv->bf_type); -+ -+ return cfg80211_vendor_cmd_reply(skb); -+} -+ -+static const struct wiphy_vendor_command mwl_vendor_commands[] = { -+ { -+ .info = { .vendor_id = MRVL_OUI, -+ .subcmd = MWL_VENDOR_CMD_SET_BF_TYPE}, -+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV, -+ .doit = mwl_vendor_cmd_set_bf_type, -+ }, -+ { -+ .info = { .vendor_id = MRVL_OUI, -+ .subcmd = MWL_VENDOR_CMD_GET_BF_TYPE}, -+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV, -+ .doit = mwl_vendor_cmd_get_bf_type, -+ } -+}; -+ -+static const struct nl80211_vendor_cmd_info mwl_vendor_events[] = { -+ { -+ .vendor_id = MRVL_OUI, -+ .subcmd = MWL_VENDOR_EVENT_DRIVER_READY, -+ }, -+ { -+ .vendor_id = MRVL_OUI, -+ .subcmd = MWL_VENDOR_EVENT_DRIVER_START_REMOVE, -+ }, -+ { -+ .vendor_id = MRVL_OUI, -+ .subcmd = MWL_VENDOR_EVENT_CMD_TIMEOUT, -+ } -+}; -+ -+void vendor_cmd_register(struct wiphy *wiphy) -+{ -+ wiphy->vendor_commands = mwl_vendor_commands; -+ wiphy->n_vendor_commands = ARRAY_SIZE(mwl_vendor_commands); -+ wiphy->vendor_events = mwl_vendor_events; -+ wiphy->n_vendor_events = ARRAY_SIZE(mwl_vendor_events); -+} -+ -+void vendor_cmd_basic_event(struct wiphy *wiphy, int event_idx) -+{ -+ struct sk_buff *skb; -+ -+ skb = cfg80211_vendor_event_alloc(wiphy, NULL, 0, -+ event_idx, GFP_KERNEL); -+ -+ if (skb) -+ cfg80211_vendor_event(skb, GFP_KERNEL); -+} -diff --git a/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h -new file mode 100644 -index 000000000000..b6fdf70c22fb ---- /dev/null -+++ b/drivers/net/wireless/marvell/mwlwifi/vendor_cmd.h -@@ -0,0 +1,60 @@ -+/* -+ * Copyright (C) 2006-2018, Marvell International Ltd. -+ * -+ * This software file (the "File") is distributed by Marvell International -+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991 -+ * (the "License"). You may use, redistribute and/or modify this File in -+ * accordance with the terms and conditions of the License, a copy of which -+ * is available by writing to the Free Software Foundation, Inc. -+ * -+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about -+ * this warranty disclaimer. -+ */ -+ -+/* Description: This file defines vendor constants and register function. */ -+ -+#ifndef _VENDOR_CMD_H_ -+#define _VENDOR_CMD_H_ -+ -+#ifdef __KERNEL__ -+void vendor_cmd_register(struct wiphy *wiphy); -+void vendor_cmd_basic_event(struct wiphy *wiphy, int event_idx); -+#endif -+ -+#define MRVL_OUI 0x005043 -+ -+enum mwl_vendor_commands { -+ MWL_VENDOR_CMD_SET_BF_TYPE, -+ MWL_VENDOR_CMD_GET_BF_TYPE, -+ -+ /* add commands here, update the command in vendor_cmd.c */ -+ -+ __MWL_VENDOR_CMD_AFTER_LAST, -+ NUM_MWL_VENDOR_CMD = __MWL_VENDOR_CMD_AFTER_LAST, -+ MWL_VENDOR_CMD_MAX = __MWL_VENDOR_CMD_AFTER_LAST - 1 -+}; -+ -+enum mwl_vendor_attributes { -+ MWL_VENDOR_ATTR_NOT_USE, -+ MWL_VENDOR_ATTR_BF_TYPE, -+ -+ /* add attributes here, update the policy in vendor_cmd.c */ -+ -+ __MWL_VENDOR_ATTR_AFTER_LAST, -+ NUM_MWL_VENDOR_ATTR = __MWL_VENDOR_ATTR_AFTER_LAST, -+ MWL_VENDOR_ATTR_MAX = __MWL_VENDOR_ATTR_AFTER_LAST - 1 -+}; -+ -+enum mwl_vendor_events { -+ MWL_VENDOR_EVENT_DRIVER_READY, -+ MWL_VENDOR_EVENT_DRIVER_START_REMOVE, -+ MWL_VENDOR_EVENT_CMD_TIMEOUT, -+ -+ __MWL_VENDOR_EVENT_AFTER_LAST, -+ NUM_MWL_VENDOR_EVENT = __MWL_VENDOR_EVENT_AFTER_LAST, -+ MWL_VENDOR_EVENT_MAX = __MWL_VENDOR_EVENT_AFTER_LAST - 1 -+}; -+ -+#endif /* _VENDOR_CMD_H_ */ --- -2.19.1 - diff --git a/patches/5.0/0012-surface-lte.patch b/patches/5.0/0012-surface-lte.patch deleted file mode 100644 index e6bcb9a5d..000000000 --- a/patches/5.0/0012-surface-lte.patch +++ /dev/null @@ -1,24 +0,0 @@ -From c58fc7ab7db232cfcf3ad7dd3b47e8884ec786b3 Mon Sep 17 00:00:00 2001 -From: Jake Day -Date: Tue, 30 Apr 2019 20:45:40 -0400 -Subject: [PATCH 12/12] surface-lte - ---- - drivers/usb/serial/qcserial.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c -index 613f91add03d..e1428222dd73 100644 ---- a/drivers/usb/serial/qcserial.c -+++ b/drivers/usb/serial/qcserial.c -@@ -177,6 +177,7 @@ static const struct usb_device_id id_table[] = { - {DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */ - {DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */ - {DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */ -+ {DEVICE_SWI(0x045e, 0x096e)}, /* Microsoft Surface Go LTE */ - - /* Huawei devices */ - {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ --- -2.19.1 - diff --git a/patches/5.1/0001-surface-acpi.patch b/patches/5.1/0001-surface-acpi.patch index eb52661ad..15cfefc24 100644 --- a/patches/5.1/0001-surface-acpi.patch +++ b/patches/5.1/0001-surface-acpi.patch @@ -1,6 +1,6 @@ -From 2d5882e7390d6fbf95d79a41e234ae115bcb24a7 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 00:54:17 +0200 +From e69519a8701cd692f2c84371b1abc0bfbc64b70c Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:40:15 -0400 Subject: [PATCH 01/12] surface-acpi --- @@ -4135,5 +4135,5 @@ index a0ac16ee6575..0dd242ff24d1 100644 return -ENODEV; -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0002-suspend.patch b/patches/5.1/0002-suspend.patch index 33ab6f3c2..3187605ae 100644 --- a/patches/5.1/0002-suspend.patch +++ b/patches/5.1/0002-suspend.patch @@ -1,6 +1,6 @@ -From 3fe86ef98282f3509f77b2b454447d792f18b7b8 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 00:56:09 +0200 +From c5b093023e84b52723da6d899902f7d4e683203f Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:40:30 -0400 Subject: [PATCH 02/12] suspend --- @@ -65,7 +65,7 @@ index a90cf5d63aac..de5d5a2b1d38 100644 { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index a077f67fe1da..f8be239321a9 100644 +index cc616a5f6a8f..e2e95850383b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1357,6 +1357,10 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, @@ -141,5 +141,5 @@ index c9ec050bcf46..155e94652093 100644 .procname = "sched_child_runs_first", .data = &sysctl_sched_child_runs_first, -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0003-buttons.patch b/patches/5.1/0003-buttons.patch index 36f31021a..33393ec4b 100644 --- a/patches/5.1/0003-buttons.patch +++ b/patches/5.1/0003-buttons.patch @@ -1,6 +1,6 @@ -From 7b0b4c6986dff87b46e9118da1aadce5f7ee50f4 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 00:57:01 +0200 +From 84dad3e0dc3463df79007844c490071666871a98 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:40:42 -0400 Subject: [PATCH 03/12] buttons --- @@ -253,5 +253,5 @@ index 1b491690ce07..eaec30380b11 100644 if (!button) return -ENOMEM; -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0004-cameras.patch b/patches/5.1/0004-cameras.patch index 847b02c24..2b27535ae 100644 --- a/patches/5.1/0004-cameras.patch +++ b/patches/5.1/0004-cameras.patch @@ -1,6 +1,6 @@ -From ce8553fab11b20873bb9105ecd0a49ae69aa8c4b Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 00:58:12 +0200 +From 37339e1807907478a17fc0f978002f6e52c9a01f Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:40:52 -0400 Subject: [PATCH 04/12] cameras --- @@ -2749,5 +2749,5 @@ index 000000000000..79aef69666e8 +}; +#endif -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0005-ipts.patch b/patches/5.1/0005-ipts.patch index 058f8271a..f7b788a7b 100644 --- a/patches/5.1/0005-ipts.patch +++ b/patches/5.1/0005-ipts.patch @@ -1,6 +1,6 @@ -From 7216e2f8bf8006e9af0852a66afb7e68b8dea82f Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:02:29 +0200 +From 2034ee48e92c8cae6c8aa6590a5ac8b55a0b48bf Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:41:03 -0400 Subject: [PATCH 05/12] ipts --- @@ -1174,7 +1174,7 @@ index 5e98fd79bd9d..523bc6354481 100644 struct intel_context *ce) { diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h -index f1aec8a6986f..2745cfb3e7dc 100644 +index f1aec8a6986f..faa403f1378d 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -112,6 +112,15 @@ void intel_execlists_show_requests(struct intel_engine_cs *engine, @@ -6174,5 +6174,5 @@ index 000000000000..f329bbfb8079 + +#endif // INTEL_IPTS_IF_H -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0006-hid.patch b/patches/5.1/0006-hid.patch index ae71c45a1..4c4d64626 100644 --- a/patches/5.1/0006-hid.patch +++ b/patches/5.1/0006-hid.patch @@ -1,6 +1,6 @@ -From 6cc9c83a79ad320d66c031be1b1be5fc75d893bf Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:03:32 +0200 +From 60b85d6a779a891319435c500ff2a57bc8ac9d3f Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:41:14 -0400 Subject: [PATCH 06/12] hid --- @@ -147,5 +147,5 @@ index 77ffba48cc73..517143d5b305 100644 { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD), HID_QUIRK_MULTI_INPUT }, -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0007-sdcard-reader.patch b/patches/5.1/0007-sdcard-reader.patch index 5f71fd994..ec2fd5085 100644 --- a/patches/5.1/0007-sdcard-reader.patch +++ b/patches/5.1/0007-sdcard-reader.patch @@ -1,6 +1,6 @@ -From 2ac6068e68209c79012a1d90a50c97cc9349bb50 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:04:02 +0200 +From 6352e8e30f86e80e27abaf9a86e280ec5c39e908 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:41:26 -0400 Subject: [PATCH 07/12] sdcard-reader --- @@ -8,7 +8,7 @@ Subject: [PATCH 07/12] sdcard-reader 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index 8d4631c81b9f..904ab3de183a 100644 +index 310eef451db8..a896e86ca0d7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4198,7 +4198,8 @@ void usb_enable_lpm(struct usb_device *udev) @@ -22,5 +22,5 @@ index 8d4631c81b9f..904ab3de183a 100644 udev->lpm_disable_count--; -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0008-wifi.patch b/patches/5.1/0008-wifi.patch index 53958bbaa..dd8497f98 100644 --- a/patches/5.1/0008-wifi.patch +++ b/patches/5.1/0008-wifi.patch @@ -1,6 +1,6 @@ -From 05691e019642cdeaa5e1519000daa9f69006e27b Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:04:58 +0200 +From f2aae4bbd6842ac44f0d7223bb3ffc11da881b00 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:41:37 -0400 Subject: [PATCH 08/12] wifi --- @@ -33,7 +33,7 @@ index 042a1d07f686..fc9041f58e9f 100644 skb_src = skb_dequeue(&pra_list->skb_head); diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -index c46f0a54a0c7..bce482bbcc6d 100644 +index e582d9b3e50c..92c49eb3c5a3 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -437,7 +437,10 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, @@ -266,5 +266,5 @@ diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl old mode 100755 new mode 100644 -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0009-surface-power.patch b/patches/5.1/0009-surface3-power.patch similarity index 98% rename from patches/5.1/0009-surface-power.patch rename to patches/5.1/0009-surface3-power.patch index 2be3f6964..5e22566d6 100644 --- a/patches/5.1/0009-surface-power.patch +++ b/patches/5.1/0009-surface3-power.patch @@ -1,7 +1,7 @@ -From 8595ec15b6e77c335c79c938eb292700c5c9abc0 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:05:57 +0200 -Subject: [PATCH 09/12] surface-power +From 6179098feb5e463e496d0049be30cc3cdd5768ed Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:42:01 -0400 +Subject: [PATCH 09/12] surface3-power --- drivers/platform/x86/Kconfig | 7 + @@ -651,5 +651,5 @@ index 000000000000..e0af01a60302 +MODULE_DESCRIPTION("mshw0011 driver"); +MODULE_LICENSE("GPL v2"); -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0010-surface-dock.patch b/patches/5.1/0010-surface-dock.patch index d9db9f1bc..080017b4a 100644 --- a/patches/5.1/0010-surface-dock.patch +++ b/patches/5.1/0010-surface-dock.patch @@ -1,6 +1,6 @@ -From d98b685791c6001530e73a6e1a6e13a308a911d9 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:06:31 +0200 +From 2a6337d5e11ff46a01b0c5a2e32ee2deb6eec812 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:42:13 -0400 Subject: [PATCH 10/12] surface-dock --- @@ -21,5 +21,5 @@ index 8bc35d53408b..11fa56cbd769 100644 { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT }, -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0011-mwlwifi.patch b/patches/5.1/0011-mwlwifi.patch index ff8740263..59da7063b 100644 --- a/patches/5.1/0011-mwlwifi.patch +++ b/patches/5.1/0011-mwlwifi.patch @@ -1,6 +1,6 @@ -From a24d901e392f83233e0d7e1fc205573a2a6e44be Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:07:23 +0200 +From 0fde5c18106ae169eeceed14675a1942f31e31c0 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:42:24 -0400 Subject: [PATCH 11/12] mwlwifi --- @@ -19751,5 +19751,5 @@ index 000000000000..b6fdf70c22fb + +#endif /* _VENDOR_CMD_H_ */ -- -2.21.0 +2.19.1 diff --git a/patches/5.1/0012-surface-lte.patch b/patches/5.1/0012-surface-lte.patch index 48e1cf84f..497722440 100644 --- a/patches/5.1/0012-surface-lte.patch +++ b/patches/5.1/0012-surface-lte.patch @@ -1,6 +1,6 @@ -From 72d8973b13354125b9a5c6f6693f15bf57717691 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 16 May 2019 01:07:48 +0200 +From 49341203210ccb8486f1f2c380e871cb9cd1aa33 Mon Sep 17 00:00:00 2001 +From: Jake Day +Date: Thu, 6 Jun 2019 13:42:35 -0400 Subject: [PATCH 12/12] surface-lte --- @@ -20,5 +20,5 @@ index 613f91add03d..e1428222dd73 100644 /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ -- -2.21.0 +2.19.1