From aa359b58113d30df324699e1dd63f7ef7e7d5bfc Mon Sep 17 00:00:00 2001 From: Tarun Karela Date: Thu, 29 Aug 2019 17:46:44 +0100 Subject: [PATCH] [RAMEN9610-19706][9610] wlbt: WLBT Kernel Driver 10.3.1.0 WLBT kernel driver release scsc_release_10.3.1.0_mx450_p_ww Change-Id: I4247b339fee79185c052c9bc486c69eb4b7646f0 SCSC-Bug-Id: Rels-3122 Signed-off-by: Tarun Karela Signed-off-by: Kim Gunho --- drivers/misc/samsung/scsc/Makefile | 4 +- drivers/misc/samsung/scsc/client_test.c | 24 +- drivers/misc/samsung/scsc/mif_reg_S5E3830.h | 222 ++ drivers/misc/samsung/scsc/mif_reg_S5E9630.h | 17 +- drivers/misc/samsung/scsc/mx140_clk.c | 23 +- drivers/misc/samsung/scsc/mx250_fm.c | 21 +- drivers/misc/samsung/scsc/mx_dbg_sampler.c | 24 +- drivers/misc/samsung/scsc/mxfwconfig.c | 2 +- drivers/misc/samsung/scsc/mxman.c | 234 +- drivers/misc/samsung/scsc/mxman.h | 18 +- drivers/misc/samsung/scsc/mxproc.c | 4 +- drivers/misc/samsung/scsc/mxsyserr.c | 138 +- drivers/misc/samsung/scsc/mxsyserr.h | 1 + drivers/misc/samsung/scsc/platform_mif.h | 2 +- drivers/misc/samsung/scsc/platform_mif_3830.c | 2227 +++++++++++++++++ drivers/misc/samsung/scsc/platform_mif_9630.c | 261 +- .../misc/samsung/scsc/scsc_log_collector.c | 4 +- drivers/misc/samsung/scsc/scsc_logring_main.c | 2 +- drivers/misc/samsung/scsc/scsc_logring_ring.c | 4 +- drivers/misc/samsung/scsc/scsc_mx_impl.c | 2 + drivers/misc/samsung/scsc/scsc_service.c | 131 +- drivers/misc/samsung/scsc/scsc_wlbtd.c | 17 +- drivers/misc/samsung/scsc/scsc_wlbtd.h | 1 + drivers/misc/samsung/scsc/srvman.h | 7 +- drivers/net/wireless/scsc/cfg80211_ops.c | 52 +- drivers/net/wireless/scsc/cm_if.c | 12 +- drivers/net/wireless/scsc/dev.h | 6 +- drivers/net/wireless/scsc/fw_test.c | 15 +- drivers/net/wireless/scsc/hip4.c | 33 +- drivers/net/wireless/scsc/hip4.h | 3 - drivers/net/wireless/scsc/hip4_sampler.c | 4 +- drivers/net/wireless/scsc/ioctl.c | 115 +- drivers/net/wireless/scsc/mgt.c | 237 +- drivers/net/wireless/scsc/mgt.h | 6 +- drivers/net/wireless/scsc/mib.h | 90 + drivers/net/wireless/scsc/mlme.c | 273 +- drivers/net/wireless/scsc/mlme.h | 3 +- drivers/net/wireless/scsc/mlme_nan.c | 10 +- drivers/net/wireless/scsc/netif.c | 2 - drivers/net/wireless/scsc/nl80211_vendor.c | 110 +- drivers/net/wireless/scsc/nl80211_vendor.h | 6 +- .../net/wireless/scsc/nl80211_vendor_nan.c | 36 +- .../net/wireless/scsc/nl80211_vendor_nan.h | 3 +- drivers/net/wireless/scsc/rx.c | 82 +- drivers/net/wireless/scsc/scsc_wifi_fcq.c | 24 +- drivers/net/wireless/scsc/tx.c | 9 +- drivers/net/wireless/scsc/utils.h | 3 +- include/scsc/scsc_mx.h | 107 +- include/scsc/scsc_release.h | 12 +- 49 files changed, 4097 insertions(+), 546 deletions(-) create mode 100644 drivers/misc/samsung/scsc/mif_reg_S5E3830.h create mode 100644 drivers/misc/samsung/scsc/platform_mif_3830.c diff --git a/drivers/misc/samsung/scsc/Makefile b/drivers/misc/samsung/scsc/Makefile index c6c8e90dc624..6bf1424f1e99 100644 --- a/drivers/misc/samsung/scsc/Makefile +++ b/drivers/misc/samsung/scsc/Makefile @@ -67,7 +67,9 @@ endif ifeq ($(CONFIG_SOC_EXYNOS9630),y) scsc_platform_mif-$(CONFIG_SCSC_PLATFORM) += platform_mif_9630.o endif - +ifeq ($(CONFIG_SOC_EXYNOS3830),y) + scsc_platform_mif-$(CONFIG_SCSC_PLATFORM) += platform_mif_3830.o +endif ifeq ($(CONFIG_SOC_EXYNOS7570),y) scsc_platform_mif-$(CONFIG_SCSC_PLATFORM) += platform_mif.o endif diff --git a/drivers/misc/samsung/scsc/client_test.c b/drivers/misc/samsung/scsc/client_test.c index d106251ed9ab..a9ef8bd8755e 100755 --- a/drivers/misc/samsung/scsc/client_test.c +++ b/drivers/misc/samsung/scsc/client_test.c @@ -54,14 +54,27 @@ static dev_t client_test_dev_t; static struct class *client_test_class; static struct cdev *client_test_cdev; -static void test_stop_on_failure(struct scsc_service_client *client) +static u8 test_failure_notification(struct scsc_service_client *client, struct mx_syserr_decode *err) { + (void) client; SCSC_TAG_DEBUG(MXMAN_TEST, "OK\n"); + return err->level; } -static void test_failure_reset(struct scsc_service_client *client, u16 scsc_panic_code) + +static bool test_stop_on_failure(struct scsc_service_client *client, struct mx_syserr_decode *err) +{ + (void) client; + (void) err; + SCSC_TAG_DEBUG(MXMAN_TEST, "OK\n"); + return false; +} + +static void test_failure_reset(struct scsc_service_client *client, u8 level, u16 scsc_syserr_code) { - (void)scsc_panic_code; + (void)client; + (void)level; + (void)scsc_syserr_code; SCSC_TAG_ERR(MXMAN_TEST, "OK\n"); } @@ -224,8 +237,9 @@ void client_module_probe(struct scsc_mx_module_client *module_client, struct scs if (!test) return; - test->test_service_client.stop_on_failure = test_stop_on_failure; - test->test_service_client.failure_reset = test_failure_reset; + test->test_service_client.failure_notification = test_failure_notification; + test->test_service_client.stop_on_failure_v2 = test_stop_on_failure; + test->test_service_client.failure_reset_v2 = test_failure_reset; test->mx = mx; switch (auto_start) { diff --git a/drivers/misc/samsung/scsc/mif_reg_S5E3830.h b/drivers/misc/samsung/scsc/mif_reg_S5E3830.h new file mode 100644 index 000000000000..0b12dbe65ed5 --- /dev/null +++ b/drivers/misc/samsung/scsc/mif_reg_S5E3830.h @@ -0,0 +1,222 @@ +/**************************************************************************** + * + * Copyright (c) 2014 - 2019 Samsung Electronics Co., Ltd. All rights reserved + * + ****************************************************************************/ + +#ifndef __MIF_REG_3830_H +#define __MIF_REG_3830_H + +/**************************************************************************** + * This header uses values from Nacho Exynos 3830 User Manual + * A copy can be found in http://cognidox/SC-508880-SP + ****************************************************************************/ + + +/*********************************/ +/* PLATFORM register definitions */ +/*********************************/ +#define NUM_MBOX_PLAT 8 +#define NUM_SEMAPHORE 12 + +#define MAILBOX_WLBT_BASE 0x0000 +#define MAILBOX_WLBT_REG(r) (MAILBOX_WLBT_BASE + (r)) +#define MCUCTRL 0x000 /* MCU Controller Register */ +/* R0 [31:16] - Int FROM R4/M4 */ +#define INTGR0 0x008 /* Interrupt Generation Register 0 (r/w) */ +#define INTCR0 0x00C /* Interrupt Clear Register 0 (w) */ +#define INTMR0 0x010 /* Interrupt Mask Register 0 (r/w) */ +#define INTSR0 0x014 /* Interrupt Status Register 0 (r) */ +#define INTMSR0 0x018 /* Interrupt Mask Status Register 0 (r) */ +/* R1 [15:0] - Int TO R4/M4 */ +#define INTGR1 0x01c /* Interrupt Generation Register 1 */ +#define INTCR1 0x020 /* Interrupt Clear Register 1 */ +#define INTMR1 0x024 /* Interrupt Mask Register 1 */ +#define INTSR1 0x028 /* Interrupt Status Register 1 */ +#define INTMSR1 0x02c /* Interrupt Mask Status Register 1 */ +#define MIF_INIT 0x04c /* MIF_init */ +#define IS_VERSION 0x050 /* Version Information Register */ +#define ISSR_BASE 0x080 /* IS_Shared_Register Base address */ +#define ISSR(r) (ISSR_BASE + (4 * (r))) +#define SEMAPHORE_BASE 0x180 /* IS_Shared_Register Base address */ +#define SEMAPHORE(r) (SEMAPHORE_BASE + (4 * (r))) +#define SEMA0CON 0x1c0 +#define SEMA0STATE 0x1c8 +#define SEMA1CON 0x1e0 +#define SEMA1STATE 0x1e8 + +#define WLBT_PBUS_BASE 0x14C00000 + +// CBUS : APM_BUS +// PBUS : CFG_BUS + +/* New WLBT SFRs for MEM config */ +#define WLBT_PBUS_D_TZPC_SFR (WLBT_PBUS_BASE + 0x10000) +#define WLBT_PBUS_BAAW_DBUS (WLBT_PBUS_BASE + 0x20000) +#define WLBT_PBUS_BAAW_CBUS (WLBT_PBUS_BASE + 0x30000) +#define WLBT_PBUS_SMAPPER (WLBT_PBUS_BASE + 0x40000) +#define WLBT_PBUS_SYSREG (WLBT_PBUS_BASE + 0x50000) +#define WLBT_PBUS_BOOT (WLBT_PBUS_BASE + 0x60000) + +#define VGPIO_TX_MONITOR 0x1700 +#define VGPIO_TX_MON_BIT29 BIT(29) + +/* Exynos 3830 UM - TODO */ +#define WLBT_CONFIGURATION 0x3100 +#define LOCAL_PWR_CFG BIT(0) /* Control power state 0: Power down 1: Power on */ + +/* Exynos 3830 UM - TODO */ +#define WLBT_STATUS 0x3104 +#define WLBT_STATUS_BIT0 BIT(0) /* Status 0 : Power down 1 : Power on */ + +/* Exynos 3830 UM - TODO */ +#define WLBT_STATES 0x3108 /* STATES [7:0] States index for debugging + * 0x00 : Reset + * 0x10 : Power up + * 0x80 : Power down + * */ +#define WLBT_OPTION 0x310C +#define WLBT_OPTION_DATA BIT(3) + +/* Exynos 3830 UM - TODO */ +#define WLBT_CTRL_NS 0x3110 /* WLBT Control SFR non-secure */ +#define WLBT_ACTIVE_CLR BIT(6) /* WLBT_ACTIVE_REQ is clear internally on WAKEUP */ +#define WLBT_ACTIVE_EN BIT(5) /* Enable of WIFI_ACTIVE_REQ */ + +/* Exynos 3830 UM - TODO */ +#define WLBT_CTRL_S 0x3114 /* WLBT Control SFR secure */ +#define WLBT_START BIT(3) /* CP control enable 0: Disable 1: Enable */ + +/* Exynos 3830 UM - TODO */ +#define WLBT_OUT 0x3120 +#define SWEEPER_BYPASS BIT(13) /* SWEEPER bypass mode control(WLBT2AP path) If + * this bit is set to 1, SWEEPER is bypass mode. + */ +#define SWEEPER_PND_CLR_REQ BIT(7) /* SWEEPER_CLEAN Request. SWPPER is the IP + * that can clean up hung transaction in the Long hop + * async Bus Interface, when get hung + * state. 0: Normal 1: SWEEPER CLEAN Requested + */ + +/* Exynos 3830 UM - Exynos3830_12_PMU.docx 1.10.566 */ +#define WLBT_IN 0x3124 +/* TODO: nacho does not have BUS_READY */ +#define BUS_READY BIT(4) /* BUS ready indication signal when reset released. 0: + * Normal 1: BUS ready state */ +#define PWRDOWN_IND BIT(2) /* PWRDOWN state indication 0: Normal 1: In the + * power down state */ +#define SWEEPER_PND_CLR_ACK BIT(0) /* SWEEPER_CLEAN ACK signal. SWPPER is the IP + * that can clean up hung transaction in the Long hop + * async Bus Interface, when get hung + * state. 0: Normal 1: SWEEPER CLEAN + * Acknowledged */ +/* Exynos 3830 UM - Exynos3830_12_PMU.docx Page 38 */ +#define WLBT_INT_EN 0x3144 +#define PWR_REQ_F BIT(3) +#define TCXO_REQ_F BIT(5) + +/* Exynos 3830 UM - TODO */ +#define WLBT_STAT 0x0058 +#define WLBT_PWRDN_DONE BIT(0) /* Check WLBT power-down status.*/ +#define WLBT_ACCESS_MIF BIT(4) /* Check whether WLBT accesses MIF domain */ + +/* Exynos 3830 UM - TODO */ +#define WLBT_DEBUG 0x005C + +/* Exynos 3830 UM - TODO */ +#define MIF_CTRL 0x3810 +#define TCXO_EN BIT(0) /* XCLKREQ enable 0: Disable 1: Enable */ + +/* Exynos 3830 UM - TODO */ +#define TOP_OUT 0x3920 +#define PWRRGTON_CP BIT(1) /* XPWRRTON_CP control 0: Disable 1: Enable */ + +/* Exynos 3830 UM - TODO */ +#define TCXO_BUF_CTRL 0x3B78 +#define TCXO_BUF_BIAS_EN_WLBT BIT(0) + +/* New WLBT SFRs for MEM config */ + +/* end address is exclusive so the ENDx register should be set to the first + * address that is not accessible through that BAAW. + * + * Another very important point to note here is we are using BAAW0 to expose + * 16MB region, so other BAAWs can be used for other purposes + */ +#define WLBT_DBUS_BAAW_0_START 0x80000000 // Start of DRAM for WLBT R7 +#define WLBT_DBUS_BAAW_0_END 0x81000000 // 16 MB +#define WLBT_DBUS_BAAW_1_START 0xC0000000 +#define WLBT_DBUS_BAAW_1_END 0xDFFFFFFF + +/* #define WLBT_DBUS_BAAW_2_START 0x80800000 +#define WLBT_DBUS_BAAW_2_END WLBT_DBUS_BAAW_3_START +#define WLBT_DBUS_BAAW_3_START 0x80C00000 +#define WLBT_DBUS_BAAW_3_END WLBT_DBUS_BAAW_4_START +#define WLBT_DBUS_BAAW_4_START 0x81000000 +#define WLBT_DBUS_BAAW_4_END 0x813FFFFF */ + +#define WLBT_BAAW_CON_INIT_DONE (1 << 31) +#define WLBT_BAAW_CON_EN_WRITE (1 << 1) +#define WLBT_BAAW_CON_EN_READ (1 << 0) +#define WLBT_BAAW_ACCESS_CTRL (WLBT_BAAW_CON_INIT_DONE | WLBT_BAAW_CON_EN_WRITE | WLBT_BAAW_CON_EN_READ) + +/* ref Confluence Maxwell152+Memory+Map */ +#define WLBT_CBUS_BAAW_0_START 0xA0000000 // CP2WLBT MBOX +#define WLBT_CBUS_BAAW_0_END 0xA000FFFF//WLBT_CBUS_BAAW_1_START +#define WLBT_CBUS_BAAW_1_START 0xA0010000 // MAILBOX_GNSS2WLBT +#define WLBT_CBUS_BAAW_1_END 0xA00CFFFF//WLBT_CBUS_BAAW_6_START // TODO +#define WLBT_CBUS_BAAW_2_START 0xA0020000 // MAILBOX_APM2WLBT +#define WLBT_CBUS_BAAW_2_END WLBT_CBUS_BAAW_3_START +#define WLBT_CBUS_BAAW_3_START 0xA0030000 // MAILBOX_AP2WLBT +#define WLBT_CBUS_BAAW_3_END WLBT_CBUS_BAAW_4_START +#define WLBT_CBUS_BAAW_4_START 0xA0040000 // MAILBOX_WLBT2ABOX +#define WLBT_CBUS_BAAW_4_END WLBT_CBUS_BAAW_5_START +#define WLBT_CBUS_BAAW_5_START 0xA0050000 // MAILBOX_WLBT2CHUB +#define WLBT_CBUS_BAAW_5_END WLBT_CBUS_BAAW_6_START +#define WLBT_CBUS_BAAW_6_START 0xA0060000 // GPIO_CMGP +#define WLBT_CBUS_BAAW_6_END WLBT_CBUS_BAAW_7_START +#define WLBT_CBUS_BAAW_7_START 0xA0070000 // ADC_CMGP_AP +#define WLBT_CBUS_BAAW_7_END WLBT_CBUS_BAAW_8_START +#define WLBT_CBUS_BAAW_8_START 0xA0080000 // ADC_CMGP_CP +#define WLBT_CBUS_BAAW_8_END WLBT_CBUS_BAAW_9_START +#define WLBT_CBUS_BAAW_9_START 0xA0090000 // SYSREG_CMGP2WLBT +#define WLBT_CBUS_BAAW_9_END WLBT_CBUS_BAAW_A_START +#define WLBT_CBUS_BAAW_A_START 0xA00A0000 // USI_CMGP00 +#define WLBT_CBUS_BAAW_A_END WLBT_CBUS_BAAW_B_START +#define WLBT_CBUS_BAAW_B_START 0xA00B0000 // reserved +#define WLBT_CBUS_BAAW_B_END WLBT_CBUS_BAAW_C_START +#define WLBT_CBUS_BAAW_C_START 0xA00C0000 // USI_CMGP01 +#define WLBT_CBUS_BAAW_C_END WLBT_CBUS_BAAW_D_START +#define WLBT_CBUS_BAAW_D_START 0xA00D0000 // CHUB_SRAM +#define WLBT_CBUS_BAAW_D_END 0xA010FFFF + +#define WLBT_PBUS_MBOX_CP2WLBT_BASE 0x11950000 +#define WLBT_PBUS_MBOX_GNSS2WLBT_BASE 0x119A0000 +#define WLBT_PBUS_MBOX_APM2WLBT_BASE 0x119B0000 +#define WLBT_PBUS_MBOX_AP2WLBT_BASE 0x119C0000 +#define WLBT_PBUS_MBOX_WLBT2ABOX_BASE 0x119D0000 +#define WLBT_PBUS_MBOX_WLBT2CHUB_BASE 0x119E0000 +#define WLBT_PBUS_GPIO_CMGP_BASE 0x11C30000 +#define WLBT_PBUS_ADC_CMGP_AP_BASE 0x11C40000 +#define WLBT_PBUS_ADC_CMGP_CP_BASE 0x11C50000 +#define WLBT_PBUS_SYSREG_CMGP2WLBT_BASE 0x11C60000 +#define WLBT_PBUS_USI_CMG00_BASE 0x11C70000 +#define WLBT_PBUS_USI_CMG01_BASE 0x11D20000 +#define WLBT_PBUS_CHUB_BASE 0x10E00000 /* TODO: confirm correct address */ + +/* EMA settings overloaded onto CHIP_VERSION_ID SFR + * (remap block) + */ +#define CHIP_VERSION_ID_VER_MASK 0xffc00000 /* [22:32] Version ID */ +#define CHIP_VERSION_ID_EMA_MASK 0x003fffff /* [0:21] EMA params */ +#define CHIP_VERSION_ID_EMA_VALUE (BIT(20) | \ + BIT(18) | \ + BIT(13) | \ + BIT(11) | \ + BIT(5) | \ + BIT(2) ) + +/* TZASC (TrustZone Address Space Controller) configuration for Katmai onwards */ +#define EXYNOS_SET_CONN_TZPC 0 +#define SMC_CMD_CONN_IF (0x82000710) +#endif /* __MIF_REG_3830_H */ diff --git a/drivers/misc/samsung/scsc/mif_reg_S5E9630.h b/drivers/misc/samsung/scsc/mif_reg_S5E9630.h index 3e6d2f55d8d9..c2ae1c862666 100644 --- a/drivers/misc/samsung/scsc/mif_reg_S5E9630.h +++ b/drivers/misc/samsung/scsc/mif_reg_S5E9630.h @@ -57,13 +57,16 @@ /* POWER */ +#define VGPIO_TX_MONITOR 0x1700 +#define VGPIO_TX_MON_BIT29 BIT(29) + /* Exynos 9630 UM - 9.8.719 */ #define WLBT_CONFIGURATION 0x3300 #define LOCAL_PWR_CFG BIT(0) /* Control power state 0: Power down 1: Power on */ /* Exynos 9630 UM - 9.8.720 */ #define WLBT_STATUS 0x3304 -#define STATUS BIT(0) /* Status 0 : Power down 1 : Power on */ +#define WLBT_STATUS_BIT0 BIT(0) /* Status 0 : Power down 1 : Power on */ /* Exynos 9630 UM - 9.8.721 */ #define WLBT_STATES 0x3308 /* STATES [7:0] States index for debugging @@ -72,6 +75,10 @@ * 0x80 : Power down * */ +/* Exynos 9630 UM - 9.8.722 */ +#define WLBT_OPTION 0x330C +#define WLBT_OPTION_DATA BIT(3) + /* Exynos 9630 UM - 9.8.723 */ #define WLBT_CTRL_NS 0x3310 /* WLBT Control SFR non-secure */ #define WLBT_ACTIVE_CLR BIT(8) /* WLBT_ACTIVE_REQ is clear internally on WAKEUP */ @@ -193,7 +200,7 @@ #define WLBT_ACCESS_MIF BIT(4) /* Check whether WLBT accesses MIF domain */ /* Exynos 9630 UM - 9.8.11 */ -#define WLBT_DEBUG 0x005c /* MIF sleep, wakeup debugging control */ +#define WLBT_DEBUG 0x005C /* MIF sleep, wakeup debugging control */ /* need to find where have they moved */ //#define EN_MIF_REQ BIT(0) /* Control MIF_REQ through GPIO_ALIVE. */ //#define EN_WLBT_ACTIVE BIT(2) /* Control WLBT_ACTIVE through GPIO_ALIVE. */ @@ -212,15 +219,15 @@ #define CLEANY_BUS_WLBT_CONFIGURATION 0x3b20 #define CLEANY_CFG_MASK 0x1 -#define CLEANY_BUS_WLBT_STATUS 0x3b24 +#define CLEANY_BUS_WLBT_STATUS 0x3B24 #define CLEANY_STATUS_MASK (BIT(17)|BIT(16)) /* Exynos 9630 UM - 9.8.763 */ -#define SYSTEM_OUT 0x3a20 +#define SYSTEM_OUT 0x3A20 #define PWRRGTON_CON BIT(9) /* XPWRRTON_CON control 0: Disable 1: Enable */ /* Exynos 9630 UM - 9.8.812 */ -#define TCXO_BUF_CTRL 0x3c10 +#define TCXO_BUF_CTRL 0x3C10 #define TCXO_BUF_BIAS_EN_WLBT BIT(2) #define TCXO_BUF_EN_WLBT BIT(3) diff --git a/drivers/misc/samsung/scsc/mx140_clk.c b/drivers/misc/samsung/scsc/mx140_clk.c index 661398034159..3ce350b747f8 100755 --- a/drivers/misc/samsung/scsc/mx140_clk.c +++ b/drivers/misc/samsung/scsc/mx140_clk.c @@ -209,9 +209,18 @@ static void mx140_clk20mhz_remove_ctrl_proc_dir(struct mx140_clk20mhz *clk20mhz) } } +/* Maxwell manager has detected a recoverable issue no action needed */ +static u8 mx140_clk20mhz_failure_notification(struct scsc_service_client *client, struct mx_syserr_decode *err) +{ + (void) client; + SCSC_TAG_INFO(CLK20, "\n"); + return err->level; +} + /* Maxwell manager has detected an issue and the service should freeze */ -static void mx140_clk20mhz_stop_on_failure(struct scsc_service_client *client) +static bool mx140_clk20mhz_stop_on_failure(struct scsc_service_client *client, struct mx_syserr_decode *err) { + (void) err; atomic_set(&clk20mhz.mx140_clk20mhz_service_failed, 1); mutex_lock(&clk_work_lock); @@ -226,12 +235,15 @@ static void mx140_clk20mhz_stop_on_failure(struct scsc_service_client *client) #endif SCSC_TAG_INFO(CLK20, "\n"); + + return false; } /* Maxwell manager has handled a failure and the chip has been resat. */ -static void mx140_clk20mhz_failure_reset(struct scsc_service_client *client, u16 scsc_panic_code) +static void mx140_clk20mhz_failure_reset(struct scsc_service_client *client, u8 level, u16 scsc_syserr_code) { - (void)scsc_panic_code; + (void)level; + (void)scsc_syserr_code; atomic_set(&clk20mhz.mx140_clk20mhz_service_failed, 1); #ifdef MX140_CLK_VERBOSE_CALLBACKS @@ -592,8 +604,9 @@ void mx140_clk20mhz_probe(struct scsc_mx_module_client *module_client, struct sc } else { SCSC_TAG_INFO(CLK20, "Maxwell probed\n"); clk20mhz.mx = mx; - clk20mhz.mx140_clk20mhz_service_client.stop_on_failure = mx140_clk20mhz_stop_on_failure; - clk20mhz.mx140_clk20mhz_service_client.failure_reset = mx140_clk20mhz_failure_reset; + clk20mhz.mx140_clk20mhz_service_client.failure_notification = mx140_clk20mhz_failure_notification; + clk20mhz.mx140_clk20mhz_service_client.stop_on_failure_v2 = mx140_clk20mhz_stop_on_failure; + clk20mhz.mx140_clk20mhz_service_client.failure_reset_v2 = mx140_clk20mhz_failure_reset; mx140_clk20mhz_create_ctrl_proc_dir(&clk20mhz); diff --git a/drivers/misc/samsung/scsc/mx250_fm.c b/drivers/misc/samsung/scsc/mx250_fm.c index df6dabc04d3b..5aa8632503b2 100755 --- a/drivers/misc/samsung/scsc/mx250_fm.c +++ b/drivers/misc/samsung/scsc/mx250_fm.c @@ -41,20 +41,30 @@ static int service_id = SCSC_SERVICE_ID_FM; static DEFINE_MUTEX(ss_lock); +static u8 fm_client_failure_notification(struct scsc_service_client *client, struct mx_syserr_decode *err) +{ + (void)client; + SCSC_TAG_DEBUG(FM, "OK\n"); + return err->level; +} + -static void fm_client_stop_on_failure(struct scsc_service_client *client) +static bool fm_client_stop_on_failure(struct scsc_service_client *client, struct mx_syserr_decode *err) { (void)client; + (void)err; mutex_lock(&ss_lock); fm_client->fm_api_available = false; mutex_unlock(&ss_lock); SCSC_TAG_DEBUG(FM, "OK\n"); + return false; } -static void fm_client_failure_reset(struct scsc_service_client *client, u16 scsc_panic_code) +static void fm_client_failure_reset(struct scsc_service_client *client, u8 level, u16 scsc_syserr_code) { (void)client; - (void)scsc_panic_code; + (void)level; + (void)scsc_syserr_code; SCSC_TAG_DEBUG(FM, "OK\n"); } @@ -307,8 +317,9 @@ void fm_client_module_probe(struct scsc_mx_module_client *module_client, struct } init_completion(&fm_client->fm_client_work_completion); fm_client_wq_init(); - fm_client->fm_service_client.stop_on_failure = fm_client_stop_on_failure; - fm_client->fm_service_client.failure_reset = fm_client_failure_reset; + fm_client->fm_service_client.failure_notification = fm_client_failure_notification; + fm_client->fm_service_client.stop_on_failure_v2 = fm_client_stop_on_failure; + fm_client->fm_service_client.failure_reset_v2 = fm_client_failure_reset; fm_client->mx = mx; } fm_client->fm_api_available = true; diff --git a/drivers/misc/samsung/scsc/mx_dbg_sampler.c b/drivers/misc/samsung/scsc/mx_dbg_sampler.c index 7c7ab995083d..5d2cf4a25754 100755 --- a/drivers/misc/samsung/scsc/mx_dbg_sampler.c +++ b/drivers/misc/samsung/scsc/mx_dbg_sampler.c @@ -126,15 +126,28 @@ static struct { static int recovery_in_progress; -static void mx_dbg_sampler_stop_on_failure(struct scsc_service_client *client) +static u8 mx_dbg_sampler_failure_notification(struct scsc_service_client *client, struct mx_syserr_decode *err) { + (void)client; + SCSC_TAG_INFO(MX_SAMPLER, "OK\n"); + return err->level; +} + + +static bool mx_dbg_sampler_stop_on_failure(struct scsc_service_client *client, struct mx_syserr_decode *err) +{ + (void)client; + (void)err; SCSC_TAG_INFO(MX_SAMPLER, "TODO\n"); recovery_in_progress = 1; + return false; } -static void mx_dbg_sampler_failure_reset(struct scsc_service_client *client, u16 scsc_panic_code) +static void mx_dbg_sampler_failure_reset(struct scsc_service_client *client, u8 level, u16 scsc_syserr_code) { - (void)scsc_panic_code; + (void)client; + (void)level; + (void)scsc_syserr_code; SCSC_TAG_INFO(MX_SAMPLER, "TODO\n"); } @@ -619,8 +632,9 @@ void mx_dbg_sampler_probe(struct scsc_mx_module_client *module_client, struct sc } mx_dbg_sampler.devs[minor].mx = mx; - mx_dbg_sampler.devs[minor].mx_client.stop_on_failure = mx_dbg_sampler_stop_on_failure; - mx_dbg_sampler.devs[minor].mx_client.failure_reset = mx_dbg_sampler_failure_reset; + mx_dbg_sampler.devs[minor].mx_client.failure_notification = mx_dbg_sampler_failure_notification; + mx_dbg_sampler.devs[minor].mx_client.stop_on_failure_v2 = mx_dbg_sampler_stop_on_failure; + mx_dbg_sampler.devs[minor].mx_client.failure_reset_v2 = mx_dbg_sampler_failure_reset; mutex_init(&mx_dbg_sampler.devs[minor].mutex); spin_lock_init(&mx_dbg_sampler.devs[minor].spinlock); diff --git a/drivers/misc/samsung/scsc/mxfwconfig.c b/drivers/misc/samsung/scsc/mxfwconfig.c index fa6ec7d01181..c724a321362b 100644 --- a/drivers/misc/samsung/scsc/mxfwconfig.c +++ b/drivers/misc/samsung/scsc/mxfwconfig.c @@ -190,7 +190,7 @@ static void mxfwconfig_get_dram_ref(struct scsc_mx *mx, struct mxmibref *cfg_ref cfg_ref->size = 0; } else { mif->get_mifram_ref(mif, mxfwconfig->shdram, &cfg_ref->offset); - cfg_ref->size = mxfwconfig->shtotal; + cfg_ref->size = (uint32_t)mxfwconfig->shtotal; } SCSC_TAG_INFO(MX_CFG, "cfg_ref: 0x%x, size %u\n", cfg_ref->offset, cfg_ref->size); diff --git a/drivers/misc/samsung/scsc/mxman.c b/drivers/misc/samsung/scsc/mxman.c index fbb39aae0868..2ad246a22ddf 100755 --- a/drivers/misc/samsung/scsc/mxman.c +++ b/drivers/misc/samsung/scsc/mxman.c @@ -84,6 +84,7 @@ static struct work_struct wlbtd_work; #define SCSC_PANIC_TECH_BT (0x2 << 13) #define SCSC_PANIC_TECH_UNSP (0x3 << 13) +#define SCSC_PANIC_CODE_MASK 0xFFFF #define SCSC_PANIC_ORIGIN_MASK 0x8000 #define SCSC_PANIC_TECH_MASK 0x6000 #define SCSC_PANIC_SUBCODE_MASK_LEGACY 0x0FFF @@ -323,12 +324,14 @@ static u32 is_fm_on; #endif static int firmware_runtime_flags; +static int syserr_command; /** * This mxman reference is initialized/nullified via mxman_init/deinit * called by scsc_mx_create/destroy on module probe/remove. */ static struct mxman *active_mxman; static bool send_fw_config_to_active_mxman(uint32_t fw_runtime_flags); +static bool send_syserr_cmd_to_active_mxman(u32 syserr_cmd); static int fw_runtime_flags_setter(const char *val, const struct kernel_param *kp) { @@ -361,6 +364,37 @@ module_param_cb(firmware_runtime_flags, &fw_runtime_kops, NULL, 0200); MODULE_PARM_DESC(firmware_runtime_flags, "0 = Proceed as normal (default); nnn = Provides FW runtime flags bitmask: unknown bits will be ignored."); +static int syserr_setter(const char *val, const struct kernel_param *kp) +{ + int ret = -EINVAL; + u32 syserr_cmd = 0; + + if (!val) + return ret; + ret = kstrtouint(val, 10, &syserr_cmd); + if (!ret) { + u8 sub_system = (u8)(syserr_cmd / 10); + u8 level = (u8)(syserr_cmd % 10); + + if (((sub_system > 2) && (sub_system < 8)) || (sub_system > 8) || (level > 7)) + ret = -EINVAL; + else if (send_syserr_cmd_to_active_mxman(syserr_cmd)) + syserr_command = syserr_cmd; + else + ret = -EINVAL; + } + return ret; +} + +static struct kernel_param_ops syserr_kops = { + .set = syserr_setter, + .get = NULL +}; + +module_param_cb(syserr_command, &syserr_kops, NULL, 0200); +MODULE_PARM_DESC(syserr_command, + "Decimal XY - Trigger Type X(0,1,2,8), Level Y(1-7). Some combinations not supported"); + /** * Maxwell Agent Management Messages. * @@ -379,6 +413,7 @@ enum { MM_FM_RADIO_CONFIG = 7, MM_LERNA_CONFIG = 8, MM_SYSERR_IND = 9, + MM_SYSERR_CMD = 10 } ma_msg; /** @@ -436,6 +471,43 @@ static bool send_fw_config_to_active_mxman(uint32_t fw_runtime_flags) return ret; } +static bool send_syserr_cmd_to_active_mxman(u32 syserr_cmd) +{ + bool ret = false; + struct srvman *srvman = NULL; + + SCSC_TAG_INFO(MXMAN, "\n"); + if (!active_mxman) { + SCSC_TAG_ERR(MXMAN, "Active MXMAN NOT FOUND...cannot send running FW config.\n"); + return ret; + } + + mutex_lock(&active_mxman->mxman_mutex); + srvman = scsc_mx_get_srvman(active_mxman->mx); + if (srvman && srvman->error) { + mutex_unlock(&active_mxman->mxman_mutex); + SCSC_TAG_INFO(MXMAN, "Called during error - ignore\n"); + return ret; + } + + if (active_mxman->mxman_state == MXMAN_STATE_STARTED) { + struct ma_msg_packet message = { .ma_msg = MM_SYSERR_CMD, + .arg = syserr_cmd}; + + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_CMD - Args %02d\n", message.arg); + mxmgmt_transport_send(scsc_mx_get_mxmgmt_transport(active_mxman->mx), + MMTRANS_CHAN_ID_MAXWELL_MANAGEMENT, &message, + sizeof(message)); + ret = true; + } else { + SCSC_TAG_INFO(MXMAN, "MXMAN is NOT STARTED...cannot send MM_SYSERR_CMD msg.\n"); + } + mutex_unlock(&active_mxman->mxman_mutex); + + return ret; +} + +#ifdef CONFIG_SCSC_FM static bool send_fm_params_to_active_mxman(struct wlbt_fm_params *params) { bool ret = false; @@ -472,6 +544,7 @@ static bool send_fm_params_to_active_mxman(struct wlbt_fm_params *params) return ret; } +#endif static void mxman_stop(struct mxman *mxman); static void print_mailboxes(struct mxman *mxman); @@ -1443,6 +1516,7 @@ static void process_panic_record(struct mxman *mxman) #ifdef CONFIG_SCSC_MX450_GDB_SUPPORT u32 m4_1_panic_record_length = 0; /* in u32s */ #endif + u32 full_panic_code = 0; bool r4_panic_record_ok = false; bool m4_panic_record_ok = false; #ifdef CONFIG_SCSC_MX450_GDB_SUPPORT @@ -1484,13 +1558,14 @@ static void process_panic_record(struct mxman *mxman) SCSC_TAG_WARNING(MXMAN, "Bad panic record length/subversion\n"); break; case SCSC_R4_V2_MINOR_52: - if (r4_panic_record_ok) - mxman->scsc_panic_code |= SCSC_PANIC_SUBCODE_MASK_LEGACY & r4_panic_record[2]; - else if (m4_panic_record_ok) - mxman->scsc_panic_code |= SCSC_PANIC_SUBCODE_MASK_LEGACY & m4_panic_record[2]; + if (r4_panic_record_ok) { + full_panic_code = r4_panic_record[2]; + mxman->scsc_panic_code |= SCSC_PANIC_CODE_MASK & full_panic_code; + } else if (m4_panic_record_ok) + mxman->scsc_panic_code |= SCSC_PANIC_CODE_MASK & m4_panic_record[2]; #ifdef CONFIG_SCSC_MX450_GDB_SUPPORT else if (m4_1_panic_record_ok) - mxman->scsc_panic_code |= SCSC_PANIC_SUBCODE_MASK_LEGACY & m4_1_panic_record[2]; + mxman->scsc_panic_code |= SCSC_PANIC_CODE_MASK & m4_1_panic_record[2]; #endif /* Set unspecified technology for now */ mxman->scsc_panic_code |= SCSC_PANIC_TECH_UNSP; @@ -1511,7 +1586,8 @@ static void process_panic_record(struct mxman *mxman) if (r4_sympathetic_panic_flag == false) { /* process R4 record */ SCSC_TAG_INFO(MXMAN, "process R4 record\n"); - mxman->scsc_panic_code |= SCSC_PANIC_SUBCODE_MASK & r4_panic_record[3]; + full_panic_code = r4_panic_record[3]; + mxman->scsc_panic_code |= SCSC_PANIC_CODE_MASK & full_panic_code; print_panic_code(mxman->scsc_panic_code); break; } @@ -1525,11 +1601,11 @@ static void process_panic_record(struct mxman *mxman) if (m4_sympathetic_panic_flag == false) { /* process M4 record */ SCSC_TAG_INFO(MXMAN, "process M4 record\n"); - mxman->scsc_panic_code |= SCSC_PANIC_SUBCODE_MASK & m4_panic_record[3]; + mxman->scsc_panic_code |= SCSC_PANIC_CODE_MASK & m4_panic_record[3]; } else if (r4_panic_record_ok) { /* process R4 record */ SCSC_TAG_INFO(MXMAN, "process R4 record\n"); - mxman->scsc_panic_code |= SCSC_PANIC_SUBCODE_MASK & r4_panic_record[3]; + mxman->scsc_panic_code |= SCSC_PANIC_CODE_MASK & r4_panic_record[3]; } print_panic_code(mxman->scsc_panic_code); } @@ -1557,6 +1633,13 @@ static void process_panic_record(struct mxman *mxman) break; } } + if (r4_panic_record_ok) { + /* Populate syserr info with panic equivalent */ + mxman->last_syserr.subsys = (u8) ((full_panic_code >> SYSERR_SUB_SYSTEM_POSN) & SYSERR_SUB_SYSTEM_MASK); + mxman->last_syserr.level = MX_SYSERR_LEVEL_7; + mxman->last_syserr.type = (u8) ((full_panic_code >> SYSERR_TYPE_POSN) & SYSERR_TYPE_MASK); + mxman->last_syserr.subcode = (u16) ((full_panic_code >> SYSERR_SUB_CODE_POSN) & SYSERR_SUB_CODE_MASK); + } } #define MAX_UHELP_TMO_MS 20000 @@ -1572,8 +1655,10 @@ static void mxman_failure_work(struct work_struct *work) int used = 0, r = 0; #ifdef CONFIG_ANDROID - wake_lock(&mxman->recovery_wake_lock); + wake_lock(&mxman->failure_recovery_wake_lock); #endif + /* Take mutex shared with syserr recovery */ + mutex_lock(&mxman->mxman_recovery_mutex); slsi_kic_system_event(slsi_kic_system_event_category_error, slsi_kic_system_events_subsystem_crashed, GFP_KERNEL); @@ -1588,9 +1673,10 @@ static void mxman_failure_work(struct work_struct *work) if (mxman->mxman_state != MXMAN_STATE_STARTED && mxman->mxman_state != MXMAN_STATE_STARTING) { SCSC_TAG_WARNING(MXMAN, "Not in started state: mxman->mxman_state=%d\n", mxman->mxman_state); #ifdef CONFIG_ANDROID - wake_unlock(&mxman->recovery_wake_lock); + wake_unlock(&mxman->failure_recovery_wake_lock); #endif mutex_unlock(&mxman->mxman_mutex); + mutex_unlock(&mxman->mxman_recovery_mutex); return; } @@ -1610,13 +1696,20 @@ static void mxman_failure_work(struct work_struct *work) mxman->mxman_state = mxman->mxman_next_state; + /* Mark any single service recovery as no longer in progress */ + mxman->syserr_recovery_in_progress = false; + mxman->last_syserr_recovery_time = 0; + + /* Safe to allow syserr recovery thread to run */ + mutex_unlock(&mxman->mxman_recovery_mutex); + if (mxman->mxman_state != MXMAN_STATE_FAILED - && mxman->mxman_state != MXMAN_STATE_FREEZED) { + && mxman->mxman_state != MXMAN_STATE_FROZEN) { WARN_ON(mxman->mxman_state != MXMAN_STATE_FAILED - && mxman->mxman_state != MXMAN_STATE_FREEZED); + && mxman->mxman_state != MXMAN_STATE_FROZEN); SCSC_TAG_ERR(MXMAN, "Bad state=%d\n", mxman->mxman_state); #ifdef CONFIG_ANDROID - wake_unlock(&mxman->recovery_wake_lock); + wake_unlock(&mxman->failure_recovery_wake_lock); #endif mutex_unlock(&mxman->mxman_mutex); return; @@ -1633,8 +1726,7 @@ static void mxman_failure_work(struct work_struct *work) SCSC_TAG_INFO(MXMAN, "Setting MIFINTRBIT_RESERVED_PANIC_M4\n"); mif->irq_bit_set(mif, MIFINTRBIT_RESERVED_PANIC_M4, SCSC_MIF_ABS_TARGET_M4); /* SCSC_MIFINTR_TARGET_M4 */ #endif - - srvman_freeze_services(srvman); + srvman_freeze_services(srvman, &mxman->last_syserr); if (mxman->mxman_state == MXMAN_STATE_FAILED) { mxman->last_panic_time = local_clock(); process_panic_record(mxman); @@ -1736,6 +1828,12 @@ static void mxman_failure_work(struct work_struct *work) /* Clean up the MIF following error handling */ if (mif->mif_cleanup && mxman_recovery_disabled()) mif->mif_cleanup(mif); + } else { + /* Populate syserr info with panic equivalent for host induced panic */ + mxman->last_syserr.subsys = SYSERR_SUB_SYSTEM_HOST; + mxman->last_syserr.level = MX_SYSERR_LEVEL_7; + mxman->last_syserr.type = 0; + mxman->last_syserr.subcode = mxman->scsc_panic_code; } SCSC_TAG_INFO(MXMAN, "Auto-recovery: %s\n", mxman_recovery_disabled() ? "off" : "on"); @@ -1759,7 +1857,7 @@ static void mxman_failure_work(struct work_struct *work) complete(&mxman->recovery_completion); #ifdef CONFIG_ANDROID - wake_unlock(&mxman->recovery_wake_lock); + wake_unlock(&mxman->failure_recovery_wake_lock); #endif } @@ -1789,6 +1887,80 @@ static void failure_wq_start(struct mxman *mxman) queue_work(mxman->failure_wq, &mxman->failure_work); } +/* + * workqueue thread + */ +static void mxman_syserr_recovery_work(struct work_struct *work) +{ + struct mxman *mxman = container_of(work, struct mxman, syserr_recovery_work); + struct srvman *srvman; + +#ifdef CONFIG_ANDROID + wake_lock(&mxman->syserr_recovery_wake_lock); +#endif + if (!mutex_trylock(&mxman->mxman_recovery_mutex)) { + SCSC_TAG_WARNING(MXMAN, "Syserr during full reset - ignored\n"); +#ifdef CONFIG_ANDROID + wake_unlock(&mxman->syserr_recovery_wake_lock); +#endif + return; + } + + mutex_lock(&mxman->mxman_mutex); + + if (mxman->mxman_state != MXMAN_STATE_STARTED && mxman->mxman_state != MXMAN_STATE_STARTING) { + SCSC_TAG_WARNING(MXMAN, "Syserr reset ignored: mxman->mxman_state=%d\n", mxman->mxman_state); +#ifdef CONFIG_ANDROID + wake_unlock(&mxman->syserr_recovery_wake_lock); +#endif + mutex_unlock(&mxman->mxman_mutex); + return; + } + + srvman = scsc_mx_get_srvman(mxman->mx); + + srvman_freeze_sub_system(srvman, &mxman->last_syserr); + +#ifdef CONFIG_SCSC_WLBTD +#ifdef CONFIG_SCSC_LOG_COLLECTION + /* Wait for log generation if not finished */ + SCSC_TAG_INFO(MXMAN, "Wait for syserr sable logging\n"); + scsc_wlbtd_wait_for_sable_logging(); + SCSC_TAG_INFO(MXMAN, "Syserr sable logging complete\n"); +#endif +#endif + + srvman_unfreeze_sub_system(srvman, &mxman->last_syserr); + +#ifdef CONFIG_ANDROID + wake_unlock(&mxman->syserr_recovery_wake_lock); +#endif + mutex_unlock(&mxman->mxman_mutex); +} + +static void syserr_recovery_wq_init(struct mxman *mxman) +{ + mxman->syserr_recovery_wq = create_singlethread_workqueue("syserr_recovery_wq"); + INIT_WORK(&mxman->syserr_recovery_work, mxman_syserr_recovery_work); +} + +static void syserr_recovery_wq_stop(struct mxman *mxman) +{ + cancel_work_sync(&mxman->syserr_recovery_work); + flush_workqueue(mxman->syserr_recovery_wq); +} + +static void syserr_recovery_wq_deinit(struct mxman *mxman) +{ + syserr_recovery_wq_stop(mxman); + destroy_workqueue(mxman->syserr_recovery_wq); +} + +static void syserr_recovery_wq_start(struct mxman *mxman) +{ + queue_work(mxman->syserr_recovery_wq, &mxman->syserr_recovery_work); +} + static void print_mailboxes(struct mxman *mxman) { struct scsc_mif_abs *mif; @@ -2105,8 +2277,16 @@ void mxman_close(struct mxman *mxman) } } -void mxman_syserr(struct mxman *mxman, struct mx_syserr *syserr) +void mxman_syserr(struct mxman *mxman, struct mx_syserr_decode *syserr) { + mxman->syserr_recovery_in_progress = true; + + mxman->last_syserr.subsys = syserr->subsys; + mxman->last_syserr.level = syserr->level; + mxman->last_syserr.type = syserr->type; + mxman->last_syserr.subcode = syserr->subcode; + + syserr_recovery_wq_start(mxman); } void mxman_fail(struct mxman *mxman, u16 scsc_panic_code, const char *reason) @@ -2128,6 +2308,12 @@ void mxman_fail(struct mxman *mxman, u16 scsc_panic_code, const char *reason) } else { SCSC_TAG_WARNING(MXMAN, "Not in MXMAN_STATE_STARTED state, ignore (state %d)\n", mxman->mxman_state); } + + /* Populate syserr info with panic equivalent or best we can */ + mxman->last_syserr.subsys = scsc_panic_code >> SYSERR_SUB_SYSTEM_POSN; + mxman->last_syserr.level = MX_SYSERR_LEVEL_7; + mxman->last_syserr.type = scsc_panic_code; + mxman->last_syserr.subcode = scsc_panic_code; } void mxman_freeze(struct mxman *mxman) @@ -2135,7 +2321,7 @@ void mxman_freeze(struct mxman *mxman) SCSC_TAG_WARNING(MXMAN, "WLBT FW frozen\n"); if (mxman->mxman_state == MXMAN_STATE_STARTED) { - mxman->mxman_next_state = MXMAN_STATE_FREEZED; + mxman->mxman_next_state = MXMAN_STATE_FROZEN; failure_wq_start(mxman); } else { SCSC_TAG_WARNING(MXMAN, "Not in MXMAN_STATE_STARTED state, ignore (state %d)\n", mxman->mxman_state); @@ -2152,14 +2338,19 @@ void mxman_init(struct mxman *mxman, struct scsc_mx *mx) #endif fw_crc_wq_init(mxman); failure_wq_init(mxman); + syserr_recovery_wq_init(mxman); #ifdef CONFIG_SCSC_WLBTD wlbtd_wq_init(mxman); #endif mutex_init(&mxman->mxman_mutex); + mutex_init(&mxman->mxman_recovery_mutex); init_completion(&mxman->recovery_completion); #ifdef CONFIG_ANDROID - wake_lock_init(&mxman->recovery_wake_lock, WAKE_LOCK_SUSPEND, "mxman_recovery"); + wake_lock_init(&mxman->failure_recovery_wake_lock, WAKE_LOCK_SUSPEND, "mxman_recovery"); + wake_lock_init(&mxman->syserr_recovery_wake_lock, WAKE_LOCK_SUSPEND, "mxman_syserr_recovery"); #endif + mxman->syserr_recovery_in_progress = false; + mxman->last_syserr_recovery_time = 0; /* set the initial state */ mxman->mxman_state = MXMAN_STATE_STOPPED; @@ -2186,12 +2377,15 @@ void mxman_deinit(struct mxman *mxman) mxproc_remove_info_proc_dir(&mxman->mxproc); fw_crc_wq_deinit(mxman); failure_wq_deinit(mxman); + syserr_recovery_wq_deinit(mxman); #ifdef CONFIG_SCSC_WLBTD wlbtd_wq_deinit(mxman); #endif #ifdef CONFIG_ANDROID - wake_lock_destroy(&mxman->recovery_wake_lock); + wake_lock_destroy(&mxman->failure_recovery_wake_lock); + wake_lock_destroy(&mxman->syserr_recovery_wake_lock); #endif + mutex_destroy(&mxman->mxman_recovery_mutex); mutex_destroy(&mxman->mxman_mutex); } diff --git a/drivers/misc/samsung/scsc/mxman.h b/drivers/misc/samsung/scsc/mxman.h index 25ac9b4160da..0bca17a8629b 100755 --- a/drivers/misc/samsung/scsc/mxman.h +++ b/drivers/misc/samsung/scsc/mxman.h @@ -22,11 +22,13 @@ void mxman_deinit(struct mxman *mxman); int mxman_open(struct mxman *mxman); void mxman_close(struct mxman *mxman); void mxman_fail(struct mxman *mxman, u16 scsc_panic_code, const char *reason); +void mxman_syserr(struct mxman *mxman, struct mx_syserr_decode *syserr); void mxman_freeze(struct mxman *mxman); int mxman_force_panic(struct mxman *mxman); int mxman_suspend(struct mxman *mxman); void mxman_resume(struct mxman *mxman); void mxman_show_last_panic(struct mxman *mxman); +void mxman_subserv_recovery(struct mxman *mxman, struct mx_syserr_decode *syserr_decode); #ifdef CONFIG_SCSC_FM void mxman_fm_on_halt_ldos_on(void); @@ -40,7 +42,7 @@ enum mxman_state { MXMAN_STATE_STARTING, MXMAN_STATE_STARTED, MXMAN_STATE_FAILED, - MXMAN_STATE_FREEZED, + MXMAN_STATE_FROZEN, }; #define SCSC_FAILURE_REASON_LEN 256 @@ -51,8 +53,10 @@ struct mxman { void *start_dram; struct workqueue_struct *fw_crc_wq; struct delayed_work fw_crc_work; - struct workqueue_struct *failure_wq; - struct work_struct failure_work; + struct workqueue_struct *failure_wq; /* For recovery from chip restart */ + struct work_struct failure_work; /* For recovery from chip restart */ + struct workqueue_struct *syserr_recovery_wq; /* For recovery from syserr sub-system restart */ + struct work_struct syserr_recovery_work; /* For recovery from syserr sub-system restart */ char *fw; u32 fw_image_size; struct completion mm_msg_start_ind_completion; @@ -62,6 +66,7 @@ struct mxman { enum mxman_state mxman_state; enum mxman_state mxman_next_state; struct mutex mxman_mutex; + struct mutex mxman_recovery_mutex; /* Syserr sub-sytem and full chip restart co-ordination */ struct mxproc mxproc; int suspended; atomic_t suspend_count; @@ -71,13 +76,18 @@ struct mxman { char fw_build_id[FW_BUILD_ID_SZ]; /* Defined in SC-505846-SW */ struct completion recovery_completion; #ifdef CONFIG_ANDROID - struct wake_lock recovery_wake_lock; + struct wake_lock failure_recovery_wake_lock; /* For recovery from chip restart */ + struct wake_lock syserr_recovery_wake_lock; /* For recovery from syserr sub-system restart */ #endif u32 rf_hw_ver; u16 scsc_panic_code; u64 last_panic_time; u32 last_panic_rec_r[PANIC_RECORD_SIZE]; /* Must be at least SCSC_R4_V2_MINOR_53 */ u16 last_panic_rec_sz; + struct mx_syserr_decode last_syserr; + unsigned long last_syserr_recovery_time; /* In jiffies */ + bool notify; + bool syserr_recovery_in_progress; #ifdef CONFIG_SCSC_FM u32 on_halt_ldos_on; #endif diff --git a/drivers/misc/samsung/scsc/mxproc.c b/drivers/misc/samsung/scsc/mxproc.c index a5f4b3783a2f..5d5d76c3135c 100644 --- a/drivers/misc/samsung/scsc/mxproc.c +++ b/drivers/misc/samsung/scsc/mxproc.c @@ -331,8 +331,8 @@ static ssize_t mx_procfs_mx_status_read(struct file *file, char __user *user_buf case MXMAN_STATE_FAILED: pos += scnprintf(buf + pos, bufsz - pos, "%s\n", "MXMAN_STATE_FAILED"); break; - case MXMAN_STATE_FREEZED: - pos += scnprintf(buf + pos, bufsz - pos, "%s\n", "MXMAN_STATE_FREEZED"); + case MXMAN_STATE_FROZEN: + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", "MXMAN_STATE_FROZEN"); break; default: return 0; diff --git a/drivers/misc/samsung/scsc/mxsyserr.c b/drivers/misc/samsung/scsc/mxsyserr.c index a4116dfc0b2a..8225f91408fe 100755 --- a/drivers/misc/samsung/scsc/mxsyserr.c +++ b/drivers/misc/samsung/scsc/mxsyserr.c @@ -25,14 +25,47 @@ #include "panicmon.h" #include "mxproc.h" #include "mxsyserr.h" +#include "scsc/scsc_log_collector.h" #include #include #include -void mx_syserr_handler(struct mxman *mx, const void *message) +/* If limits below are exceeded, a service level reset will be raised to level 7 */ +#define SYSERR_RESET_HISTORY_SIZE (4) +/* Minimum time between system error service resets (ms) */ +#define SYSERR_RESET_MIN_INTERVAL (300000) +/* No more then SYSERR_RESET_HISTORY_SIZE system error service resets in this period (ms)*/ +#define SYSERR_RESET_MONITOR_PERIOD (3600000) + + +/* Time stamps of last service resets in jiffies */ +static unsigned long syserr_reset_history[SYSERR_RESET_HISTORY_SIZE] = {0}; +static int syserr_reset_history_index; + +static uint syserr_reset_min_interval = SYSERR_RESET_MIN_INTERVAL; +module_param(syserr_reset_min_interval, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(syserr_reset_min_interval, "Minimum time between system error service resets (ms)"); + +static uint syserr_reset_monitor_period = SYSERR_RESET_MONITOR_PERIOD; +module_param(syserr_reset_monitor_period, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(syserr_reset_monitor_period, "No more then 4 system error service resets in this period (ms)"); + + +void mx_syserr_init(void) +{ + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_INIT: syserr_reset_min_interval %lu syserr_reset_monitor_period %lu\n", + syserr_reset_min_interval, syserr_reset_monitor_period); +} + +void mx_syserr_handler(struct mxman *mxman, const void *message) { const struct mx_syserr_msg *msg = (const struct mx_syserr_msg *)message; + struct srvman *srvman; + + struct mx_syserr_decode decode; + + srvman = scsc_mx_get_srvman(mxman->mx); SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND len: %u, ts: 0x%08X, tf: 0x%08X, str: 0x%x, code: 0x%08x, p0: 0x%x, p1: 0x%x\n", msg->syserr.length, @@ -42,4 +75,107 @@ void mx_syserr_handler(struct mxman *mx, const void *message) msg->syserr.syserr_code, msg->syserr.param[0], msg->syserr.param[1]); + + decode.subsys = (u8) ((msg->syserr.syserr_code >> SYSERR_SUB_SYSTEM_POSN) & SYSERR_SUB_SYSTEM_MASK); + decode.level = (u8) ((msg->syserr.syserr_code >> SYSERR_LEVEL_POSN) & SYSERR_LEVEL_MASK); + decode.type = (u8) ((msg->syserr.syserr_code >> SYSERR_TYPE_POSN) & SYSERR_TYPE_MASK); + decode.subcode = (u16) ((msg->syserr.syserr_code >> SYSERR_SUB_CODE_POSN) & SYSERR_SUB_CODE_MASK); + + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND Subsys %d, Level %d, Type %d, Subcode 0x%04x\n", + decode.subsys, decode.level, decode.type, decode.subcode); + + /* Level 1 just gets logged without bothering anyone else */ + if (decode.level == MX_SYSERR_LEVEL_1) { + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x log only\n", + msg->syserr.syserr_code); + return; + } + + /* Ignore if panic reset in progress */ + if ((srvman->error) || (mxman->mxman_state == MXMAN_STATE_FAILED)) { + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x ignored (reset in progess)\n", + msg->syserr.syserr_code); + return; + } + + /* Ignore any system errors for the same sub-system if recovery is in progress */ + if ((mxman->syserr_recovery_in_progress) && (mxman->last_syserr.subsys == decode.subsys)) { + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x ignored (recovery in progess)\n", + msg->syserr.syserr_code); + return; + } + + + /* Let affected sevices escalate if needed - this also checks if only one sub-system is running + * and handles race conditions with shutting down service + */ + decode.level = srvman_notify_sub_system(srvman, &decode); + + if (decode.level >= MX_SYSERR_LEVEL_5) { + unsigned long now = jiffies; + int i; + + /* We use 0 as a NULL timestamp so avoid this */ + now = (now) ? now : 1; + + if ((decode.level == MX_SYSERR_LEVEL_7) || (mxman->syserr_recovery_in_progress)) { + /* If full reset has been requested or a service restart is needed and one is + * already in progress, trigger a full reset + */ + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x triggered full reset\n", + msg->syserr.syserr_code); + + mxman_fail(mxman, SCSC_PANIC_CODE_HOST << 15, __func__); + return; + } + + /* last_syserr_recovery_time is always zero-ed before we restart the chip */ + if (mxman->last_syserr_recovery_time) { + /* Have we had a too recent system error service reset + * Chance of false positive here is low enough to be acceptable + */ + if ((syserr_reset_min_interval) && (time_in_range(now, mxman->last_syserr_recovery_time, + mxman->last_syserr_recovery_time + msecs_to_jiffies(syserr_reset_min_interval)))) { + + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x triggered full reset (less than %dms after last)\n", + msg->syserr.syserr_code, syserr_reset_min_interval); + mxman_fail(mxman, SCSC_PANIC_CODE_HOST << 15, __func__); + return; + } else if (syserr_reset_monitor_period) { + /* Have we had too many system error service resets in one period? */ + /* This will be the case if all our stored history was in this period */ + bool out_of_danger_period_found = false; + + for (i = 0; (i < SYSERR_RESET_HISTORY_SIZE) && (!out_of_danger_period_found); i++) + out_of_danger_period_found = ((!syserr_reset_history[i]) || + (!time_in_range(now, syserr_reset_history[i], + syserr_reset_history[i] + msecs_to_jiffies(syserr_reset_monitor_period)))); + + if (!out_of_danger_period_found) { + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x triggered full reset (too many within %dms)\n", + msg->syserr.syserr_code, syserr_reset_monitor_period); + mxman_fail(mxman, SCSC_PANIC_CODE_HOST << 15, __func__); + return; + } + } + } else + /* First syserr service reset since chip was (re)started - zap history */ + for (i = 0; i < SYSERR_RESET_HISTORY_SIZE; i++) + syserr_reset_history[i] = 0; + + /* Otherwise trigger recovery of the affected subservices */ + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x triggered service recovery\n", + msg->syserr.syserr_code); + syserr_reset_history[syserr_reset_history_index++ % SYSERR_RESET_HISTORY_SIZE] = now; + mxman->last_syserr_recovery_time = now; + mxman_syserr(mxman, &decode); + } + +#ifdef CONFIG_SCSC_WLBTD +#ifdef CONFIG_SCSC_LOG_COLLECTION + /* Trigger sable log collection */ + SCSC_TAG_INFO(MXMAN, "MM_SYSERR_IND code: 0x%08x requested log collection\n", msg->syserr.syserr_code); + scsc_log_collector_schedule_collection(SCSC_LOG_SYS_ERR, decode.subcode); +#endif +#endif } diff --git a/drivers/misc/samsung/scsc/mxsyserr.h b/drivers/misc/samsung/scsc/mxsyserr.h index 43e134fdbb47..5991836897b4 100755 --- a/drivers/misc/samsung/scsc/mxsyserr.h +++ b/drivers/misc/samsung/scsc/mxsyserr.h @@ -22,6 +22,7 @@ struct mx_syserr_msg { struct mx_syserr syserr; } __packed; +void mx_syserr_init(void); void mx_syserr_handler(struct mxman *mx, const void *message); #endif diff --git a/drivers/misc/samsung/scsc/platform_mif.h b/drivers/misc/samsung/scsc/platform_mif.h index b145cec280a8..7db2867a1881 100644 --- a/drivers/misc/samsung/scsc/platform_mif.h +++ b/drivers/misc/samsung/scsc/platform_mif.h @@ -12,7 +12,7 @@ enum wlbt_irqs { PLATFORM_MIF_MBOX, PLATFORM_MIF_ALIVE, PLATFORM_MIF_WDOG, -#if defined(CONFIG_SOC_EXYNOS9610) || defined(CONFIG_SOC_EXYNOS9630) +#if defined(CONFIG_SOC_EXYNOS9610) || defined(CONFIG_SOC_EXYNOS9630) || defined(CONFIG_SOC_EXYNOS3830) PLATFORM_MIF_CFG_REQ, #endif /* must be last */ diff --git a/drivers/misc/samsung/scsc/platform_mif_3830.c b/drivers/misc/samsung/scsc/platform_mif_3830.c new file mode 100644 index 000000000000..f2199c4b5e04 --- /dev/null +++ b/drivers/misc/samsung/scsc/platform_mif_3830.c @@ -0,0 +1,2227 @@ +/**************************************************************************** + * + * Copyright (c) 2014 - 2019 Samsung Electronics Co., Ltd. All rights reserved + * + ****************************************************************************/ + +/* Implements interface */ + +#include "platform_mif.h" + +/* Interfaces it Uses */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#include +#endif +#include +#include +#include +#include +#include "mif_reg_S5E3830.h" +#include "platform_mif_module.h" +#ifdef CONFIG_ARCH_EXYNOS +#include +#endif +#ifdef CONFIG_SOC_EXYNOS3830 +#include +#endif + +#ifdef CONFIG_SCSC_SMAPPER +#include +#include "mif_reg_smapper.h" +#endif +#ifdef CONFIG_SCSC_QOS +#include +#endif + +#if !defined(CONFIG_SOC_EXYNOS3830) +#error Target processor CONFIG_SOC_EXYNOS3830 not selected +#endif + +#ifdef CONFIG_SCSC_LOG_COLLECTION +#include +#endif +/* Time to wait for CFG_REQ IRQ on 3830 */ +#define WLBT_BOOT_TIMEOUT (HZ) + +#ifdef CONFIG_OF_RESERVED_MEM +#include +#endif +static unsigned long sharedmem_base; +static size_t sharedmem_size; + +#ifdef CONFIG_SCSC_CHV_SUPPORT +static bool chv_disable_irq; +module_param(chv_disable_irq, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(chv_disable_irq, "Do not register for irq"); +#endif + +static bool enable_platform_mif_arm_reset = true; +module_param(enable_platform_mif_arm_reset, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(enable_platform_mif_arm_reset, "Enables WIFIBT ARM cores reset"); + +#ifdef CONFIG_SCSC_QOS +struct qos_table { + unsigned int freq_mif; + unsigned int freq_int; + unsigned int freq_cl0; + unsigned int freq_cl1; +}; +#endif + +struct platform_mif { + struct scsc_mif_abs interface; + struct scsc_mbox_s *mbox; + struct platform_device *pdev; + + struct device *dev; + + struct { + int irq_num; + int flags; + atomic_t irq_disabled_cnt; + } wlbt_irq[PLATFORM_MIF_NUM_IRQS]; + + /* MIF registers preserved during suspend */ + struct { + u32 irq_bit_mask; + } mif_preserve; + + /* register MBOX memory space */ + size_t reg_start; + size_t reg_size; + void __iomem *base; + + /* register CMU memory space */ + struct regmap *cmu_base; + + void __iomem *con0_base; + + /* pmu syscon regmap */ + struct regmap *pmureg; +#if defined(CONFIG_SOC_EXYNOS3830) + struct regmap *i3c_apm_pmic; + struct regmap *dbus_baaw; + struct regmap *pbus_baaw; + struct regmap *wlbt_remap; + struct regmap *boot_cfg; + + /* Signalled when CFG_REQ IRQ handled */ + struct completion cfg_ack; + + /* State of CFG_REQ handler */ + enum wlbt_boot_state { + WLBT_BOOT_IN_RESET = 0, + WLBT_BOOT_WAIT_CFG_REQ, + WLBT_BOOT_CFG_DONE, + WLBT_BOOT_CFG_ERROR + } boot_state; + +#endif +#ifdef CONFIG_SCSC_SMAPPER + /* SMAPPER */ + void __iomem *smapper_base; + u8 smapper_banks; + struct { + u8 bank; + u32 ws; + bool large; + struct scsc_mif_smapper_info bank_info; + } *smapper; +#endif + /* Shared memory space - reserved memory */ + unsigned long mem_start; + size_t mem_size; + void __iomem *mem; + + /* Callback function and dev pointer mif_intr manager handler */ + void (*r4_handler)(int irq, void *data); + void *irq_dev; + /* spinlock to serialize driver access */ + spinlock_t mif_spinlock; + void (*reset_request_handler)(int irq, void *data); + void *irq_reset_request_dev; + +#ifdef CONFIG_SCSC_QOS + /* QoS table */ + struct qos_table *qos; + bool qos_enabled; +#endif + /* Suspend/resume handlers */ + int (*suspend_handler)(struct scsc_mif_abs *abs, void *data); + void (*resume_handler)(struct scsc_mif_abs *abs, void *data); + void *suspendresume_data; +}; + +static void power_supplies_on(struct platform_mif *platform); + +extern int mx140_log_dump(void); + +#define platform_mif_from_mif_abs(MIF_ABS_PTR) container_of(MIF_ABS_PTR, struct platform_mif, interface) + +inline void platform_mif_reg_write(struct platform_mif *platform, u16 offset, u32 value) +{ + writel(value, platform->base + offset); +} + +inline u32 platform_mif_reg_read(struct platform_mif *platform, u16 offset) +{ + return readl(platform->base + offset); +} + +#ifdef CONFIG_SCSC_SMAPPER +inline void platform_mif_reg_write_smapper(struct platform_mif *platform, u16 offset, u32 value) +{ + writel(value, platform->smapper_base + offset); +} + +inline u32 platform_mif_reg_read_smapper(struct platform_mif *platform, u16 offset) +{ + return readl(platform->smapper_base + offset); +} + +#define PLATFORM_MIF_SHIFT_SMAPPER_ADDR 11 /* From 36 bits addres to 25 bits */ +#define PLATFORM_MIF_SHIFT_SMAPPER_END 4 /* End address aligment */ + +/* Platform is responsible to give the phys mapping of the SMAPPER maps */ +static int platform_mif_smapper_get_mapping(struct scsc_mif_abs *interface, u8 *phy_map, u16 *align) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u8 i; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Mapping %d banks\n", platform->smapper_banks); + + if (!platform->smapper_banks) + return -EINVAL; + + for (i = 0; i < platform->smapper_banks; i++) { + if (platform->smapper[i].large) + phy_map[i] = SCSC_MIF_ABS_LARGE_BANK; + else + phy_map[i] = SCSC_MIF_ABS_SMALL_BANK; + } + + if (align) + *align = 1 << PLATFORM_MIF_SHIFT_SMAPPER_ADDR; + + return 0; +} + +static int platform_mif_smapper_get_bank_info(struct scsc_mif_abs *interface, u8 bank, struct scsc_mif_smapper_info *bank_info) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + if (!platform->smapper_banks) + return -EINVAL; + + bank_info->num_entries = platform->smapper[bank].bank_info.num_entries; + bank_info->mem_range_bytes = platform->smapper[bank].bank_info.mem_range_bytes; + + return 0; +} + +static u8 platform_mif_smapper_granularity_to_bits(u32 granularity) +{ + if (granularity <= 2 * 1024) + return 0; + if (granularity <= 4 * 1024) + return 1; + if (granularity <= 8 * 1024) + return 2; + if (granularity <= 16 * 1024) + return 3; + if (granularity <= 32 * 1024) + return 4; + if (granularity <= 64 * 1024) + return 5; + if (granularity <= 128 * 1024) + return 6; + return 7; +} + +static u32 platform_mif_smapper_get_bank_base_address(struct scsc_mif_abs *interface, u8 bank) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + if (!platform->smapper) + return 0; + + return platform->smapper[bank].ws; +} + +/* Configure smapper according the memory map and range */ +static void platform_mif_smapper_configure(struct scsc_mif_abs *interface, u32 granularity) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u8 i; + u8 gran; + u8 nb = platform->smapper_banks; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Configure SMAPPER with granularity %d\n", granularity); + + gran = platform_mif_smapper_granularity_to_bits(granularity); + + platform_mif_reg_write_smapper(platform, SMAPPER_QCH_DISABLE, 1); + platform_mif_reg_write_smapper(platform, ORIGIN_ADDR_AR, 0); + platform_mif_reg_write_smapper(platform, ORIGIN_ADDR_AW, 0); + /* Program SMAPPER memmap */ + for (i = 0; i < nb; i++) { + /* Set ADDR_MAP_EN to 1'b0*/ + platform_mif_reg_write_smapper(platform, ADDR_MAP_EN(i), 0); + /* Set START_ADDR */ + platform_mif_reg_write_smapper(platform, START_ADDR(i), platform->smapper[i].ws); + /* Set ADDR_GRANULARITY - FIXED AT 4KB */ + platform_mif_reg_write_smapper(platform, ADDR_GRANULARITY(i), gran); + /* WLAN_ADDR_MAP operation is started */ + } + /* Set access window control (MSB 32bits Start/End address) */ + /* Remapped address should be ranged from AW_START_ADDR to AW_EN_ADDR */ + platform_mif_reg_write_smapper(platform, AW_START_ADDR, 0); + platform_mif_reg_write_smapper(platform, AW_END_ADDR, dma_get_mask(platform->dev) >> PLATFORM_MIF_SHIFT_SMAPPER_END); + smp_mb(); +} + +/* Caller is responsible of validating the phys address (alignment) */ +static int platform_mif_smapper_write_sram(struct scsc_mif_abs *interface, u8 bank, u8 num_entries, u8 first_entry, dma_addr_t *addr) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u8 i; + u32 rb; + + if (!platform->smapper_banks) + return -EINVAL; + + if (!platform->smapper_base) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "SMAPPER not enabled\n"); + return -EINVAL; + } + + /* Set ADDR_MAP_EN to 1'b0*/ + platform_mif_reg_write_smapper(platform, ADDR_MAP_EN(bank), 0); + /* Write mapping table to SRAM. Each entry consists of 25 bits MSB address to remap */ + for (i = 0; i < num_entries; i++) { + if (!addr[i]) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "ADDR IS NULL at bank %d entry %d/%d\n", bank, first_entry + i, num_entries); + return -EINVAL; + } + /* Set SRAM_WRITE_CTRL to 1'b1*/ + platform_mif_reg_write_smapper(platform, SRAM_WRITE_CTRL(bank), 1); + platform_mif_reg_write_smapper(platform, SRAM_BANK_INDEX(bank, first_entry + i), addr[i] >> PLATFORM_MIF_SHIFT_SMAPPER_ADDR); + /* check incorrect writings */ + platform_mif_reg_write_smapper(platform, SRAM_WRITE_CTRL(bank), 0); + rb = platform_mif_reg_read_smapper(platform, SRAM_BANK_INDEX(bank, first_entry + i)); + if (rb != addr[i] >> PLATFORM_MIF_SHIFT_SMAPPER_ADDR) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "incorrect mapping detected rb 0x%x, addr 0x%x\n", rb, (u32)addr[i] >> PLATFORM_MIF_SHIFT_SMAPPER_ADDR); + return -EFAULT; + } + } + platform_mif_reg_write_smapper(platform, ADDR_MAP_EN(bank), 1); + smp_mb(); + return 0; +} + +static int platform_mif_parse_smapper(struct platform_mif *platform, struct device_node *np, u8 num_banks) +{ + /* SMAPPER parsing */ + struct device_node *np_banks; + char node_name[50]; + u32 val[2]; + u8 i; + u32 bank = 0, ws = 0, wsz = 0, ent = 0, large = 0; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "banks found: %d szof %zu\n", num_banks, sizeof(*platform->smapper)); + + platform->smapper = kmalloc_array(num_banks, sizeof(*platform->smapper), GFP_KERNEL); + + if (!platform->smapper) + return -ENOMEM; + + for (i = 0; i < num_banks; i++) { + snprintf(node_name, sizeof(node_name), "smapper_bank_%d", i); + np_banks = of_find_node_by_name(np, node_name); + if (!np_banks) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "%s: could not find smapper_bank\n", + node_name); + kfree(platform->smapper); + platform->smapper = NULL; + return -ENOENT; + } + of_property_read_u32(np_banks, "bank_num", &bank); + of_property_read_u32(np_banks, "fw_window_start", &ws); + of_property_read_u32(np_banks, "fw_window_size", &wsz); + of_property_read_u32(np_banks, "num_entries", &ent); + of_property_read_u32(np_banks, "is_large", &large); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "bank %d fw_w_start 0x%x fw_w_sz 0x%x entries %d is_large %d\n", + bank, ws, wsz, ent, large); + + platform->smapper[i].bank = (u8)bank; + platform->smapper[i].ws = ws; + platform->smapper[i].large = (bool)large; + platform->smapper[i].bank_info.num_entries = ent; + platform->smapper[i].bank_info.mem_range_bytes = wsz; + } + + /* Update the number of banks before returning */ + platform->smapper_banks = num_banks; + + of_property_read_u32_array(np, "smapper_reg", val, 2); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "smapper reg address 0x%x size 0x%x\n", val[0], val[1]); + platform->smapper_base = + devm_ioremap_nocache(platform->dev, val[0], val[1]); + + if (!platform->smapper_base) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Error mapping smapper register region\n"); + kfree(platform->smapper); + platform->smapper = NULL; + return -ENOENT; + } + + return 0; +} +#endif +#ifdef CONFIG_SCSC_QOS +static int platform_mif_parse_qos(struct platform_mif *platform, struct device_node *np) +{ + int len, i; + + platform->qos_enabled = false; + + len = of_property_count_u32_elems(np, "qos_table"); + if (!(len == 12)) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "No qos table for wlbt, or incorrect size\n"); + return -ENOENT; + } + + platform->qos = devm_kzalloc(platform->dev, sizeof(struct qos_table) * len / 4, GFP_KERNEL); + if (!platform->qos) + return -ENOMEM; + + of_property_read_u32_array(np, "qos_table", (unsigned int *)platform->qos, len); + + for (i = 0; i < len / 4; i++) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "QoS Table[%d] mif : %u int : %u cl0 : %u cl1: %u\n", i, + platform->qos[i].freq_mif, + platform->qos[i].freq_int, + platform->qos[i].freq_cl0, + platform->qos[i].freq_cl1); + } + + platform->qos_enabled = true; + return 0; +} + +struct qos_table platform_mif_pm_qos_get_table(struct platform_mif *platform, enum scsc_qos_config config) +{ + struct qos_table table; + + switch (config) { + case SCSC_QOS_MIN: + table.freq_mif = platform->qos[0].freq_mif; + table.freq_int = platform->qos[0].freq_int; + table.freq_cl0 = platform->qos[0].freq_cl0; + table.freq_cl1 = platform->qos[0].freq_cl1; + break; + + case SCSC_QOS_MED: + table.freq_mif = platform->qos[1].freq_mif; + table.freq_int = platform->qos[1].freq_int; + table.freq_cl0 = platform->qos[1].freq_cl0; + table.freq_cl1 = platform->qos[1].freq_cl1; + break; + + case SCSC_QOS_MAX: + table.freq_mif = platform->qos[2].freq_mif; + table.freq_int = platform->qos[2].freq_int; + table.freq_cl0 = platform->qos[2].freq_cl0; + table.freq_cl1 = platform->qos[2].freq_cl1; + break; + + default: + table.freq_mif = 0; + table.freq_int = 0; + table.freq_cl0 = 0; + table.freq_cl1 = 0; + } + + return table; +} + +static int platform_mif_pm_qos_add_request(struct scsc_mif_abs *interface, struct scsc_mifqos_request *qos_req, enum scsc_qos_config config) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + struct qos_table table; + + if (!platform) + return -ENODEV; + + if (!platform->qos_enabled) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS not configured\n"); + return -EOPNOTSUPP; + } + + table = platform_mif_pm_qos_get_table(platform, config); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "PM QoS add request: %u. MIF %u INT %u CL0 %u CL1 %u\n", config, table.freq_mif, table.freq_int, table.freq_cl0, table.freq_cl1); + + pm_qos_add_request(&qos_req->pm_qos_req_mif, PM_QOS_BUS_THROUGHPUT, table.freq_mif); + pm_qos_add_request(&qos_req->pm_qos_req_int, PM_QOS_DEVICE_THROUGHPUT, table.freq_int); + pm_qos_add_request(&qos_req->pm_qos_req_cl0, PM_QOS_CLUSTER0_FREQ_MIN, table.freq_cl0); + pm_qos_add_request(&qos_req->pm_qos_req_cl1, PM_QOS_CLUSTER1_FREQ_MIN, table.freq_cl1); + + return 0; +} + +static int platform_mif_pm_qos_update_request(struct scsc_mif_abs *interface, struct scsc_mifqos_request *qos_req, enum scsc_qos_config config) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + struct qos_table table; + + if (!platform) + return -ENODEV; + + if (!platform->qos_enabled) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS not configured\n"); + return -EOPNOTSUPP; + } + + table = platform_mif_pm_qos_get_table(platform, config); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "PM QoS update request: %u. MIF %u INT %u CL0 %u CL1 %u\n", config, table.freq_mif, table.freq_int, table.freq_cl0, table.freq_cl1); + + pm_qos_update_request(&qos_req->pm_qos_req_mif, table.freq_mif); + pm_qos_update_request(&qos_req->pm_qos_req_int, table.freq_int); + pm_qos_update_request(&qos_req->pm_qos_req_cl0, table.freq_cl0); + pm_qos_update_request(&qos_req->pm_qos_req_cl1, table.freq_cl1); + + return 0; +} + +static int platform_mif_pm_qos_remove_request(struct scsc_mif_abs *interface, struct scsc_mifqos_request *qos_req) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + if (!platform) + return -ENODEV; + + + if (!platform->qos_enabled) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS not configured\n"); + return -EOPNOTSUPP; + } + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS remove request\n"); + pm_qos_remove_request(&qos_req->pm_qos_req_mif); + pm_qos_remove_request(&qos_req->pm_qos_req_int); + pm_qos_remove_request(&qos_req->pm_qos_req_cl0); + pm_qos_remove_request(&qos_req->pm_qos_req_cl1); + + return 0; +} +#endif + +static void platform_mif_irq_default_handler(int irq, void *data) +{ + /* Avoid unused parameter error */ + (void)irq; + (void)data; + + /* int handler not registered */ + SCSC_TAG_INFO_DEV(PLAT_MIF, NULL, "INT handler not registered\n"); +} + +static void platform_mif_irq_reset_request_default_handler(int irq, void *data) +{ + /* Avoid unused parameter error */ + (void)irq; + (void)data; + + /* int handler not registered */ + SCSC_TAG_INFO_DEV(PLAT_MIF, NULL, "INT reset_request handler not registered\n"); +} + +irqreturn_t platform_mif_isr(int irq, void *data) +{ + struct platform_mif *platform = (struct platform_mif *)data; + + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "INT %pS\n", platform->r4_handler); + if (platform->r4_handler != platform_mif_irq_default_handler) + platform->r4_handler(irq, platform->irq_dev); + else + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "MIF Interrupt Handler not registered\n"); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_SCSC_ENABLE_ALIVE_IRQ +irqreturn_t platform_alive_isr(int irq, void *data) +{ + struct platform_mif *platform = (struct platform_mif *)data; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INT received\n"); + + return IRQ_HANDLED; +} +#endif + +irqreturn_t platform_wdog_isr(int irq, void *data) +{ + //int ret = 0; + struct platform_mif *platform = (struct platform_mif *)data; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INT received\n"); + if (platform->reset_request_handler != platform_mif_irq_reset_request_default_handler) { + disable_irq_nosync(platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num); + platform->reset_request_handler(irq, platform->irq_reset_request_dev); + } else { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WDOG Interrupt reset_request_handler not registered\n"); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Disabling unhandled WDOG IRQ.\n"); + disable_irq_nosync(platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num); + atomic_inc(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt); + } + + /* TODO: need to find correct register to set here */ +#if 0 + ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, + WLBT_RESET_REQ_CLR, WLBT_RESET_REQ_CLR); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Clearing WLBT_RESET_REQ\n"); + if (ret < 0) + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to Set WLBT_CTRL_NS[WLBT_RESET_REQ_CLR]: %d\n", ret); +#endif + + return IRQ_HANDLED; +} + +/* + * Attached array contains the replacement PMU boot code which should + * be programmed using the CBUS during the config phase. + */ +uint32_t ka_patch[] = { + // Maxwell142 PMU+PROC combined boot ROM + // IP Version: 0xFF + // Major Version: 0xF, Minor Version: 0xF + // PMU ROM version: 0x2 + // PROC ROM version: 0x0 + // received initial version on 030719 + 0x90750002, + 0x11a4c218, + 0x755a118a, + 0x9075e090, + 0x30b3e5e7, + 0xa230f8e0, + 0xf5077411, + 0xfdb2b5b1, + 0x7400b153, + 0xb5acf507, + 0xb475fdad, + 0x20b3e501, + 0xb475fbe0, + 0x75a3d200, + 0xd2801890, + 0x7907a075, + 0xe6b07837, + 0x07b40754, + 0xd90b8002, + 0xf5c404f6, + 0x00af7590, + 0x90750380, + 0xf7532290, + 0xeff753f7, + 0x53dff753, + 0x0479fece, + 0xce53fed9, + 0xd90c79fd, + 0xfbce53fe, + 0x53159275, + 0xce53fd91, + 0x02f943f7, + 0x22fef953, + 0xd8fed9f9, + 0x9e7522fb, + 0xcfc17501, + 0x75a4c275, + 0xc4754ac3, + 0xa4c57547, + 0x7561c675, + 0xd27540c7, + 0x80d37503, + 0x7500c975, + 0xcb75d0ca, + 0x00cc7500, + 0x75009a75, + 0x9c75c09b, + 0x009d7500, + 0x82740278, + 0x43848012, + 0x057802c6, + 0x8012d074, + 0x20c34384, + 0x75d09075, + 0x93750291, + 0x029e7501, + 0x00000022, +}; + +irqreturn_t platform_cfg_req_isr(int irq, void *data) +{ + struct platform_mif *platform = (struct platform_mif *)data; + u64 ret64 = 0; + const u64 EXYNOS_WLBT = 0x1; + /*s32 ret = 0;*/ + unsigned int ka_addr = 0x1000; + uint32_t *ka_patch_addr = ka_patch; + //u32 id; + +#define CHECK(x) do { \ + int retval = (x); \ + if (retval < 0) {\ + pr_err("%s failed at L%d", __FUNCTION__, __LINE__); \ + goto cfg_error; \ + } \ +} while (0) + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INT received\n"); + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "disable_irq\n"); + + /* mask the irq */ + disable_irq_nosync(platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num); + + /* Was the CFG_REQ irq received from WLBT before we expected it? + * Typically this indicates an issue returning WLBT HW to reset. + */ + if (platform->boot_state != WLBT_BOOT_WAIT_CFG_REQ) { + u32 val; + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Spurious CFG_REQ IRQ from WLBT!\n"); + + regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_CTRL_NS 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_CTRL_S, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_CTRL_S 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_DEBUG, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val); + + return IRQ_HANDLED; + } + + /* Set TZPC to non-secure mode */ + ret64 = exynos_smc(SMC_CMD_CONN_IF, (EXYNOS_WLBT << 32) | EXYNOS_SET_CONN_TZPC, 0, 0); + if (ret64) + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to set TZPC to non-secure mode: %llu\n", ret64); + else + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "SMC_CMD_CONN_IF run successfully : %llu\n", ret64); + + /* WLBT_REMAP PMU_REMAP */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WLBT_REMAP begin\n"); + CHECK(regmap_write(platform->wlbt_remap, 0x400, WLBT_DBUS_BAAW_0_START >> 12)); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WLBT_REMAP end\n"); + + /* DBUS_BAAW regions */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "DBUS_BAAW begin\n"); // PMU_DBUS_BAAW + + /* Shared DRAM mapping. The destination address is the location reserved + * by the kernel. + */ + CHECK(regmap_write(platform->dbus_baaw, 0x0, WLBT_DBUS_BAAW_0_START >> 12)); + CHECK(regmap_write(platform->dbus_baaw, 0x4, WLBT_DBUS_BAAW_0_END >> 12)); + CHECK(regmap_write(platform->dbus_baaw, 0x8, platform->mem_start >> 12)); // FW AP base addr >> 12 + CHECK(regmap_write(platform->dbus_baaw, 0xC, WLBT_BAAW_ACCESS_CTRL)); +#if 0 + /* Additional DRAM mappings for future use */ + CHECK(regmap_write(platform->dbus_baaw, 0x10, 0x000C0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x14, 0x000D0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x18, 0x000D0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x1C, WLBT_BAAW_ACCESS_CTRL)); + + CHECK(regmap_write(platform->dbus_baaw, 0x20, 0x000D0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x24, 0x000E0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x28, 0x000E0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x2C, WLBT_BAAW_ACCESS_CTRL)); + + CHECK(regmap_write(platform->dbus_baaw, 0x30, 0x000E0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x34, 0x000F0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x38, 0x000F0000)); + CHECK(regmap_write(platform->dbus_baaw, 0x3C, WLBT_BAAW_ACCESS_CTRL)); +#endif + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "DBUS_BAAW end\n"); + + /* PBUS_BAAW regions */ + /* ref wlbt_if_S5E3830.c, updated for MX152 memory map */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PBUS_BAAW begin\n"); + + /* Range for CP2WLBT mailbox */ + CHECK(regmap_write(platform->pbus_baaw, 0x0, WLBT_CBUS_BAAW_0_START >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x4, WLBT_CBUS_BAAW_0_END >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x8, WLBT_PBUS_MBOX_CP2WLBT_BASE >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0xC, WLBT_BAAW_ACCESS_CTRL)); + + /* Range includes AP2WLBT,APM2WLBT,GNSS2WLBT mailboxes */ + CHECK(regmap_write(platform->pbus_baaw, 0x10, WLBT_CBUS_BAAW_1_START >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x14, WLBT_CBUS_BAAW_1_END >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x18, WLBT_PBUS_MBOX_GNSS2WLBT_BASE >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x1C, WLBT_BAAW_ACCESS_CTRL)); +#if 0 + /* These mappings are not yet used by WLBT FW */ + CHECK(regmap_write(platform->pbus_baaw, 0x20, WLBT_CBUS_BAAW_2_START >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x24, WLBT_CBUS_BAAW_2_END >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x28, WLBT_PBUS_MBOX_APM2WLBT_BASE >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x2C, WLBT_BAAW_ACCESS_CTRL)); + + CHECK(regmap_write(platform->pbus_baaw, 0x30, WLBT_CBUS_BAAW_3_START >>12)); + CHECK(regmap_write(platform->pbus_baaw, 0x34, WLBT_CBUS_BAAW_3_END >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x38, WLBT_PBUS_MBOX_AP2WLBT_BASE >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x3C, WLBT_BAAW_ACCESS_CTRL)); + + CHECK(regmap_write(platform->pbus_baaw, 0x40, WLBT_CBUS_BAAW_4_START >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x44, WLBT_CBUS_BAAW_4_END >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x48, WLBT_PBUS_MBOX_WLBT2ABOX_BASE >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x4C, WLBT_BAAW_ACCESS_CTRL)); + + CHECK(regmap_write(platform->pbus_baaw, 0x50, WLBT_CBUS_BAAW_5_START >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x54, WLBT_CBUS_BAAW_5_END >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x58, WLBT_PBUS_MBOX_WLBT2CHUB_BASE >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x5C, WLBT_BAAW_ACCESS_CTRL)); + + CHECK(regmap_write(platform->pbus_baaw, 0x60, WLBT_CBUS_BAAW_6_START >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x64, WLBT_CBUS_BAAW_6_START >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x68, WLBT_PBUS_CHUB_BASE >> 12)); + CHECK(regmap_write(platform->pbus_baaw, 0x6C, WLBT_BAAW_ACCESS_CTRL)); +#endif + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PBUS_BAAW end\n"); + + /* PMU boot patch + * BOOT_SOURCE_CFG 0x0001 KA RAM access granted to CFG longhop. Any 8051 accesses are waited if this setting is used. + */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "BOOT_WLBT begin\n"); + CHECK(regmap_write(platform->boot_cfg, 0x0, 0x1)); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "BOOT_WLBT done\n"); + + while (ka_patch_addr < (ka_patch + ARRAY_SIZE(ka_patch))) { + CHECK(regmap_write(platform->boot_cfg, ka_addr, *ka_patch_addr)); + ka_addr += sizeof(ka_patch[0]); + ka_patch_addr++; + } + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "KA patch done\n"); + + /* Notify PMU of configuration done + * BOOT_SOURCE_ROM 0x0000 KA RAM access granted to 8051. CBUS accesses are ignored if this setting is used. + */ + CHECK(regmap_write(platform->boot_cfg, 0x0, 0x0)); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "BOOT config done\n"); + + /* BOOT_CFG_ACK is 1-bit wide */ + CHECK(regmap_write(platform->boot_cfg, 0x4, 0x1)); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "BOOT_CFG_ACK done\n"); + + /* Mark as CFQ_REQ handled, so boot may continue */ + platform->boot_state = WLBT_BOOT_CFG_DONE; + + /* Signal triggering function that the IRQ arrived and CFG was done */ + complete(&platform->cfg_ack); + + /* Re-enable IRQ here to allow spurious interrupt to be tracked */ + enable_irq(platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num); + + /* as per wlbt_if_S5E3830.c - end */ + + return IRQ_HANDLED; +cfg_error: + platform->boot_state = WLBT_BOOT_CFG_ERROR; + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "ERROR: WLBT Config failed. WLBT will not work\n"); + complete(&platform->cfg_ack); + return IRQ_HANDLED; +} + +static void platform_mif_unregister_irq(struct platform_mif *platform) +{ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unregistering IRQs\n"); + + devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_MBOX].irq_num, platform); + devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num, platform); + /* Reset irq_disabled_cnt for WDOG IRQ since the IRQ itself is here unregistered and disabled */ + atomic_set(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt, 0); +#ifdef CONFIG_SCSC_ENABLE_ALIVE_IRQ + /* if ALIVE irq is required */ + devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_ALIVE].irq_num, platform); +#endif + devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num, platform); +} + +static int platform_mif_register_irq(struct platform_mif *platform) +{ + int err; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering IRQs\n"); + + /* Register MBOX irq */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering MBOX irq: %d flag 0x%x\n", + platform->wlbt_irq[PLATFORM_MIF_MBOX].irq_num, platform->wlbt_irq[PLATFORM_MIF_MBOX].flags); + + err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_MBOX].irq_num, platform_mif_isr, + platform->wlbt_irq[PLATFORM_MIF_MBOX].flags, DRV_NAME, platform); + if (IS_ERR_VALUE((unsigned long)err)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to register MBOX handler: %d. Aborting.\n", err); + err = -ENODEV; + return err; + } + + /* Register WDOG irq */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering WDOG irq: %d flag 0x%x\n", + platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num, platform->wlbt_irq[PLATFORM_MIF_WDOG].flags); + + err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num, platform_wdog_isr, + platform->wlbt_irq[PLATFORM_MIF_WDOG].flags, DRV_NAME, platform); + if (IS_ERR_VALUE((unsigned long)err)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to register WDOG handler: %d. Aborting.\n", err); + err = -ENODEV; + return err; + } + +#ifdef CONFIG_SCSC_ENABLE_ALIVE_IRQ + /* Register ALIVE irq */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering ALIVE irq: %d flag 0x%x\n", + platform->wlbt_irq[PLATFORM_MIF_ALIVE].irq_num, platform->wlbt_irq[PLATFORM_MIF_ALIVE].flags); + + err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_ALIVE].irq_num, platform_alive_isr, + platform->wlbt_irq[PLATFORM_MIF_ALIVE].flags, DRV_NAME, platform); + if (IS_ERR_VALUE(err)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to register ALIVE handler: %d. Aborting.\n", err); + err = -ENODEV; + return err; + } +#endif + + /* Mark as WLBT in reset before enabling IRQ to guard against spurious IRQ */ + platform->boot_state = WLBT_BOOT_IN_RESET; + smp_wmb(); /* commit before irq */ + + /* Register WB2AP_CFG_REQ irq */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering CFG_REQ irq: %d flag 0x%x\n", + platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num, platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].flags); + + err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num, platform_cfg_req_isr, + platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].flags, DRV_NAME, platform); + if (IS_ERR_VALUE((unsigned long)err)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to register CFG_REQ handler: %d. Aborting.\n", err); + err = -ENODEV; + return err; + } + + /* Leave disabled until ready to handle */ + disable_irq_nosync(platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num); + + return 0; +} + +static void platform_mif_destroy(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + platform_mif_unregister_irq(platform); +} + +static char *platform_mif_get_uid(struct scsc_mif_abs *interface) +{ + /* Avoid unused parameter error */ + (void)interface; + return "0"; +} + +static void wlbt_regdump(struct platform_mif *platform) +{ + u32 val = 0; + + regmap_read(platform->pmureg, WLBT_CTRL_S, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_CTRL_S 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_CONFIGURATION, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_CONFIGURATION 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_CTRL_NS 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_IN, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_IN 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_OUT, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_OUT 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_STATUS, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_STATUS 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_STATES, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_STATES 0x%x\n", val); + + regmap_read(platform->pmureg, WLBT_DEBUG, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val); +} + +/* WLBT START */ +static int platform_mif_start(struct scsc_mif_abs *interface, bool start) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "start %d\n", start); + /* At this point WLBT should assert the CFG_REQ IRQ, so wait for it */ + if (start && + wait_for_completion_timeout(&platform->cfg_ack, WLBT_BOOT_TIMEOUT) == 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Timeout waiting for CFG_REQ IRQ\n"); + wlbt_regdump(platform); + return -ETIMEDOUT; + } + + wlbt_regdump(platform); + + /* only continue if CFG_REQ IRQ configured WLBT/PMU correctly */ + if (platform->boot_state == WLBT_BOOT_CFG_ERROR) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "CFG_REQ failed to configure WLBT.\n"); + return -EIO; + } + return 0; +} + +static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + int ret = 0; + u32 val = 0; + u32 v = 0; + unsigned long timeout; + static bool init_done; + + /* We're now ready for the IRQ */ + platform->boot_state = WLBT_BOOT_WAIT_CFG_REQ; + smp_wmb(); /* commit before irq */ + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "on boot_state = WLBT_BOOT_WAIT_CFG_REQ\n"); + + /* INIT SEQUENCE - First WLBT boot only + * Cold reset wrt. AP power sequencer, cold reset for WLBT + */ + if (!init_done) { + /* init sequence from excite - PMUCAL + * SetBits((uinteger)REG_WLBT_CTRL_S, WLBT_CTRL_S_START_POS, 0x1, 0x1); + * SetBits((uinteger)REG_WLBT_OPTION, 3, 0x1, 0x1); + * udelay(1000); + * SetBits((uinteger)REG_TOP_OUT, 1, 0x1, 0x1); + * while (GetBits((uinteger)REG_VGPIO_TX_MONITOR, 29, 0x1) != 1) + * ; + * SetBits((uinteger)REG_WLBT_CONFIGURATION, WLBT_CONFIGURATION_LOCAL_PWR_CFG_POS, 0x1, 0x1); + * while (GetBits((uinteger)REG_WLBT_STATUS, 0, 0x1) != WLBT_STATUS_STATUS_POWERON) + * ; + * SetBits((uinteger)REG_WLBT_CTRL_NS, WLBT_CTRL_NS_ACTIVE_CLR_POS, 0x1, 0x0); + * SetBits((uinteger)REG_WLBT_CTRL_NS, WLBT_CTRL_NS_ACTIVE_EN_POS, 0x1, 0x1); + * + */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "init\n"); + + /* WLBT_CTRL_S[WLBT_START] = 1 enable */ + ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_S, + WLBT_START, WLBT_START); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_CTRL_S[WLBT_START]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_CTRL_S, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_CTRL_S[WLBT_START]: 0x%x\n", val); + + /* WLBT_OPTION[WLBT_OPTION_DATA] = 1 Power On */ + ret = regmap_update_bits(platform->pmureg, WLBT_OPTION, + WLBT_OPTION_DATA, WLBT_OPTION_DATA); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_OPTION[WLBT_OPTION_DATA]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_OPTION, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_OPTION[WLBT_OPTION_DATA]: 0x%x\n", val); + + /* TOP_OUT[PWRRGTON_CP] = 1 Power On */ + ret = regmap_update_bits(platform->pmureg, TOP_OUT, + PWRRGTON_CP, PWRRGTON_CP); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update TOP_OUT[PWRRGTON_CP]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, TOP_OUT, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully TOP_OUT[PWRRGTON_CP]: 0x%x\n", val); + + /* VGPIO_TX_MONITOR = 0x1 */ + timeout = jiffies + msecs_to_jiffies(500); + do { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + val &= (u32)VGPIO_TX_MON_BIT29; + if (val) { + SCSC_TAG_INFO(PLAT_MIF, "VGPIO_TX_MONITOR 0x%x\n", val); + break; + } + } while (time_before(jiffies, timeout)); + + if (!val) { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + SCSC_TAG_INFO(PLAT_MIF, "timeout waiting for VGPIO_TX_MONITOR time-out: " + "VGPIO_TX_MONITOR 0x%x\n", val); + } + + /* WLBT_CONFIGURATION[LOCAL_PWR_CFG] = 1 Power On */ + ret = regmap_update_bits(platform->pmureg, WLBT_CONFIGURATION, + LOCAL_PWR_CFG, 0x1); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_CONFIGURATION[LOCAL_PWR_CFG]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_CONFIGURATION, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_CONFIGURATION[LOCAL_PWR_CFG]: 0x%x\n", val); + + /* wait for power up complete WLBT_STATUS[WLBT_STATUS_BIT0] = 1 for Power On */ + timeout = jiffies + msecs_to_jiffies(500); + do { + regmap_read(platform->pmureg, WLBT_STATUS, &val); + val &= (u32)WLBT_STATUS_BIT0; + if (val) { + /* Power On complete */ + SCSC_TAG_INFO(PLAT_MIF, "Power On complete: WLBT_STATUS 0x%x\n", val); + /* re affirming power on by reading WLBT_STATES */ + /* STATES[7:0] = 0x10 for Power Up */ + regmap_read(platform->pmureg, WLBT_STATES, &v); + SCSC_TAG_INFO(PLAT_MIF, "Power On complete: WLBT_STATES 0x%x\n", v); + break; + } + } while (time_before(jiffies, timeout)); + + if (!val) { + regmap_read(platform->pmureg, WLBT_STATUS, &val); + SCSC_TAG_INFO(PLAT_MIF, "timeout waiting for power on time-out: " + "WLBT_STATUS 0x%x, WLBT_STATES 0x%x\n", val, v); + } + + /* WLBT_CTRL_NS[WLBT_ACTIVE_CLR] = 0 Active interrupt clear */ + ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, + WLBT_ACTIVE_CLR, 0x0); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_CTRL_NS[WLBT_ACTIVE_CLR]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_CTRL_NS[WLBT_ACTIVE_CLR]: 0x%x\n", val); + + /* WLBT_CTRL_NS[WLBT_ACTIVE_EN] = 1 Active interrupt enable */ + ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, + WLBT_ACTIVE_EN, WLBT_ACTIVE_EN); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_CTRL_NS[WLBT_ACTIVE_EN]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_CTRL_NS[WLBT_ACTIVE_EN]: 0x%x\n", val); + + init_done = true; + + goto init_code_done; + } + + /* RESET RELEASE - Subsequent WLBT reboots */ + /* wlbt_if_reset_release - from excite code + * SetBits((uinteger)REG_WLBT_CONFIGURATION, WLBT_CONFIGURATION_LOCAL_PWR_CFG_POS, 0x1, 0x1); + * while (GetBits((uinteger)REG_WLBT_STATUS, WLBT_STATUS_STATUS_POS, 0x1) != WLBT_STATUS_STATUS_POWERON) + * ; + * SetBits((uinteger)REG_WLBT_INT_EN, WLBT_INT_EN_PWR_REQ__F_POS, 0x1, 0x1); + * SetBits((uinteger)REG_WLBT_INT_EN, WLBT_INT_EN_TCXO_REQ__F_POS, 0x1, 0x1); + * udelay(2000); + * */ + + /* Warm reset wrt. AP power sequencer, but cold reset for WLBT */ + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "release\n"); + + /* Power Up */ + ret = regmap_update_bits(platform->pmureg, WLBT_CONFIGURATION, + LOCAL_PWR_CFG, 0x1); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_CONFIGURATION[LOCAL_PWR_CFG]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_CONFIGURATION, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_CONFIGURATION[LOCAL_PWR_CFG]: 0x%x\n", val); + + /* wait for power up complete WLBT_STATUS[0] = 0 for power down */ + timeout = jiffies + msecs_to_jiffies(500); + do { + regmap_read(platform->pmureg, WLBT_STATUS, &val); + val &= (u32)WLBT_STATUS_BIT0; + if (val) { + /* Power up complete */ + SCSC_TAG_INFO(PLAT_MIF, "Power up complete: WLBT_STATUS 0x%x\n", val); + /* re affirming power down by reading WLBT_STATES */ + /* STATES[7:0] = 0x10 for Power Up */ + regmap_read(platform->pmureg, WLBT_STATES, &v); + SCSC_TAG_INFO(PLAT_MIF, "Power up complete: WLBT_STATES 0x%x\n", v); + break; + } + } while (time_before(jiffies, timeout)); + + if (!val) { + regmap_read(platform->pmureg, WLBT_STATUS, &val); + SCSC_TAG_INFO(PLAT_MIF, "Timeout waiting for Power up complete: " + "WLBT_STATUS 0x%x, WLBT_STATES 0x%x\n", val, v); + } + + /* enable PWR_REQ_F and TCXO_REQ_F interrupts */ + ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, + PWR_REQ_F, PWR_REQ_F); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_INT_EN[PWR_REQ_F]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_INT_EN, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_INT_EN[PWR_REQ_F]: 0x%x\n", val); + + ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, + TCXO_REQ_F, TCXO_REQ_F); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_INT_EN[TCXO_REQ_F]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_INT_EN, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_INT_EN[TCXO_REQ_F]: 0x%x\n", val); + +init_code_done: + /* Now handle the CFG_REQ IRQ */ + enable_irq(platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num); + + ret = platform_mif_start(interface, true); + if (ret) + return ret; + + return ret; +} + +static int platform_mif_pmu_reset_assert(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + unsigned long timeout; + int ret; + u32 val; + + /* wlbt_if_reset_assertion() - from wlbt_if_S5E3830.c - PMUCAL + * SetBits((uinteger)REG_WLBT_OPTION, 3, 0x1, 0x1); + * SetBits((uinteger)REG_MIF_CTRL, MIF_CTRL_TCXO_EN_POS, 0x1, 0x1); + * SetBits((uinteger)REG_TCXO_BUF_CTRL, TCXO_BUF_CTRL_BIAS_EN_POS, 0x1, 0x1); + * SetBits((uinteger)REG_TOP_OUT, TOP_OUT_PWRRGTON_CP_POS, 0x1, 0x1); + * while (GetBits((uinteger)REG_VGPIO_TX_MONITOR, 29, 0x1) != 1) + * ; + * udelay(1000); //1 msec wait??? + * SetBits((uinteger)REG_WLBT_INT_EN, WLBT_INT_EN_PWR_REQ__F_POS, 0x00000001, 0x00000000); + * SetBits((uinteger)REG_WLBT_INT_EN, WLBT_INT_EN_TCXO_REQ__F_POS, 0x00000001, 0x00000000); + * SetBits((uinteger)REG_WLBT_CTRL_NS, WLBT_CTRL_NS_ACTIVE_EN_POS, 0x1, 0x0); + * SetBits((uinteger)REG_WLBT_CONFIGURATION, WLBT_CONFIGURATION_LOCAL_PWR_CFG_POS, 0x1, 0x0); + * while (GetBits((uinteger)REG_WLBT_STATUS, WLBT_STATUS_STATUS_POS, 0x1) != WLBT_STATUS_STATUS_POWERDOWN) + */ + + /* WLBT_OPTION[WLBT_OPTION_DATA] = 1 Power On */ + ret = regmap_update_bits(platform->pmureg, WLBT_OPTION, + WLBT_OPTION_DATA, WLBT_OPTION_DATA); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_OPTION[WLBT_OPTION_DATA]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_OPTION, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_OPTION[WLBT_OPTION_DATA]: 0x%x\n", val); + + ret = regmap_update_bits(platform->pmureg, MIF_CTRL, + TCXO_EN, TCXO_EN); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update MIF_CTRL[TCXO_EN]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, TCXO_BUF_CTRL, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully MIF_CTRL[TCXO_EN]: 0x%x\n", val & TCXO_EN); + + ret = regmap_update_bits(platform->pmureg, TCXO_BUF_CTRL, + TCXO_BUF_BIAS_EN_WLBT, TCXO_BUF_BIAS_EN_WLBT); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update TCXO_BUF_CTRL[TCXO_BUF_BIAS_EN_WLBT]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, TCXO_BUF_CTRL, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully TCXO_BUF_CTRL[TCXO_BUF_BIAS_EN_WLBT]: 0x%x\n", val & TCXO_BUF_BIAS_EN_WLBT); + + ret = regmap_update_bits(platform->pmureg, TOP_OUT, + PWRRGTON_CP, PWRRGTON_CP); + + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update TOP_OUT[PWRRGTON_CP]: %d\n", ret); + return ret; + } + /* VGPIO_TX_MONITOR = 0x1 */ + timeout = jiffies + msecs_to_jiffies(500); + do { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + val &= (u32)VGPIO_TX_MON_BIT29; + if (val) { + SCSC_TAG_INFO(PLAT_MIF, "VGPIO_TX_MONITOR 0x%x\n", val); + break; + } + } while (time_before(jiffies, timeout)); + + if (!val) { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + SCSC_TAG_INFO(PLAT_MIF, "timeout waiting for VGPIO_TX_MONITOR time-out: " + "VGPIO_TX_MONITOR 0x%x\n", val); + } + + regmap_read(platform->pmureg, TCXO_BUF_CTRL, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully TOP_OUT[PWRRGTON_CP]: 0x%x\n", val & PWRRGTON_CP); + + udelay(1000); + + /* disable PWR_REQ_F and TCXO_REQ_F interrupts */ + ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, + PWR_REQ_F, 0x0); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_INT_EN[PWR_REQ_F]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_INT_EN, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_INT_EN[PWR_REQ_F]: 0x%x\n", val); + + ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, + TCXO_REQ_F, 0x0); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_INT_EN[TCXO_REQ_F]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_INT_EN, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_INT_EN[TCXO_REQ_F]: 0x%x\n", val); + + /* Active interrupt disable */ + ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, + WLBT_ACTIVE_EN, 0x0); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_CTRL_NS[WLBT_ACTIVE_EN]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_CTRL_NS[WLBT_ACTIVE_EN]: 0x%x\n", val); + + /* Power Down */ + ret = regmap_write_bits(platform->pmureg, WLBT_CONFIGURATION, + LOCAL_PWR_CFG, 0x0); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_CONFIGURATION[LOCAL_PWR_CFG]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_CONFIGURATION, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_CONFIGURATION[LOCAL_PWR_CFG]: 0x%x\n", val); + + /* Wait for power Off WLBT_STATUS[WLBT_STATUS_BIT0] = 0 */ + timeout = jiffies + msecs_to_jiffies(500); + do { + regmap_read(platform->pmureg, WLBT_STATUS, &val); + val &= (u32)WLBT_STATUS_BIT0; + if (val == 0) { + SCSC_TAG_INFO(PLAT_MIF, "WLBT_STATUS 0x%x\n", val); + /* re affirming power down by reading WLBT_STATES */ + /* STATES[7:0] = 0x80 for Power Down */ + regmap_read(platform->pmureg, WLBT_STATES, &val); + SCSC_TAG_INFO(PLAT_MIF, "Power down complete: WLBT_STATES 0x%x\n", val); + + return 0; /* OK - return */ + } + } while (time_before(jiffies, timeout)); + + /* Timed out */ + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Timeout waiting for WLBT_STATUS status\n"); + + regmap_read(platform->pmureg, WLBT_STATUS, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_STATUS 0x%x\n", val); + regmap_read(platform->pmureg, WLBT_DEBUG, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val); + regmap_read(platform->pmureg, WLBT_STATES, &val); + SCSC_TAG_INFO(PLAT_MIF, "WLBT_STATES 0x%x\n", val); + return -ETIME; +} + +/* reset=0 - release from reset */ +/* reset=1 - hold reset */ +static int platform_mif_reset(struct scsc_mif_abs *interface, bool reset) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u32 ret = 0; + + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n"); + + if (enable_platform_mif_arm_reset || !reset) { + if (!reset) { /* Release from reset */ +#ifdef CONFIG_ARCH_EXYNOS + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "SOC_VERSION: product_id 0x%x, rev 0x%x\n", + exynos_soc_info.product_id, exynos_soc_info.revision); +#endif + power_supplies_on(platform); + + ret = platform_mif_pmu_reset_release(interface); + } else { + /* Put back into reset */ + ret = platform_mif_pmu_reset_assert(interface); + } + } else + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Not resetting ARM Cores - enable_platform_mif_arm_reset: %d\n", + enable_platform_mif_arm_reset); + return ret; +} + +static void __iomem *platform_mif_map_region(unsigned long phys_addr, size_t size) +{ + int i; + struct page **pages; + void *vmem; + + size = PAGE_ALIGN(size); + + pages = kmalloc((size >> PAGE_SHIFT) * sizeof(*pages), GFP_KERNEL); + if (!pages) + return NULL; + + /* Map NORMAL_NC pages with kernel virtual space */ + for (i = 0; i < (size >> PAGE_SHIFT); i++) { + pages[i] = phys_to_page(phys_addr); + phys_addr += PAGE_SIZE; + } + + vmem = vmap(pages, size >> PAGE_SHIFT, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + + kfree(pages); + return (void __iomem *)vmem; +} + +static void platform_mif_unmap_region(void *vmem) +{ + vunmap(vmem); +} + +static void *platform_mif_map(struct scsc_mif_abs *interface, size_t *allocated) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u8 i; + + if (allocated) + *allocated = 0; + + platform->mem = + platform_mif_map_region(platform->mem_start, platform->mem_size); + + if (!platform->mem) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Error remaping shared memory\n"); + return NULL; + } + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Map: virt %p phys %lx\n", platform->mem, (uintptr_t)platform->mem_start); + + /* Initialise MIF registers with documented defaults */ + /* MBOXes */ + for (i = 0; i < NUM_MBOX_PLAT; i++) + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(ISSR(i)), 0x00000000); + + /* MRs */ /*1's - set all as Masked */ + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), 0xffff0000); + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR1), 0x0000ffff); + /* CRs */ /* 1's - clear all the interrupts */ + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR0), 0xffff0000); + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR1), 0x0000ffff); + +#ifdef CONFIG_SCSC_CHV_SUPPORT + if (chv_disable_irq == true) { + if (allocated) + *allocated = platform->mem_size; + return platform->mem; + } +#endif + /* register interrupts */ + if (platform_mif_register_irq(platform)) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unmap: virt %p phys %lx\n", platform->mem, (uintptr_t)platform->mem_start); + platform_mif_unmap_region(platform->mem); + return NULL; + } + + if (allocated) + *allocated = platform->mem_size; + /* Set the CR4 base address in Mailbox??*/ + return platform->mem; +} + +/* HERE: Not sure why mem is passed in - its stored in platform - as it should be */ +static void platform_mif_unmap(struct scsc_mif_abs *interface, void *mem) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + /* Avoid unused parameter error */ + (void)mem; + + /* MRs */ /*1's - set all as Masked */ + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), 0xffff0000); + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR1), 0x0000ffff); + +#ifdef CONFIG_SCSC_CHV_SUPPORT + /* Restore PIO changed by Maxwell subsystem */ + if (chv_disable_irq == false) + /* Unregister IRQs */ + platform_mif_unregister_irq(platform); +#else + platform_mif_unregister_irq(platform); +#endif + /* CRs */ /* 1's - clear all the interrupts */ + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR0), 0xffff0000); + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR1), 0x0000ffff); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unmap: virt %p phys %lx\n", platform->mem, (uintptr_t)platform->mem_start); + platform_mif_unmap_region(platform->mem); + platform->mem = NULL; +} + +static u32 platform_mif_irq_bit_mask_status_get(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u32 val; + + val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0)) >> 16; + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Getting INTMR0: 0x%x\n", val); + return val; +} + +static u32 platform_mif_irq_get(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u32 val; + + /* Function has to return the interrupts that are enabled *AND* not masked */ + val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMSR0)) >> 16; + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Getting INT-INTMSR0: 0x%x\n", val); + + return val; +} + +static void platform_mif_irq_bit_set(struct scsc_mif_abs *interface, int bit_num, enum scsc_mif_abs_target target) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u32 reg; + + if (bit_num >= 16) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num); + return; + } + + reg = INTGR1; + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(reg), (1 << bit_num)); + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Setting INTGR1: bit %d on target %d\n", bit_num, target); +} + +static void platform_mif_irq_bit_clear(struct scsc_mif_abs *interface, int bit_num) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + if (bit_num >= 16) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num); + return; + } + /* WRITE : 1 = Clears Interrupt */ + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR0), ((1 << bit_num) << 16)); + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Setting INTCR0: bit %d\n", bit_num); +} + +static void platform_mif_irq_bit_mask(struct scsc_mif_abs *interface, int bit_num) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u32 val; + unsigned long flags; + + if (bit_num >= 16) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num); + return; + } + spin_lock_irqsave(&platform->mif_spinlock, flags); + val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0)); + /* WRITE : 1 = Mask Interrupt */ + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), val | ((1 << bit_num) << 16)); + spin_unlock_irqrestore(&platform->mif_spinlock, flags); + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Setting INTMR0: 0x%x bit %d\n", val | (1 << bit_num), bit_num); +} + +static void platform_mif_irq_bit_unmask(struct scsc_mif_abs *interface, int bit_num) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u32 val; + unsigned long flags; + + if (bit_num >= 16) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num); + return; + } + spin_lock_irqsave(&platform->mif_spinlock, flags); + val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0)); + /* WRITE : 0 = Unmask Interrupt */ + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), val & ~((1 << bit_num) << 16)); + spin_unlock_irqrestore(&platform->mif_spinlock, flags); + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "UNMASK Setting INTMR0: 0x%x bit %d\n", val & ~((1 << bit_num) << 16), bit_num); +} + +/* Return the contents of the mask register */ +static u32 __platform_mif_irq_bit_mask_read(struct platform_mif *platform) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&platform->mif_spinlock, flags); + val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0)); + spin_unlock_irqrestore(&platform->mif_spinlock, flags); + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Read INTMR0: 0x%x\n", val); + + return val; +} + +/* Write the mask register, destroying previous contents */ +static void __platform_mif_irq_bit_mask_write(struct platform_mif *platform, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&platform->mif_spinlock, flags); + platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), val); + spin_unlock_irqrestore(&platform->mif_spinlock, flags); + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Write INTMR0: 0x%x\n", val); +} + +static void platform_mif_irq_reg_handler(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + unsigned long flags; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering mif int handler %pS in %p %p\n", handler, platform, interface); + spin_lock_irqsave(&platform->mif_spinlock, flags); + platform->r4_handler = handler; + platform->irq_dev = dev; + spin_unlock_irqrestore(&platform->mif_spinlock, flags); +} + +static void platform_mif_irq_unreg_handler(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + unsigned long flags; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unregistering mif int handler %pS\n", interface); + spin_lock_irqsave(&platform->mif_spinlock, flags); + platform->r4_handler = platform_mif_irq_default_handler; + platform->irq_dev = NULL; + spin_unlock_irqrestore(&platform->mif_spinlock, flags); +} + +static void platform_mif_irq_reg_reset_request_handler(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering mif reset_request int handler %pS in %p %p\n", handler, platform, interface); + platform->reset_request_handler = handler; + platform->irq_reset_request_dev = dev; + if (atomic_read(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt)) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "Default WDOG handler disabled by spurios IRQ...re-enabling.\n"); + enable_irq(platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num); + atomic_set(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt, 0); + } +} + +static void platform_mif_irq_unreg_reset_request_handler(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "UnRegistering mif reset_request int handler %pS\n", interface); + platform->reset_request_handler = platform_mif_irq_reset_request_default_handler; + platform->irq_reset_request_dev = NULL; +} + +static void platform_mif_suspend_reg_handler(struct scsc_mif_abs *interface, + int (*suspend)(struct scsc_mif_abs *abs, void *data), + void (*resume)(struct scsc_mif_abs *abs, void *data), + void *data) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering mif suspend/resume handlers in %p %p\n", platform, interface); + platform->suspend_handler = suspend; + platform->resume_handler = resume; + platform->suspendresume_data = data; +} + +static void platform_mif_suspend_unreg_handler(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unregistering mif suspend/resume handlers in %p %p\n", platform, interface); + platform->suspend_handler = NULL; + platform->resume_handler = NULL; + platform->suspendresume_data = NULL; +} + +static u32 *platform_mif_get_mbox_ptr(struct scsc_mif_abs *interface, u32 mbox_index) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + u32 *addr; + + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "mbox_index 0x%x\n", mbox_index); + addr = platform->base + MAILBOX_WLBT_REG(ISSR(mbox_index)); + return addr; +} + +static int platform_mif_get_mifram_ref(struct scsc_mif_abs *interface, void *ptr, scsc_mifram_ref *ref) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n"); + + if (!platform->mem) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Memory unmmaped\n"); + return -ENOMEM; + } + + /* Check limits! */ + if (ptr >= (platform->mem + platform->mem_size)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Unable to get pointer reference\n"); + return -ENOMEM; + } + + *ref = (scsc_mifram_ref)((uintptr_t)ptr - (uintptr_t)platform->mem); + + return 0; +} + +static void *platform_mif_get_mifram_ptr(struct scsc_mif_abs *interface, scsc_mifram_ref ref) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n"); + + if (!platform->mem) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Memory unmmaped\n"); + return NULL; + } + + /* Check limits */ + if (ref >= 0 && ref < platform->mem_size) + return (void *)((uintptr_t)platform->mem + (uintptr_t)ref); + else + return NULL; +} + +static void *platform_mif_get_mifram_phy_ptr(struct scsc_mif_abs *interface, scsc_mifram_ref ref) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n"); + + if (!platform->mem_start) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Memory unmmaped\n"); + return NULL; + } + + return (void *)((uintptr_t)platform->mem_start + (uintptr_t)ref); +} + +static uintptr_t platform_mif_get_mif_pfn(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + return vmalloc_to_pfn(platform->mem); +} + +static struct device *platform_mif_get_mif_device(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n"); + + return platform->dev; +} + +static void platform_mif_irq_clear(void) +{ + /* Implement if required */ +} + +static int platform_mif_read_register(struct scsc_mif_abs *interface, u64 id, u32 *val) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + if (id == SCSC_REG_READ_WLBT_STAT) { + regmap_read(platform->pmureg, WLBT_STAT, val); + return 0; + } + + return -EIO; +} + +static void platform_mif_dump_register(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + unsigned long flags; + + spin_lock_irqsave(&platform->mif_spinlock, flags); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTGR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTGR0))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTGR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTGR1))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTCR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTCR0))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTCR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTCR1))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR1))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTSR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTSR0))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTSR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTSR1))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMSR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMSR0))); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMSR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMSR1))); + + spin_unlock_irqrestore(&platform->mif_spinlock, flags); +} + +static void platform_mif_cleanup(struct scsc_mif_abs *interface) +{ +} + +static void platform_mif_restart(struct scsc_mif_abs *interface) +{ +} + +#ifdef CONFIG_OF_RESERVED_MEM +static int __init platform_mif_wifibt_if_reserved_mem_setup(struct reserved_mem *remem) +{ + SCSC_TAG_DEBUG(PLAT_MIF, "memory reserved: mem_base=%#lx, mem_size=%zd\n", + (unsigned long)remem->base, (size_t)remem->size); + + sharedmem_base = remem->base; + sharedmem_size = remem->size; + return 0; +} +RESERVEDMEM_OF_DECLARE(wifibt_if, "exynos,wifibt_if", platform_mif_wifibt_if_reserved_mem_setup); +#endif + +struct scsc_mif_abs *platform_mif_create(struct platform_device *pdev) +{ + struct scsc_mif_abs *platform_if; + struct platform_mif *platform = + (struct platform_mif *)devm_kzalloc(&pdev->dev, sizeof(struct platform_mif), GFP_KERNEL); + int err = 0; + u8 i = 0; + struct resource *reg_res; + +#ifdef CONFIG_SCSC_SMAPPER + u32 smapper_banks = 0; +#endif + + if (!platform) + return NULL; + + SCSC_TAG_INFO_DEV(PLAT_MIF, &pdev->dev, "Creating MIF platform device\n"); + + platform_if = &platform->interface; + + /* initialise interface structure */ + platform_if->destroy = platform_mif_destroy; + platform_if->get_uid = platform_mif_get_uid; + platform_if->reset = platform_mif_reset; + platform_if->map = platform_mif_map; + platform_if->unmap = platform_mif_unmap; + platform_if->irq_bit_set = platform_mif_irq_bit_set; + platform_if->irq_get = platform_mif_irq_get; + platform_if->irq_bit_mask_status_get = platform_mif_irq_bit_mask_status_get; + platform_if->irq_bit_clear = platform_mif_irq_bit_clear; + platform_if->irq_bit_mask = platform_mif_irq_bit_mask; + platform_if->irq_bit_unmask = platform_mif_irq_bit_unmask; + platform_if->irq_reg_handler = platform_mif_irq_reg_handler; + platform_if->irq_unreg_handler = platform_mif_irq_unreg_handler; + platform_if->irq_reg_reset_request_handler = platform_mif_irq_reg_reset_request_handler; + platform_if->irq_unreg_reset_request_handler = platform_mif_irq_unreg_reset_request_handler; + platform_if->suspend_reg_handler = platform_mif_suspend_reg_handler; + platform_if->suspend_unreg_handler = platform_mif_suspend_unreg_handler; + platform_if->get_mbox_ptr = platform_mif_get_mbox_ptr; + platform_if->get_mifram_ptr = platform_mif_get_mifram_ptr; + platform_if->get_mifram_ref = platform_mif_get_mifram_ref; + platform_if->get_mifram_pfn = platform_mif_get_mif_pfn; + platform_if->get_mifram_phy_ptr = platform_mif_get_mifram_phy_ptr; + platform_if->get_mif_device = platform_mif_get_mif_device; + platform_if->irq_clear = platform_mif_irq_clear; + platform_if->mif_dump_registers = platform_mif_dump_register; + platform_if->mif_read_register = platform_mif_read_register; + platform_if->mif_cleanup = platform_mif_cleanup; + platform_if->mif_restart = platform_mif_restart; +#ifdef CONFIG_SCSC_SMAPPER + platform_if->mif_smapper_get_mapping = platform_mif_smapper_get_mapping; + platform_if->mif_smapper_get_bank_info = platform_mif_smapper_get_bank_info; + platform_if->mif_smapper_write_sram = platform_mif_smapper_write_sram; + platform_if->mif_smapper_configure = platform_mif_smapper_configure; + platform_if->mif_smapper_get_bank_base_address = platform_mif_smapper_get_bank_base_address; +#endif +#ifdef CONFIG_SCSC_QOS + platform_if->mif_pm_qos_add_request = platform_mif_pm_qos_add_request; + platform_if->mif_pm_qos_update_request = platform_mif_pm_qos_update_request; + platform_if->mif_pm_qos_remove_request = platform_mif_pm_qos_remove_request; +#endif + /* Update state */ + platform->pdev = pdev; + platform->dev = &pdev->dev; + + platform->r4_handler = platform_mif_irq_default_handler; + platform->irq_dev = NULL; + platform->reset_request_handler = platform_mif_irq_reset_request_default_handler; + platform->irq_reset_request_dev = NULL; + platform->suspend_handler = NULL; + platform->resume_handler = NULL; + platform->suspendresume_data = NULL; + +#ifdef CONFIG_OF_RESERVED_MEM + platform->mem_start = sharedmem_base; + platform->mem_size = sharedmem_size; +#else + /* If CONFIG_OF_RESERVED_MEM is not defined, sharedmem values should be + * parsed from the scsc_wifibt binding + */ + if (of_property_read_u32(pdev->dev.of_node, "sharedmem-base", &sharedmem_base)) { + err = -EINVAL; + goto error_exit; + } + platform->mem_start = sharedmem_base; + + if (of_property_read_u32(pdev->dev.of_node, "sharedmem-size", &sharedmem_size)) { + err = -EINVAL; + goto error_exit; + } + platform->mem_size = sharedmem_size; +#endif +#ifdef CONFIG_SCSC_SMAPPER + platform->smapper = NULL; +#endif + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "platform->mem_start 0x%x platform->mem_size 0x%x\n", + (u32)platform->mem_start, (u32)platform->mem_size); + if (platform->mem_start == 0) + SCSC_TAG_WARNING_DEV(PLAT_MIF, platform->dev, "platform->mem_start is 0"); + + if (platform->mem_size == 0) { + /* We return return if mem_size is 0 as it does not make any sense. + * This may be an indication of an incorrect platform device binding. + */ + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "platform->mem_size is 0"); + err = -EINVAL; + goto error_exit; + } + + /* Memory resource - Phys Address of MAILBOX_WLBT register map */ + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!reg_res) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Error getting mem resource for MAILBOX_WLBT\n"); + err = -ENOENT; + goto error_exit; + } + + platform->reg_start = reg_res->start; + platform->reg_size = resource_size(reg_res); + + platform->base = + devm_ioremap_nocache(platform->dev, reg_res->start, resource_size(reg_res)); + + if (!platform->base) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Error mapping register region\n"); + err = -EBUSY; + goto error_exit; + } + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "platform->reg_start %lx size %x base %p\n", + (uintptr_t)platform->reg_start, (u32)platform->reg_size, platform->base); + + /* Get the 4 IRQ resources */ + for (i = 0; i < 4; i++) { + struct resource *irq_res; + int irqtag; + + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, i); + if (!irq_res) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "No IRQ resource at index %d\n", i); + err = -ENOENT; + goto error_exit; + } + + if (!strcmp(irq_res->name, "MBOX")) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "MBOX irq %d flag 0x%x\n", + (u32)irq_res->start, (u32)irq_res->flags); + irqtag = PLATFORM_MIF_MBOX; + } else if (!strcmp(irq_res->name, "ALIVE")) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "ALIVE irq %d flag 0x%x\n", + (u32)irq_res->start, (u32)irq_res->flags); + irqtag = PLATFORM_MIF_ALIVE; + } else if (!strcmp(irq_res->name, "WDOG")) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WDOG irq %d flag 0x%x\n", + (u32)irq_res->start, (u32)irq_res->flags); + irqtag = PLATFORM_MIF_WDOG; + } else if (!strcmp(irq_res->name, "CFG_REQ")) { + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "CFG_REQ irq %d flag 0x%x\n", + (u32)irq_res->start, (u32)irq_res->flags); + irqtag = PLATFORM_MIF_CFG_REQ; + } else { + SCSC_TAG_ERR_DEV(PLAT_MIF, &pdev->dev, "Invalid irq res name: %s\n", + irq_res->name); + err = -EINVAL; + goto error_exit; + } + platform->wlbt_irq[irqtag].irq_num = irq_res->start; + platform->wlbt_irq[irqtag].flags = (irq_res->flags & IRQF_TRIGGER_MASK); + atomic_set(&platform->wlbt_irq[irqtag].irq_disabled_cnt, 0); + } + + /* PMU reg map - syscon */ + platform->pmureg = syscon_regmap_lookup_by_phandle(platform->dev->of_node, + "samsung,syscon-phandle"); + if (IS_ERR(platform->pmureg)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "syscon regmap lookup failed. Aborting. %ld\n", + PTR_ERR(platform->pmureg)); + err = -EINVAL; + goto error_exit; + } + + /* Completion event and state used to indicate CFG_REQ IRQ occurred */ + init_completion(&platform->cfg_ack); + platform->boot_state = WLBT_BOOT_IN_RESET; + + /* I3C_APM_PMIC */ + platform->i3c_apm_pmic = syscon_regmap_lookup_by_phandle(platform->dev->of_node, + "samsung,i3c_apm_pmic-syscon-phandle"); + if (IS_ERR(platform->i3c_apm_pmic)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "i3c_apm_pmic regmap lookup failed. Aborting. %ld\n", + PTR_ERR(platform->i3c_apm_pmic)); + err = -EINVAL; + goto error_exit; + } + + /* DBUS_BAAW */ + platform->dbus_baaw = syscon_regmap_lookup_by_phandle(platform->dev->of_node, + "samsung,dbus_baaw-syscon-phandle"); + if (IS_ERR(platform->dbus_baaw)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "dbus_baaw regmap lookup failed. Aborting. %ld\n", + PTR_ERR(platform->dbus_baaw)); + err = -EINVAL; + goto error_exit; + } + + /* PBUS_BAAW */ + platform->pbus_baaw = syscon_regmap_lookup_by_phandle(platform->dev->of_node, + "samsung,pbus_baaw-syscon-phandle"); + if (IS_ERR(platform->pbus_baaw)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "pbus_baaw regmap lookup failed. Aborting. %ld\n", + PTR_ERR(platform->pbus_baaw)); + err = -EINVAL; + goto error_exit; + } + + /* WLBT_REMAP */ + platform->wlbt_remap = syscon_regmap_lookup_by_phandle(platform->dev->of_node, + "samsung,wlbt_remap-syscon-phandle"); + if (IS_ERR(platform->wlbt_remap)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "wlbt_remap regmap lookup failed. Aborting. %ld\n", + PTR_ERR(platform->wlbt_remap)); + err = -EINVAL; + goto error_exit; + } + + /* BOOT_CFG */ + platform->boot_cfg = syscon_regmap_lookup_by_phandle(platform->dev->of_node, + "samsung,boot_cfg-syscon-phandle"); + if (IS_ERR(platform->boot_cfg)) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "boot_cfg regmap lookup failed. Aborting. %ld\n", + PTR_ERR(platform->boot_cfg)); + err = -EINVAL; + goto error_exit; + } + +#ifdef CONFIG_SCSC_SMAPPER + /* SMAPPER parsing */ + if (!of_property_read_u32(pdev->dev.of_node, "smapper_num_banks", &smapper_banks)) + platform_mif_parse_smapper(platform, platform->dev->of_node, smapper_banks); + +#endif +#ifdef CONFIG_SCSC_QOS + platform_mif_parse_qos(platform, platform->dev->of_node); +#endif + /* Initialize spinlock */ + spin_lock_init(&platform->mif_spinlock); + + return platform_if; + +error_exit: + devm_kfree(&pdev->dev, platform); + return NULL; +} + +void platform_mif_destroy_platform(struct platform_device *pdev, struct scsc_mif_abs *interface) +{ +} + +struct platform_device *platform_mif_get_platform_dev(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + BUG_ON(!interface || !platform); + + return platform->pdev; +} + +struct device *platform_mif_get_dev(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + BUG_ON(!interface || !platform); + + return platform->dev; +} + +/* Preserve MIF registers during suspend. + * If all users of the MIF (AP, mx140, CP, etc) release it, the registers + * will lose their values. Save the useful subset here. + * + * Assumption: the AP will not change the register values between the suspend + * and resume handlers being called! + */ +static void platform_mif_reg_save(struct platform_mif *platform) +{ + platform->mif_preserve.irq_bit_mask = __platform_mif_irq_bit_mask_read(platform); +} + +/* Restore MIF registers that may have been lost during suspend */ +static void platform_mif_reg_restore(struct platform_mif *platform) +{ + __platform_mif_irq_bit_mask_write(platform, platform->mif_preserve.irq_bit_mask); +} + +int platform_mif_suspend(struct scsc_mif_abs *interface) +{ + int r = 0; + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + + if (platform->suspend_handler) + r = platform->suspend_handler(interface, platform->suspendresume_data); + + /* Save the MIF registers. + * This must be done last as the suspend_handler may use the MIF + */ + platform_mif_reg_save(platform); + + return r; +} + +void platform_mif_resume(struct scsc_mif_abs *interface) +{ + struct platform_mif *platform = platform_mif_from_mif_abs(interface); + s32 ret; + + /* Restore the MIF registers. + * This must be done first as the resume_handler may use the MIF. + */ + platform_mif_reg_restore(platform); + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Clear WLBT_ACTIVE_CLR flag\n"); + /* Clear WLBT_ACTIVE_CLR flag in WLBT_CTRL_NS */ + ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, WLBT_ACTIVE_CLR, 1); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to Set WLBT_CTRL_NS[WLBT_ACTIVE_CLR]: %d\n", ret); + } + + if (platform->resume_handler) + platform->resume_handler(interface, platform->suspendresume_data); +} + +/* Temporary workaround to power up slave PMIC LDOs before FW APM/WLBT signalling + * is complete + */ +static void power_supplies_on(struct platform_mif *platform) +{ + struct i2c_client i2c; + + /* HACK: Note only addr field is needed by s2mpu11_write_reg() */ + i2c.addr = 0x1; + + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WLBT LDOs on (PMIC i2c_addr = 0x%x)\n", i2c.addr); + /* SLAVE PMIC + * adb shell "echo 0x3C > sys/class/pmic/s2mpu12/s2mpu12_read" + * adb shell "echo 0x3C 0xE0 > sys/class/pmic/s2mpu12/s2mpu12_write" + * adb shell "echo 0x3D > /sys/class/pmic/s2mpu12/s2mpu12_read" + * adb shell "echo 0x3D 0xE0 > /sys/class/pmic/s2mpu12/s2mpu12_write" + * adb shell "echo 0x3E > /sys/class/pmic/s2mpu12/s2mpu12_read" + * adb shell "echo 0x3E 0xEC > /sys/class/pmic/s2mpu12/s2mpu12_write" + * adb shell "echo 0x40 > /sys/class/pmic/s2mpu12/s2mpu12_read" + * adb shell "echo 0x40 0xFC > /sys/class/pmic/s2mpu12/s2mpu12_write" + */ + + s2mpu12_write_reg(&i2c, 0x3C, 0xE0); /* LDO 18 */ + s2mpu12_write_reg(&i2c, 0x3D, 0xE0); /* LDO 19 */ + s2mpu12_write_reg(&i2c, 0x3E, 0xEC); /* LDO 20 */ + s2mpu12_write_reg(&i2c, 0x40, 0xFC); /* LDO 22 */ +} diff --git a/drivers/misc/samsung/scsc/platform_mif_9630.c b/drivers/misc/samsung/scsc/platform_mif_9630.c index fa0f87d58580..97d39d8de7e5 100644 --- a/drivers/misc/samsung/scsc/platform_mif_9630.c +++ b/drivers/misc/samsung/scsc/platform_mif_9630.c @@ -116,7 +116,7 @@ struct platform_mif { /* pmu syscon regmap */ struct regmap *pmureg; #if defined(CONFIG_SOC_EXYNOS9630) - struct regmap *baaw_p_wlbt; + struct regmap *i3c_apm_pmic; struct regmap *dbus_baaw; struct regmap *pbus_baaw; struct regmap *wlbt_remap; @@ -728,25 +728,7 @@ irqreturn_t platform_cfg_req_isr(int irq, void *data) else SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "SMC_CMD_CONN_IF run successfully : %llu\n", ret64); -#if 0 - /* WLBT_REMAP PMU_REMAP - PROC_RMP_BOOT_ADDR 0x14450400 */ - SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "WLBT_REMAP begin\n"); - CHECK(regmap_write(platform->wlbt_remap, 0x400, WLBT_DBUS_BAAW_0_START >> 12)); - SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "WLBT_REMAP end\n"); -#endif - - /* update EMA parameters if required in future */ - /* BAAW1_P_WLBT */ -#if 0 - /* Not used by DRAM boot */ - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "BAAW_P_WLBT begin\n"); - CHECK(regmap_write(platform->baaw_p_wlbt, 0x0, 0x0 >> 12)); - CHECK(regmap_write(platform->baaw_p_wlbt, 0x4, 0x1ffff + 1)); - CHECK(regmap_write(platform->baaw_p_wlbt, 0x8, 0x0 >> 12)); - CHECK(regmap_write(platform->baaw_p_wlbt, 0xC, WLBT_BAAW_ACCESS_CTRL)); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "BAAW_P_WLBT end\n"); -#endif /* WLBT_REMAP PMU_REMAP - PROC_RMP_BOOT_ADDR 0x14450400 */ SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WLBT_REMAP begin\n"); CHECK(regmap_write(platform->wlbt_remap, 0x400, WLBT_DBUS_BAAW_0_START >> 12)); @@ -1078,25 +1060,21 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) */ if (!init_done) { /* init sequence from excite + * //20190812 APM team Change PMUCAL guide doc. * SetBits((uinteger)REG_WLBT_CTRL_S, 3, 0x1, 0x1); + * SetBits((uinteger)REG_WLBT_OPTION, 3, 0x1, 0x1); + * udelay(1000); + * SetBits((uinteger)SYSTEM_OUT, 9, 0x1, 0x1); * SetBits((uinteger)REG_WLBT_CONFIGURATION, 0, 0x1, 0x1); - * while (GetBits((uinteger)REG_WLBT_STATUS, 0, 0x1) != 0x1); - * wlbt_if_check_wlbt2ap_bus_ready(); - * SetBits((uinteger)REG_WLBT_CTRL_NS, 8, 0x1, 0x0); - * SetBits((uinteger)REG_WLBT_CTRL_NS, 7, 0x1, 0x1); + * while (GetBits((uinteger)REG_VGPIO_TX_MONITOR, 29, 0x1) != 0x1) + * ; + * while (GetBits((uinteger)REG_WLBT_STATUS, 0, 0x1) != 0x1) + * ; + * while (GetBits((uinteger)REG_WLBT_IN, 4, 0x1) != 0x1) + * ; */ SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "init\n"); - /* setup TZPC */ - /*SCSC_TAG_INFO(PLAT_MIF, "trying to set TZPC register via exynos_smc(SMC_CMD_CONN_IF, (EXYNOS_WLBT << 32) | EXYNOS_SET_CONN_TZPC, 0, 0)\n"); - ret64 = exynos_smc(SMC_CMD_CONN_IF, (EXYNOS_WLBT << 32) | EXYNOS_SET_CONN_TZPC, 0, 0); - if (ret64) - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to set TZPC to non-secure mode: %llu\n", ret64); - else - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "SMC_CMD_CONN_IF run successfully : %llu\n", ret64);*/ - /* WLBT_CTRL_S[WLBT_START] = 1 enable */ ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_S, WLBT_START, WLBT_START); @@ -1109,9 +1087,33 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "updated successfully WLBT_CTRL_S[WLBT_START]: 0x%x\n", val); + /* WLBT_OPTION[WLBT_OPTION_DATA] = 1 Power On */ + ret = regmap_update_bits(platform->pmureg, WLBT_OPTION, + WLBT_OPTION_DATA, WLBT_OPTION_DATA); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update WLBT_OPTION[WLBT_OPTION_DATA]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, WLBT_OPTION, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully WLBT_OPTION[WLBT_OPTION_DATA]: 0x%x\n", val); + + /* SYSTEM_OUT[PWRRGTON_CON] = 1 Power On */ + ret = regmap_update_bits(platform->pmureg, SYSTEM_OUT, + PWRRGTON_CON, PWRRGTON_CON); + if (ret < 0) { + SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, + "Failed to update SYSTEM_OUT[PWRRGTON_CON]: %d\n", ret); + return ret; + } + regmap_read(platform->pmureg, SYSTEM_OUT, &val); + SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, + "updated successfully SYSTEM_OUT[PWRRGTON_CON]: 0x%x\n", val); + /* WLBT_CONFIGURATION[LOCAL_PWR_CFG] = 1 Power On */ ret = regmap_update_bits(platform->pmureg, WLBT_CONFIGURATION, - LOCAL_PWR_CFG, 0x1); + LOCAL_PWR_CFG, LOCAL_PWR_CFG); if (ret < 0) { SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Failed to update WLBT_CONFIGURATION[LOCAL_PWR_CFG]: %d\n", ret); @@ -1119,13 +1121,30 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) } regmap_read(platform->pmureg, WLBT_CONFIGURATION, &val); SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_CONFIGURATION[LOCAL_PWR_CFG]: 0x%x\n", val); + "updated successfully WLBT_CONFIGURATION[PWRRGTON_CON]: 0x%x\n", val); - /* wait for power up complete WLBT_STATUS[STATUS] = 1 for Power On */ + /* VGPIO_TX_MONITOR[VGPIO_TX_MON_BIT29] = 0x1 */ + timeout = jiffies + msecs_to_jiffies(500); + do { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + val &= (u32)VGPIO_TX_MON_BIT29; + if (val) { + SCSC_TAG_INFO(PLAT_MIF, "VGPIO_TX_MONITOR 0x%x\n", val); + break; + } + } while (time_before(jiffies, timeout)); + + if (!val) { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + SCSC_TAG_INFO(PLAT_MIF, "timeout waiting for VGPIO_TX_MONITOR time-out: " + "VGPIO_TX_MONITOR 0x%x\n", val); + } + + /* wait for power up complete WLBT_STATUS[WLBT_STATUS_BIT0] = 1 for Power On */ timeout = jiffies + msecs_to_jiffies(500); do { regmap_read(platform->pmureg, WLBT_STATUS, &val); - val &= STATUS; + val &= (u32)WLBT_STATUS_BIT0; if (val) { /* Power On complete */ SCSC_TAG_INFO(PLAT_MIF, "Power On complete: WLBT_STATUS 0x%x\n", val); @@ -1147,7 +1166,7 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) timeout = jiffies + msecs_to_jiffies(500); do { regmap_read(platform->pmureg, WLBT_IN, &val); - val &= BUS_READY; + val &= (u32)BUS_READY; if (val) { /* BUS ready indication signal -> 1: BUS READY state */ SCSC_TAG_INFO(PLAT_MIF, "Bus Ready: WLBT_IN 0x%x\n", val); @@ -1162,30 +1181,6 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) SCSC_TAG_INFO(PLAT_MIF, "timeout waiting for Bus Ready: WLBT_IN 0x%x\n", val); } - /* WLBT_CTRL_NS[WLBT_ACTIVE_CLR] = 0 Active interrupt clear */ - ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, - WLBT_ACTIVE_CLR, 0x0); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update WLBT_CTRL_NS[WLBT_ACTIVE_CLR]: %d\n", ret); - return ret; - } - regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_CTRL_NS[WLBT_ACTIVE_CLR]: 0x%x\n", val); - - /* WLBT_CTRL_NS[WLBT_ACTIVE_EN] = 1 Active interrupt enable */ - ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, - WLBT_ACTIVE_EN, WLBT_ACTIVE_EN); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update WLBT_CTRL_NS[WLBT_ACTIVE_EN]: %d\n", ret); - return ret; - } - regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_CTRL_NS[WLBT_ACTIVE_EN]: 0x%x\n", val); - init_done = true; goto init_code_done; @@ -1193,12 +1188,13 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) /* RESET RELEASE - Subsequent WLBT reboots */ /* wlbt_if_reset_release - from excite code + * 20190812 APM team Change PMUCAL guide doc. * SetBits((uinteger)REG_WLBT_CONFIGURATION, 0, 0x1, 0x1); - * while (GetBits((uinteger)REG_WLBT_STATUS, 0, 0x1) != 0x1); - * SetBits((uinteger)REG_WLBT_INT_EN, 3, 0x1, 0x1); - * SetBits((uinteger)REG_WLBT_INT_EN, 5, 0x1, 0x1); - * while (GetBits((uinteger)REG_WLBT_IN, 4, 0x1) != 0x1); - * */ + * while (GetBits((uinteger)REG_WLBT_STATUS, 0, 0x1) != 0x1) + * ; + * while (GetBits((uinteger)REG_WLBT_IN, 4, 0x1) != 0x1) + ; + */ /* Warm reset wrt. AP power sequencer, but cold reset for WLBT */ SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "release\n"); @@ -1219,7 +1215,7 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) timeout = jiffies + msecs_to_jiffies(500); do { regmap_read(platform->pmureg, WLBT_STATUS, &val); - val &= STATUS; + val &= (u32)WLBT_STATUS_BIT0; if (val) { /* Power up complete */ SCSC_TAG_INFO(PLAT_MIF, "Power up complete: WLBT_STATUS 0x%x\n", val); @@ -1237,34 +1233,11 @@ static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface) "WLBT_STATUS 0x%x, WLBT_STATES 0x%x\n", val, v); } - /* enable PWR_REQ_F and TCXO_REQ_F interrupts */ - ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, - PWR_REQ_F, 0x1); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update WLBT_INT_EN[PWR_REQ_F]: %d\n", ret); - return ret; - } - regmap_read(platform->pmureg, WLBT_INT_EN, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_INT_EN[PWR_REQ_F]: 0x%x\n", val); - - ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, - TCXO_REQ_F, 0x1); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update WLBT_INT_EN[TCXO_REQ_F]: %d\n", ret); - return ret; - } - regmap_read(platform->pmureg, WLBT_INT_EN, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_INT_EN[TCXO_REQ_F]: 0x%x\n", val); - /* wait for WLBT_IN[BUS_READY] = 0 for BUS RESET RELEASED state */ timeout = jiffies + msecs_to_jiffies(500); do { regmap_read(platform->pmureg, WLBT_IN, &val); - val &= BUS_READY; + val &= (u32)BUS_READY; if (val) { /* BUS ready indication signal -> 1: BUS RESET RELEASED Normal state */ SCSC_TAG_INFO(PLAT_MIF, "Bus Ready: WLBT_IN 0x%x\n", val); @@ -1298,86 +1271,56 @@ static int platform_mif_pmu_reset_assert(struct scsc_mif_abs *interface) u32 val; /* wlbt_if_reset_assertion() - from wlbt_if_S5E9630.c - * SetBits((uinteger)TCXO_BUF_CTRL, 2, 0x1, 0x1); - * SetBits((uinteger)TCXO_BUF_CTRL, 3, 0x1, 0x1); + * 20190812 APM team Change PMUCAL guide doc. + * SetBits((uinteger)REG_WLBT_OPTION, 3, 0x1, 0x1); * SetBits((uinteger)SYSTEM_OUT, 9, 0x1, 0x1); + * while (GetBits((uinteger)REG_VGPIO_TX_MONITOR, 29, 0x1) != 0x1) + * ; * udelay(1000); - * SetBits((uinteger)REG_WLBT_INT_EN, 3, 0x1, 0x0); - * SetBits((uinteger)REG_WLBT_INT_EN, 5, 0x1, 0x0); - * SetBits((uinteger)REG_WLBT_CTRL_NS, 7, 0x1, 0x0); * SetBits((uinteger)REG_WLBT_CONFIGURATION, 0, 0x1, 0x0); - * while (GetBits((uinteger)REG_WLBT_STATUS, 0, 0x1) != 0x0); + * udelay(1000); + * while (GetBits((uinteger)REG_WLBT_STATUS, 0, 0x1) != 0x0) + * ; */ - ret = regmap_update_bits(platform->pmureg, TCXO_BUF_CTRL, - TCXO_BUF_BIAS_EN_WLBT, TCXO_BUF_BIAS_EN_WLBT); + ret = regmap_update_bits(platform->pmureg, WLBT_OPTION, + WLBT_OPTION_DATA, WLBT_OPTION_DATA); if (ret < 0) { SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update TCXO_BUF_CTRL[TCXO_BUF_BIAS_EN_WLBT]: %d\n", ret); + "Failed to update WLBT_OPTION[WLBT_OPTION_DATA]: %d\n", ret); return ret; } - regmap_read(platform->pmureg, TCXO_BUF_CTRL, &val); + regmap_read(platform->pmureg, WLBT_OPTION, &val); SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully TCXO_BUF_CTRL[TCXO_BUF_BIAS_EN_WLBT]: 0x%x\n", val & TCXO_BUF_BIAS_EN_WLBT); - - ret = regmap_update_bits(platform->pmureg, TCXO_BUF_CTRL, - TCXO_BUF_EN_WLBT, TCXO_BUF_EN_WLBT); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update TCXO_BUF_CTRL[TCXO_BUF_EN_WLBT]: %d\n", ret); - return ret; - } - regmap_read(platform->pmureg, TCXO_BUF_CTRL, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully TCXO_BUF_CTRL[TCXO_BUF_EN_WLBT]: 0x%x\n", val & TCXO_BUF_EN_WLBT); + "updated successfully WLBT_OPTION[WLBT_OPTION_DATA]: 0x%x\n", val); ret = regmap_update_bits(platform->pmureg, SYSTEM_OUT, PWRRGTON_CON, PWRRGTON_CON); if (ret < 0) { SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update SYSTEM_OUT[PWRRGTON_CON]: %d\n", ret); - return ret; - } - regmap_read(platform->pmureg, TCXO_BUF_CTRL, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully SYSTEM_OUT[PWRRGTON_CON]: 0x%x\n", val & PWRRGTON_CON); - - udelay(1000); - - /* disable PWR_REQ_F and TCXO_REQ_F interrupts */ - ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, - PWR_REQ_F, 0x0); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update WLBT_INT_EN[PWR_REQ_F]: %d\n", ret); + "Failed to update SYSTEM_OUT[PWRRGTON_CON]: %d\n", ret); return ret; } - regmap_read(platform->pmureg, WLBT_INT_EN, &val); + regmap_read(platform->pmureg, SYSTEM_OUT, &val); SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_INT_EN[PWR_REQ_F]: 0x%x\n", val); + "updated successfully SYSTEM_OUT[PWRRGTON_CON]: 0x%x\n", val); - ret = regmap_update_bits(platform->pmureg, WLBT_INT_EN, - TCXO_REQ_F, 0x0); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update WLBT_INT_EN[TCXO_REQ_F]: %d\n", ret); - return ret; - } - regmap_read(platform->pmureg, WLBT_INT_EN, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_INT_EN[TCXO_REQ_F]: 0x%x\n", val); + /* VGPIO_TX_MONITOR = 0x1 */ + timeout = jiffies + msecs_to_jiffies(500); + do { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + val &= (u32)VGPIO_TX_MON_BIT29; + if (val) { + SCSC_TAG_INFO(PLAT_MIF, "VGPIO_TX_MONITOR 0x%x\n", val); + break; + } + } while (time_before(jiffies, timeout)); - /* Active interrupt disable */ - ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, - WLBT_ACTIVE_EN, 0x0); - if (ret < 0) { - SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "Failed to update WLBT_CTRL_NS[WLBT_ACTIVE_EN]: %d\n", ret); - return ret; + if (!val) { + regmap_read(platform->i3c_apm_pmic, VGPIO_TX_MONITOR, &val); + SCSC_TAG_INFO(PLAT_MIF, "timeout waiting for VGPIO_TX_MONITOR time-out: " + "VGPIO_TX_MONITOR 0x%x\n", val); } - regmap_read(platform->pmureg, WLBT_CTRL_NS, &val); - SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, - "updated successfully WLBT_CTRL_NS[WLBT_ACTIVE_EN]: 0x%x\n", val); /* Power Down */ ret = regmap_write_bits(platform->pmureg, WLBT_CONFIGURATION, @@ -1391,11 +1334,11 @@ static int platform_mif_pmu_reset_assert(struct scsc_mif_abs *interface) SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "updated successfully WLBT_CONFIGURATION[LOCAL_PWR_CFG]: 0x%x\n", val); - /* Wait for power Off WLBT_STATUS[STATUS] = 0 */ + /* Wait for power Off WLBT_STATUS[WLBT_STATUS_BIT0] = 0 */ timeout = jiffies + msecs_to_jiffies(500); do { regmap_read(platform->pmureg, WLBT_STATUS, &val); - val &= STATUS; + val &= (u32)WLBT_STATUS_BIT0; if (val == 0) { SCSC_TAG_INFO(PLAT_MIF, "WLBT_STATUS 0x%x\n", val); /* re affirming power down by reading WLBT_STATES */ @@ -2065,13 +2008,13 @@ struct scsc_mif_abs *platform_mif_create(struct platform_device *pdev) init_completion(&platform->cfg_ack); platform->boot_state = WLBT_BOOT_IN_RESET; - /* BAAW_P_WLBT */ - platform->baaw_p_wlbt = syscon_regmap_lookup_by_phandle(platform->dev->of_node, - "samsung,baaw_p_wlbt-syscon-phandle"); - if (IS_ERR(platform->baaw_p_wlbt)) { + /* I3C_APM_PMIC */ + platform->i3c_apm_pmic = syscon_regmap_lookup_by_phandle(platform->dev->of_node, + "samsung,i3c_apm_pmic-syscon-phandle"); + if (IS_ERR(platform->i3c_apm_pmic)) { SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, - "baaw_p_wlbt regmap lookup failed. Aborting. %ld\n", - PTR_ERR(platform->baaw_p_wlbt)); + "i3c_apm_pmic regmap lookup failed. Aborting. %ld\n", + PTR_ERR(platform->i3c_apm_pmic)); err = -EINVAL; goto error_exit; } diff --git a/drivers/misc/samsung/scsc/scsc_log_collector.c b/drivers/misc/samsung/scsc/scsc_log_collector.c index b45dffc92448..818d09e32854 100644 --- a/drivers/misc/samsung/scsc/scsc_log_collector.c +++ b/drivers/misc/samsung/scsc/scsc_log_collector.c @@ -124,8 +124,8 @@ static void collection_worker(struct work_struct *work) struct scsc_log_status *ls; ls = container_of(work, struct scsc_log_status, collect_work); - if (!ls) - return; + /* ls cannot be NULL due to pointer arithmetic */ + pr_info("SCSC running scheduled Log Collection - collect reason:%d reason code:%d\n", ls->collect_reason, ls->reason_code); scsc_log_collector_collect(ls->collect_reason, ls->reason_code); diff --git a/drivers/misc/samsung/scsc/scsc_logring_main.c b/drivers/misc/samsung/scsc/scsc_logring_main.c index 9b623c4529f3..d985068e7dbe 100644 --- a/drivers/misc/samsung/scsc/scsc_logring_main.c +++ b/drivers/misc/samsung/scsc/scsc_logring_main.c @@ -258,7 +258,7 @@ ADD_DEBUG_MODULE_PARAM(test_me, SCSC_FULL_DEBUG, TEST_ME); /* Extend this list when you add ADD_DEBUG_MODULE_PARAM, above. * You must also extend "enum scsc_logring_tags" */ -int *scsc_droplevels[] = { +int *scsc_droplevels[MAX_TAG + 1] = { &scsc_droplevel_binary, &scsc_droplevel_bin_wifi_ctrl_rx, &scsc_droplevel_bin_wifi_data_rx, diff --git a/drivers/misc/samsung/scsc/scsc_logring_ring.c b/drivers/misc/samsung/scsc/scsc_logring_ring.c index 432eb6e24ec9..85d09e0bbf1a 100644 --- a/drivers/misc/samsung/scsc/scsc_logring_ring.c +++ b/drivers/misc/samsung/scsc/scsc_logring_ring.c @@ -21,7 +21,7 @@ SCSC_MODPARAM_DESC(scsc_decode_binary_len, * it also where required, taking care to maintain the same ordering. * (Search 4 NOTE_CREATING_TAGS) */ -const char *tagstr[] = { +const char *tagstr[MAX_TAG + 1] = { "binary", "bin_wifi_ctrl_rx", "bin_wifi_data_rx", @@ -59,6 +59,8 @@ const char *tagstr[] = { "kic_common", "wlbtd", "wlog", + "lerna", + "mx_cfg", #ifdef CONFIG_SCSC_DEBUG_COMPATIBILITY "init_deinit", "netdev", diff --git a/drivers/misc/samsung/scsc/scsc_mx_impl.c b/drivers/misc/samsung/scsc/scsc_mx_impl.c index a4928e145c15..cbc3dd88a7ae 100644 --- a/drivers/misc/samsung/scsc/scsc_mx_impl.c +++ b/drivers/misc/samsung/scsc/scsc_mx_impl.c @@ -20,6 +20,7 @@ #include "mifproc.h" #include "mxman.h" #include "mxproc.h" +#include "mxsyserr.h" #include "srvman.h" #include "mxmgmt_transport.h" #include "gdb_transport.h" @@ -87,6 +88,7 @@ struct scsc_mx *scsc_mx_create(struct scsc_mif_abs *mif) #ifdef CONFIG_SCSC_WLBTD scsc_wlbtd_init(); #endif + mx_syserr_init(); SCSC_TAG_DEBUG(MXMAN, "Hurray Maxwell is here with %p\n", mx); return mx; } diff --git a/drivers/misc/samsung/scsc/scsc_service.c b/drivers/misc/samsung/scsc/scsc_service.c index d9cd4e079aca..291c867831a6 100755 --- a/drivers/misc/samsung/scsc/scsc_service.c +++ b/drivers/misc/samsung/scsc/scsc_service.c @@ -50,6 +50,11 @@ struct scsc_service { struct completion sm_msg_stop_completion; }; +/* true if a service is part of a sub-system that is reported by system error */ +#define SERVICE_IN_SUBSYSTEM(service, subsys) \ + (((subsys == SYSERR_SUBSYS_WLAN) && (service == SCSC_SERVICE_ID_WLAN)) || \ + ((subsys == SYSERR_SUBSYS_BT) && ((service == SCSC_SERVICE_ID_BT) || (service == SCSC_SERVICE_ID_ANT)))) + void srvman_init(struct srvman *srvman, struct scsc_mx *mx) { SCSC_TAG_INFO(MXMAN, "\n"); @@ -445,15 +450,21 @@ int srvman_resume_services(struct srvman *srvman) return 0; } -void srvman_freeze_services(struct srvman *srvman) +void srvman_freeze_services(struct srvman *srvman, struct mx_syserr_decode *syserr) { struct scsc_service *service; struct mxman *mxman = scsc_mx_get_mxman(srvman->mx); SCSC_TAG_INFO(MXMAN, "\n"); + mxman->notify = false; mutex_lock(&srvman->service_list_mutex); list_for_each_entry(service, &srvman->service_list, list) { - service->client->stop_on_failure(service->client); + if (service->client->stop_on_failure) { + service->client->stop_on_failure(service->client); + mxman->notify = true; + } else if ((service->client->stop_on_failure_v2) && + (service->client->stop_on_failure_v2(service->client, syserr))) + mxman->notify = true; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) @@ -465,19 +476,104 @@ void srvman_freeze_services(struct srvman *srvman) SCSC_TAG_INFO(MXMAN, "OK\n"); } +void srvman_freeze_sub_system(struct srvman *srvman, struct mx_syserr_decode *syserr) +{ + struct scsc_service *service; + struct mxman *mxman = scsc_mx_get_mxman(srvman->mx); + + SCSC_TAG_INFO(MXMAN, "\n"); + mxman->notify = false; + mutex_lock(&srvman->service_list_mutex); + list_for_each_entry(service, &srvman->service_list, list) { + if ((SERVICE_IN_SUBSYSTEM(service->id, syserr->subsys) && (service->client->stop_on_failure_v2))) + if (service->client->stop_on_failure_v2(service->client, syserr)) + mxman->notify = true; + } + mutex_unlock(&srvman->service_list_mutex); + SCSC_TAG_INFO(MXMAN, "OK\n"); +} + void srvman_unfreeze_services(struct srvman *srvman, u16 scsc_panic_code) { struct scsc_service *service; + struct mxman *mxman = scsc_mx_get_mxman(srvman->mx); SCSC_TAG_INFO(MXMAN, "\n"); mutex_lock(&srvman->service_list_mutex); list_for_each_entry(service, &srvman->service_list, list) { - service->client->failure_reset(service->client, scsc_panic_code); + if (service->client->failure_reset) + service->client->failure_reset(service->client, scsc_panic_code); + else if (service->client->failure_reset_v2) + service->client->failure_reset_v2(service->client, MX_SYSERR_LEVEL_7, + mxman->notify ? scsc_panic_code : MX_NULL_SYSERR); } mutex_unlock(&srvman->service_list_mutex); SCSC_TAG_INFO(MXMAN, "OK\n"); } +void srvman_unfreeze_sub_system(struct srvman *srvman, struct mx_syserr_decode *syserr) +{ + struct scsc_service *service; + struct mxman *mxman = scsc_mx_get_mxman(srvman->mx); + + SCSC_TAG_INFO(MXMAN, "\n"); + mutex_lock(&srvman->service_list_mutex); + list_for_each_entry(service, &srvman->service_list, list) { + if ((SERVICE_IN_SUBSYSTEM(service->id, syserr->subsys) && (service->client->failure_reset_v2))) + service->client->failure_reset_v2(service->client, syserr->level, + mxman->notify ? syserr->subcode : MX_NULL_SYSERR); + } + mutex_unlock(&srvman->service_list_mutex); + SCSC_TAG_INFO(MXMAN, "OK\n"); +} + +u8 srvman_notify_sub_system(struct srvman *srvman, struct mx_syserr_decode *syserr) +{ + struct scsc_service *service; + u8 initial_level = syserr->level; + u8 final_level = syserr->level; + bool wlan_active = false; + bool bt_active = false; + bool affected_service_found = false; + + SCSC_TAG_INFO(MXMAN, "\n"); + mutex_lock(&srvman->service_list_mutex); + list_for_each_entry(service, &srvman->service_list, list) { + if (SERVICE_IN_SUBSYSTEM(service->id, SYSERR_SUBSYS_WLAN)) + wlan_active = true; + else if (SERVICE_IN_SUBSYSTEM(service->id, SYSERR_SUBSYS_BT)) + bt_active = true; + if ((SERVICE_IN_SUBSYSTEM(service->id, syserr->subsys) && (service->client->failure_notification))) { + u8 level = service->client->failure_notification(service->client, syserr); + + affected_service_found = true; + if (level > final_level) + final_level = level; + } + } + mutex_unlock(&srvman->service_list_mutex); + + if (final_level == MX_SYSERR_LEVEL_7) + SCSC_TAG_INFO(MXMAN, "System error level %d raised to full reset", initial_level); + else if ((!(wlan_active && bt_active)) && (final_level >= MX_SYSERR_LEVEL_5)) { + final_level = MX_SYSERR_LEVEL_6; /* Still a sub-system reset even though we will do a full restart */ + SCSC_TAG_INFO(MXMAN, "System error %d now level %d with 1 service active", initial_level, final_level); + } + + SCSC_TAG_INFO(MXMAN, "OK\n"); + + /* Handle race condition with affected service being closed by demoting severity to stop any recovery + * should not be possible, but best be careful anyway + */ + if ((!affected_service_found) && (final_level >= MX_SYSERR_LEVEL_5)) { + SCSC_TAG_INFO(MXMAN, "System error %d demoted to 4 as no services affected", final_level); + final_level = MX_SYSERR_LEVEL_4; + } + + return final_level; +} + + /** Signal a failure detected by the Client. This will trigger the systemwide * failure handling procedure: _All_ Clients will be called back via * their stop_on_failure() handler as a side-effect. @@ -526,6 +622,9 @@ int scsc_mx_service_close(struct scsc_service *service) wake_lock(&srvman->sm_wake_lock); #endif + /* TODO - Race conditions here unless we protect better + * code assumes srvman->error and mxman->state can't change, but they can + */ if (srvman->error) { tval = ns_to_timeval(mxman->last_panic_time); SCSC_TAG_ERR(MXMAN, "error: refused due to previous f/w failure scsc_panic_code=0x%x happened at [%6lu.%06ld]\n", @@ -560,9 +659,29 @@ int scsc_mx_service_close(struct scsc_service *service) /* unregister channel handler */ mxmgmt_transport_register_channel_handler(scsc_mx_get_mxmgmt_transport(mx), MMTRANS_CHAN_ID_SERVICE_MANAGEMENT, NULL, NULL); + /* Clear any system error information */ + mxman->syserr_recovery_in_progress = false; + mxman->last_syserr_recovery_time = 0; + } else if (mxman->syserr_recovery_in_progress) { + /* If we have syserr_recovery_in_progress and all the services we have asked to close are now closed, + * we can clear it now - don't wait for open as it may not come - do it now! + */ + struct scsc_service *serv; + bool all_cleared = true; + + mutex_lock(&srvman->service_list_mutex); + list_for_each_entry(serv, &srvman->service_list, list) { + if (SERVICE_IN_SUBSYSTEM(serv->id, mxman->last_syserr.subsys)) + all_cleared = false; + } + mutex_unlock(&srvman->service_list_mutex); + + if (all_cleared) + mxman->syserr_recovery_in_progress = false; } kfree(service); + mxman_close(mxman); #ifdef CONFIG_ANDROID wake_unlock(&srvman->sm_wake_lock); @@ -588,7 +707,11 @@ struct scsc_service *scsc_mx_service_open(struct scsc_mx *mx, enum scsc_service_ #ifdef CONFIG_ANDROID wake_lock(&srvman->sm_wake_lock); #endif - if (srvman->error) { + /* TODO - need to close potential race conditions - see close */ + /* Have to check for disabled here as there is a small window where error is asserted + * even if we are going to allow recovery later on - in these cases we will want to block + */ + if ((srvman->error) && (mxman_recovery_disabled())) { tval = ns_to_timeval(mxman->last_panic_time); SCSC_TAG_ERR(MXMAN, "error: refused due to previous f/w failure scsc_panic_code=0x%x happened at [%6lu.%06ld]\n", mxman->scsc_panic_code, tval.tv_sec, tval.tv_usec); diff --git a/drivers/misc/samsung/scsc/scsc_wlbtd.c b/drivers/misc/samsung/scsc/scsc_wlbtd.c index cc2100a056ed..070f19c905a0 100755 --- a/drivers/misc/samsung/scsc/scsc_wlbtd.c +++ b/drivers/misc/samsung/scsc/scsc_wlbtd.c @@ -135,7 +135,7 @@ static int msg_from_wlbtd_sable_cb(struct sk_buff *skb, struct genl_info *info) } if (!completion_done(&event_done)) { SCSC_TAG_INFO(WLBTD, "completing event_done\n"); - complete(&event_done); + complete_all(&event_done); } break; case SCSC_WLBTD_FW_PANIC_TAR_GENERATED: @@ -161,7 +161,7 @@ static int msg_from_wlbtd_sable_cb(struct sk_buff *skb, struct genl_info *info) case SCSC_WLBTD_OTHER_IGNORE_TRIGGER: if (!completion_done(&event_done)) { SCSC_TAG_INFO(WLBTD, "completing event_done\n"); - complete(&event_done); + complete_all(&event_done); } break; default: @@ -569,6 +569,19 @@ error: } EXPORT_SYMBOL(call_wlbtd_sable); +void scsc_wlbtd_wait_for_sable_logging(void) +{ + unsigned long completion_jiffies = 0; + unsigned long max_timeout_jiffies = msecs_to_jiffies(MAX_TIMEOUT); + /* Just waits for the log collection not tarring */ + completion_jiffies = wait_for_completion_timeout(&event_done, + max_timeout_jiffies); + if (!completion_jiffies) + SCSC_TAG_ERR(WLBTD, "wait for sable logging timed out !\n"); +} +EXPORT_SYMBOL(scsc_wlbtd_wait_for_sable_logging); + + int call_wlbtd(const char *script_path) { struct sk_buff *skb; diff --git a/drivers/misc/samsung/scsc/scsc_wlbtd.h b/drivers/misc/samsung/scsc/scsc_wlbtd.h index 5176ecd659b1..fa265e18fdd6 100644 --- a/drivers/misc/samsung/scsc/scsc_wlbtd.h +++ b/drivers/misc/samsung/scsc/scsc_wlbtd.h @@ -80,4 +80,5 @@ int scsc_wlbtd_deinit(void); int call_wlbtd(const char *script_path); int wlbtd_write_file(const char *path, const char *content); int call_wlbtd_sable(u8 trigger_code, u16 reason_code); +void scsc_wlbtd_wait_for_sable_logging(void); int scsc_wlbtd_get_and_print_build_type(void); diff --git a/drivers/misc/samsung/scsc/srvman.h b/drivers/misc/samsung/scsc/srvman.h index 03d213e1ca43..c981280f6873 100755 --- a/drivers/misc/samsung/scsc/srvman.h +++ b/drivers/misc/samsung/scsc/srvman.h @@ -11,13 +11,18 @@ #include #endif +#include "scsc/scsc_mx.h" + struct srvman; void srvman_init(struct srvman *srvman, struct scsc_mx *mx); int srvman_suspend_services(struct srvman *srvman); int srvman_resume_services(struct srvman *srvman); -void srvman_freeze_services(struct srvman *srvman); +void srvman_freeze_services(struct srvman *srvman, struct mx_syserr_decode *syserr); +void srvman_freeze_sub_system(struct srvman *srvman, struct mx_syserr_decode *syserr); void srvman_unfreeze_services(struct srvman *srvman, u16 scsc_panic_code); +void srvman_unfreeze_sub_system(struct srvman *srvman, struct mx_syserr_decode *syserr); +u8 srvman_notify_sub_system(struct srvman *srvman, struct mx_syserr_decode *syserr); void srvman_set_error(struct srvman *srvman); void srvman_clear_error(struct srvman *srvman); void srvman_deinit(struct srvman *srvman); diff --git a/drivers/net/wireless/scsc/cfg80211_ops.c b/drivers/net/wireless/scsc/cfg80211_ops.c index 417d3b987d70..f67eaf52d6dd 100755 --- a/drivers/net/wireless/scsc/cfg80211_ops.c +++ b/drivers/net/wireless/scsc/cfg80211_ops.c @@ -1104,7 +1104,6 @@ int slsi_connect(struct wiphy *wiphy, struct net_device *dev, ndev_vif->sta.crypto.wpa_versions = 0; } #endif - r = slsi_mlme_connect(sdev, dev, sme, channel, bssid); if (r != 0) { ndev_vif->sta.is_wps = false; @@ -1198,7 +1197,7 @@ int slsi_disconnect(struct wiphy *wiphy, struct net_device *dev, if (r != 0) SLSI_NET_ERR(dev, "Disconnection returned with failure\n"); /* Even if we fail to disconnect cleanly, tidy up. */ - r = slsi_handle_disconnect(sdev, dev, peer->address, 0); + r = slsi_handle_disconnect(sdev, dev, peer->address, 0, NULL, 0); break; } @@ -1390,7 +1389,7 @@ int slsi_del_station(struct wiphy *wiphy, struct net_device *dev, if (r != 0) SLSI_NET_ERR(dev, "Disconnection returned with failure\n"); /* Even if we fail to disconnect cleanly, tidy up. */ - r = slsi_handle_disconnect(sdev, dev, peer->address, WLAN_REASON_DEAUTH_LEAVING); + r = slsi_handle_disconnect(sdev, dev, peer->address, WLAN_REASON_DEAUTH_LEAVING, NULL, 0); } } @@ -1967,13 +1966,13 @@ static int slsi_ap_start_validate(struct net_device *dev, struct slsi_dev *sdev, goto exit_with_error; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) - if ((ndev_vif->chan->hw_value <= 14) && (!sdev->fw_2g_40mhz_enabled) && + if ((ndev_vif->chan->hw_value <= 14) && (!sdev->fw_SoftAp_2g_40mhz_enabled) && (ndev_vif->chandef->width == NL80211_CHAN_WIDTH_40)) { SLSI_NET_ERR(dev, "Configuration error: 40 MHz on 2.4 GHz is not supported. Channel_no: %d Channel_width: %d\n", ndev_vif->chan->hw_value, slsi_get_chann_info(sdev, ndev_vif->chandef)); goto exit_with_error; } #else - if ((ndev_vif->chan->hw_value <= 14) && (!sdev->fw_2g_40mhz_enabled) && + if ((ndev_vif->chan->hw_value <= 14) && (!sdev->fw_SoftAp_2g_40mhz_enabled) && (ndev_vif->channel_type > NL80211_CHAN_HT20)) { SLSI_NET_ERR(dev, "Configuration error: 40 MHz on 2.4 GHz is not supported. Channel_no: %d Channel_width: %d\n", ndev_vif->chan->hw_value, slsi_get_chann_info(sdev, ndev_vif->channel_type)); goto exit_with_error; @@ -2057,21 +2056,23 @@ int slsi_start_ap(struct wiphy *wiphy, struct net_device *dev, #ifdef CONFIG_SCSC_WLAN_WIFI_SHARING ndev_sta_vif = netdev_priv(wlan_dev); if (SLSI_IS_VIF_INDEX_MHS(sdev, ndev_vif)) { - SLSI_MUTEX_LOCK(ndev_sta_vif->vif_mutex); - if ((ndev_sta_vif->activated) && (ndev_sta_vif->vif_type == FAPI_VIFTYPE_STATION) && - (ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTING || - ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED)) { - invalid_channel = slsi_select_wifi_sharing_ap_channel(wiphy, dev, settings, sdev, - &wifi_sharing_channel_switched); - skip_indoor_check_for_wifi_sharing = 1; - if (invalid_channel) { - SLSI_NET_ERR(dev, "Rejecting AP start req at host (invalid channel)\n"); - SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex); - r = -EINVAL; - goto exit_with_vif_mutex; + if (ndev_sta_vif) { + SLSI_MUTEX_LOCK(ndev_sta_vif->vif_mutex); + if ((ndev_sta_vif->activated) && (ndev_sta_vif->vif_type == FAPI_VIFTYPE_STATION) && + (ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTING || + ndev_sta_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED)) { + invalid_channel = slsi_select_wifi_sharing_ap_channel(wiphy, dev, settings, sdev, + &wifi_sharing_channel_switched); + skip_indoor_check_for_wifi_sharing = 1; + if (invalid_channel) { + SLSI_NET_ERR(dev, "Rejecting AP start req at host (invalid channel)\n"); + SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex); + r = -EINVAL; + goto exit_with_vif_mutex; + } } + SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex); } - SLSI_MUTEX_UNLOCK(ndev_sta_vif->vif_mutex); } #endif @@ -2137,8 +2138,11 @@ int slsi_start_ap(struct wiphy *wiphy, struct net_device *dev, goto exit_with_vif_mutex; if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) { + struct net_device *p2p_dev; + slsi_p2p_group_start_remove_unsync_vif(sdev); - SLSI_ETHER_COPY(device_address, dev->dev_addr); + p2p_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2P); + SLSI_ETHER_COPY(device_address, p2p_dev->dev_addr); if (keep_alive_period != SLSI_P2PGO_KEEP_ALIVE_PERIOD_SEC) if (slsi_set_uint_mib(sdev, NULL, SLSI_PSID_UNIFI_MLMEGO_KEEP_ALIVE_TIMEOUT, keep_alive_period) != 0) { @@ -2184,7 +2188,7 @@ int slsi_start_ap(struct wiphy *wiphy, struct net_device *dev, } else if (sdev->fw_ht_enabled && sdev->allow_switch_40_mhz && slsi_get_max_bw_mhz(sdev, ndev_vif->chandef->chan->center_freq) >= 40 && ((ndev_vif->chandef->chan->hw_value < 165 && ndev_vif->chandef->chan->hw_value >= 36) || - (ndev_vif->chandef->chan->hw_value < 12 && sdev->fw_2g_40mhz_enabled && + (ndev_vif->chandef->chan->hw_value < 12 && sdev->fw_SoftAp_2g_40mhz_enabled && ndev_vif->iftype == NL80211_IFTYPE_P2P_GO))) { /* HT40 configuration (5GHz/2GHz and HT) */ u16 oper_chan = ndev_vif->chandef->chan->hw_value; @@ -3242,7 +3246,12 @@ static const u32 slsi_cipher_suites[] = { WLAN_CIPHER_SUITE_CCMP, WLAN_CIPHER_SUITE_AES_CMAC, WLAN_CIPHER_SUITE_SMS4, - WLAN_CIPHER_SUITE_PMK + WLAN_CIPHER_SUITE_PMK, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, + WLAN_CIPHER_SUITE_CCMP_256, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256 }; static const struct ieee80211_txrx_stypes @@ -3480,6 +3489,7 @@ struct slsi_dev *slsi_cfg80211_new(struct device *dev) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) #ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; #endif #endif #ifdef CONFIG_SCSC_WLAN_SAE_CONFIG diff --git a/drivers/net/wireless/scsc/cm_if.c b/drivers/net/wireless/scsc/cm_if.c index 43fc3de3ed25..38c8dc46c817 100755 --- a/drivers/net/wireless/scsc/cm_if.c +++ b/drivers/net/wireless/scsc/cm_if.c @@ -665,12 +665,22 @@ void slsi_sm_wlan_service_close(struct slsi_dev *sdev) mutex_lock(&slsi_start_mutex); cm_if_state = atomic_read(&sdev->cm_if.cm_if_state); if (cm_if_state != SCSC_WIFI_CM_IF_STATE_STOPPED) { - SLSI_INFO(sdev, "Service not stopped\n"); + SLSI_INFO(sdev, "Service not stopped. cm_if_state = %d\n", cm_if_state); + + /** + * Close the service if failure has occurred after service has successfully opened + * but before service has attempted to start + */ + if (cm_if_state == SCSC_WIFI_CM_IF_STATE_PROBED) { + SLSI_INFO_NODEV("Closing WLAN service on error\n"); + r = scsc_mx_service_close(sdev->service); + } goto exit; } SLSI_INFO_NODEV("Closing WLAN service\n"); scsc_mx_service_mifram_free(sdev->service, sdev->hip4_inst.hip_ref); + r = scsc_mx_service_close(sdev->service); if (r == -EIO) { int retry_counter; diff --git a/drivers/net/wireless/scsc/dev.h b/drivers/net/wireless/scsc/dev.h index 45cb73563f38..3ccd3b5b6379 100755 --- a/drivers/net/wireless/scsc/dev.h +++ b/drivers/net/wireless/scsc/dev.h @@ -573,6 +573,8 @@ struct slsi_vif_sta { struct slsi_wmm_ac wmm_ac[4]; bool nd_offload_enabled; + unsigned long data_rate_mbps; + unsigned long max_rate_mbps; /*This structure is used to store last disconnected bss info and valid even when vif is deactivated. */ struct slsi_last_connected_bss last_connected_bss; @@ -580,6 +582,8 @@ struct slsi_vif_sta { /* Variable to indicate if roamed_ind needs to be dropped in driver, to maintain roam synchronization. */ atomic_t drop_roamed_ind; + u8 *vendor_disconnect_ies; + int vendor_disconnect_ies_len; }; struct slsi_vif_unsync { @@ -1135,7 +1139,7 @@ struct slsi_dev { int wifi_sharing_5g_restricted_channels[25]; int num_5g_restricted_channels; #endif - bool fw_2g_40mhz_enabled; + bool fw_SoftAp_2g_40mhz_enabled; bool nan_enabled; u16 assoc_result_code; /* Status of latest association in STA mode */ bool allow_switch_40_mhz; /* Used in AP cert to disable HT40 when not configured */ diff --git a/drivers/net/wireless/scsc/fw_test.c b/drivers/net/wireless/scsc/fw_test.c index ea99e0e0b51a..31af6c81f2c1 100755 --- a/drivers/net/wireless/scsc/fw_test.c +++ b/drivers/net/wireless/scsc/fw_test.c @@ -1,6 +1,6 @@ /***************************************************************************** * - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved * *****************************************************************************/ #include @@ -581,8 +581,11 @@ static void slsi_fw_test_disconnect_station(struct slsi_dev *sdev, struct net_de return; netif_carrier_off(dev); - if (peer) + if (peer) { + slsi_spinlock_lock(&ndev_vif->peer_lock); slsi_peer_remove(sdev, dev, peer); + slsi_spinlock_unlock(&ndev_vif->peer_lock); + } slsi_vif_deactivated(sdev, dev); } @@ -603,8 +606,11 @@ static void slsi_fw_test_disconnect_network(struct slsi_dev *sdev, struct net_de SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Network Peer Disconnect(vif:%d)\n", ndev_vif->ifnum); - if (peer) + if (peer) { + slsi_spinlock_lock(&ndev_vif->peer_lock); slsi_peer_remove(sdev, dev, peer); + slsi_spinlock_unlock(&ndev_vif->peer_lock); + } } static void slsi_fw_test_disconnected_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb) @@ -691,6 +697,7 @@ static void slsi_fw_test_tdls_event_disconnected(struct slsi_dev *sdev, struct n SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); SLSI_NET_DBG1(dev, SLSI_MLME, "TDLS dis-connect (vif:%d, mac:%pM)\n", ndev_vif->ifnum, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address)); + slsi_spinlock_lock(&ndev_vif->peer_lock); peer = slsi_get_peer_from_mac(sdev, dev, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address)); if (!peer || (peer->aid == 0)) { WARN_ON(!peer || (peer->aid == 0)); @@ -703,8 +710,8 @@ static void slsi_fw_test_tdls_event_disconnected(struct slsi_dev *sdev, struct n /* move TDLS packets from TDLS Q to STA Q */ slsi_tdls_move_packets(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, false); slsi_peer_remove(sdev, dev, peer); - out: + slsi_spinlock_unlock(&ndev_vif->peer_lock); SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); } diff --git a/drivers/net/wireless/scsc/hip4.c b/drivers/net/wireless/scsc/hip4.c index 6d7a6732b025..05827b9c24cb 100644 --- a/drivers/net/wireless/scsc/hip4.c +++ b/drivers/net/wireless/scsc/hip4.c @@ -1164,6 +1164,7 @@ static void hip4_irq_handler_fb(int irq, void *data) schedule_work(&hip->hip_priv->intr_wq_fb); else queue_work(hip->hip_priv->hip4_workq, &hip->hip_priv->intr_wq_fb); + /* Clear interrupt */ scsc_service_mifintrbit_bit_clear(sdev->service, irq); SCSC_HIP4_SAMPLER_INT_OUT(hip->hip_priv->minor, 2); @@ -1341,6 +1342,7 @@ static void hip4_irq_handler_ctrl(int irq, void *data) schedule_work(&hip->hip_priv->intr_wq_ctrl); else queue_work(hip->hip_priv->hip4_workq, &hip->hip_priv->intr_wq_ctrl); + /* Clear interrupt */ scsc_service_mifintrbit_bit_clear(sdev->service, irq); SCSC_HIP4_SAMPLER_INT_OUT(hip->hip_priv->minor, 1); @@ -1568,6 +1570,7 @@ static void hip4_irq_handler_dat(int irq, void *data) /* Mask interrupt to avoid interrupt storm and let BH run */ scsc_service_mifintrbit_bit_mask(sdev->service, hip->hip_priv->intr_tohost_mul[HIP4_MIF_Q_TH_DAT]); + /* Clear interrupt */ scsc_service_mifintrbit_bit_clear(sdev->service, hip->hip_priv->intr_tohost_mul[HIP4_MIF_Q_TH_DAT]); SCSC_HIP4_SAMPLER_INT_OUT(hip->hip_priv->minor, 0); @@ -1993,9 +1996,6 @@ static void hip4_irq_handler(int irq, void *data) SCSC_HIP4_SAMPLER_INT(hip->hip_priv->minor, 0); SCSC_HIP4_SAMPLER_INT(hip->hip_priv->minor, 1); SCSC_HIP4_SAMPLER_INT(hip->hip_priv->minor, 2); - if (!atomic_read(&hip->hip_priv->rx_ready)) - goto end; - intr_received = ktime_get(); #ifdef CONFIG_ANDROID @@ -2037,7 +2037,7 @@ static void hip4_irq_handler(int irq, void *data) schedule_work(&hip->hip_priv->intr_wq); else queue_work(hip->hip_priv->hip4_workq, &hip->hip_priv->intr_wq); -end: + /* Clear interrupt */ scsc_service_mifintrbit_bit_clear(sdev->service, hip->hip_priv->intr_tohost); SCSC_HIP4_SAMPLER_INT_OUT(hip->hip_priv->minor, 0); @@ -2207,9 +2207,6 @@ int hip4_init(struct slsi_hip4 *hip) /* Reset Sample q values sending 0xff */ SCSC_HIP4_SAMPLER_RESET(hip->hip_priv->minor); - /* Set driver is not ready to receive interrupts */ - atomic_set(&hip->hip_priv->rx_ready, 0); - /***** VERSION 4 *******/ /* TOHOST Handler allocator */ #ifdef CONFIG_SCSC_WLAN_RX_NAPI @@ -2652,9 +2649,6 @@ int hip4_setup(struct slsi_hip4 *hip) atomic_set(&hip->hip_priv->closing, 0); - /* Driver is ready to process IRQ */ - atomic_set(&hip->hip_priv->rx_ready, 1); - #ifdef CONFIG_SCSC_WLAN_RX_NAPI if (conf_hip4_ver == 4) { scsc_service_mifintrbit_bit_unmask(service, hip->hip_priv->intr_tohost_mul[HIP4_MIF_Q_FH_RFB]); @@ -2794,7 +2788,6 @@ void hip4_freeze(struct slsi_hip4 *hip) #endif flush_workqueue(hip->hip_priv->hip4_workq); destroy_workqueue(hip->hip_priv->hip4_workq); - atomic_set(&hip->hip_priv->rx_ready, 0); atomic_set(&hip->hip_priv->watchdog_timer_active, 0); /* Deactive the wd timer prior its expiration */ @@ -2830,14 +2823,6 @@ void hip4_deinit(struct slsi_hip4 *hip) hip4_smapper_is_enabled = false; hip4_smapper_deinit(sdev, hip); } -#endif -#ifdef CONFIG_ANDROID -#ifdef CONFIG_SCSC_WLAN_RX_NAPI - wake_lock_destroy(&hip->hip_priv->hip4_wake_lock_tx); - wake_lock_destroy(&hip->hip_priv->hip4_wake_lock_ctrl); - wake_lock_destroy(&hip->hip_priv->hip4_wake_lock_data); -#endif - wake_lock_destroy(&hip->hip_priv->hip4_wake_lock); #endif closing = ktime_get(); atomic_set(&hip->hip_priv->closing, 1); @@ -2871,11 +2856,19 @@ void hip4_deinit(struct slsi_hip4 *hip) scsc_service_mifintrbit_free_fromhost(service, hip->hip_priv->intr_fromhost, SCSC_MIFINTR_TARGET_R4); +#ifdef CONFIG_ANDROID +#ifdef CONFIG_SCSC_WLAN_RX_NAPI + wake_lock_destroy(&hip->hip_priv->hip4_wake_lock_tx); + wake_lock_destroy(&hip->hip_priv->hip4_wake_lock_ctrl); + wake_lock_destroy(&hip->hip_priv->hip4_wake_lock_data); +#endif + wake_lock_destroy(&hip->hip_priv->hip4_wake_lock); +#endif + /* If we get to that point with rx_lock/tx_lock claimed, trigger BUG() */ WARN_ON(atomic_read(&hip->hip_priv->in_tx)); WARN_ON(atomic_read(&hip->hip_priv->in_rx)); - atomic_set(&hip->hip_priv->rx_ready, 0); atomic_set(&hip->hip_priv->watchdog_timer_active, 0); /* Deactive the wd timer prior its expiration */ del_timer_sync(&hip->hip_priv->watchdog); diff --git a/drivers/net/wireless/scsc/hip4.h b/drivers/net/wireless/scsc/hip4.h index 65f3bcf2f034..bed0b1fd646f 100755 --- a/drivers/net/wireless/scsc/hip4.h +++ b/drivers/net/wireless/scsc/hip4.h @@ -293,9 +293,6 @@ struct hip4_priv { struct wake_lock hip4_wake_lock; #endif - /* Control the hip4 init */ - atomic_t rx_ready; - /* Control the hip4 deinit */ atomic_t closing; atomic_t in_tx; diff --git a/drivers/net/wireless/scsc/hip4_sampler.c b/drivers/net/wireless/scsc/hip4_sampler.c index 791950f2e6ec..cfe215b691c7 100755 --- a/drivers/net/wireless/scsc/hip4_sampler.c +++ b/drivers/net/wireless/scsc/hip4_sampler.c @@ -373,8 +373,8 @@ void hip4_sampler_tput_monitor(void *client_ctx, u32 state, u32 tput_tx, u32 tpu if ((g_tput_tx == tput_tx) && (g_tput_rx == tput_rx)) return; - g_tput_rx == tput_tx; - g_tput_rx == tput_rx; + g_tput_tx = tput_tx; + g_tput_rx = tput_rx; if (hip4_sampler_dynamic) { /* Call the dynamic switcher with the computed bps diff --git a/drivers/net/wireless/scsc/ioctl.c b/drivers/net/wireless/scsc/ioctl.c index 6e75aac7b78a..b282968a5a88 100755 --- a/drivers/net/wireless/scsc/ioctl.c +++ b/drivers/net/wireless/scsc/ioctl.c @@ -78,6 +78,7 @@ #define CMD_SET_LATENCY_MODE "SET_LATENCY_MODE" #define CMD_SET_POWER_MGMT "SET_POWER_MGMT" #endif +#define CMD_SET_DISCONNECT_IES "SET_DISCONNECT_IES" #define CMD_SETBAND "SETBAND" #define CMD_GETBAND "GETBAND" @@ -132,6 +133,7 @@ #ifdef SCSC_WLAN_ABNORMAL_MULTICAST_PKT_FILTER #define CMD_ABNORMAL_MULTICAST_PKT_FILTER "ABNORMAL_MULTICAST_PKT_FILTER" #endif +#define CMD_GET_MAX_LINK_SPEED "GET_MAX_LINK_SPEED" #ifdef CONFIG_SCSC_WLAN_SET_NUM_ANTENNAS #define CMD_SET_NUM_ANTENNAS "SET_NUM_ANTENNAS" @@ -1367,7 +1369,7 @@ static ssize_t slsi_auto_chan_read(struct net_device *dev, char *command, int bu return result; } -static ssize_t slsi_auto_chan_write(struct net_device *dev, char *command, int buf_len) +static ssize_t slsi_auto_chan_write(struct net_device *dev, char *command) { struct netdev_vif *ndev_vif = netdev_priv(dev); struct slsi_dev *sdev = ndev_vif->sdev; @@ -1376,13 +1378,14 @@ static ssize_t slsi_auto_chan_write(struct net_device *dev, char *command, int b int count_channels; int offset; int chan; + int index = 0; #ifdef CONFIG_SCSC_WLAN_WIFI_SHARING struct net_device *sta_dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN); struct netdev_vif *ndev_sta_vif = netdev_priv(sta_dev); int sta_frequency; #endif - offset = slsi_str_to_int(&command[21], &n_channels); + offset = slsi_str_to_int(&command[index], &n_channels); if (!offset) { SLSI_ERR(sdev, "channel count: failed to read a numeric value"); return -EINVAL; @@ -2310,32 +2313,47 @@ static int slsi_get_bss_info(struct net_device *dev, char *command, int buf_len) return len; } -static int slsi_get_assoc_reject_info(struct net_device *dev, char *command, int buf_len) +static int slsi_get_linkspeed(struct net_device *dev, char *command, int buf_len) { struct netdev_vif *ndev_vif = netdev_priv(dev); struct slsi_dev *sdev = ndev_vif->sdev; int len = 0; - len = snprintf(command, buf_len, "assoc_reject.status : %d %s", sdev->assoc_result_code, - slsi_get_assoc_status(sdev->assoc_result_code)); + SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); + if (!ndev_vif->activated) { + SLSI_ERR(sdev, "Not Activated, Command not allowed vif.activated:%d, vif.type:%d\n", + ndev_vif->activated, ndev_vif->vif_type); + SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); + return -EINVAL; + } + + if ((ndev_vif->vif_type != FAPI_VIFTYPE_STATION) && (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)) { + SLSI_NET_ERR(dev, "sta is not in connected state\n"); + SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); + return -EPERM; + } + + len = snprintf(command, buf_len, "MAX_SPEED %u CURRENT_LINK_SPEED %u", + ndev_vif->sta.max_rate_mbps, ndev_vif->sta.data_rate_mbps); + + SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); return len; } -#ifdef CONFIG_SCSC_WLAN_LOW_LATENCY_MODE -int slsi_set_latency_mode(struct net_device *dev, char *cmd, int cmd_len) +static int slsi_get_assoc_reject_info(struct net_device *dev, char *command, int buf_len) { struct netdev_vif *ndev_vif = netdev_priv(dev); struct slsi_dev *sdev = ndev_vif->sdev; - bool enable_roaming; + int len = 0; - /* latency_mode =0 (Normal), latency_mode =1 (Low) */ - enable_roaming = (cmd[0] == '0') ? true : false; - SLSI_DBG1(sdev, SLSI_CFG80211, "Setting latency mode %d\n", cmd[0] - '0'); + len = snprintf(command, buf_len, "assoc_reject.status : %d %s", sdev->assoc_result_code, + slsi_get_assoc_status(sdev->assoc_result_code)); - return slsi_set_mib_soft_roaming_enabled(sdev, dev, enable_roaming); + return len; } +#ifdef CONFIG_SCSC_WLAN_LOW_LATENCY_MODE static int slsi_set_power_mode(struct net_device *dev, char *command, int buf_len) { struct netdev_vif *ndev_vif = netdev_priv(dev); @@ -2359,6 +2377,65 @@ static int slsi_set_power_mode(struct net_device *dev, char *command, int buf_le } #endif +int slsi_set_disconnect_ies(struct net_device *dev, char *cmd, int cmd_len) +{ + struct netdev_vif *ndev_vif = netdev_priv(dev); + struct slsi_dev *sdev = ndev_vif->sdev; + char *disconnect_ies = cmd + strlen(CMD_SET_DISCONNECT_IES) + 1; + int ie_len = 0; + u8 *disconnect_ies_bin; + u8 temp_byte; + int i; + int j; + int len; + + SLSI_DBG1(sdev, SLSI_CFG80211, "Setting disconnect IE's\n"); + while (disconnect_ies[ie_len]) + ie_len++; + + /* ie_len has been trimmed to even, as odd length would mean that ie is invalid */ + ie_len &= (~0x01); + SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); + ndev_vif->sta.vendor_disconnect_ies_len = (ie_len / 2); + disconnect_ies_bin = kmalloc(ndev_vif->sta.vendor_disconnect_ies_len, GFP_KERNEL); + if (!disconnect_ies_bin) { + SLSI_ERR(sdev, "Malloc failed\n"); + SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); + return -ENOMEM; + } + + for (i = 0, j = 0; j < ie_len; j += 2) { + temp_byte = slsi_parse_hex(disconnect_ies[j]) << 4 | slsi_parse_hex(disconnect_ies[j + 1]); + disconnect_ies_bin[i++] = temp_byte; + } + + /* check if the IE is valid */ + for (i = 0; i < ndev_vif->sta.vendor_disconnect_ies_len;) { + if (disconnect_ies_bin[i] == 0xdd) { + len = disconnect_ies_bin[i + 1]; + if ((ndev_vif->sta.vendor_disconnect_ies_len - (i + 2)) < len) { + SLSI_WARN(sdev, "The length of disconnect IE's is not proper\n"); + ndev_vif->sta.vendor_disconnect_ies_len = 0; + kfree(disconnect_ies_bin); + SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); + return -EINVAL; + } + + i = i + 2 + len; + } else { + SLSI_WARN(sdev, "The tag of disconnect IE's is not proper\n"); + ndev_vif->sta.vendor_disconnect_ies_len = 0; + kfree(disconnect_ies_bin); + SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); + return -EINVAL; + } + } + ndev_vif->sta.vendor_disconnect_ies = disconnect_ies_bin; + SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); + + return 0; +} + #ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT static int slsi_enhanced_arp_start_stop(struct net_device *dev, char *command, int buf_len) { @@ -2627,7 +2704,9 @@ int slsi_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } else if (strncasecmp(command, CMD_HAPD_GET_CHANNEL, strlen(CMD_HAPD_GET_CHANNEL)) == 0) { ret = slsi_auto_chan_read(dev, command, priv_cmd.total_len); } else if (strncasecmp(command, CMD_SET_SAP_CHANNEL_LIST, strlen(CMD_SET_SAP_CHANNEL_LIST)) == 0) { - ret = slsi_auto_chan_write(dev, command, priv_cmd.total_len); + int skip = strlen(CMD_SET_SAP_CHANNEL_LIST) + 1; + + ret = slsi_auto_chan_write(dev, command + skip); } else if (strncasecmp(command, CMD_REASSOC, strlen(CMD_REASSOC)) == 0) { int skip = strlen(CMD_REASSOC) + 1; @@ -2704,12 +2783,16 @@ int slsi_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ret = slsi_get_assoc_reject_info(dev, command, priv_cmd.total_len); #ifdef CONFIG_SCSC_WLAN_LOW_LATENCY_MODE } else if (strncasecmp(command, CMD_SET_LATENCY_MODE, strlen(CMD_SET_LATENCY_MODE)) == 0) { - ret = slsi_set_latency_mode(dev, command + strlen(CMD_SET_LATENCY_MODE) + 1, - priv_cmd.total_len - (strlen(CMD_SET_LATENCY_MODE) + 1)); + int latency_mode = *(command + strlen(CMD_SET_LATENCY_MODE) + 1) - '0'; + int cmd_len = priv_cmd.total_len - (strlen(CMD_SET_LATENCY_MODE) + 1); + + ret = slsi_set_latency_mode(dev, latency_mode, cmd_len); } else if (strncasecmp(command, CMD_SET_POWER_MGMT, strlen(CMD_SET_POWER_MGMT)) == 0) { ret = slsi_set_power_mode(dev, command + strlen(CMD_SET_POWER_MGMT) + 1, priv_cmd.total_len - (strlen(CMD_SET_POWER_MGMT) + 1)); #endif + } else if (strncasecmp(command, CMD_SET_DISCONNECT_IES, strlen(CMD_SET_DISCONNECT_IES)) == 0) { + ret = slsi_set_disconnect_ies(dev, command, priv_cmd.total_len); #ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT } else if (strncasecmp(command, CMD_SET_ENHANCED_ARP_TARGET, strlen(CMD_SET_ENHANCED_ARP_TARGET)) == 0) { int skip = strlen(CMD_SET_ENHANCED_ARP_TARGET) + 1; @@ -2770,6 +2853,8 @@ int slsi_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ret = slsi_set_num_antennas(dev, num_of_antennas); SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); #endif + } else if ((strncasecmp(command, CMD_GET_MAX_LINK_SPEED, strlen(CMD_GET_MAX_LINK_SPEED)) == 0)) { + ret = slsi_get_linkspeed(dev, command, priv_cmd.total_len); } else { ret = -ENOTSUPP; } diff --git a/drivers/net/wireless/scsc/mgt.c b/drivers/net/wireless/scsc/mgt.c index abf334127ad1..320ff3fb3eee 100755 --- a/drivers/net/wireless/scsc/mgt.c +++ b/drivers/net/wireless/scsc/mgt.c @@ -718,23 +718,27 @@ int slsi_start(struct slsi_dev *sdev) #endif #ifdef CONFIG_SCSC_WLAN_SET_PREFERRED_ANTENNA - /* reading antenna mode from /data/vendor/conn/.ant.info */ - file_ptr = filp_open(ant_file_path, O_RDONLY, 0); - - if (!file_ptr) { - SLSI_DBG1(sdev, SLSI_CFG80211, "%s doesn't exist\n", ant_file_path); - } else if (IS_ERR(file_ptr)) { - SLSI_DBG1(sdev, SLSI_CFG80211, "%s open returned error %d\n", ant_file_path, IS_ERR(file_ptr)); - } else { + if (slsi_is_rf_test_mode_enabled()) { + /* reading antenna mode from /data/vendor/conn/.ant.info */ + file_ptr = filp_open(ant_file_path, O_RDONLY, 0); + /* if file is not found, set the default antenna value to 3(ANT_ALL) */ + if (!file_ptr) { + SLSI_DBG1(sdev, SLSI_CFG80211, "%s doesn't exist\n", ant_file_path); + slsi_set_mib_preferred_antenna(sdev, 3); + } else if (IS_ERR(file_ptr)) { + SLSI_DBG1(sdev, SLSI_CFG80211, "%s open returned error %d\n", ant_file_path, IS_ERR(file_ptr)); + slsi_set_mib_preferred_antenna(sdev, 3); + } else { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) - kernel_read(file_ptr, &ant_mode, 1, &file_ptr->f_pos); + kernel_read(file_ptr, &ant_mode, 1, &file_ptr->f_pos); #else - kernel_read(file_ptr, file_ptr->f_pos, &ant_mode, 1); + kernel_read(file_ptr, file_ptr->f_pos, &ant_mode, 1); #endif - antenna = ant_mode - '0'; - filp_close(file_ptr, NULL); + antenna = ant_mode - '0'; + filp_close(file_ptr, NULL); - slsi_set_mib_preferred_antenna(sdev, antenna); + slsi_set_mib_preferred_antenna(sdev, antenna); + } } #endif @@ -889,7 +893,7 @@ void slsi_vif_cleanup(struct slsi_dev *sdev, struct net_device *dev, bool hw_ava if (hw_available) { if (ndev_vif->sta.sta_bss) { slsi_mlme_disconnect(sdev, dev, ndev_vif->sta.sta_bss->bssid, FAPI_REASONCODE_UNSPECIFIED_REASON, true); - slsi_handle_disconnect(sdev, dev, ndev_vif->sta.sta_bss->bssid, 0); + slsi_handle_disconnect(sdev, dev, ndev_vif->sta.sta_bss->bssid, 0, NULL, 0); already_disconnected = true; } else { slsi_mlme_del_vif(sdev, dev); @@ -1389,18 +1393,15 @@ static int slsi_mib_initial_get(struct slsi_dev *sdev) #ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT { SLSI_PSID_UNIFI_ARP_DETECT_ACTIVATED, {0, 0} }, #endif - { SLSI_PSID_UNIFI_APF_ENABLED, {0, 0} } + { SLSI_PSID_UNIFI_APF_ENABLED, {0, 0} }, + { SLSI_PSID_UNIFI_SOFT_AP40_MHZ_ON24G, {0, 0} } };/*Check the mibrsp.dataLength when a new mib is added*/ - /* 40 MHz bandwidth is not supported in 2.4 GHz in AP/GO Mode Currently. - * Reading the mib to check the support is removed and is initialized to 0. - */ - sdev->fw_2g_40mhz_enabled = 0; r = slsi_mib_encode_get_list(&mibreq, sizeof(get_values) / sizeof(struct slsi_mib_get_entry), get_values); if (r != SLSI_MIB_STATUS_SUCCESS) return -ENOMEM; - mibrsp.dataLength = 184; + mibrsp.dataLength = 194; mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL); if (!mibrsp.data) { kfree(mibreq.data); @@ -1580,6 +1581,11 @@ static int slsi_mib_initial_get(struct slsi_dev *sdev) else SLSI_WARN(sdev, "Error reading APF Support mib\n"); + if (values[++mib_index].type != SLSI_MIB_TYPE_NONE) /* 40MHz for Soft AP */ + sdev->fw_SoftAp_2g_40mhz_enabled = values[mib_index].u.boolValue; + else + SLSI_WARN(sdev, "Error reading 40MHz for Soft AP\n"); + kfree(values); } kfree(mibrsp.data); @@ -2287,11 +2293,6 @@ int slsi_peer_remove(struct slsi_dev *sdev, struct net_device *dev, struct slsi_ slsi_rx_ba_stop_all(dev, peer); - /* Take the peer lock to protect the transmit data path - * when accessing peer records. - */ - slsi_spinlock_lock(&ndev_vif->peer_lock); - /* The information is no longer valid so first update the flag to ensure that * another process doesn't try to use it any more. */ @@ -2304,8 +2305,6 @@ int slsi_peer_remove(struct slsi_dev *sdev, struct net_device *dev, struct slsi_ else ndev_vif->peer_sta_records--; - slsi_spinlock_unlock(&ndev_vif->peer_lock); - ndev_vif->cfg80211_sinfo_generation++; scsc_wifi_fcq_qset_deinit(dev, &peer->data_qs, sdev, ndev_vif->ifnum, peer); @@ -2398,7 +2397,10 @@ void slsi_vif_deactivated(struct slsi_dev *sdev, struct net_device *dev) if (peer && peer->valid) { if (ndev_vif->vif_type == FAPI_VIFTYPE_AP && peer->assoc_ie) cfg80211_del_sta(dev, peer->address, GFP_KERNEL); + + slsi_spinlock_lock(&ndev_vif->peer_lock); slsi_peer_remove(sdev, dev, peer); + slsi_spinlock_unlock(&ndev_vif->peer_lock); } } @@ -2441,7 +2443,7 @@ void slsi_vif_deactivated(struct slsi_dev *sdev, struct net_device *dev) sdev->device_config.qos_info = -1; } -static int slsi_sta_ieee80211_mode(struct net_device *dev, u16 current_bss_channel_frequency) +int slsi_sta_ieee80211_mode(struct net_device *dev, u16 current_bss_channel_frequency) { struct netdev_vif *ndev_vif = netdev_priv(dev); const u8 *ie; @@ -2601,8 +2603,8 @@ int slsi_populate_bss_record(struct net_device *dev) if (values[7].type != SLSI_MIB_TYPE_NONE) { /* TX_DATA_RATE */ SLSI_CHECK_TYPE(sdev, values[7].type, SLSI_MIB_TYPE_UINT); fw_tx_rate = values[7].u.uintValue; - slsi_fw_tx_rate_calc(fw_tx_rate, NULL, - (unsigned long *)(&ndev_vif->sta.last_connected_bss.tx_data_rate)); + slsi_decode_fw_rate(fw_tx_rate, NULL, + (unsigned long *)(&ndev_vif->sta.last_connected_bss.tx_data_rate)); } if (values[8].type != SLSI_MIB_TYPE_NONE) { /* ROAMING_AKM */ @@ -2767,8 +2769,8 @@ static int slsi_fill_last_disconnected_sta_info(struct slsi_dev *sdev, struct ne if (values[3].type != SLSI_MIB_TYPE_NONE) { /* LAST_PEER_TX_DATA_RATE */ SLSI_CHECK_TYPE(sdev, values[3].type, SLSI_MIB_TYPE_UINT); fw_tx_rate = values[3].u.uintValue; - slsi_fw_tx_rate_calc(fw_tx_rate, NULL, - (unsigned long *)&ndev_vif->ap.last_disconnected_sta.tx_data_rate); + slsi_decode_fw_rate(fw_tx_rate, NULL, + (unsigned long *)&ndev_vif->ap.last_disconnected_sta.tx_data_rate); } kfree(values); @@ -2777,7 +2779,8 @@ static int slsi_fill_last_disconnected_sta_info(struct slsi_dev *sdev, struct ne return 0; } -int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *peer_address, u16 reason) +int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *peer_address, u16 reason, + u8 *disassoc_rsp_ie, int disassoc_rsp_ie_len) { struct netdev_vif *ndev_vif = netdev_priv(dev); @@ -2817,9 +2820,11 @@ int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *pe SLSI_NET_INFO(dev, "Ignoring Deauth notification to cfg80211 from the peer during WPS procedure\n"); else { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) - cfg80211_disconnected(dev, reason, NULL, 0, false, GFP_KERNEL); + cfg80211_disconnected(dev, reason, disassoc_rsp_ie, disassoc_rsp_ie_len, + false, GFP_KERNEL); #else - cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); + cfg80211_disconnected(dev, reason, disassoc_rsp_ie, disassoc_rsp_ie_len, + GFP_KERNEL); #endif SLSI_NET_DBG3(dev, SLSI_MLME, "Received disconnect from AP, reason = %d\n", reason); } @@ -2829,17 +2834,17 @@ int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *pe reason == FAPI_REASONCODE_SYNCHRONISATION_LOSS) reason = WLAN_REASON_DEAUTH_LEAVING; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) - cfg80211_disconnected(dev, reason, NULL, 0, true, GFP_KERNEL); + cfg80211_disconnected(dev, reason, disassoc_rsp_ie, disassoc_rsp_ie_len, true, GFP_KERNEL); #else - cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); + cfg80211_disconnected(dev, reason, disassoc_rsp_ie, disassoc_rsp_ie_len, GFP_KERNEL); #endif SLSI_NET_DBG3(dev, SLSI_MLME, "Completion of disconnect from AP\n"); } else { /* Vif status is in erronus state.*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) - cfg80211_disconnected(dev, reason, NULL, 0, false, GFP_KERNEL); + cfg80211_disconnected(dev, reason, disassoc_rsp_ie, disassoc_rsp_ie_len, false, GFP_KERNEL); #else - cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); + cfg80211_disconnected(dev, reason, disassoc_rsp_ie, disassoc_rsp_ie_len, GFP_KERNEL); #endif SLSI_NET_WARN(dev, "disconnect in wrong state vif_status(%d)\n", ndev_vif->sta.vif_status); } @@ -2860,6 +2865,7 @@ int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *pe } #ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT memset(&ndev_vif->enhanced_arp_stats, 0, sizeof(ndev_vif->enhanced_arp_stats)); + ndev_vif->enhanced_arp_detect_enabled = false; #endif slsi_mlme_del_vif(sdev, dev); slsi_vif_deactivated(sdev, dev); @@ -2881,7 +2887,9 @@ int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *pe if ((peer->connected_state == SLSI_STA_CONN_STATE_CONNECTED) || (peer->connected_state == SLSI_STA_CONN_STATE_DOING_KEY_CONFIG)) cfg80211_del_sta(dev, peer->address, GFP_KERNEL); + slsi_spinlock_lock(&ndev_vif->peer_lock); slsi_peer_remove(sdev, dev, peer); + slsi_spinlock_unlock(&ndev_vif->peer_lock); /* If last client disconnects (after WPA2 handshake) then take wakelock till group is removed * to avoid possibility of delay in group removal if platform suspends at this point. @@ -3102,7 +3110,7 @@ int slsi_band_update(struct slsi_dev *sdev, int band) r = slsi_mlme_disconnect(sdev, dev, ndev_vif->sta.sta_bss->bssid, WLAN_REASON_DEAUTH_LEAVING, true); LOG_CONDITIONALLY(r != 0, SLSI_ERR(sdev, "slsi_mlme_disconnect(%pM) failed with %d\n", ndev_vif->sta.sta_bss->bssid, r)); - r = slsi_handle_disconnect(sdev, dev, ndev_vif->sta.sta_bss->bssid, 0); + r = slsi_handle_disconnect(sdev, dev, ndev_vif->sta.sta_bss->bssid, 0, NULL, 0); LOG_CONDITIONALLY(r != 0, SLSI_ERR(sdev, "slsi_handle_disconnect(%pM) failed with %d\n", ndev_vif->sta.sta_bss->bssid, r)); } SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); @@ -3749,57 +3757,60 @@ void slsi_set_packet_filters(struct slsi_dev *sdev, struct net_device *dev) if (ie) { SLSI_NET_DBG1(dev, SLSI_CFG80211, "Connected to HS2 AP "); - if (slsi_is_proxy_arp_supported_on_ap(peer->assoc_resp_ie)) { - SLSI_NET_DBG1(dev, SLSI_CFG80211, "Proxy ARP service supported on HS2 AP "); + if (slsi_is_proxy_arp_supported_on_ap(peer->assoc_resp_ie)) { + SLSI_NET_DBG1(dev, SLSI_CFG80211, "Proxy ARP service supported on HS2 AP "); - /* Opt out Gratuitous ARP packets (ARP Announcement) in active and suspended mode. - * For suspended mode, gratituous ARP is dropped by "opt out all broadcast" that will be - * set in slsi_set_common_packet_filters on screen off - */ - num_pattern_desc = 0; - pattern_desc[num_pattern_desc].offset = 0; /*filtering on MAC destination Address*/ - pattern_desc[num_pattern_desc].mask_length = ETH_ALEN; - SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].mask, addr_mask); - SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].pattern, addr_mask); - num_pattern_desc++; + /* Opt out Gratuitous ARP packets (ARP Announcement) in active and suspended mode. + * For suspended mode, gratituous ARP is dropped by "opt out all broadcast" that will be + * set in slsi_set_common_packet_filters on screen off + */ + num_pattern_desc = 0; + pattern_desc[num_pattern_desc].offset = 0; /*filtering on MAC destination Address*/ + pattern_desc[num_pattern_desc].mask_length = ETH_ALEN; + SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].mask, addr_mask); + SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].pattern, addr_mask); + num_pattern_desc++; - SET_ETHERTYPE_PATTERN_DESC(pattern_desc[num_pattern_desc], ETH_P_ARP); - num_pattern_desc++; + SET_ETHERTYPE_PATTERN_DESC(pattern_desc[num_pattern_desc], ETH_P_ARP); + num_pattern_desc++; - slsi_create_packet_filter_element(SLSI_PROXY_ARP_FILTER_ID, FAPI_PACKETFILTERMODE_OPT_OUT, - num_pattern_desc, pattern_desc, &pkt_filter_elem[num_filters], &pkt_filters_len); - num_filters++; + slsi_create_packet_filter_element(SLSI_PROXY_ARP_FILTER_ID, FAPI_PACKETFILTERMODE_OPT_OUT, + num_pattern_desc, pattern_desc, &pkt_filter_elem[num_filters], + &pkt_filters_len); + num_filters++; #ifndef CONFIG_SCSC_WLAN_BLOCK_IPV6 - /* Opt out unsolicited Neighbor Advertisement packets .For suspended mode, NA is dropped by - * "opt out all IPv6 multicast" already set in slsi_create_common_packet_filters - */ - num_pattern_desc = 0; - - pattern_desc[num_pattern_desc].offset = 0; /*filtering on MAC destination Address*/ - pattern_desc[num_pattern_desc].mask_length = ETH_ALEN; - SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].mask, addr_mask); - SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].pattern, solicited_node_addr_mask); - num_pattern_desc++; - - SET_ETHERTYPE_PATTERN_DESC(pattern_desc[num_pattern_desc], 0x86DD); - num_pattern_desc++; - - pattern_desc[num_pattern_desc].offset = 0x14; /*filtering on next header*/ - pattern_desc[num_pattern_desc].mask_length = 1; - pattern_desc[num_pattern_desc].mask[0] = 0xff; - pattern_desc[num_pattern_desc].pattern[0] = 0x3a; - num_pattern_desc++; - - pattern_desc[num_pattern_desc].offset = 0x36; /*filtering on ICMP6 packet type*/ - pattern_desc[num_pattern_desc].mask_length = 1; - pattern_desc[num_pattern_desc].mask[0] = 0xff; - pattern_desc[num_pattern_desc].pattern[0] = 0x88; /* Neighbor Advertisement type in ICMPv6 */ - num_pattern_desc++; + /* Opt out unsolicited Neighbor Advertisement packets .For suspended mode, NA is dropped by + * "opt out all IPv6 multicast" already set in slsi_create_common_packet_filters + */ - slsi_create_packet_filter_element(SLSI_PROXY_ARP_NA_FILTER_ID, FAPI_PACKETFILTERMODE_OPT_OUT, - num_pattern_desc, pattern_desc, &pkt_filter_elem[num_filters], &pkt_filters_len); - num_filters++; + num_pattern_desc = 0; + + pattern_desc[num_pattern_desc].offset = 0; /*filtering on MAC destination Address*/ + pattern_desc[num_pattern_desc].mask_length = ETH_ALEN; + SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].mask, addr_mask); + SLSI_ETHER_COPY(pattern_desc[num_pattern_desc].pattern, solicited_node_addr_mask); + num_pattern_desc++; + + SET_ETHERTYPE_PATTERN_DESC(pattern_desc[num_pattern_desc], 0x86DD); + num_pattern_desc++; + + pattern_desc[num_pattern_desc].offset = 0x14; /*filtering on next header*/ + pattern_desc[num_pattern_desc].mask_length = 1; + pattern_desc[num_pattern_desc].mask[0] = 0xff; + pattern_desc[num_pattern_desc].pattern[0] = 0x3a; + num_pattern_desc++; + + pattern_desc[num_pattern_desc].offset = 0x36; /*filtering on ICMP6 packet type*/ + pattern_desc[num_pattern_desc].mask_length = 1; + pattern_desc[num_pattern_desc].mask[0] = 0xff; + pattern_desc[num_pattern_desc].pattern[0] = 0x88; /* Neighbor Advertisement type in ICMPv6 */ + num_pattern_desc++; + + slsi_create_packet_filter_element(SLSI_PROXY_ARP_NA_FILTER_ID, FAPI_PACKETFILTERMODE_OPT_OUT, + num_pattern_desc, pattern_desc, &pkt_filter_elem[num_filters], + &pkt_filters_len); + num_filters++; #endif } } @@ -5403,6 +5414,10 @@ int slsi_select_wifi_sharing_ap_channel(struct wiphy *wiphy, struct net_device * *wifi_sharing_channel_switched = 1; settings->chandef.chan = ieee80211_get_channel(wiphy, sta_frequency); settings->chandef.center_freq1 = sta_frequency; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) + if (!sdev->fw_SoftAp_2g_40mhz_enabled && (settings->chandef.width == NL80211_CHAN_WIDTH_40)) + settings->chandef.width = NL80211_CHAN_WIDTH_20; +#endif } } #endif @@ -5435,7 +5450,7 @@ int slsi_select_wifi_sharing_ap_channel(struct wiphy *wiphy, struct net_device * settings->chandef.chan = ieee80211_get_channel(wiphy, SLSI_2G_CHANNEL_ONE); settings->chandef.center_freq1 = SLSI_2G_CHANNEL_ONE; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) - if (!sdev->fw_2g_40mhz_enabled && (settings->chandef.width == NL80211_CHAN_WIDTH_40)) + if (!sdev->fw_SoftAp_2g_40mhz_enabled && (settings->chandef.width == NL80211_CHAN_WIDTH_40)) settings->chandef.width = NL80211_CHAN_WIDTH_20; #endif } @@ -5593,6 +5608,13 @@ int slsi_set_mac_randomisation_mask(struct slsi_dev *sdev, u8 *mac_address_mask) SLSI_DBG1(sdev, SLSI_CFG80211, "Mask is :%pM\n", mac_address_mask); r = slsi_mib_encode_octet(&mib_data, SLSI_PSID_UNIFI_MAC_ADDRESS_RANDOMISATION_MASK, ETH_ALEN, mac_address_mask, 0); + if (r != SLSI_MIB_STATUS_SUCCESS) { + return -ENOMEM; + } + + r = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength); + kfree(mib_data.data); + if (r != SLSI_MIB_STATUS_SUCCESS) { SLSI_ERR(sdev, "Err setting unifiMacAddrRandomistaionMask MIB. error = %d\n", r); if (sdev->scan_addr_set) { @@ -5600,30 +5622,20 @@ int slsi_set_mac_randomisation_mask(struct slsi_dev *sdev, u8 *mac_address_mask) r = slsi_mib_encode_bool(&mib_data_randomization_activated, SLSI_PSID_UNIFI_MAC_ADDRESS_RANDOMISATION, 1, 0); - if (r != SLSI_MIB_STATUS_SUCCESS) { - SLSI_ERR(sdev, "UNIFI_MAC_ADDRESS_RANDOMISATION_ACTIVATED: no mem for MIB\n"); + if (r != SLSI_MIB_STATUS_SUCCESS) return -ENOMEM; - } r = slsi_mlme_set(sdev, NULL, mib_data_randomization_activated.data, mib_data_randomization_activated.dataLength); kfree(mib_data_randomization_activated.data); - if (r) + if (r) { SLSI_ERR(sdev, "Err setting unifiMacAddrRandomistaionActivated MIB. error = %d\n", r); return r; } - return -ENOMEM; } - if (mib_data.dataLength == 0) { - SLSI_WARN(sdev, "Mib Data length is Zero\n"); - return -EINVAL; } - r = slsi_mlme_set(sdev, NULL, mib_data.data, mib_data.dataLength); - if (r) - SLSI_ERR(sdev, "Err setting Randomized mac mask= %d\n", r); - kfree(mib_data.data); return r; } #endif @@ -6087,3 +6099,32 @@ int slsi_set_num_antennas(struct net_device *dev, const u16 num_of_antennas) return ret; } #endif + +int slsi_set_latency_mode(struct net_device *dev, int latency_mode, int cmd_len) +{ + struct netdev_vif *ndev_vif = netdev_priv(dev); + struct slsi_dev *sdev = ndev_vif->sdev; + int ret = 0; + u8 host_state; + + SLSI_DBG1(sdev, SLSI_CFG80211, "latency_mode = %d\n", latency_mode); + + SLSI_MUTEX_LOCK(sdev->device_config_mutex); + host_state = sdev->device_config.host_state; + + /* latency_mode =0 (Normal), latency_mode =1 (Low) */ + if (latency_mode) + host_state = host_state | FAPI_HOSTSTATE_LOW_LATENCY_ACTIVE; + else + host_state = host_state & ~FAPI_HOSTSTATE_LOW_LATENCY_ACTIVE; + + ret = slsi_mlme_set_host_state(sdev, dev, host_state); + if (ret != 0) + SLSI_NET_ERR(dev, "Error in setting the Host State, ret=%d", ret); + else + sdev->device_config.host_state = host_state; + + SLSI_MUTEX_UNLOCK(sdev->device_config_mutex); + + return ret; +} diff --git a/drivers/net/wireless/scsc/mgt.h b/drivers/net/wireless/scsc/mgt.h index 98ea8581fde6..b61a5c620896 100755 --- a/drivers/net/wireless/scsc/mgt.h +++ b/drivers/net/wireless/scsc/mgt.h @@ -29,6 +29,7 @@ #define SLSI_IEEE8021X_TYPE_EAP_PACKET 0 #define SLSI_EAPOL_KEY_INFO_KEY_TYPE_BIT_IN_LOWER_BYTE BIT(3) /* Group = 0, Pairwise = 1 */ +#define SLSI_EAPOL_KEY_INFO_ACK_BIT_IN_LOWER_BYTE BIT(7) #define SLSI_EAPOL_KEY_INFO_MIC_BIT_IN_HIGHER_BYTE BIT(0) #define SLSI_EAPOL_KEY_INFO_SECURE_BIT_IN_HIGHER_BYTE BIT(1) /* pkt_data would start from 802.1X Authentication field (pkt_data[0] = Version). @@ -449,9 +450,11 @@ int slsi_del_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac) int slsi_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac); #endif +int slsi_sta_ieee80211_mode(struct net_device *dev, u16 current_bss_channel_frequency); int slsi_vif_activated(struct slsi_dev *sdev, struct net_device *dev); void slsi_vif_deactivated(struct slsi_dev *sdev, struct net_device *dev); -int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *peer_address, u16 reason); +int slsi_handle_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *peer_address, u16 reason, + u8 *disassoc_rsp_ie, int disassoc_rsp_ie_len); int slsi_band_update(struct slsi_dev *sdev, int band); int slsi_ip_address_changed(struct slsi_dev *sdev, struct net_device *dev, __be32 ipaddress); int slsi_send_gratuitous_arp(struct slsi_dev *sdev, struct net_device *dev); @@ -567,4 +570,5 @@ int slsi_find_chan_idx(u16 chan, u8 hw_mode); #ifdef CONFIG_SCSC_WLAN_SET_NUM_ANTENNAS int slsi_set_num_antennas(struct net_device *dev, const u16 num_of_antennas); #endif +int slsi_set_latency_mode(struct net_device *dev, int latency_mode, int cmd_len); #endif /*__SLSI_MGT_H__*/ diff --git a/drivers/net/wireless/scsc/mib.h b/drivers/net/wireless/scsc/mib.h index b86113856775..f2d0e761ce75 100644 --- a/drivers/net/wireless/scsc/mib.h +++ b/drivers/net/wireless/scsc/mib.h @@ -3048,6 +3048,22 @@ void slsi_mib_buf_append(struct slsi_mib_data *dst, size_t bufferLength, u8 *buf *******************************************************************************/ #define SLSI_PSID_UNIFI_MA_PACKET_FATE_ENABLED 0x0911 +/******************************************************************************* + * NAME : unifiStaVifLinkNss + * PSID : 2324 (0x0914) + * PER INTERFACE?: NO + * TYPE : unifiAntennaMode + * MIN : 0 + * MAX : 255 + * DEFAULT : + * DESCRIPTION : + * Sta Vif (Not P2P) while connected to an AP. + * Specifies the max number of NSS that the link can use : 0 = SISO, 1 = MIMO (2x2), 2 = MIMO + * (3x3), 3 = MIMO (4x4) + *******************************************************************************/ +/* TODO : MIB autogen */ +#define SLSI_PSID_UNIFI_STA_VIF_LINK_NSS 0x0914 + /******************************************************************************* * NAME : UnifiFrameRxCounters * PSID : 2326 (0x0916) @@ -3802,6 +3818,80 @@ void slsi_mib_buf_append(struct slsi_mib_data *dst, size_t bufferLength, u8 *buf *******************************************************************************/ #define SLSI_PSID_UNIFI_AP_IDLE_MODE_ENABLED 0x09C1 +/******************************************************************************* + * NAME : UnifiBssMaxIdlePeriod + * PSID : 2488 (0x09B8) + * PER INTERFACE?: NO + * TYPE : SlsiUint16 + * UNITS : second + * MIN : 0 + * MAX : 300 + * DEFAULT : 300 + * DESCRIPTION : + * BSS Idle MAX Period. Used to cap the value coming from BSS Max Idle + * Period IE, in seconds + *******************************************************************************/ +#define SLSI_PSID_UNIFI_BSS_MAX_IDLE_PERIOD 0x09B8 + +/******************************************************************************* + * NAME : UnifiIdlemodeListenIntervalSkippingDtim + * PSID : 2495 (0x09BF) + * PER INTERFACE?: NO + * TYPE : SlsiUint32 + * UNITS : DTIM intervals + * MIN : 0 + * MAX : 4294967295 + * DEFAULT : 0X00054645 + * DESCRIPTION : + * Listen interval of beacons when in single-vif power saving mode, + * receiving DTIMs is enabled and idle mode enabled. No DTIMs are skipped + * during MVIF operation. A maximum of the listen interval beacons are + * skipped, which may be less than the number of DTIMs that can be skipped. + * The value is a lookup table for DTIM counts. Each 4bits, in LSB order, + * represent DTIM1, DTIM2, DTIM3, DTIM4, DTIM5, (unused). This key is only + * used for STA VIF, connected to an AP. For P2P group client intervals, + * refer to unifiIdlemodeP2PListenIntervalSkippingDTIM, PSID=2496. + *******************************************************************************/ +#define SLSI_PSID_UNIFI_IDLEMODE_LISTEN_INTERVAL_SKIPPING_DTIM 0x09BF + +/******************************************************************************* + * NAME : UnifiIdlemodeP2PListenIntervalSkippingDtim + * PSID : 2496 (0x09C0) + * PER INTERFACE?: NO + * TYPE : SlsiUint32 + * UNITS : DTIM intervals + * MIN : 0 + * MAX : 4294967295 + * DEFAULT : 0X00000002 + * DESCRIPTION : + * Listen interval of beacons when in single-vif, P2P client power saving + * mode,receiving DTIMs and idle mode enabled. No DTIMs are skipped during + * MVIF operation. A maximum of (listen interval - 1) beacons are skipped, + * which may be less than the number of DTIMs that can be skipped. The value + * is a lookup table for DTIM counts. Each 4bits, in LSB order, represent + * DTIM1, DTIM2, DTIM3, DTIM4, DTIM5, (unused). This key is only used for + * P2P group client. For STA connected to an AP, refer to + * unifiIdlemodeListenIntervalSkippingDTIM, PSID=2495. + *******************************************************************************/ +#define SLSI_PSID_UNIFI_IDLEMODE_P2_PLISTEN_INTERVAL_SKIPPING_DTIM 0x09C0 + +/******************************************************************************* + * NAME : UnifiApIdleModeEnabled + * PSID : 2497 (0x09C1) + * PER INTERFACE?: NO + * TYPE : SlsiBool + * MIN : 0 + * MAX : 1 + * DEFAULT : FALSE + * DESCRIPTION : + * Enables AP Idle mode which can transmit beacons in MIFLess mode, if + * softAP is active, and there has been no activity for a time. This mib has + * priority over unifiIdleModeLiteEnabled. If unifiAPIdleEnabled is enabled, + * Idle Mode Lite won't be activated. This mib value will be runtime + * (post-wlan enable) applied only if "unifiRameUpdateMibs" mib is toggled + *******************************************************************************/ +#define SLSI_PSID_UNIFI_AP_IDLE_MODE_ENABLED 0x09C1 + /******************************************************************************* * NAME : UnifiFastPowerSaveTimeout * PSID : 2500 (0x09C4) diff --git a/drivers/net/wireless/scsc/mlme.c b/drivers/net/wireless/scsc/mlme.c index 7bba88987145..5d6ef87862d9 100755 --- a/drivers/net/wireless/scsc/mlme.c +++ b/drivers/net/wireless/scsc/mlme.c @@ -1242,6 +1242,22 @@ int slsi_mlme_add_sched_scan(struct slsi_dev *sdev, WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_mutex)); +#ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) + if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + if (sdev->fw_mac_randomization_enabled) { + memcpy(sdev->scan_mac_addr, request->mac_addr, ETH_ALEN); + r = slsi_set_mac_randomisation_mask(sdev, request->mac_addr_mask); + if (!r) + sdev->scan_addr_set = 1; + } else { + SLSI_NET_INFO(dev, "Mac Randomization is not enabled in Firmware\n"); + sdev->scan_addr_set = 0; + } + } +#endif +#endif + alloc_data_size += sizeof(scan_timing_ie) + ies_len + SLSI_SCAN_PRIVATE_IE_CHANNEL_LIST_HEADER_LEN + (request->n_channels * SLSI_SCAN_CHANNEL_DESCRIPTOR_SIZE); @@ -1935,45 +1951,36 @@ static int slsi_mlme_connect_info_elems_ie_prep(struct slsi_dev *sdev, const u8 const u8 *ie_pos = NULL; int info_elem_length = 0; u16 curr_ie_len; + int i = 0; + u8 ie_eid[] = {SLSI_WLAN_EID_INTERWORKING, + SLSI_WLAN_EID_EXTENSION, + WLAN_EID_VENDOR_SPECIFIC}; /*Vendor IE has to be the last element */ if (is_copy && (!ie_dest || ie_dest_len == 0)) return -EINVAL; - /* find interworking ie id:107 */ - ie_pos = cfg80211_find_ie(SLSI_WLAN_EID_INTERWORKING, connect_ie, connect_ie_len); - if (ie_pos) { - curr_ie_len = *(ie_pos + 1) + 2; - if (is_copy) { - if (ie_dest_len >= curr_ie_len) { - memcpy(ie_dest, ie_pos, curr_ie_len); - ie_dest += curr_ie_len; - /* free space avail in ie_dest for next ie*/ - ie_dest_len -= curr_ie_len; - } else { - SLSI_ERR_NODEV("interwork ie extract error (ie_copy_l:%d, c_ie_l:%d):\n", ie_dest_len, curr_ie_len); - return -EINVAL; - } - } else { - info_elem_length = curr_ie_len; - } - } - - /* vendor specific IEs will be the last elements. */ - ie_pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, connect_ie, connect_ie_len); - if (ie_pos) { - /* length of all the vendor specific IEs */ - curr_ie_len = connect_ie_len - (ie_pos - connect_ie); - if (is_copy) { - if (ie_dest_len >= curr_ie_len) { - memcpy(ie_dest, ie_pos, curr_ie_len); - ie_dest += curr_ie_len; - ie_dest_len -= curr_ie_len; + for (i = 0; i < sizeof(ie_eid) / sizeof(u8); i++) { + ie_pos = cfg80211_find_ie(ie_eid[i], connect_ie, connect_ie_len); + if (ie_pos) { + if (ie_eid[i] == WLAN_EID_VENDOR_SPECIFIC) /*Vendor IE will be the last element */ + curr_ie_len = connect_ie_len - (ie_pos - connect_ie); + else + curr_ie_len = *(ie_pos + 1) + 2; + SLSI_DBG2(sdev, SLSI_MLME, "IE[%d] is present having length:%d\n", ie_eid[i], curr_ie_len); + if (is_copy) { + if (ie_dest_len >= curr_ie_len) { + memcpy(ie_dest, ie_pos, curr_ie_len); + ie_dest += curr_ie_len; + /* free space avail in ie_dest for next ie*/ + ie_dest_len -= curr_ie_len; + } else { + SLSI_ERR_NODEV("IE[%d] extract error (ie_copy_l:%d, c_ie_l:%d):\n", ie_eid[i], + ie_dest_len, curr_ie_len); + return -EINVAL; + } } else { - SLSI_ERR_NODEV("vendor ie extract error (ie_copy_l:%d, c_ie_l:%d):\n", ie_dest_len, curr_ie_len); - return -EINVAL; + info_elem_length += curr_ie_len; } - } else { - info_elem_length += curr_ie_len; } } @@ -2290,12 +2297,19 @@ int slsi_mlme_disconnect(struct slsi_dev *sdev, struct net_device *dev, u8 *mac, SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_disconnect_req(vif:%u, bssid:%pM, reason:%d)\n", ndev_vif->ifnum, mac, reason_code); - /* No data reference required */ - req = fapi_alloc(mlme_disconnect_req, MLME_DISCONNECT_REQ, ndev_vif->ifnum, 0); + req = fapi_alloc(mlme_disconnect_req, MLME_DISCONNECT_REQ, ndev_vif->ifnum, + ndev_vif->sta.vendor_disconnect_ies_len); + if (!req) return -ENOMEM; SLSI_INFO(sdev, "Send DEAUTH, reason = %d\n", reason_code); fapi_set_u16(req, u.mlme_disconnect_req.reason_code, reason_code); + if (ndev_vif->sta.vendor_disconnect_ies_len > 0) + fapi_append_data(req, ndev_vif->sta.vendor_disconnect_ies, ndev_vif->sta.vendor_disconnect_ies_len); + kfree(ndev_vif->sta.vendor_disconnect_ies); + ndev_vif->sta.vendor_disconnect_ies = NULL; + ndev_vif->sta.vendor_disconnect_ies_len = 0; + if (mac) fapi_set_memcpy(req, u.mlme_disconnect_req.peer_sta_address, mac); else @@ -2445,67 +2459,103 @@ int slsi_mlme_get_key(struct slsi_dev *sdev, struct net_device *dev, u16 key_id, return r; } -void slsi_fw_tx_rate_calc(u16 fw_rate, struct rate_info *tx_rate, unsigned long *data_rate_mbps) +void slsi_calc_max_data_rate(struct net_device *dev, u8 bandwidth, u8 antenna_mode) +{ + struct netdev_vif *ndev_vif = netdev_priv(dev); + u8 bandwidth_index, sta_mode, mcs_index; + + if (bandwidth == 0 || antenna_mode > 3) { + SLSI_NET_ERR(dev, "MIB value is wrong."); + return; + } + + WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex)); + + /* Bandwidth (BW): 0x0= 20 MHz, 0x1= 40 MHz, 0x2= 80 MHz, 0x3= 160/ 80+80 MHz. 0x3 is not supported */ + bandwidth_index = bandwidth / 40; + sta_mode = slsi_sta_ieee80211_mode(dev, ndev_vif->sta.sta_bss->channel->center_freq); + + if (sta_mode == SLSI_80211_MODE_11B) { + ndev_vif->sta.max_rate_mbps = 11; + } else if (sta_mode == SLSI_80211_MODE_11G || sta_mode == SLSI_80211_MODE_11A) { + ndev_vif->sta.max_rate_mbps = 54; + } else if (sta_mode == SLSI_80211_MODE_11N) { /* max mcs index = 7 */ + ndev_vif->sta.max_rate_mbps = (unsigned long)(slsi_rates_table[bandwidth_index][1][7] * (antenna_mode + 1)) / 10; + } else if (sta_mode == SLSI_80211_MODE_11AC) { + if (bandwidth_index == 0) + mcs_index = 8; + else + mcs_index = 9; + ndev_vif->sta.max_rate_mbps = (unsigned long)(slsi_rates_table[bandwidth_index][1][mcs_index] * (antenna_mode + 1)) / 10; + } + SLSI_NET_INFO(dev, "sta_mode : %u, freq : %u, bandwidth : %u, antenna_mode : %u, max_rate_mbps : %u\n", + sta_mode, ndev_vif->sta.sta_bss->channel->center_freq, bandwidth, antenna_mode, ndev_vif->sta.max_rate_mbps); +} + +void slsi_decode_fw_rate(u16 fw_rate, struct rate_info *rate, unsigned long *data_rate_mbps) { const int fw_rate_idx_to_80211_rate[] = { 0, 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 }; - if (tx_rate) { - tx_rate->flags = 0; - tx_rate->legacy = 0; + if (rate) { + rate->flags = 0; + rate->legacy = 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) - tx_rate->bw = 0; + rate->bw = 0; #endif } + if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_NON_HT_SELECTED) { u16 fw_rate_idx = fw_rate & SLSI_FW_API_RATE_INDEX_FIELD; if (fw_rate > 0 && fw_rate_idx < ARRAY_SIZE(fw_rate_idx_to_80211_rate)) { - if (tx_rate) - tx_rate->legacy = fw_rate_idx_to_80211_rate[fw_rate_idx]; + if (rate) + rate->legacy = fw_rate_idx_to_80211_rate[fw_rate_idx]; if (data_rate_mbps) *data_rate_mbps = fw_rate_idx_to_80211_rate[fw_rate_idx] / 10; } - } else if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_HT_SELECTED) { - u8 mcs = SLSI_FW_API_RATE_HT_MCS_FIELD & fw_rate; + u8 mcs_idx = SLSI_FW_API_RATE_HT_MCS_FIELD & fw_rate; u8 nss = ((SLSI_FW_API_RATE_HT_NSS_FIELD & fw_rate) >> 6) + 1; - if (tx_rate) { - tx_rate->flags |= RATE_INFO_FLAGS_MCS; - tx_rate->mcs = mcs; + if (rate) { + rate->flags |= RATE_INFO_FLAGS_MCS; + rate->mcs = mcs_idx; if ((fw_rate & SLSI_FW_API_RATE_BW_FIELD) == SLSI_FW_API_RATE_BW_40MHZ) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) - tx_rate->bw |= RATE_INFO_BW_40; + rate->bw |= RATE_INFO_BW_40; #else - tx_rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; #endif if (fw_rate & SLSI_FW_API_RATE_SGI) - tx_rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; } if (data_rate_mbps) { int chan_bw_idx; int gi_idx; - int mcs_idx; chan_bw_idx = (fw_rate & SLSI_FW_API_RATE_BW_FIELD) >> 9; gi_idx = ((fw_rate & SLSI_FW_API_RATE_SGI) == SLSI_FW_API_RATE_SGI) ? 1 : 0; - mcs_idx = SLSI_FW_API_RATE_HT_MCS_FIELD & fw_rate; - if ((chan_bw_idx < 2) && (mcs_idx <= 7)) { - *data_rate_mbps = (unsigned long)(nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx]) / 10; - } else if (mcs == 32 && chan_bw_idx == 1) { - if (gi_idx == 1) - *data_rate_mbps = (unsigned long)(nss * 67)/10; - else - *data_rate_mbps = nss * 6; + /* nss will be 1 when mcs_idx <= 7 or mcs == 32 */ + if (chan_bw_idx < 2) { + if (mcs_idx <= 7) { + *data_rate_mbps = slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx] / 10; + } else if (mcs_idx <= 15) { + *data_rate_mbps = (unsigned long)(nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx - 8]) / 10; + } else if (mcs_idx == 32 && chan_bw_idx == 1) { + /* TODO: Fix this : unsigned long will not hold decimal values */ + if (gi_idx == 1) + *data_rate_mbps = (unsigned long) 6.7; + else + *data_rate_mbps = 6; + } } else { SLSI_WARN_NODEV("FW DATA RATE decode error fw_rate:%x, bw:%x, mcs_idx:%x, nss : %d\n", fw_rate, chan_bw_idx, mcs_idx, nss); } } - } else if ((fw_rate & SLSI_FW_API_RATE_HT_SELECTOR_FIELD) == SLSI_FW_API_RATE_VHT_SELECTED) { int chan_bw_idx; int gi_idx; @@ -2522,8 +2572,8 @@ void slsi_fw_tx_rate_calc(u16 fw_rate, struct rate_info *tx_rate, unsigned long mcs_idx = SLSI_FW_API_RATE_VHT_MCS_FIELD & fw_rate; /* Bandwidth (BW): 0x0= 20 MHz, 0x1= 40 MHz, 0x2= 80 MHz, 0x3= 160/ 80+80 MHz. 0x3 is not supported */ if ((chan_bw_idx <= 2) && (mcs_idx <= 9)) { - if (tx_rate) - tx_rate->legacy = nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx]; + if (rate) + rate->legacy = nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx]; if (data_rate_mbps) *data_rate_mbps = (unsigned long)(nss * slsi_rates_table[chan_bw_idx][gi_idx][mcs_idx]) / 10; } else { @@ -2539,10 +2589,13 @@ int slsi_mlme_get_sinfo_mib(struct slsi_dev *sdev, struct net_device *dev, struct slsi_mib_data mibreq = { 0, NULL }; struct slsi_mib_data mibrsp = { 0, NULL }; struct slsi_mib_value *values = NULL; + u8 bandwidth = 0, antenna_mode = 4; int data_length = 0; int r = 0; + int mib_index = 0; static const struct slsi_mib_get_entry get_values[] = { { SLSI_PSID_UNIFI_TX_DATA_RATE, { 0, 0 } }, /* to get STATION_INFO_TX_BITRATE*/ + { SLSI_PSID_UNIFI_RX_DATA_RATE, { 0, 0 } }, /* to get STATION_INFO_RX_BITRATE*/ { SLSI_PSID_UNIFI_RSSI, { 0, 0 } }, /* to get STATION_INFO_SIGNAL_AVG*/ { SLSI_PSID_UNIFI_THROUGHPUT_DEBUG, { 3, 0 } }, /* bad_fcs_count*/ { SLSI_PSID_UNIFI_THROUGHPUT_DEBUG, { 25, 0 } }, /* mac_bad_sig_count*/ @@ -2550,9 +2603,11 @@ int slsi_mlme_get_sinfo_mib(struct slsi_dev *sdev, struct net_device *dev, { SLSI_PSID_UNIFI_FRAME_TX_COUNTERS, { 1, 0 } }, /*tx good count*/ { SLSI_PSID_UNIFI_FRAME_TX_COUNTERS, { 2, 0 } }, /*tx bad count*/ { SLSI_PSID_UNIFI_FRAME_RX_COUNTERS, { 1, 0 } }, /*rx good count*/ + { SLSI_PSID_UNIFI_CURRENT_BSS_BANDWIDTH, { 0, 0 } }, /* bss bandwidth */ #ifdef CONFIG_SCSC_ENHANCED_PACKET_STATS { SLSI_PSID_UNIFI_FRAME_TX_COUNTERS, { 3, 0 } }, /*tx retry count*/ #endif + { SLSI_PSID_UNIFI_STA_VIF_LINK_NSS, { 0, 0 } } /* current nss */ }; int rx_counter = 0; @@ -2573,10 +2628,10 @@ int slsi_mlme_get_sinfo_mib(struct slsi_dev *sdev, struct net_device *dev, return -ENOMEM; /* Fixed fields len (5) : 2 bytes(PSID) + 2 bytes (Len) + 1 byte (VLDATA header ) [10 for 2 PSIDs] - * Data : 3 bytes for SLSI_PSID_UNIFI_TX_DATA_RATE , 1 byte for SLSI_PSID_UNIFI_RSSI + * Data : 3*2 bytes for SLSI_PSID_UNIFI_TX_DATA_RATE & SLSI_PSID_UNIFI_RX_DATA_RATE, 1 byte for SLSI_PSID_UNIFI_RSSI * 10*7 bytes for 3 Throughput Mib's and 4 counter Mib's */ - mibrsp.dataLength = 84; + mibrsp.dataLength = 114; mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL); if (!mibrsp.data) { @@ -2599,63 +2654,91 @@ int slsi_mlme_get_sinfo_mib(struct slsi_dev *sdev, struct net_device *dev, return -ENOMEM; } - if (values[0].type != SLSI_MIB_TYPE_NONE) { - SLSI_CHECK_TYPE(sdev, values[0].type, SLSI_MIB_TYPE_UINT); - slsi_fw_tx_rate_calc((u16)values[0].u.uintValue, &peer->sinfo.txrate, NULL); + if (values[mib_index].type != SLSI_MIB_TYPE_NONE) { + SLSI_CHECK_TYPE(sdev, values[mib_index].type, SLSI_MIB_TYPE_UINT); + slsi_decode_fw_rate((u16)values[mib_index].u.uintValue, &peer->sinfo.txrate, &ndev_vif->sta.data_rate_mbps); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) peer->sinfo.filled |= BIT(NL80211_STA_INFO_TX_BITRATE); #else peer->sinfo.filled |= STATION_INFO_TX_BITRATE; #endif - SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_TX_DATA_RATE = %d\n", - values[0].u.uintValue); + SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_TX_DATA_RATE = 0x%x\n", + values[mib_index].u.uintValue); } + else + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); - if (values[1].type != SLSI_MIB_TYPE_NONE) { - SLSI_CHECK_TYPE(sdev, values[1].type, SLSI_MIB_TYPE_INT); - if (values[1].u.intValue >= 0) + if (values[++mib_index].type != SLSI_MIB_TYPE_NONE) { + SLSI_CHECK_TYPE(sdev, values[mib_index].type, SLSI_MIB_TYPE_UINT); + slsi_decode_fw_rate((u16)values[mib_index].u.uintValue, &peer->sinfo.rxrate, NULL); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) + peer->sinfo.filled |= BIT(NL80211_STA_INFO_RX_BITRATE); +#else + peer->sinfo.filled |= STATION_INFO_RX_BITRATE; +#endif + SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_RX_DATA_RATE = 0x%x\n", + values[mib_index].u.uintValue); + } + else + SLSI_DBG3(sdev, SLSI_MLME, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + + if (values[++mib_index].type != SLSI_MIB_TYPE_NONE) { + SLSI_CHECK_TYPE(sdev, values[mib_index].type, SLSI_MIB_TYPE_INT); + if (values[mib_index].u.intValue >= 0) peer->sinfo.signal = -1; else - peer->sinfo.signal = (s8)values[1].u.intValue; + peer->sinfo.signal = (s8)values[mib_index].u.intValue; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) peer->sinfo.filled |= BIT(NL80211_STA_INFO_SIGNAL); #else peer->sinfo.filled |= STATION_INFO_SIGNAL; #endif SLSI_DBG3(sdev, SLSI_MLME, "SLSI_PSID_UNIFI_RSSI = %d\n", - values[1].u.intValue); + values[mib_index].u.intValue); } + else + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); - if (values[2].type == SLSI_MIB_TYPE_UINT) - rx_counter += values[2].u.uintValue; /*bad_fcs_count*/ + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + rx_counter += values[mib_index].u.uintValue; /*bad_fcs_count*/ else - SLSI_ERR(sdev, "invalid type. iter:%d", 2); - if (values[3].type == SLSI_MIB_TYPE_UINT) - rx_counter += values[3].u.uintValue; /*mac_bad_sig_count*/ + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + rx_counter += values[mib_index].u.uintValue; /*mac_bad_sig_count*/ else - SLSI_ERR(sdev, "invalid type. iter:%d", 3); - if (values[4].type == SLSI_MIB_TYPE_UINT) - rx_counter += values[4].u.uintValue; /*rx_error_count*/ + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + rx_counter += values[mib_index].u.uintValue; /*rx_error_count*/ else - SLSI_ERR(sdev, "invalid type. iter:%d", 4); - if (values[5].type == SLSI_MIB_TYPE_UINT) - peer->sinfo.tx_packets = values[5].u.uintValue; /*tx good count*/ + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + peer->sinfo.tx_packets = values[mib_index].u.uintValue; /*tx good count*/ else - SLSI_ERR(sdev, "invalid type. iter:%d", 5); - if (values[6].type == SLSI_MIB_TYPE_UINT) - peer->sinfo.tx_failed = values[6].u.uintValue; /*tx bad count*/ + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + peer->sinfo.tx_failed = values[mib_index].u.uintValue; /*tx bad count*/ else - SLSI_ERR(sdev, "invalid type. iter:%d", 6); - if (values[7].type == SLSI_MIB_TYPE_UINT) - peer->sinfo.rx_packets = values[7].u.uintValue; /*rx good count*/ + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + peer->sinfo.rx_packets = values[mib_index].u.uintValue; /*rx good count*/ else - SLSI_ERR(sdev, "invalid type. iter:%d", 7); + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + bandwidth = values[mib_index].u.uintValue; /* bss bandwidth */ + else + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); #ifdef CONFIG_SCSC_ENHANCED_PACKET_STATS - if (values[8].type == SLSI_MIB_TYPE_UINT) - peer->sinfo.tx_retries = values[8].u.uintValue; /*tx retry count*/ + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) + peer->sinfo.tx_retries = values[mib_index].u.uintValue; /*tx retry count*/ else - SLSI_ERR(sdev, "invalid type. iter:%d", 8); + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); #endif + if (values[++mib_index].type == SLSI_MIB_TYPE_UINT) { + antenna_mode = values[mib_index].u.uintValue; /* current nss */ + slsi_calc_max_data_rate(dev, bandwidth, antenna_mode); + } else { + SLSI_ERR(sdev, "Invalid type: PSID = 0x%x\n", get_values[mib_index].psid); + } peer->sinfo.rx_dropped_misc = rx_counter; diff --git a/drivers/net/wireless/scsc/mlme.h b/drivers/net/wireless/scsc/mlme.h index ad59afc8f3ad..9a08f56b9719 100755 --- a/drivers/net/wireless/scsc/mlme.h +++ b/drivers/net/wireless/scsc/mlme.h @@ -24,6 +24,7 @@ enum slsi_ac_index_wmm_pe { #define SLSI_WLAN_EID_VENDOR_SPECIFIC 0xdd #define SLSI_WLAN_EID_INTERWORKING 107 +#define SLSI_WLAN_EID_EXTENSION 255 #define SLSI_WLAN_OUI_TYPE_WFA_HS20_IND 0x10 #define SLSI_WLAN_OUI_TYPE_WFA_OSEN 0x12 @@ -272,7 +273,7 @@ int slsi_mlme_arp_detect_request(struct slsi_dev *sdev, struct net_device *dev, int slsi_mlme_set_ctwindow(struct slsi_dev *sdev, struct net_device *dev, unsigned int ct_param); int slsi_mlme_set_p2p_noa(struct slsi_dev *sdev, struct net_device *dev, unsigned int noa_count, unsigned int interval, unsigned int duration); -void slsi_fw_tx_rate_calc(u16 fw_rate, struct rate_info *tx_rate, unsigned long *data_rate_mbps); +void slsi_decode_fw_rate(u16 fw_rate, struct rate_info *rate, unsigned long *data_rate_mbps); int slsi_test_sap_configure_monitor_mode(struct slsi_dev *sdev, struct net_device *dev, struct cfg80211_chan_def *chandef); struct sk_buff *slsi_mlme_req_cfm(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, u16 cfm_id); diff --git a/drivers/net/wireless/scsc/mlme_nan.c b/drivers/net/wireless/scsc/mlme_nan.c index 2a14f1e6552a..83a7ab946c95 100755 --- a/drivers/net/wireless/scsc/mlme_nan.c +++ b/drivers/net/wireless/scsc/mlme_nan.c @@ -345,9 +345,9 @@ int slsi_mlme_nan_publish(struct slsi_dev *sdev, struct net_device *dev, struct } if (hal_req && !r) - ndev_vif->nan.publish_id_map |= BIT(publish_id); + ndev_vif->nan.publish_id_map |= (u32)BIT(publish_id); else - ndev_vif->nan.publish_id_map &= ~BIT(publish_id); + ndev_vif->nan.publish_id_map &= (u32)~BIT(publish_id); slsi_kfree_skb(cfm); return r; } @@ -479,9 +479,9 @@ int slsi_mlme_nan_subscribe(struct slsi_dev *sdev, struct net_device *dev, struc } if (hal_req && !r) - ndev_vif->nan.subscribe_id_map |= BIT(subscribe_id); + ndev_vif->nan.subscribe_id_map |= (u32)BIT(subscribe_id); else - ndev_vif->nan.subscribe_id_map &= ~BIT(subscribe_id); + ndev_vif->nan.subscribe_id_map &= (u32)~BIT(subscribe_id); slsi_kfree_skb(cfm); return r; } @@ -532,7 +532,7 @@ int slsi_mlme_nan_tx_followup(struct slsi_dev *sdev, struct net_device *dev, fapi_set_u16(req, u.mlme_nan_followup_req.publish_subscribe_id, hal_req->publish_subscribe_id); fapi_set_u16(req, u.mlme_nan_followup_req.peer_id, hal_req->requestor_instance_id); - fapi_set_u16(req, u.mlme_nan_subscribe_req.nan_sdf_flags, nan_sdf_flags); + fapi_set_u16(req, u.mlme_nan_followup_req.nan_sdf_flags, nan_sdf_flags); slsi_mlme_nan_followup_fapi_data(req, hal_req); diff --git a/drivers/net/wireless/scsc/netif.c b/drivers/net/wireless/scsc/netif.c index 263810d67951..cfef769db585 100755 --- a/drivers/net/wireless/scsc/netif.c +++ b/drivers/net/wireless/scsc/netif.c @@ -671,7 +671,6 @@ void slsi_tdls_move_packets(struct slsi_dev *sdev, struct net_device *dev, slsi_spinlock_unlock(&tcp_ack->lock); } - slsi_spinlock_lock(&netdev_vif->peer_lock); /** * For TDLS connection set PEER valid to true. After this ndo_select_queue() will select TDLSQ instead of STAQ * For TDLS teardown set PEER valid to false. After this ndo_select_queue() will select STAQ instead of TDLSQ @@ -769,7 +768,6 @@ void slsi_tdls_move_packets(struct slsi_dev *sdev, struct net_device *dev, if (unlikely(skb_to_free)) kfree_skb_list(skb_to_free); #endif - slsi_spinlock_unlock(&netdev_vif->peer_lock); /* Teardown - after teardown there should not be any packet in TDLS queues */ if (!connection) diff --git a/drivers/net/wireless/scsc/nl80211_vendor.c b/drivers/net/wireless/scsc/nl80211_vendor.c index 628ebf421d29..6c803c3e0871 100755 --- a/drivers/net/wireless/scsc/nl80211_vendor.c +++ b/drivers/net/wireless/scsc/nl80211_vendor.c @@ -105,6 +105,8 @@ char *slsi_print_event_name(int event_id) return "SLSI_NL80211_RTT_COMPLETE_EVENT"; case SLSI_NL80211_VENDOR_ACS_EVENT: return "SLSI_NL80211_VENDOR_ACS_EVENT"; + case SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS: + return "SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS"; default: return "UNKNOWN_EVENT"; } @@ -279,10 +281,16 @@ static int slsi_gscan_get_valid_channel(struct wiphy *wiphy, u32 chan_count = 0, mem_len = 0; struct sk_buff *reply; + if (len < SLSI_NL_VENDOR_DATA_OVERHEAD) + return -EINVAL; + type = nla_type(data); - if (type == GSCAN_ATTRIBUTE_BAND) + if (type == GSCAN_ATTRIBUTE_BAND) { + if (nla_len((struct nlattr *)data) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; band = nla_get_u32(data); + } else return -EINVAL; @@ -691,18 +699,28 @@ static int slsi_gscan_add_read_params(struct slsi_nl_gscan_param *nl_gscan_param switch (type) { case GSCAN_ATTRIBUTE_BASE_PERIOD: + if (nla_len(iter) != SLSI_NL_ATTRIBUTE_U32_LEN) + return -EINVAL; nl_gscan_param->base_period = nla_get_u32(iter); break; case GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN: + if (nla_len(iter) != SLSI_NL_ATTRIBUTE_U32_LEN) + return -EINVAL; nl_gscan_param->max_ap_per_scan = nla_get_u32(iter); break; case GSCAN_ATTRIBUTE_REPORT_THRESHOLD: + if (nla_len(iter) != SLSI_NL_ATTRIBUTE_U32_LEN) + return -EINVAL; nl_gscan_param->report_threshold_percent = nla_get_u32(iter); break; case GSCAN_ATTRIBUTE_REPORT_THRESHOLD_NUM_SCANS: + if (nla_len(iter) != SLSI_NL_ATTRIBUTE_U32_LEN) + return -EINVAL; nl_gscan_param->report_threshold_num_scans = nla_get_u32(iter); break; case GSCAN_ATTRIBUTE_NUM_BUCKETS: + if (nla_len(iter) != SLSI_NL_ATTRIBUTE_U32_LEN) + return -EINVAL; nl_gscan_param->num_buckets = nla_get_u32(iter); break; case GSCAN_ATTRIBUTE_CH_BUCKET_1: @@ -715,38 +733,61 @@ static int slsi_gscan_add_read_params(struct slsi_nl_gscan_param *nl_gscan_param case GSCAN_ATTRIBUTE_CH_BUCKET_8: nla_for_each_nested(iter1, iter, tmp1) { type = nla_type(iter1); + nl_bucket = nl_gscan_param->nl_bucket; switch (type) { case GSCAN_ATTRIBUTE_BUCKET_ID: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].bucket_index = nla_get_u32(iter1); break; case GSCAN_ATTRIBUTE_BUCKET_PERIOD: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].period = nla_get_u32(iter1); break; case GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].num_channels = nla_get_u32(iter1); break; case GSCAN_ATTRIBUTE_BUCKET_CHANNELS: nla_for_each_nested(iter2, iter1, tmp2) { + if (k >= SLSI_GSCAN_MAX_CHANNELS) + break; + + if (nla_len(iter2) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; + nl_bucket[j].channels[k].channel = nla_get_u32(iter2); k++; } k = 0; break; case GSCAN_ATTRIBUTE_BUCKETS_BAND: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].band = nla_get_u32(iter1); break; case GSCAN_ATTRIBUTE_REPORT_EVENTS: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].report_events = nla_get_u32(iter1); break; case GSCAN_ATTRIBUTE_BUCKET_EXPONENT: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].exponent = nla_get_u32(iter1); break; case GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].step_count = nla_get_u32(iter1); break; case GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD: + if (nla_len(iter1) != (SLSI_NL_ATTRIBUTE_U32_LEN - NLA_HDRLEN)) + return -EINVAL; nl_bucket[j].max_period = nla_get_u32(iter1); break; default: @@ -897,7 +938,7 @@ static int slsi_gscan_add_mlme(struct slsi_dev *sdev, struct slsi_nl_gscan_param int ret = 0; int i; #ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION - u8 mac_addr_mask[ETH_ALEN] = {0xFF}; + u8 mac_addr_mask[ETH_ALEN]; #endif dev = slsi_gscan_get_netdev(sdev); @@ -934,17 +975,15 @@ static int slsi_gscan_add_mlme(struct slsi_dev *sdev, struct slsi_nl_gscan_param report_mode |= FAPI_REPORTMODE_NO_BATCH; #ifdef CONFIG_SCSC_WLAN_ENABLE_MAC_RANDOMISATION + memset(mac_addr_mask, 0xFF, ETH_ALEN); if (sdev->scan_addr_set == 1) { - memset(mac_addr_mask, 0xFF, ETH_ALEN); for (i = 3; i < ETH_ALEN; i++) mac_addr_mask[i] = 0x00; ret = slsi_set_mac_randomisation_mask(sdev, mac_addr_mask); if (ret) sdev->scan_addr_set = 0; - } else { - memset(mac_addr_mask, 0xFF, ETH_ALEN); + } else slsi_set_mac_randomisation_mask(sdev, mac_addr_mask); - } #endif ret = slsi_mlme_add_scan(sdev, dev, @@ -1134,6 +1173,8 @@ static int slsi_gscan_get_scan_results(struct wiphy *wiphy, switch (type) { case GSCAN_ATTRIBUTE_NUM_OF_RESULTS: + if (nla_len(attr) != SLSI_NL_ATTRIBUTE_U32_LEN) + return -EINVAL; nl_num_results = nla_get_u32(attr); break; default: @@ -2463,6 +2504,10 @@ static int slsi_gscan_set_oui(struct wiphy *wiphy, switch (type) { case SLSI_NL_ATTRIBUTE_PNO_RANDOM_MAC_OUI: { + if (nla_len(attr) != 3) { + ret = -EINVAL; + break; + } memcpy(&scan_oui, nla_data(attr), 3); memcpy(sdev->scan_mac_addr, scan_oui, 6); sdev->scan_addr_set = 1; @@ -2640,7 +2685,7 @@ static int slsi_apf_set_filter(struct wiphy *wiphy, struct wireless_dev *wdev, c int type; const struct nlattr *attr; u32 program_len = 0; - u8 *program; + u8 *program = NULL; SLSI_DBG3(sdev, SLSI_GSCAN, "Received apf_set_filter command\n"); SLSI_MUTEX_LOCK(sdev->device_config_mutex); @@ -2663,6 +2708,10 @@ static int slsi_apf_set_filter(struct wiphy *wiphy, struct wireless_dev *wdev, c { program_len = nla_get_u32(attr); program = kmalloc(program_len, GFP_KERNEL); + if (!program) { + ret = -ENOMEM; + goto exit; + } break; } case SLSI_APF_ATTR_PROGRAM: @@ -2684,6 +2733,7 @@ static int slsi_apf_set_filter(struct wiphy *wiphy, struct wireless_dev *wdev, c goto exit; } exit: + kfree(program); SLSI_MUTEX_UNLOCK(sdev->device_config_mutex); return ret; } @@ -4424,6 +4474,41 @@ static int slsi_acs_init(struct wiphy *wiphy, return r; } +static int slsi_configure_latency_mode(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) +{ + struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy); + struct net_device *dev = wdev->netdev; + int temp = 0; + int type = 0; + const struct nlattr *attr; + int ret = 0; + int low_latency_mode = 0; + + if (!dev) { + SLSI_ERR(sdev, "dev is NULL!!\n"); + return -EINVAL; + } + + nla_for_each_attr(attr, data, len, temp) { + type = nla_type(attr); + switch (type) { + case SLSI_NL_ATTRIBUTE_LATENCY_MODE: + low_latency_mode = nla_get_u8(attr); + break; + default: + SLSI_ERR_NODEV("Unknown attribute: %d\n", type); + ret = -EINVAL; + goto exit; + } + } + + ret = slsi_set_latency_mode(dev, low_latency_mode, len); + if (ret) + SLSI_ERR(sdev, "Error in setting low latency mode ret:%d\n", ret); +exit: + return ret; +} + static const struct nl80211_vendor_cmd_info slsi_vendor_events[] = { /**********Deprecated now due to fapi updates.Do not remove*/ { OUI_GOOGLE, SLSI_NL80211_SIGNIFICANT_CHANGE_EVENT }, @@ -4458,7 +4543,8 @@ static const struct nl80211_vendor_cmd_info slsi_vendor_events[] = { { OUI_GOOGLE, SLSI_NL80211_RTT_COMPLETE_EVENT}, { OUI_SAMSUNG, SLSI_NL80211_VENDOR_ACS_EVENT}, { OUI_SAMSUNG, SLSI_NL80211_VENDOR_FORWARD_BEACON}, - { OUI_SAMSUNG, SLSI_NL80211_VENDOR_FORWARD_BEACON_ABORT} + { OUI_SAMSUNG, SLSI_NL80211_VENDOR_FORWARD_BEACON_ABORT}, + { OUI_GOOGLE, SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS} }; static const struct wiphy_vendor_command slsi_vendor_cmd[] = { @@ -4883,6 +4969,14 @@ static const struct wiphy_vendor_command slsi_vendor_cmd[] = { }, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = slsi_apf_read_filter + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = SLSI_NL80211_VENDOR_SUBCMD_SET_LATENCY_MODE + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = slsi_configure_latency_mode } }; diff --git a/drivers/net/wireless/scsc/nl80211_vendor.h b/drivers/net/wireless/scsc/nl80211_vendor.h index 125bb7cfa5c1..92269fe5f952 100755 --- a/drivers/net/wireless/scsc/nl80211_vendor.h +++ b/drivers/net/wireless/scsc/nl80211_vendor.h @@ -38,6 +38,8 @@ #define SLSI_NL_ATTRIBUTE_U32_LEN (NLA_HDRLEN + 4) #define SLSI_NL_ATTRIBUTE_COUNTRY_CODE (4) +#define SLSI_NL_ATTRIBUTE_LATENCY_MODE (5) + #define SLSI_NL_VENDOR_ID_OVERHEAD SLSI_NL_ATTRIBUTE_U32_LEN #define SLSI_NL_VENDOR_SUBCMD_OVERHEAD SLSI_NL_ATTRIBUTE_U32_LEN #define SLSI_NL_VENDOR_DATA_OVERHEAD (NLA_HDRLEN) @@ -311,6 +313,7 @@ enum slsi_hal_vendor_subcmds { SLSI_NL80211_VENDOR_SUBCMD_CONFIGURE_ND_OFFLOAD, SLSI_NL80211_VENDOR_SUBCMD_GET_ROAMING_CAPABILITIES, SLSI_NL80211_VENDOR_SUBCMD_SET_ROAMING_STATE, + SLSI_NL80211_VENDOR_SUBCMD_SET_LATENCY_MODE, SLSI_NL80211_VENDOR_SUBCMD_START_LOGGING = SLSI_NL80211_LOGGING_SUBCMD_RANGE_START, SLSI_NL80211_VENDOR_SUBCMD_TRIGGER_FW_MEM_DUMP, SLSI_NL80211_VENDOR_SUBCMD_GET_FW_MEM_DUMP, @@ -378,7 +381,8 @@ enum slsi_vendor_event_values { SLSI_NL80211_RTT_COMPLETE_EVENT, SLSI_NL80211_VENDOR_ACS_EVENT, SLSI_NL80211_VENDOR_FORWARD_BEACON, - SLSI_NL80211_VENDOR_FORWARD_BEACON_ABORT + SLSI_NL80211_VENDOR_FORWARD_BEACON_ABORT, + SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS, }; enum slsi_lls_interface_mode { diff --git a/drivers/net/wireless/scsc/nl80211_vendor_nan.c b/drivers/net/wireless/scsc/nl80211_vendor_nan.c index e53c006d45df..23abce3a0720 100755 --- a/drivers/net/wireless/scsc/nl80211_vendor_nan.c +++ b/drivers/net/wireless/scsc/nl80211_vendor_nan.c @@ -187,7 +187,7 @@ static int slsi_nan_get_ranging_cfg_nl(struct slsi_dev *sdev, struct slsi_nan_ra static int slsi_nan_get_security_info_nl(struct slsi_dev *sdev, struct slsi_nan_security_info *sec_info, const struct nlattr *iter, int nl_attr_id) { - u32 len; + u32 len = 0; switch (nl_attr_id) { case NAN_REQ_ATTR_CIPHER_TYPE: @@ -1449,7 +1449,7 @@ int slsi_nan_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev, co goto exit_with_mibrsp; } - for (i = 0; i < ARRAY_SIZE(get_values); i++) { + for (i = 0; i < (int)ARRAY_SIZE(get_values); i++) { if (values[i].type == SLSI_MIB_TYPE_UINT) { *capabilities_mib_val[i] = values[i].u.uintValue; SLSI_DBG2(sdev, SLSI_GSCAN, "MIB value = %ud\n", *capabilities_mib_val[i]); @@ -1484,7 +1484,22 @@ void slsi_nan_event(struct slsi_dev *sdev, struct net_device *dev, struct sk_buf event = fapi_get_u16(skb, u.mlme_nan_event_ind.event); identifier = fapi_get_u16(skb, u.mlme_nan_event_ind.identifier); mac_addr = fapi_get_buff(skb, u.mlme_nan_event_ind.address_or_identifier); - evt_reason = fapi_get_u16(skb, u.mlme_nan_event_ind.reason_code); + + switch(fapi_get_u16(skb, u.mlme_nan_event_ind.reason_code)) { + case FAPI_REASONCODE_NAN_SERVICE_TERMINATED_TIMEOUT: + case FAPI_REASONCODE_NAN_SERVICE_TERMINATED_COUNT_REACHED: + case FAPI_REASONCODE_NAN_SERVICE_TERMINATED_DISCOVERY_SHUTDOWN: + case FAPI_REASONCODE_NAN_SERVICE_TERMINATED_USER_REQUEST: + case FAPI_REASONCODE_NAN_TRANSMIT_FOLLOWUP_SUCCESS: + evt_reason = SLSI_HAL_NAN_STATUS_SUCCESS; + break; + case FAPI_REASONCODE_NAN_TRANSMIT_FOLLOWUP_FAILURE: + evt_reason = SLSI_HAL_NAN_STATUS_PROTOCOL_FAILURE; + break; + default : + evt_reason = SLSI_HAL_NAN_STATUS_INTERNAL_FAILURE; + break; + } switch (event) { case FAPI_EVENT_WIFI_EVENT_NAN_PUBLISH_TERMINATED: @@ -1508,6 +1523,9 @@ void slsi_nan_event(struct slsi_dev *sdev, struct net_device *dev, struct sk_buf disc_event_type = NAN_EVENT_ID_JOINED_CLUSTER; hal_event = SLSI_NL80211_NAN_DISCOVERY_ENGINE_EVENT; break; + case FAPI_EVENT_WIFI_EVENT_NAN_TRANSMIT_FOLLOWUP: + hal_event = SLSI_NL80211_NAN_TRANSMIT_FOLLOWUP_STATUS; + break; default: return; } @@ -1527,20 +1545,18 @@ void slsi_nan_event(struct slsi_dev *sdev, struct net_device *dev, struct sk_buf return; } + res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_STATUS, evt_reason); switch (hal_event) { case SLSI_NL80211_NAN_PUBLISH_TERMINATED_EVENT: res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_PUBLISH_ID, identifier); - res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_PUBLISH_ID, evt_reason); - ndev_vif->nan.publish_id_map &= ~BIT(identifier); + ndev_vif->nan.publish_id_map &= (u32)~BIT(identifier); break; case SLSI_NL80211_NAN_MATCH_EXPIRED_EVENT: res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_MATCH_PUBLISH_SUBSCRIBE_ID, identifier); - res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_MATCH_REQUESTOR_INSTANCE_ID, evt_reason); break; case SLSI_NL80211_NAN_SUBSCRIBE_TERMINATED_EVENT: res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_SUBSCRIBE_ID, identifier); - res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_SUBSCRIBE_REASON, evt_reason); - ndev_vif->nan.subscribe_id_map &= ~BIT(identifier); + ndev_vif->nan.subscribe_id_map &= (u32)~BIT(identifier); break; case SLSI_NL80211_NAN_DISCOVERY_ENGINE_EVENT: res |= nla_put_be16(nl_skb, NAN_EVT_ATTR_DISCOVERY_ENGINE_EVT_TYPE, disc_event_type); @@ -1610,6 +1626,7 @@ void slsi_nan_followup_ind(struct slsi_dev *sdev, struct net_device *dev, struct if (stitched_ie_p[1] + 2 < (ptr - stitched_ie_p) + tag_len) { SLSI_ERR(sdev, "TLV error\n"); kfree(hal_evt); + kfree(stitched_ie_p); return; } if (tag_id == SLSI_FAPI_NAN_SERVICE_SPECIFIC_INFO) { @@ -1761,9 +1778,10 @@ void slsi_nan_service_ind(struct slsi_dev *sdev, struct net_device *dev, struct ie_ptr = cfg80211_find_vendor_ie(SLSI_OUI, SLSI_OUI_TYPE_NAN_PARAMS, ie_ptr, ie_len); if (!ie_ptr) break; - if (ie_len > 9 && ie_ptr[1] > 5 && ie_ptr[6] == 9) + if (ie_len > 9 && ie_ptr[1] > 5 && ie_ptr[6] == 9) { hal_evt->sec_info.cipher_type = ie_ptr[8]; break; + } ie_len -= ie_ptr[1] + 2; if (ie_len < 3) ie_ptr = NULL; diff --git a/drivers/net/wireless/scsc/nl80211_vendor_nan.h b/drivers/net/wireless/scsc/nl80211_vendor_nan.h index 797d182404ce..251ba84e92bb 100755 --- a/drivers/net/wireless/scsc/nl80211_vendor_nan.h +++ b/drivers/net/wireless/scsc/nl80211_vendor_nan.h @@ -240,7 +240,8 @@ enum SLSI_NAN_EVT_ATTRIBUTES { NAN_EVT_ATTR_SDEA_PARAM_QOS_CFG = 60, NAN_EVT_ATTR_RANGE_MEASUREMENT_MM, NAN_EVT_ATTR_RANGEING_EVENT_TYPE, - NAN_EVT_ATTR_SECURITY_CIPHER_TYPE + NAN_EVT_ATTR_SECURITY_CIPHER_TYPE, + NAN_EVT_ATTR_STATUS }; #define SLSI_FAPI_NAN_CONFIG_PARAM_SID_BEACON 0X0003 diff --git a/drivers/net/wireless/scsc/rx.c b/drivers/net/wireless/scsc/rx.c index 575fba63ad57..6febfc4077dc 100755 --- a/drivers/net/wireless/scsc/rx.c +++ b/drivers/net/wireless/scsc/rx.c @@ -1352,7 +1352,7 @@ void slsi_rx_roamed_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk SLSI_INFO(sdev, "procedure-started-ind not received before roamed-ind\n"); netif_carrier_off(dev); slsi_mlme_disconnect(sdev, dev, peer->address, 0, true); - slsi_handle_disconnect(sdev, dev, peer->address, 0); + slsi_handle_disconnect(sdev, dev, peer->address, 0, NULL, 0); } else { u8 *assoc_ie = NULL; int assoc_ie_len = 0; @@ -1540,14 +1540,16 @@ static void slsi_tdls_event_connected(struct slsi_dev *sdev, struct net_device * if (WARN(ndev_vif->vif_type != FAPI_VIFTYPE_STATION, "STA VIF")) goto exit_with_lock; - /* Check for MAX client */ - if ((ndev_vif->sta.tdls_peer_sta_records) + 1 > SLSI_TDLS_PEER_CONNECTIONS_MAX) { - SLSI_NET_ERR(dev, "MAX TDLS peer limit reached. Ignore ind for peer_index:%d\n", peer_index); + if (peer_index < SLSI_TDLS_PEER_INDEX_MIN || peer_index > SLSI_TDLS_PEER_INDEX_MAX) { + SLSI_NET_ERR(dev, "Received incorrect peer_index: %d\n", peer_index); goto exit_with_lock; } - if (peer_index < SLSI_TDLS_PEER_INDEX_MIN || peer_index > SLSI_TDLS_PEER_INDEX_MAX) { - SLSI_NET_ERR(dev, "Received incorrect peer_index: %d\n", peer_index); + slsi_spinlock_lock(&ndev_vif->peer_lock); + /* Check for MAX client */ + if ((ndev_vif->sta.tdls_peer_sta_records) + 1 > SLSI_TDLS_PEER_CONNECTIONS_MAX) { + SLSI_NET_ERR(dev, "MAX TDLS peer limit reached. Ignore ind for peer_index:%d\n", peer_index); + slsi_spinlock_unlock(&ndev_vif->peer_lock); goto exit_with_lock; } @@ -1555,6 +1557,7 @@ static void slsi_tdls_event_connected(struct slsi_dev *sdev, struct net_device * if (!peer) { SLSI_NET_ERR(dev, "Peer NOT Created\n"); + slsi_spinlock_unlock(&ndev_vif->peer_lock); goto exit_with_lock; } @@ -1565,6 +1568,7 @@ static void slsi_tdls_event_connected(struct slsi_dev *sdev, struct net_device * /* Move TDLS packets from STA_Q to TDLS_Q */ slsi_tdls_move_packets(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, true); + slsi_spinlock_unlock(&ndev_vif->peer_lock); /* Handling MLME-TDLS-PEER.response */ slsi_mlme_tdls_peer_resp(sdev, dev, peer_index, tdls_event); @@ -1594,11 +1598,13 @@ static void slsi_tdls_event_disconnected(struct slsi_dev *sdev, struct net_devic goto exit; } + slsi_spinlock_lock(&ndev_vif->peer_lock); peer = slsi_get_peer_from_mac(sdev, dev, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address)); if (!peer || (peer->aid == 0)) { WARN_ON(!peer || (peer->aid == 0)); SLSI_NET_DBG1(dev, SLSI_MLME, "peer NOT found by MAC address\n"); + slsi_spinlock_unlock(&ndev_vif->peer_lock); goto exit; } @@ -1608,11 +1614,11 @@ static void slsi_tdls_event_disconnected(struct slsi_dev *sdev, struct net_devic slsi_tdls_move_packets(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, false); slsi_peer_remove(sdev, dev, peer); + slsi_spinlock_unlock(&ndev_vif->peer_lock); slsi_mlme_tdls_peer_resp(sdev, dev, pid, tdls_event); exit: SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); - slsi_kfree_skb(skb); } @@ -1920,6 +1926,22 @@ void slsi_rx_connect_ind(struct slsi_dev *sdev, struct net_device *dev, struct s } else if (fw_result_code >= 0x8200 && fw_result_code <= 0x82FF) { fw_result_code = fw_result_code & 0x00FF; SLSI_INFO(sdev, "Connect failed(Assoc Failure), Result code:0x%04x\n", fw_result_code); + if (fapi_get_datalen(skb)) { + int mgmt_hdr_len; + struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); + + if (ieee80211_is_assoc_resp(mgmt->frame_control)) { + mgmt_hdr_len = (mgmt->u.assoc_resp.variable - (u8 *)mgmt); + } else if (ieee80211_is_reassoc_resp(mgmt->frame_control)) { + mgmt_hdr_len = (mgmt->u.reassoc_resp.variable - (u8 *)mgmt); + } else { + SLSI_NET_DBG1(dev, SLSI_MLME, "Assoc/Reassoc response not found!\n"); + goto exit_with_lock; + } + + assoc_rsp_ie = (char *)mgmt + mgmt_hdr_len; + assoc_rsp_ie_len = fapi_get_datalen(skb) - mgmt_hdr_len; + } } else { SLSI_INFO(sdev, "Connect failed,Result code:0x%04x\n", fw_result_code); } @@ -2006,9 +2028,12 @@ void slsi_rx_connect_ind(struct slsi_dev *sdev, struct net_device *dev, struct s if (!ndev_vif->sta.sta_bss) { if (peer) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) - ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, NULL, peer->address, NULL, 0, IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, ndev_vif->chan, peer->address, + NULL, 0, IEEE80211_BSS_TYPE_ANY, + IEEE80211_PRIVACY_ANY); #else - ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, NULL, peer->address, NULL, 0, 0, 0); + ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, ndev_vif->chan, peer->address, + NULL, 0, 0, 0); #endif if (!ndev_vif->sta.sta_bss) { SLSI_NET_ERR(dev, "sta_bss is not available, terminating the connection (peer: %p)\n", peer); @@ -2017,14 +2042,21 @@ void slsi_rx_connect_ind(struct slsi_dev *sdev, struct net_device *dev, struct s } } - /* cfg80211_connect_result will take a copy of any ASSOC or ASSOC RSP IEs passed to it */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) + cfg80211_ref_bss(sdev->wiphy, ndev_vif->sta.sta_bss); + cfg80211_connect_bss(dev, bssid, ndev_vif->sta.sta_bss, assoc_ie, assoc_ie_len, assoc_rsp_ie, + assoc_rsp_ie_len, status, GFP_KERNEL, NL80211_TIMEOUT_UNSPECIFIED); +#else + /* cfg80211_connect_result will take a copy of any ASSOC or + * ASSOC RSP IEs passed to it + */ cfg80211_connect_result(dev, bssid, assoc_ie, assoc_ie_len, assoc_rsp_ie, assoc_rsp_ie_len, status, GFP_KERNEL); - +#endif if (status == WLAN_STATUS_SUCCESS) { ndev_vif->sta.vif_status = SLSI_VIF_STATUS_CONNECTED; @@ -2075,9 +2107,9 @@ void slsi_rx_connect_ind(struct slsi_dev *sdev, struct net_device *dev, struct s */ if ((fw_result_code == FAPI_RESULTCODE_SUCCESS) && peer) { slsi_mlme_disconnect(sdev, dev, peer->address, FAPI_REASONCODE_UNSPECIFIED_REASON, true); - slsi_handle_disconnect(sdev, dev, peer->address, FAPI_REASONCODE_UNSPECIFIED_REASON); + slsi_handle_disconnect(sdev, dev, peer->address, FAPI_REASONCODE_UNSPECIFIED_REASON, NULL, 0); } else { - slsi_handle_disconnect(sdev, dev, NULL, FAPI_REASONCODE_UNSPECIFIED_REASON); + slsi_handle_disconnect(sdev, dev, NULL, FAPI_REASONCODE_UNSPECIFIED_REASON, NULL, 0); } } @@ -2106,6 +2138,8 @@ void slsi_rx_disconnect_ind(struct slsi_dev *sdev, struct net_device *dev, struc slsi_handle_disconnect(sdev, dev, fapi_get_buff(skb, u.mlme_disconnect_ind.peer_sta_address), + 0, + NULL, 0); SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); @@ -2116,6 +2150,8 @@ void slsi_rx_disconnected_ind(struct slsi_dev *sdev, struct net_device *dev, str { struct netdev_vif *ndev_vif = netdev_priv(dev); u16 reason; + u8 *disassoc_rsp_ie = NULL; + int disassoc_rsp_ie_len = 0; SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); reason = fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code); @@ -2129,7 +2165,7 @@ void slsi_rx_disconnected_ind(struct slsi_dev *sdev, struct net_device *dev, str #else mx140_log_dump(); #endif - if (reason >= 0 && reason <= 0xFF) { + if (reason <= 0xFF) { SLSI_INFO(sdev, "Received DEAUTH, reason = %d\n", reason); } else if (reason >= 0x8100 && reason <= 0x81FF) { reason = reason & 0x00FF; @@ -2141,6 +2177,20 @@ void slsi_rx_disconnected_ind(struct slsi_dev *sdev, struct net_device *dev, str SLSI_INFO(sdev, "Received DEAUTH, reason = Local Disconnect <%d>\n", reason); } + if (fapi_get_datalen(skb)) { + struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); + + if (ieee80211_is_deauth(mgmt->frame_control)) { + disassoc_rsp_ie = (char *)&mgmt->u.deauth.reason_code + 2; + disassoc_rsp_ie_len = fapi_get_datalen(skb) - 2; + } else if (ieee80211_is_disassoc(mgmt->frame_control)) { + disassoc_rsp_ie = (char *)&mgmt->u.disassoc.reason_code + 2; + disassoc_rsp_ie_len = fapi_get_datalen(skb) - 2; + } else { + SLSI_NET_DBG1(dev, SLSI_MLME, "Not a disassoc/deauth packet\n"); + } + } + if (ndev_vif->vif_type == FAPI_VIFTYPE_AP) { if (fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code) == FAPI_REASONCODE_HOTSPOT_MAX_CLIENT_REACHED) { @@ -2155,7 +2205,9 @@ void slsi_rx_disconnected_ind(struct slsi_dev *sdev, struct net_device *dev, str slsi_handle_disconnect(sdev, dev, fapi_get_buff(skb, u.mlme_disconnected_ind.peer_sta_address), - fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code)); + fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code), + disassoc_rsp_ie, + disassoc_rsp_ie_len); exit: SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); diff --git a/drivers/net/wireless/scsc/scsc_wifi_fcq.c b/drivers/net/wireless/scsc/scsc_wifi_fcq.c index 71a2b2532a07..cb8e25228f16 100755 --- a/drivers/net/wireless/scsc/scsc_wifi_fcq.c +++ b/drivers/net/wireless/scsc/scsc_wifi_fcq.c @@ -94,7 +94,8 @@ struct peers_cache { bool is_unicast; }; -LIST_HEAD(peers_cache_list); +static LIST_HEAD(peers_cache_list); +static DEFINE_SPINLOCK(peers_cache_lock); /* AC qmod mapping */ /* 0 - indicates not active */ @@ -172,7 +173,7 @@ static inline void fcq_stop_all_queues(struct slsi_dev *sdev) { int i; struct peers_cache *pc_node, *next; - + spin_lock_bh(&peers_cache_lock); list_for_each_entry_safe(pc_node, next, &peers_cache_list, list) { /* Stop queues all queues */ for (i = 0; i < SLSI_NETIF_Q_PER_PEER; i++) { @@ -180,6 +181,7 @@ static inline void fcq_stop_all_queues(struct slsi_dev *sdev) netif_stop_subqueue(pc_node->dev, pc_node->qs->ac_q[i].head.netif_queue_id); } } + spin_unlock_bh(&peers_cache_lock); } /* Should be called from locked context */ @@ -188,6 +190,7 @@ static inline void fcq_wake_all_queues(struct slsi_dev *sdev) int i; struct peers_cache *pc_node, *next; + spin_lock_bh(&peers_cache_lock); list_for_each_entry_safe(pc_node, next, &peers_cache_list, list) { /* Wake queues that reported to be active, leave stopped the others. Do not wake queues in pause state */ for (i = 0; i < SLSI_NETIF_Q_PER_PEER; i++) { @@ -199,6 +202,7 @@ static inline void fcq_wake_all_queues(struct slsi_dev *sdev) } } } + spin_unlock_bh(&peers_cache_lock); } void scsc_wifi_fcq_pause_queues(struct slsi_dev *sdev) @@ -309,6 +313,7 @@ static void fcq_redistribute_smod(struct net_device *dev, struct slsi_dev *sdev, /* Saturate if number is lower than certian low level */ if (new_smod < scsc_wifi_fcq_minimum_smod) new_smod = scsc_wifi_fcq_minimum_smod; + spin_lock_bh(&peers_cache_lock); list_for_each_entry_safe(pc_node, next, &peers_cache_list, list) { if (pc_node->is_unicast) { qs_redis = pc_node->qs; @@ -339,6 +344,7 @@ static void fcq_redistribute_smod(struct net_device *dev, struct slsi_dev *sdev, fcq_redistribute_qmod(pc_node->dev, pc_node->qs, sdev, pc_node->peer_index, pc_node->vif); } } + spin_unlock_bh(&peers_cache_lock); } #ifdef EXPERIMENTAL_DYNAMIC_SMOD_ADAPTATION @@ -351,6 +357,7 @@ static int fcq_redistribute_smod_before_stopping(struct net_device *dev, struct return false; /* Search for nodes that were empty and are candidates to be redistributed */ + spin_lock_bh(&peers_cache_lock); list_for_each_entry_safe(pc_node, next, &peers_cache_list, list) { if (pc_node->is_unicast) { if (pc_node->qs->can_be_distributed && @@ -358,12 +365,14 @@ static int fcq_redistribute_smod_before_stopping(struct net_device *dev, struct pc_node->qs->in_sleep = true; total_in_sleep += 1; SLSI_DBG4_NODEV(SLSI_WIFI_FCQ, "Smod qs empty. Can be redistributed for vif %d peer_index %d qs->can_be_distributed %d\n", pc_node->vif, pc_node->peer_index, pc_node->qs->can_be_distributed); - fcq_redistribute_smod(dev, sdev, total_to_distribute); pc_node->qs->can_be_distributed = false; + spin_unlock_bh(&peers_cache_lock); + fcq_redistribute_smod(dev, sdev, total_to_distribute); return true; } } } + spin_unlock_bh(&peers_cache_lock); return false; } #endif @@ -595,12 +604,15 @@ int scsc_wifi_fcq_transmit_data(struct net_device *dev, struct scsc_wifi_fcq_dat spin_lock_bh(&qs->cp_lock); /* Check caller matches an existing peer record */ + spin_lock_bh(&peers_cache_lock); list_for_each_entry_safe(pc_node, next, &peers_cache_list, list) { if (pc_node->qs == qs && pc_node->peer_index == peer_index && pc_node->vif == vif && pc_node->dev == dev) { + spin_unlock_bh(&peers_cache_lock); goto found; } } + spin_unlock_bh(&peers_cache_lock); SLSI_DBG4_NODEV(SLSI_WIFI_FCQ, "Packet dropped. Detected incorrect peer record\n"); spin_unlock_bh(&qs->cp_lock); return -EINVAL; @@ -1044,7 +1056,9 @@ int scsc_wifi_fcq_unicast_qset_init(struct net_device *dev, struct scsc_wifi_fcq pc_new_node->is_unicast = true; SLSI_DBG1_NODEV(SLSI_WIFI_FCQ, "Add new peer qs %p vif %d peer->aid %d\n", qs, vif, peer->aid); + spin_lock_bh(&peers_cache_lock); list_add_tail(&pc_new_node->list, &peers_cache_list); + spin_unlock_bh(&peers_cache_lock); if (total == 0) { /* No peers. Reset gcod. */ @@ -1091,7 +1105,9 @@ int scsc_wifi_fcq_multicast_qset_init(struct net_device *dev, struct scsc_wifi_f pc_node->is_unicast = false; SLSI_DBG1_NODEV(SLSI_WIFI_FCQ, "Add Multicast Qset %p vif %d peer->aid 0\n", qs, vif); + spin_lock_bh(&peers_cache_lock); list_add_tail(&pc_node->list, &peers_cache_list); + spin_unlock_bh(&peers_cache_lock); fcq_redistribute_qmod(pc_node->dev, pc_node->qs, sdev, pc_node->peer_index, pc_node->vif); @@ -1140,12 +1156,14 @@ void scsc_wifi_fcq_qset_deinit(struct net_device *dev, struct scsc_wifi_fcq_data else SLSI_DBG1_NODEV(SLSI_WIFI_FCQ, "Delete qs %p vif %d Multicast\n", qs, vif); + spin_lock_bh(&peers_cache_lock); list_for_each_entry_safe(pc_node, next, &peers_cache_list, list) { if (pc_node->qs == qs) { list_del(&pc_node->list); kfree(pc_node); } } + spin_unlock_bh(&peers_cache_lock); /* Only count unicast qs */ if (total > 0 && peer) total--; diff --git a/drivers/net/wireless/scsc/tx.c b/drivers/net/wireless/scsc/tx.c index e0eca7cb19cb..c8236ee3e51b 100755 --- a/drivers/net/wireless/scsc/tx.c +++ b/drivers/net/wireless/scsc/tx.c @@ -76,7 +76,9 @@ static int slsi_tx_eapol(struct slsi_dev *sdev, struct net_device *dev, struct s * In M4 packet, * - MIC bit set in key info * - Key type bit set in key info (pairwise=1, Group=0) - * - Key Data Length would be 0 + * - ACK bit will not be set + * - Secure bit will be set in key type RSN (WPA2/WPA3 Personal/WPA3 Enterprise) + * - Key Data length check for Zero is for WPA as Secure bit will not be set */ if ((skb->len - sizeof(struct ethhdr)) >= 99) eapol = skb->data + sizeof(struct ethhdr); @@ -85,9 +87,10 @@ static int slsi_tx_eapol(struct slsi_dev *sdev, struct net_device *dev, struct s if ((eapol[SLSI_EAPOL_TYPE_POS] == SLSI_EAPOL_TYPE_RSN_KEY || eapol[SLSI_EAPOL_TYPE_POS] == SLSI_EAPOL_TYPE_WPA_KEY) && (eapol[SLSI_EAPOL_KEY_INFO_LOWER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_KEY_TYPE_BIT_IN_LOWER_BYTE) && + (!(eapol[SLSI_EAPOL_KEY_INFO_LOWER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_ACK_BIT_IN_LOWER_BYTE)) && (eapol[SLSI_EAPOL_KEY_INFO_HIGHER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_MIC_BIT_IN_HIGHER_BYTE) && - (eapol[SLSI_EAPOL_KEY_DATA_LENGTH_HIGHER_BYTE_POS] == 0) && - (eapol[SLSI_EAPOL_KEY_DATA_LENGTH_LOWER_BYTE_POS] == 0)) { + ((eapol[SLSI_EAPOL_KEY_INFO_HIGHER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_SECURE_BIT_IN_HIGHER_BYTE) || + ((eapol[SLSI_EAPOL_KEY_DATA_LENGTH_HIGHER_BYTE_POS] == 0) && (eapol[SLSI_EAPOL_KEY_DATA_LENGTH_LOWER_BYTE_POS] == 0)))) { msg_type = FAPI_MESSAGETYPE_EAPOL_KEY_M4; dwell_time = 0; } diff --git a/drivers/net/wireless/scsc/utils.h b/drivers/net/wireless/scsc/utils.h index b40653f2e037..278a759ab9f9 100755 --- a/drivers/net/wireless/scsc/utils.h +++ b/drivers/net/wireless/scsc/utils.h @@ -67,8 +67,7 @@ extern "C" { extern uint slsi_sg_host_align_mask; #define SLSI_HIP_FH_SIG_PREAMBLE_LEN 4 -#define SLSI_SKB_GET_ALIGNMENT_OFFSET(skb) (offset_in_page(skb->data + SLSI_NETIF_SKB_HEADROOM - SLSI_HIP_FH_SIG_PREAMBLE_LEN) \ - & slsi_sg_host_align_mask) +#define SLSI_SKB_GET_ALIGNMENT_OFFSET(skb) (0) /* Get the Compiler to ignore Unused parameters */ #define SLSI_UNUSED_PARAMETER(x) ((void)(x)) diff --git a/include/scsc/scsc_mx.h b/include/scsc/scsc_mx.h index 95c6c3b26116..cf4da45383e8 100755 --- a/include/scsc/scsc_mx.h +++ b/include/scsc/scsc_mx.h @@ -61,6 +61,71 @@ enum scsc_qos_config { }; #endif +/* SYSTEM ERROR SUB-SYSTEMS */ +#define SYSERR_SUBSYS_COMMON (0) +#define SYSERR_SUBSYS_BT (1) +#define SYSERR_SUBSYS_WLAN (2) +#define SYSERR_SUBSYS_HOST (8) + + +/* SYSTEM ERROR levels */ + +/* System Error level 1 + * Minor warning from firmware + * Should not be escalted by driver + */ +#define MX_SYSERR_LEVEL_1 (1) + +/* System Error level 2 + * More severe warning from firmware + * May be escalated by driver + */ +#define MX_SYSERR_LEVEL_2 (2) + +/* System Error level 2 + * Minor error handled in firmware + * may be escalated by driver + */ +#define MX_SYSERR_LEVEL_3 (3) + +/* System Error level 3 + * More severe error handled in firmware + * May be escalated by driver + */ +#define MX_SYSERR_LEVEL_4 (4) + +/* System Error level 5 + * Firmware requested service restart + * May be escalated by driver + */ +#define MX_SYSERR_LEVEL_5 (5) + +/* System Error level 5 + * Firmware requested service restart (firmware may have restarted some hardware) + * May be escalated by driver + */ +#define MX_SYSERR_LEVEL_6 (6) + +/* System Error level 7 + * Firmware halt and full restart required + */ +#define MX_SYSERR_LEVEL_7 (7) + +/* Null error code */ +#define MX_NULL_SYSERR (0xFFFF) + +/* Details for decoding */ +#define SYSERR_SUB_CODE_POSN (0) +#define SYSERR_SUB_CODE_MASK (0xFFFF) +#define SYSERR_SUB_SYSTEM_POSN (12) +#define SYSERR_SUB_SYSTEM_MASK (0xF) +#define SYSERR_LEVEL_POSN (24) +#define SYSERR_LEVEL_MASK (0xF) +#define SYSERR_TYPE_POSN (28) +#define SYSERR_TYPE_MASK (0xFF) +#define SYSERR_SUB_SYSTEM_HOST (8) + + /* Core Driver Module registration */ struct scsc_mx_module_client { char *name; @@ -70,16 +135,42 @@ struct scsc_mx_module_client { /* Service Client interface */ +/* Decoded syserr_code */ +struct mx_syserr_decode { + u8 subsys; + u8 level; + u16 type; + u16 subcode; +}; + struct scsc_service_client; struct scsc_service_client { - /** Called on Maxwell failure. The Client should Stop all SDRAM & MIF - * Mailbox access as fast as possible and inform the Manager by calling - * client_stopped() */ + /** Called on Maxwell System Error. The Client should use its internal state information + * and the information provided by this call to return an appropriate recovery level + * which must be greater than or equal to that passed in as parameter within the + * mx_syserr_decode structure. + */ + u8 (*failure_notification)(struct scsc_service_client *client, struct mx_syserr_decode *err); + /** Called on Maxwell failure requiring a service restart or full chip restart. + * The level within the mx_syserr_decode structure indicates the recover level taking place. + * The Client should Stop all SDRAM & MIF Mailbox access as fast as possible + * and inform the Manager by calling client_stopped(). The boolean return value + * indicates that this failure should trigger an slsi_send_hanged_vendor_event + * if WLAN is active (common code will ensure this happens by passing scsc_syserr_code + * parameter with a value other than MX_NULL_SYSERR when failure_reset is called subsequently) + */ + bool (*stop_on_failure_v2)(struct scsc_service_client *client, struct mx_syserr_decode *err); + /* Old version to be depricated */ void (*stop_on_failure)(struct scsc_service_client *client); - /** Called when Maxwell failure has handled and the Maxwell has been - * reset. The Client should assume that any Maxwell resources it held are - * invalid */ + /** Called when Maxwell failure has been handled and the Maxwell has been + * reset if the level has demanded it. The Client should assume that any Maxwell + * resources it held are invalid. If a scsc_syserr_code other than MX_NULL_SYSERR is provided, + * then this may be propogated by the WLAN driver as a slsi_send_hanged_vendor_event + * to notify the host of the failure and its cause + */ + void (*failure_reset_v2)(struct scsc_service_client *client, u8 level, u16 scsc_syserr_code); + /* Old version to be depricated */ void (*failure_reset)(struct scsc_service_client *client, u16 scsc_panic_code); /* called when AP processor is going into suspend. */ int (*suspend)(struct scsc_service_client *client); @@ -89,7 +180,6 @@ struct scsc_service_client { void (*log)(struct scsc_service_client *client, u16 reason); }; -#ifdef CONFIG_SCSC_FM /* * This must be used by FM Radio Service only. Other services must not use it. * FM Radio client must allocate memory for this structure using scsc_mx_service_mifram_alloc() @@ -110,7 +200,6 @@ struct wlbt_fm_params { u32 freq; /* Frequency (Hz) in use by FM radio */ }; -#endif #define PANIC_RECORD_SIZE 64 #define PANIC_RECORD_DUMP_BUFFER_SZ 4096 @@ -147,7 +236,7 @@ int scsc_mx_service_close(struct scsc_service *service); int scsc_mx_service_mif_dump_registers(struct scsc_service *service); /** Signal a failure detected by the Client. This will trigger the systemwide - * failure handling procedure: _All_ Clients will be called back via + * MX_SYSERR_LEVEL_7 failure handling procedure: _All_ Clients will be called back via * their stop_on_failure() handler as a side-effect. */ void scsc_mx_service_service_failed(struct scsc_service *service, const char *reason); diff --git a/include/scsc/scsc_release.h b/include/scsc/scsc_release.h index 1688e3ebc460..1b805988223f 100644 --- a/include/scsc/scsc_release.h +++ b/include/scsc/scsc_release.h @@ -7,10 +7,20 @@ #ifndef _SCSC_RELEASE_H #define _SCSC_RELEASE_H +#ifdef CONFIG_SOC_EXYNOS3830 +#define SCSC_RELEASE_SOLUTION "mx152" +#elif defined(CONFIG_SOC_EXYNOS9630) +#define SCSC_RELEASE_SOLUTION "mx450" +#elif defined(CONFIG_SOC_EXYNOS9610) #define SCSC_RELEASE_SOLUTION "mx250" +#else +#define SCSC_RELEASE_SOLUTION "wlbt" +#endif + + #define SCSC_RELEASE_PRODUCT 10 -#define SCSC_RELEASE_ITERATION 1 +#define SCSC_RELEASE_ITERATION 3 #define SCSC_RELEASE_CANDIDATE 1 #define SCSC_RELEASE_POINT 0 -- 2.20.1