diff --git a/android/GKI_VERSION b/android/GKI_VERSION
index da598c937dca..0add1e35cf7d 100644
--- a/android/GKI_VERSION
+++ b/android/GKI_VERSION
@@ -1 +1 @@
-LTS_5.4.86_7d99cf8c84a7
+LTS_5.4.86_fe9e8630bfaf
diff --git a/android/abi_gki_aarch64.xml b/android/abi_gki_aarch64.xml
index 2e8113c8c557..ff1a17ee7753 100644
--- a/android/abi_gki_aarch64.xml
+++ b/android/abi_gki_aarch64.xml
@@ -3967,6 +3967,7 @@
+
@@ -128457,6 +128458,13 @@
+
+
+
+
+
+
+
diff --git a/android/abi_gki_aarch64_7d99cf8c84a7.xml b/android/abi_gki_aarch64_e399f16519fa.xml
similarity index 99%
rename from android/abi_gki_aarch64_7d99cf8c84a7.xml
rename to android/abi_gki_aarch64_e399f16519fa.xml
index f65ea5b69f70..f6a0b4d8467f 100644
--- a/android/abi_gki_aarch64_7d99cf8c84a7.xml
+++ b/android/abi_gki_aarch64_e399f16519fa.xml
@@ -94598,6 +94598,12 @@
+
+
+
+
+
+
@@ -128690,15 +128696,15 @@
-
-
-
-
+
+
+
+
-
-
-
+
+
+
@@ -130490,12 +130496,6 @@
-
-
-
-
-
-
diff --git a/android/abi_gki_aarch64_exynos b/android/abi_gki_aarch64_exynos
index 1f795dac9b27..4316e90e289d 100644
--- a/android/abi_gki_aarch64_exynos
+++ b/android/abi_gki_aarch64_exynos
@@ -1598,6 +1598,7 @@
uart_update_timeout
uart_write_wakeup
__udelay
+ ufshcd_config_pwr_mode
ufshcd_dme_get_attr
ufshcd_dme_set_attr
ufshcd_pltfrm_init
diff --git a/android/abi_gki_aarch64_oplus b/android/abi_gki_aarch64_oplus
index 15ac9a66a517..c2eacbe29161 100644
--- a/android/abi_gki_aarch64_oplus
+++ b/android/abi_gki_aarch64_oplus
@@ -198,6 +198,7 @@
remove_proc_subtree
request_firmware
rtc_time64_to_tm
+ spi_set_cs_timing
spi_sync
strcat
system_wq
diff --git a/android/abi_gki_aarch64_xiaomi b/android/abi_gki_aarch64_xiaomi
index 95acdab2fb70..824b53703ce5 100644
--- a/android/abi_gki_aarch64_xiaomi
+++ b/android/abi_gki_aarch64_xiaomi
@@ -16,6 +16,13 @@
regcache_drop_region
snd_soc_dapm_del_routes
+# required by us_prox.ko
+ iio_device_unregister
+ iio_device_free
+ __iio_device_register
+ __kfifo_to_user
+ iio_device_alloc
+
# required by tfa98xx_dlkm.ko
device_remove_bin_file
snd_pcm_hw_constraint_mask64
@@ -32,15 +39,21 @@
# required by msm_drm.ko
kobject_set_name
+ wakeup_source_destroy
+ wakeup_source_remove
+ wakeup_source_add
+ wakeup_source_create
# required by exfat.ko
block_write_full_page
__blockdev_direct_IO
__bread_gfp
__brelse
+ clear_nlink
cont_write_begin
d_find_alias
d_move
+ d_splice_alias
filemap_fdatawait_range
filemap_fdatawrite_range
full_name_hash
@@ -53,6 +66,7 @@
generic_fillattr
generic_write_end
__getblk_gfp
+ igrab
ilookup
in_group_p
inc_nlink
@@ -76,14 +90,20 @@
sb_min_blocksize
sb_set_blocksize
setattr_copy
+ setattr_prepare
+ set_nlink
sync_blockdev
sync_dirty_buffer
sync_mapping_buffers
__remove_inode_hash
touch_buffer
truncate_pagecache
+ truncate_setsize
unload_nls
unlock_buffer
utf16s_to_utf8s
utf8s_to_utf16s
write_inode_now
+
+# required by incrementalfs.ko
+ revert_creds
diff --git a/arch/arm/configs/vendor/sdxlemur.config b/arch/arm/configs/vendor/sdxlemur.config
index 97f39227174d..7639d9fd35fd 100644
--- a/arch/arm/configs/vendor/sdxlemur.config
+++ b/arch/arm/configs/vendor/sdxlemur.config
@@ -342,6 +342,8 @@ CONFIG_QCOM_EUD=y
CONFIG_POWER_RESET_QCOM_DOWNLOAD_MODE=y
CONFIG_POWER_RESET_QCOM_DOWNLOAD_MODE_DEFAULT=y
CONFIG_POWER_RESET_QCOM_REBOOT_REASON=y
+CONFIG_POWER_RESET_MSM=y
+CONFIG_QCOM_MINIDUMP=y
CONFIG_ENABLE_SFE=y
# CONFIG_ENABLE_VMALLOC_SAVING is not set
# CONFIG_SLUB_DEBUG is not set
@@ -355,9 +357,6 @@ CONFIG_IOSS=m
CONFIG_AQFWD_IOSS=m
CONFIG_R8125=y
CONFIG_R8125_IOSS=m
-CONFIG_IPA_ETH=y
-CONFIG_IPA_ETH_DEBUG=y
-# CONFIG_IPA_ETH_NOAUTO is not set
CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_HARDENED_USERCOPY=y
# CONFIG_HARDENED_USERCOPY_FALLBACK is not set
@@ -371,3 +370,5 @@ CONFIG_QPS615=m
# CONFIG_BLK_CGROUP is not set
# CONFIG_BLK_DEBUG_FS is not set
# CONFIG_VT is not set
+CONFIG_DRM=y
+CONFIG_GKI_HIDDEN_DRM_CONFIGS=y
diff --git a/drivers/bluetooth/btpower.c b/drivers/bluetooth/btpower.c
index 93a7fbb688b9..e149f5a135dd 100644
--- a/drivers/bluetooth/btpower.c
+++ b/drivers/bluetooth/btpower.c
@@ -32,12 +32,6 @@
#endif
#include
-#define PWR_SRC_STATUS_SET(index, status) do { \
- if (index >= PWR_SRC_INIT_STATE_IDX && index < BT_POWER_SRC_SIZE) { \
- bt_power_src_status[index] = (int) status; \
- } \
-} while (0)
-
#define PWR_SRC_NOT_AVAILABLE -2
#define DEFAULT_INVALID_VALUE -1
#define PWR_SRC_INIT_STATE_IDX 0
@@ -76,9 +70,24 @@ enum power_src_pos {
BT_VDD_RFA_0p8_CURRENT,
BT_VDD_RFACMN_CURRENT,
BT_VDD_IPA_2p2,
- BT_VDD_IPA_2p2_CURRENT
+ BT_VDD_IPA_2p2_CURRENT,
+ /* The below bucks are voted for HW WAR on some platform which supports
+ * WNC39xx.
+ */
+ BT_VDD_SMPS,
+ BT_VDD_SMPS_CURRENT,
+ /* New entries need to be added before PWR_SRC_SIZE.
+ * Its hold the max size of power sources states.
+ */
+ BT_POWER_SRC_SIZE,
};
+#define PWR_SRC_STATUS_SET(index, status) do { \
+ if (index >= PWR_SRC_INIT_STATE_IDX && index < BT_POWER_SRC_SIZE) { \
+ bt_power_src_status[index] = (int) status; \
+ } \
+} while (0)
+
// Regulator structure for QCA6174/QCA9377/QCA9379 BT SoC series
static struct bt_power_vreg_data bt_vregs_info_qca61x4_937x[] = {
{NULL, "qcom,bt-vdd-aon", 928000, 928000, 0, false, false,
@@ -116,6 +125,8 @@ static struct bt_power_vreg_data bt_vregs_info_qca6xx0[] = {
static struct bt_power bt_vreg_info_wcn399x = {
.compatible = "qcom,wcn3990",
.vregs = (struct bt_power_vreg_data []) {
+ {NULL, "qcom,bt-vdd-smps", 984000, 984000, 0, false, false,
+ {BT_VDD_SMPS, BT_VDD_SMPS_CURRENT}},
{NULL, "qcom,bt-vdd-io", 1700000, 1900000, 0, false, false,
{BT_VDD_IO_LDO, BT_VDD_IO_LDO_CURRENT}},
{NULL, "qcom,bt-vdd-core", 1304000, 1304000, 0, false, false,
@@ -125,7 +136,7 @@ static struct bt_power bt_vreg_info_wcn399x = {
{NULL, "qcom,bt-vdd-xtal", 1700000, 1900000, 0, false, false,
{BT_VDD_XTAL_LDO, BT_VDD_XTAL_LDO_CURRENT}},
},
- .num_vregs = 4,
+ .num_vregs = 5,
};
static struct bt_power bt_vreg_info_qca_auto = {
diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c
index 526d7fd140e8..f026131cf3ab 100644
--- a/drivers/bus/mhi/core/mhi_init.c
+++ b/drivers/bus/mhi/core/mhi_init.c
@@ -92,9 +92,12 @@ struct mhi_controller *find_mhi_controller_by_name(const char *name)
const char *to_mhi_pm_state_str(enum MHI_PM_STATE state)
{
- int index = find_last_bit((unsigned long *)&state, 32);
+ int index;
- if (index >= ARRAY_SIZE(mhi_pm_state_str))
+ if (state)
+ index = __fls(state);
+
+ if (!state || index >= ARRAY_SIZE(mhi_pm_state_str))
return "Invalid State";
return mhi_pm_state_str[index];
diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c
index 0fad46875bab..49a425a6e374 100644
--- a/drivers/bus/mhi/core/mhi_pm.c
+++ b/drivers/bus/mhi/core/mhi_pm.c
@@ -1063,8 +1063,8 @@ void mhi_control_error(struct mhi_controller *mhi_cntrl)
sfr_info->buf_addr);
}
- /* link is not down if device is in RDDM */
- transition_state = (mhi_cntrl->ee == MHI_EE_RDDM) ?
+ /* link is not down if device supports RDDM */
+ transition_state = (mhi_cntrl->rddm_supported) ?
MHI_PM_DEVICE_ERR_DETECT : MHI_PM_LD_ERR_FATAL_DETECT;
write_lock_irq(&mhi_cntrl->pm_lock);
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index e40f344609f8..391761765f3b 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -2895,7 +2895,12 @@ static int alpha_pll_lucid_set_rate(struct clk_hw *hw, unsigned long rate,
/* Wait for 2 reference cycles before checking the ACK bit. */
udelay(1);
regmap_read(pll->clkr.regmap, PLL_MODE(pll), ®val);
- if (!(regval & ALPHA_PLL_ACK_LATCH)) {
+ if (!(regval & PLL_UPDATE_BYPASS)) {
+ ret = wait_for_pll_update(pll);
+ if (ret)
+ WARN_CLK(&pll->clkr.hw, 1, "PLL Update clear failed\n");
+ return ret;
+ } else if (!(regval & ALPHA_PLL_ACK_LATCH)) {
WARN_CLK(&pll->clkr.hw, 1,
"PLL latch failed. Output may be unstable!\n");
return -EINVAL;
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 9fc95cf85c6c..43c772826b18 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -247,7 +247,8 @@ static void qcom_cc_set_critical(struct device *dev, struct qcom_cc *cc)
if (i >= cc->num_rclks)
continue;
- cc->rclks[i]->flags |= QCOM_CLK_IS_CRITICAL;
+ if (cc->rclks[i])
+ cc->rclks[i]->flags |= QCOM_CLK_IS_CRITICAL;
}
of_property_for_each_u32(dev->of_node, "qcom,critical-devices", prop, p, i) {
@@ -265,7 +266,8 @@ static void qcom_cc_set_critical(struct device *dev, struct qcom_cc *cc)
if (args.np != dev->of_node || clock_idx >= cc->num_rclks)
continue;
- cc->rclks[clock_idx]->flags |= QCOM_CLK_IS_CRITICAL;
+ if (cc->rclks[clock_idx])
+ cc->rclks[clock_idx]->flags |= QCOM_CLK_IS_CRITICAL;
of_node_put(args.np);
}
diff --git a/drivers/clk/qcom/debugcc-blair.c b/drivers/clk/qcom/debugcc-blair.c
index 0217171d265f..9b21682f12d3 100644
--- a/drivers/clk/qcom/debugcc-blair.c
+++ b/drivers/clk/qcom/debugcc-blair.c
@@ -230,6 +230,7 @@ static const char *const gcc_debug_mux_parent_names[] = {
"gcc_video_xo_clk",
"gpu_cc_debug_mux",
"mc_cc_debug_mux",
+ "measure_only_cnoc_clk",
"measure_only_gcc_camera_ahb_clk",
"measure_only_gcc_camera_xo_clk",
"measure_only_gcc_cpuss_gnoc_clk",
@@ -237,6 +238,12 @@ static const char *const gcc_debug_mux_parent_names[] = {
"measure_only_gcc_disp_xo_clk",
"measure_only_gcc_gpu_cfg_ahb_clk",
"measure_only_gcc_video_ahb_clk",
+ "measure_only_hwkm_ahb_clk",
+ "measure_only_hwkm_km_core_clk",
+ "measure_only_ipa_2x_clk",
+ "measure_only_pka_ahb_clk",
+ "measure_only_pka_core_clk",
+ "measure_only_snoc_clk",
};
static int gcc_debug_mux_sels[] = {
@@ -350,6 +357,7 @@ static int gcc_debug_mux_sels[] = {
0x3F, /* gcc_video_xo_clk */
0xFB, /* gpu_cc_debug_mux */
0xAE, /* mc_cc_debug_mux */
+ 0x1D, /* measure_only_cnoc_clk */
0x38, /* measure_only_gcc_camera_ahb_clk */
0x40, /* measure_only_gcc_camera_xo_clk */
0xBA, /* measure_only_gcc_cpuss_gnoc_clk */
@@ -357,6 +365,12 @@ static int gcc_debug_mux_sels[] = {
0x41, /* measure_only_gcc_disp_xo_clk */
0xF9, /* measure_only_gcc_gpu_cfg_ahb_clk */
0x37, /* measure_only_gcc_video_ahb_clk */
+ 0x166, /* measure_only_hwkm_ahb_clk */
+ 0x167, /* measure_only_hwkm_km_core_clk */
+ 0xD7, /* measure_only_ipa_2x_clk */
+ 0x162, /* measure_only_pka_ahb_clk */
+ 0x163, /* measure_only_pka_core_clk */
+ 0x7, /* measure_only_snoc_clk */
};
static struct clk_debug_mux gcc_debug_mux = {
@@ -450,6 +464,14 @@ static struct mux_regmap_names mux_list[] = {
{ .mux = &mc_cc_debug_mux, .regmap_name = "qcom,mccc" },
};
+static struct clk_dummy measure_only_cnoc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_cnoc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
static struct clk_dummy measure_only_mccc_clk = {
.rrate = 1000,
.hw.init = &(struct clk_init_data){
@@ -530,6 +552,54 @@ static struct clk_dummy measure_only_gcc_video_ahb_clk = {
},
};
+static struct clk_dummy measure_only_hwkm_ahb_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_hwkm_ahb_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_hwkm_km_core_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_hwkm_km_core_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_ipa_2x_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_ipa_2x_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_pka_ahb_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_pka_ahb_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_pka_core_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_pka_core_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_snoc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_snoc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
static struct clk_dummy perfcl_clk = {
.rrate = 1000,
.hw.init = &(struct clk_init_data){
@@ -555,6 +625,7 @@ static struct clk_dummy l3_clk = {
};
static struct clk_hw *debugcc_blair_hws[] = {
+ &measure_only_cnoc_clk.hw,
&measure_only_disp_cc_sleep_clk.hw,
&measure_only_disp_cc_xo_clk.hw,
&measure_only_gcc_camera_ahb_clk.hw,
@@ -564,7 +635,13 @@ static struct clk_hw *debugcc_blair_hws[] = {
&measure_only_gcc_disp_xo_clk.hw,
&measure_only_gcc_gpu_cfg_ahb_clk.hw,
&measure_only_gcc_video_ahb_clk.hw,
+ &measure_only_ipa_2x_clk.hw,
&measure_only_mccc_clk.hw,
+ &measure_only_snoc_clk.hw,
+ &measure_only_hwkm_km_core_clk.hw,
+ &measure_only_hwkm_ahb_clk.hw,
+ &measure_only_pka_core_clk.hw,
+ &measure_only_pka_ahb_clk.hw,
&perfcl_clk.hw,
&pwrcl_clk.hw,
&l3_clk.hw,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 2388ccd48b70..f68815f3cf1a 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -933,9 +934,15 @@ static struct kgsl_process_private *kgsl_process_private_new(
/* Search in the process list */
list_for_each_entry(private, &kgsl_driver.process_list, list) {
if (private->pid == cur_pid) {
- if (!kgsl_process_private_get(private)) {
- private = ERR_PTR(-EINVAL);
- }
+ if (!kgsl_process_private_get(private))
+ /*
+ * This will happen only if refcount is zero
+ * i.e. destroy is triggered but didn't complete
+ * yet. Return -EEXIST to indicate caller that
+ * destroy is pending to allow caller to take
+ * appropriate action.
+ */
+ private = ERR_PTR(-EEXIST);
/*
* We need to hold only one reference to the PID for
* each process struct to avoid overflowing the
@@ -1043,8 +1050,7 @@ static void kgsl_process_private_close(struct kgsl_device_private *dev_priv,
kgsl_process_private_put(private);
}
-
-static struct kgsl_process_private *kgsl_process_private_open(
+static struct kgsl_process_private *_process_private_open(
struct kgsl_device *device)
{
struct kgsl_process_private *private;
@@ -1062,6 +1068,27 @@ done:
return private;
}
+static struct kgsl_process_private *kgsl_process_private_open(
+ struct kgsl_device *device)
+{
+ struct kgsl_process_private *private;
+ int i;
+
+ private = _process_private_open(device);
+
+ /*
+ * If we get error and error is -EEXIST that means previous process
+ * private destroy is triggered but didn't complete. Retry creating
+ * process private after sometime to allow previous destroy to complete.
+ */
+ for (i = 0; (PTR_ERR_OR_ZERO(private) == -EEXIST) && (i < 5); i++) {
+ usleep_range(10, 100);
+ private = _process_private_open(device);
+ }
+
+ return private;
+}
+
int kgsl_gpu_frame_count(pid_t pid, u64 *frame_count)
{
struct kgsl_process_private *p;
@@ -3042,11 +3069,22 @@ void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
}
spin_unlock(&kgsl_dmabuf_lock);
}
+
+unsigned long kgsl_get_dmabuf_inode_number(struct kgsl_mem_entry *entry)
+{
+ struct kgsl_dma_buf_meta *meta = entry->priv_data;
+
+ return meta ? file_inode(meta->dmabuf->file)->i_ino : 0;
+}
#else
void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
int *egl_surface_count, int *egl_image_count)
{
}
+
+unsigned long kgsl_get_dmabuf_inode_number(struct kgsl_mem_entry *entry)
+{
+}
#endif
long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index fc2cb005722c..bb5ee365117d 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -453,6 +453,8 @@ void kgsl_mem_entry_destroy(struct kref *kref);
void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
int *egl_surface_count, int *egl_image_count);
+unsigned long kgsl_get_dmabuf_inode_number(struct kgsl_mem_entry *entry);
+
struct kgsl_mem_entry * __must_check
kgsl_sharedmem_find(struct kgsl_process_private *private, uint64_t gpuaddr);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 937cbb669485..8261d9ad2457 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2002,2008-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002,2008-2021, The Linux Foundation. All rights reserved.
*/
#include
@@ -177,6 +177,7 @@ static int print_mem_entry(void *data, void *ptr)
struct kgsl_memdesc *m = &entry->memdesc;
unsigned int usermem_type = kgsl_memdesc_usermem_type(m);
int egl_surface_count = 0, egl_image_count = 0;
+ unsigned long inode_number = 0;
flags[0] = kgsl_memdesc_is_global(m) ? 'g' : '-';
flags[1] = '-';
@@ -192,11 +193,13 @@ static int print_mem_entry(void *data, void *ptr)
kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
- if (usermem_type == KGSL_MEM_ENTRY_ION)
+ if (usermem_type == KGSL_MEM_ENTRY_ION) {
kgsl_get_egl_counts(entry, &egl_surface_count,
&egl_image_count);
+ inode_number = kgsl_get_dmabuf_inode_number(entry);
+ }
- seq_printf(s, "%pK %pK %16llu %5d %9s %10s %16s %5d %16llu %6d %6d",
+ seq_printf(s, "%pK %pK %16llu %5d %9s %10s %16s %5d %16llu %6d %6d %10lu",
(uint64_t *)(uintptr_t) m->gpuaddr,
/*
* Show zero for the useraddr - we can't reliably track
@@ -205,7 +208,7 @@ static int print_mem_entry(void *data, void *ptr)
0, m->size, entry->id, flags,
memtype_str(usermem_type),
usage, (m->sgt ? m->sgt->nents : 0), m->size,
- egl_surface_count, egl_image_count);
+ egl_surface_count, egl_image_count, inode_number);
if (entry->metadata[0] != 0)
seq_printf(s, " %s", entry->metadata);
@@ -273,9 +276,9 @@ static void *process_mem_seq_next(struct seq_file *s, void *ptr,
static int process_mem_seq_show(struct seq_file *s, void *ptr)
{
if (ptr == SEQ_START_TOKEN) {
- seq_printf(s, "%16s %16s %16s %5s %9s %10s %16s %5s %16s %6s %6s\n",
+ seq_printf(s, "%16s %16s %16s %5s %9s %10s %16s %5s %16s %6s %6s %10s\n",
"gpuaddr", "useraddr", "size", "id", "flags", "type",
- "usage", "sglen", "mapsize", "eglsrf", "eglimg");
+ "usage", "sglen", "mapsize", "eglsrf", "eglimg", "inode");
return 0;
} else
return print_mem_entry(s, ptr);
diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c
index 7a785ed2e65d..c26294f1615c 100644
--- a/drivers/hwtracing/coresight/coresight-byte-cntr.c
+++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c
@@ -674,7 +674,8 @@ static void etr_pcie_client_cb(struct mhi_dev_client_cb_data *cb_data)
byte_cntr_data = cb_data->user_data;
if (!byte_cntr_data)
return;
-
+ if (tmcdrvdata->pcie_path != TMC_ETR_PCIE_SW_PATH)
+ return;
switch (cb_data->ctrl_info) {
case MHI_STATE_CONNECTED:
if (cb_data->channel == byte_cntr_data->pcie_out_chan) {
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index f661bcde5bb8..02e55f98b5cf 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1706,8 +1706,6 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
tmc_etr_byte_cntr_start(drvdata->byte_cntr);
- if (drvdata->out_mode == TMC_ETR_OUT_MODE_PCIE)
- etr_pcie_start(drvdata->byte_cntr);
if (drvdata->out_mode == TMC_ETR_OUT_MODE_PCIE
&& drvdata->pcie_path == TMC_ETR_PCIE_SW_PATH)
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index 0c7713b4ef2e..fc670d7ef4e7 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -89,6 +90,24 @@
#define ADC_APP_SID_MASK 0xf
#define ADC7_CONV_TIMEOUT_MS 501
+/* For ADC_PBS on PMIC7 with SW calibration */
+#define ADC7_SW_CALIB_PBS_CALREF_FLAG 0x57
+#define ADC7_SW_CALIB_PBS_CALREF_RDY BIT(7)
+
+#define ADC7_SW_CALIB_PBS_GND_REF_D0 0x58
+#define ADC7_SW_CALIB_PBS_GND_REF_D1 0x59
+#define ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D0 0x5a
+#define ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D1 0x5b
+#define ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D0 0x5c
+#define ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D1 0x5d
+
+/* For ADC_CMN on PMIC7 with SW calibration */
+#define ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL 0x4e
+#define ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ BIT(2)
+#define ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ_MODE BIT(1)
+#define ADC7_PBUS_WRITE_SYNC_BYPASS BIT(0)
+
+
enum adc5_cal_method {
ADC5_NO_CAL = 0,
ADC5_RATIOMETRIC_CAL,
@@ -134,6 +153,8 @@ struct adc5_channel_prop {
* @regmap: SPMI ADC5 peripheral register map field.
* @dev: SPMI ADC5 device.
* @base: base address for the ADC peripheral.
+ * @cmn_base: base address for the ADC_CMN peripheral, needed
+ * for SW calibrated ADC.
* @nchannels: number of ADC channels.
* @chan_props: array of ADC channel properties.
* @iio_chans: array of IIO channels specification.
@@ -146,6 +167,7 @@ struct adc5_chip {
struct regmap *regmap;
struct device *dev;
u16 base;
+ u16 cmn_base;
unsigned int nchannels;
struct adc5_channel_prop *chan_props;
struct iio_chan_spec *iio_chans;
@@ -175,17 +197,50 @@ static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
{
- return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
+ int ret;
+
+ ret = regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
+ if (ret)
+ pr_err("adc read to register %#x of length:%d failed, ret=%d\n",
+ offset, len, ret);
+
+ return ret;
}
static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
{
- return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
+ int ret;
+
+ ret = regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
+ if (ret)
+ pr_err("adc write to register %#x of length:%d failed, ret=%d\n",
+ offset, len, ret);
+
+ return ret;
}
static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
{
- return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
+ int ret;
+
+ ret = regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
+ if (ret)
+ pr_err("adc masked write to register %#x with mask:0x%x failed, ret=%d\n",
+ offset, mask, ret);
+
+ return ret;
+}
+
+static int adc5_cmn_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ int ret;
+
+ ret = regmap_bulk_write(adc->regmap, adc->cmn_base + offset, data, len);
+ if (ret)
+ pr_err("adc_cmn write to register %#x of length:%d failed, ret=%d\n",
+ offset, len, ret);
+
+ return ret;
}
static int adc5_prescaling_from_dt(u32 num, u32 den)
@@ -242,11 +297,11 @@ static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
int ret;
u8 rslt_lsb, rslt_msb;
- ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
+ ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, 1);
if (ret)
return ret;
- ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
+ ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, 1);
if (ret)
return ret;
@@ -385,6 +440,52 @@ static int adc7_configure(struct adc5_chip *adc,
return ret;
}
+static int adc7_sw_calib_configure(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop)
+{
+ int ret;
+ u8 buf[5], val = 0;
+
+ /* Read registers 0x42 through 0x46 */
+ ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ /* Digital param selection */
+ adc5_update_dig_param(adc, prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
+ buf[1] |= prop->avg_samples | ADC5_USR_FAST_AVG_CTL_EN;
+
+ /* Select ADC channel */
+ buf[2] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
+ buf[3] |= prop->hw_settle_time;
+
+ /* Select ADC enable */
+ buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
+
+ if (!adc->poll_eoc)
+ reinit_completion(&adc->complete);
+
+ ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ val = ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ | ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ_MODE;
+
+ ret = adc5_cmn_write(adc, ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL, &val, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Select CONV request */
+ val = ADC5_USR_CONV_REQ_REQ;
+ return adc5_write(adc, ADC5_USR_CONV_REQ, &val, 1);
+}
+
static int adc5_do_conversion(struct adc5_chip *adc,
struct adc5_channel_prop *prop,
struct iio_chan_spec const *chan,
@@ -494,6 +595,59 @@ unlock:
return ret;
}
+#define ADC7_SW_CALIB_CONV_TIMEOUT_MS 150
+static int adc7_sw_calib_do_conversion(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop, u16 *adc_code_volt)
+{
+ int ret;
+ unsigned long rc;
+ u8 status = 0, val;
+
+ mutex_lock(&adc->lock);
+
+ ret = adc7_sw_calib_configure(adc, prop);
+ if (ret) {
+ pr_err("ADC configure failed with %d\n", ret);
+ goto unlock;
+ }
+
+ /* No support for polling mode at present*/
+ rc = wait_for_completion_timeout(&adc->complete,
+ msecs_to_jiffies(ADC7_SW_CALIB_CONV_TIMEOUT_MS));
+ if (!rc) {
+ pr_err("Reading ADC channel %s timed out\n",
+ prop->datasheet_name);
+ ret = -ETIMEDOUT;
+ goto unlock;
+ }
+
+ ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
+ if (ret < 0)
+ goto unlock;
+
+ if (!(status & ADC5_USR_STATUS1_EOC)) {
+ pr_err("ADC channel %s EOC bit not set, status=%#x\n",
+ prop->datasheet_name, status);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ ret = adc5_read_voltage_data(adc, adc_code_volt);
+ if (ret < 0)
+ goto unlock;
+
+ val = 0;
+ ret = adc5_write(adc, ADC5_USR_EN_CTL1, &val, 1);
+ if (ret < 0)
+ goto unlock;
+
+ ret = adc5_cmn_write(adc, ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL, &val, 1);
+unlock:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
static irqreturn_t adc5_isr(int irq, void *dev_id)
{
struct adc5_chip *adc = dev_id;
@@ -503,6 +657,21 @@ static irqreturn_t adc5_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static struct adc5_channel_prop *adc7_get_channel(struct adc5_chip *adc,
+ unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 0; i < adc->nchannels; i++) {
+ if (adc->chan_props[i].channel == num)
+ return &adc->chan_props[i];
+ }
+
+ pr_err("Invalid channel %02x\n", num);
+
+ return NULL;
+}
+
static int adc5_of_xlate(struct iio_dev *indio_dev,
const struct of_phandle_args *iiospec)
{
@@ -609,6 +778,118 @@ static int adc5_read_raw(struct iio_dev *indio_dev,
return 0;
}
+static int adc7_calib(struct adc5_chip *adc)
+{
+ int ret = 0;
+ u16 gnd, vref_1p25, vref_vdd;
+ u8 buf[2];
+ struct adc5_channel_prop *gnd_prop, *vref_1p25_prop, *vref_vdd_prop;
+
+ /* These channels are mandatory, they are used as reference points */
+ gnd_prop = adc7_get_channel(adc, ADC7_REF_GND);
+ if (!gnd_prop) {
+ dev_err(adc->dev, "GND channel not defined for SW calibration\n");
+ return -ENODEV;
+ }
+
+ vref_1p25_prop = adc7_get_channel(adc, ADC7_1P25VREF);
+ if (!vref_1p25_prop) {
+ dev_err(adc->dev, "1.25VREF channel not defined for SW calibration\n");
+ return -ENODEV;
+ }
+
+ vref_vdd_prop = adc7_get_channel(adc, ADC7_VREF_VADC);
+ if (!vref_vdd_prop) {
+ dev_err(adc->dev, "VDD channel not defined for SW calibration\n");
+ return -ENODEV;
+ }
+
+ ret = adc7_sw_calib_do_conversion(adc, gnd_prop, &gnd);
+ if (ret) {
+ dev_err(adc->dev, "Failed to read GND channel, ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = adc7_sw_calib_do_conversion(adc, vref_1p25_prop, &vref_1p25);
+ if (ret) {
+ dev_err(adc->dev, "Failed to read 1.25VREF channel, ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = adc7_sw_calib_do_conversion(adc, vref_vdd_prop, &vref_vdd);
+ if (ret) {
+ dev_err(adc->dev, "Failed to read VDD channel, ret = %d\n", ret);
+ return ret;
+ }
+
+ buf[0] = gnd & 0xff;
+ buf[1] = gnd >> 8;
+ ret = adc5_write(adc, ADC7_SW_CALIB_PBS_GND_REF_D0, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ vref_vdd -= gnd;
+ buf[0] = vref_vdd & 0xff;
+ buf[1] = vref_vdd >> 8;
+ ret = adc5_write(adc, ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D0, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ vref_1p25 -= gnd;
+ buf[0] = vref_1p25 & 0xff;
+ buf[1] = vref_1p25 >> 8;
+ ret = adc5_write(adc, ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D0, buf, sizeof(buf));
+
+ if (!ret)
+ dev_dbg(adc->dev, "SW calibration done, gnd:0x%x vref_vdd:0x%x vref_1p25:0x%x\n",
+ gnd, vref_vdd, vref_1p25);
+
+ return ret;
+}
+
+static int adc7_sw_calib_conv(struct adc5_chip *adc, struct adc5_channel_prop *prop, int *val)
+{
+ int ret = 0;
+ u16 adc_code_volt;
+
+ ret = adc7_calib(adc);
+ if (ret)
+ return ret;
+
+ ret = adc7_sw_calib_do_conversion(adc, prop, &adc_code_volt);
+ if (ret)
+ return ret;
+
+ return qcom_adc5_hw_scale(prop->scale_fn_type,
+ &adc5_prescale_ratios[prop->prescale],
+ adc->data,
+ adc_code_volt, val);
+}
+
+static int adc7_sw_calib_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ struct adc5_channel_prop *prop;
+ int ret;
+
+ prop = &adc->chan_props[chan->address];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = adc7_sw_calib_conv(adc, prop, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct iio_info adc5_info = {
.read_raw = adc5_read_raw,
.of_xlate = adc5_of_xlate,
@@ -619,6 +900,11 @@ static const struct iio_info adc7_info = {
.of_xlate = adc7_of_xlate,
};
+static const struct iio_info adc7_sw_calib_info = {
+ .read_raw = adc7_sw_calib_read_raw,
+ .of_xlate = adc5_of_xlate,
+};
+
struct adc5_channels {
const char *datasheet_name;
unsigned int prescale_index;
@@ -711,6 +997,8 @@ static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_DEFAULT)
[ADC7_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
SCALE_HW_CALIB_DEFAULT)
+ [ADC7_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0,
+ SCALE_HW_CALIB_DEFAULT)
[ADC7_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
SCALE_HW_CALIB_DEFAULT)
[ADC7_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
@@ -904,6 +1192,8 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
if (of_property_read_bool(node, "qcom,ratiometric"))
prop->cal_method = ADC5_RATIOMETRIC_CAL;
+ else if (of_property_read_bool(node, "qcom,no-cal"))
+ prop->cal_method = ADC5_NO_CAL;
else
prop->cal_method = ADC5_ABSOLUTE_CAL;
@@ -980,6 +1270,10 @@ static const struct of_device_id adc5_match_table[] = {
.compatible = "qcom,spmi-adc7",
.data = &adc7_data_pmic,
},
+ {
+ .compatible = "qcom,spmi-adc7-sw-calib",
+ .data = &adc7_data_pmic,
+ },
{
.compatible = "qcom,spmi-adc-rev2",
.data = &adc5_data_pmic_rev2,
@@ -1062,8 +1356,10 @@ static int adc5_probe(struct platform_device *pdev)
struct adc5_chip *adc;
struct regmap *regmap;
const char *irq_name;
+ const __be32 *prop_addr;
int ret, irq_eoc;
u32 reg;
+ u8 val;
regmap = dev_get_regmap(dev->parent, NULL);
if (!regmap)
@@ -1080,13 +1376,32 @@ static int adc5_probe(struct platform_device *pdev)
adc = iio_priv(indio_dev);
adc->regmap = regmap;
adc->dev = dev;
- adc->base = reg;
+
+ prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
+ if (!prop_addr) {
+ pr_err("invalid IO resource\n");
+ return -EINVAL;
+ }
+ adc->base = be32_to_cpu(*prop_addr);
+
+ prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
+ if (!prop_addr)
+ pr_debug("invalid cmn resource\n");
+ else
+ adc->cmn_base = be32_to_cpu(*prop_addr);
platform_set_drvdata(pdev, adc);
if (of_device_is_compatible(node, "qcom,spmi-adc7")) {
indio_dev->info = &adc7_info;
adc->is_pmic7 = true;
+ } else if (of_device_is_compatible(node, "qcom,spmi-adc7-sw-calib")) {
+ if (!adc->cmn_base) {
+ pr_err("ADC_CMN undefined\n");
+ return -ENODEV;
+ }
+ indio_dev->info = &adc7_sw_calib_info;
+ adc->is_pmic7 = true;
} else {
indio_dev->info = &adc5_info;
}
@@ -1116,6 +1431,17 @@ static int adc5_probe(struct platform_device *pdev)
return ret;
}
+ if (of_device_is_compatible(node, "qcom,spmi-adc7-sw-calib")) {
+ ret = adc7_calib(adc);
+ if (ret)
+ return ret;
+
+ val = ADC7_SW_CALIB_PBS_CALREF_RDY;
+ ret = adc5_write(adc, ADC7_SW_CALIB_PBS_CALREF_FLAG, &val, 1);
+ if (ret < 0)
+ return ret;
+ }
+
indio_dev->dev.parent = dev;
indio_dev->dev.of_node = node;
indio_dev->name = pdev->name;
diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c
index 734af9d8adb9..40ae40c13d91 100644
--- a/drivers/input/misc/qcom-hv-haptics.c
+++ b/drivers/input/misc/qcom-hv-haptics.c
@@ -71,8 +71,11 @@
#define DRV_SLEW_RATE_MASK GENMASK(2, 0)
#define HAP_CFG_VMAX_REG 0x48
-#define VMAX_STEP_MV 50
+#define VMAX_HV_STEP_MV 50
+#define VMAX_MV_STEP_MV 32
#define MAX_VMAX_MV 11000
+#define MAX_HV_VMAX_MV 10000
+#define MAX_MV_VMAX_MV 6000
#define CLAMPED_VMAX_MV 5000
#define DEFAULT_VMAX_MV 5000
@@ -175,6 +178,9 @@
#define CAL_RC_CLK_MANUAL_VAL 2
/* These registers are only applicable for PM5100 */
+#define HAP_CFG_HW_CONFIG_REG 0x0D
+#define HV_HAP_DRIVER_BIT BIT(1)
+
#define HAP_CFG_HPWR_INTF_CTL_REG 0x80
#define INTF_CTL_MASK GENMASK(1, 0)
#define INTF_CTL_BOB 1
@@ -358,6 +364,10 @@ enum pmic_type {
PM5100,
};
+enum wa_flags {
+ TOGGLE_CAL_RC_CLK = BIT(0),
+};
+
static const char * const src_str[] = {
"FIFO",
"DIRECT_PLAY",
@@ -505,15 +515,18 @@ struct haptics_chip {
u32 cfg_addr_base;
u32 ptn_addr_base;
u32 hbst_addr_base;
+ u32 wa_flags;
u8 cfg_revision;
u8 ptn_revision;
u8 hpwr_intf_ctl;
u16 hbst_revision;
+ u16 max_vmax_mv;
enum pmic_type pmic_type;
bool fifo_empty_irq_en;
bool swr_slave_enabled;
bool clamp_at_5v;
bool hpwr_vreg_enabled;
+ bool is_hv_haptics;
};
struct haptics_reg_info {
@@ -1163,18 +1176,20 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip,
static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv)
{
int rc = 0;
- u8 val;
+ u8 val, vmax_step;
- if (vmax_mv > MAX_VMAX_MV) {
+ if (vmax_mv > chip->max_vmax_mv) {
dev_err(chip->dev, "vmax (%d) exceed the max value: %d\n",
- vmax_mv, MAX_VMAX_MV);
+ vmax_mv, chip->max_vmax_mv);
return -EINVAL;
}
if (chip->clamp_at_5v && (vmax_mv > CLAMPED_VMAX_MV))
vmax_mv = CLAMPED_VMAX_MV;
- val = vmax_mv / VMAX_STEP_MV;
+ vmax_step = (chip->is_hv_haptics) ?
+ VMAX_HV_STEP_MV : VMAX_MV_STEP_MV;
+ val = vmax_mv / vmax_step;
rc = haptics_write(chip, chip->cfg_addr_base,
HAP_CFG_VMAX_REG, &val, 1);
if (rc < 0)
@@ -1441,8 +1456,9 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en)
if (rc < 0)
return rc;
- if ((val & CAL_RC_CLK_MASK) ==
- CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT) {
+ if ((chip->wa_flags & TOGGLE_CAL_RC_CLK) &&
+ ((val & CAL_RC_CLK_MASK) ==
+ CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT)) {
val = CAL_RC_CLK_DISABLED_VAL << CAL_RC_CLK_SHIFT;
rc = haptics_masked_write(chip, chip->cfg_addr_base,
HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK,
@@ -2570,6 +2586,23 @@ static int haptics_config_openloop_lra_period(struct haptics_chip *chip,
HAP_CFG_TLRA_OL_HIGH_REG, val, 2);
}
+static int haptics_config_wa(struct haptics_chip *chip)
+{
+ switch (chip->pmic_type) {
+ case PM8350B:
+ chip->wa_flags |= TOGGLE_CAL_RC_CLK;
+ break;
+ case PM5100:
+ break;
+ default:
+ dev_err(chip->dev, "PMIC type %d does not match\n",
+ chip->pmic_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int haptics_hw_init(struct haptics_chip *chip)
{
struct haptics_hw_config *config = &chip->config;
@@ -2578,6 +2611,10 @@ static int haptics_hw_init(struct haptics_chip *chip)
u8 val[2];
u32 t_lra_us;
+ rc = haptics_config_wa(chip);
+ if (rc < 0)
+ return rc;
+
/* Store CL brake settings */
rc = haptics_store_cl_brake_settings(chip);
if (rc < 0)
@@ -2592,6 +2629,20 @@ static int haptics_hw_init(struct haptics_chip *chip)
chip->clamp_at_5v = val[0] & CLAMP_5V_BIT;
}
+ chip->is_hv_haptics = true;
+ chip->max_vmax_mv = MAX_VMAX_MV;
+
+ if (chip->pmic_type == PM5100) {
+ rc = haptics_read(chip, chip->cfg_addr_base,
+ HAP_CFG_HW_CONFIG_REG, val, 1);
+ if (rc < 0)
+ return rc;
+
+ chip->is_hv_haptics = val[0] & HV_HAP_DRIVER_BIT;
+ chip->max_vmax_mv = (chip->is_hv_haptics) ?
+ MAX_HV_VMAX_MV : MAX_MV_VMAX_MV;
+ }
+
/* Config VMAX */
rc = haptics_set_vmax_mv(chip, config->vmax_mv);
if (rc < 0)
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
index 9eefb3e783f4..823caa4fdc8e 100644
--- a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
@@ -2476,6 +2476,9 @@ static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata)
FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d",
pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio);
+ pdata->power_always_on =
+ of_property_read_bool(np, "focaltech,power-always-on");
+
ret = of_property_read_u32(np, "focaltech,ic-type", &temp_val);
if (ret < 0)
pdata->type = _FT3518;
@@ -2903,6 +2906,7 @@ static int fts_ts_suspend(struct device *dev)
{
int ret = 0;
struct fts_ts_data *ts_data = fts_data;
+ struct fts_ts_platform_data *pdata = ts_data->pdata;
FTS_FUNC_ENTER();
if (ts_data->suspended) {
@@ -2934,7 +2938,8 @@ static int fts_ts_suspend(struct device *dev)
if (ret < 0)
FTS_ERROR("set TP to sleep mode fail, ret=%d", ret);
- if (!ts_data->ic_info.is_incell) {
+ if (!ts_data->ic_info.is_incell && !pdata->power_always_on) {
+
#if FTS_POWER_SOURCE_CUST_EN
ret = fts_power_source_suspend(ts_data);
if (ret < 0) {
@@ -2953,6 +2958,7 @@ static int fts_ts_suspend(struct device *dev)
static int fts_ts_resume(struct device *dev)
{
struct fts_ts_data *ts_data = fts_data;
+ struct fts_ts_platform_data *pdata = ts_data->pdata;
FTS_FUNC_ENTER();
if (!ts_data->suspended) {
@@ -2971,7 +2977,8 @@ static int fts_ts_resume(struct device *dev)
if (!ts_data->ic_info.is_incell) {
#if FTS_POWER_SOURCE_CUST_EN
- fts_power_source_resume(ts_data);
+ if (!pdata->power_always_on)
+ fts_power_source_resume(ts_data);
#endif
fts_reset_proc(200);
}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
index c87cc52e5e47..3c8701f98c62 100644
--- a/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
@@ -139,6 +139,7 @@ struct fts_ts_platform_data {
u32 x_min;
u32 y_min;
u32 max_touch_number;
+ bool power_always_on;
};
struct ts_event {
diff --git a/drivers/media/platform/msm/synx/synx_util.c b/drivers/media/platform/msm/synx/synx_util.c
index 7720141eb133..3739f81a13d3 100644
--- a/drivers/media/platform/msm/synx/synx_util.c
+++ b/drivers/media/platform/msm/synx/synx_util.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "synx: " fmt
@@ -158,8 +158,8 @@ void synx_util_object_destroy(struct synx_coredata *synx_obj)
data = bind_desc->external_data;
bind_ops = synx_util_get_bind_ops(type);
if (!bind_ops) {
- pr_err("bind ops fail id: %d, type: %u, err: %d\n",
- sync_id, type, rc);
+ pr_err("bind ops fail id: %d, type: %u\n",
+ sync_id, type);
continue;
}
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index b99ff071d256..25b53cca4705 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -223,40 +223,6 @@ DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
"%llu\n");
#if defined(CONFIG_SDC_QTI)
-static int mmc_scale_get(void *data, u64 *val)
-{
- struct mmc_host *host = data;
-
- *val = host->clk_scaling.curr_freq;
-
- return 0;
-}
-
-static int mmc_scale_set(void *data, u64 val)
-{
- int err = 0;
- struct mmc_host *host = data;
-
- mmc_claim_host(host);
-
- /* change frequency from sysfs manually */
- err = mmc_clk_update_freq(host, val, host->clk_scaling.state);
- if (err == -EAGAIN)
- err = 0;
- else if (err)
- pr_err("%s: clock scale to %llu failed with error %d\n",
- mmc_hostname(host), val, err);
- else
- pr_debug("%s: clock change to %llu finished successfully (%s)\n",
- mmc_hostname(host), val, current->comm);
-
- mmc_release_host(host);
-
- return err;
-}
-
-DEFINE_DEBUGFS_ATTRIBUTE(mmc_scale_fops, mmc_scale_get, mmc_scale_set,
- "%llu\n");
static int mmc_max_clock_get(void *data, u64 *val)
{
@@ -314,8 +280,6 @@ void mmc_add_host_debugfs(struct mmc_host *host)
#if defined(CONFIG_SDC_QTI)
debugfs_create_file("max_clock", 0600, root, host,
&mmc_max_clock_fops);
- debugfs_create_file("scale", 0600, root, host,
- &mmc_scale_fops);
debugfs_create_bool("skip_clk_scale_freq_update",
0600, root,
&host->clk_scaling.skip_clk_scale_freq_update);
diff --git a/drivers/mmc/host/cqhci-crypto-qti.c b/drivers/mmc/host/cqhci-crypto-qti.c
index 5c97d1a56603..37766d13854d 100644
--- a/drivers/mmc/host/cqhci-crypto-qti.c
+++ b/drivers/mmc/host/cqhci-crypto-qti.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2020, Linux Foundation. All rights reserved.
+ * Copyright (c) 2020 - 2021, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,7 @@ static struct cqhci_host_crypto_variant_ops __maybe_unused cqhci_crypto_qti_vari
.disable = cqhci_crypto_qti_disable,
.resume = cqhci_crypto_qti_resume,
.debug = cqhci_crypto_qti_debug,
+ .recovery_finish = cqhci_crypto_qti_recovery_finish,
};
static bool ice_cap_idx_valid(struct cqhci_host *host,
@@ -317,5 +318,11 @@ int cqhci_crypto_qti_resume(struct cqhci_host *host)
return crypto_qti_resume(host->crypto_vops->priv);
}
+int cqhci_crypto_qti_recovery_finish(struct cqhci_host *host)
+{
+ keyslot_manager_reprogram_all_keys(host->mmc->ksm);
+ return 0;
+}
+
MODULE_DESCRIPTION("Vendor specific CQHCI Crypto Engine Support");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/cqhci-crypto-qti.h b/drivers/mmc/host/cqhci-crypto-qti.h
index 07300624b6a1..9675a147daa6 100644
--- a/drivers/mmc/host/cqhci-crypto-qti.h
+++ b/drivers/mmc/host/cqhci-crypto-qti.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2020 - 2021, The Linux Foundation. All rights reserved.
*/
#ifndef _UFSHCD_CRYPTO_QTI_H
@@ -28,4 +28,6 @@ void cqhci_crypto_qti_set_vops(struct cqhci_host *host)
int cqhci_crypto_qti_resume(struct cqhci_host *host);
+int cqhci_crypto_qti_recovery_finish(struct cqhci_host *host);
+
#endif /* _UFSHCD_ICE_QTI_H */
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 9b30ce9562b9..8288f6b01d39 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include "sdhci-pltfm.h"
#include "cqhci.h"
@@ -164,6 +165,10 @@
#define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13)
#define RCLK_TOGGLE BIT(2)
+/* enum for writing to TLMM_NORTH_SPARE register as defined by pinctrl API */
+#define TLMM_NORTH_SPARE 2
+#define TLMM_NORTH_SPARE_CORE_IE BIT(15)
+
struct sdhci_msm_offset {
u32 core_hc_mode;
u32 core_mci_data_cnt;
@@ -2226,6 +2231,7 @@ static int sdhci_msm_setup_vreg(struct sdhci_msm_host *msm_host,
struct sdhci_msm_vreg_data *curr_slot;
struct sdhci_msm_reg_data *vreg_table[2];
struct mmc_host *mmc = msm_host->mmc;
+ u32 val = 0;
curr_slot = msm_host->vreg_data;
if (!curr_slot) {
@@ -2241,6 +2247,20 @@ static int sdhci_msm_setup_vreg(struct sdhci_msm_host *msm_host,
if (!enable && vreg_table[1]->is_always_on && !mmc->card)
vreg_table[1]->is_always_on = false;
+ if (!enable && !(mmc->caps & MMC_CAP_NONREMOVABLE)) {
+
+ /*
+ * Disable Receiver of the Pad to avoid crowbar currents
+ * when Pad power supplies are collapsed. Provide SW control
+ * on the core_ie of SDC2 Pads. SW write 1’b0
+ * into the bit 15 of register TLMM_NORTH_SPARE.
+ */
+
+ val = msm_spare_read(TLMM_NORTH_SPARE);
+ val &= ~TLMM_NORTH_SPARE_CORE_IE;
+ msm_spare_write(TLMM_NORTH_SPARE, val);
+ }
+
for (i = 0; i < ARRAY_SIZE(vreg_table); i++) {
if (vreg_table[i]) {
if (enable)
@@ -2251,6 +2271,20 @@ static int sdhci_msm_setup_vreg(struct sdhci_msm_host *msm_host,
goto out;
}
}
+
+ if (enable && !(mmc->caps & MMC_CAP_NONREMOVABLE)) {
+
+ /*
+ * Disable Receiver of the Pad to avoid crowbar currents
+ * when Pad power supplies are collapsed. Provide SW control
+ * on the core_ie of SDC2 Pads. SW write 1’b1
+ * into the bit 15 of register TLMM_NORTH_SPARE.
+ */
+
+ val = msm_spare_read(TLMM_NORTH_SPARE);
+ val |= TLMM_NORTH_SPARE_CORE_IE;
+ msm_spare_write(TLMM_NORTH_SPARE, val);
+ }
out:
return ret;
}
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 8e37e1f58c4b..d78c6327f65b 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1431,12 +1431,20 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
headroom = skb_headroom(skb) - 8;
+#ifdef CONFIG_ENABLE_SFE
+ if (((!(skb->fast_forwarded) && skb_header_cloned(skb)) ||
+ headroom < 0) &&
+ pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) {
+ dev_kfree_skb_any(skb);
+ return NULL;
+ }
+#else
if ((skb_header_cloned(skb) || headroom < 0) &&
pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) {
dev_kfree_skb_any(skb);
return NULL;
}
-
+#endif
ptr = skb_push(skb, 8);
put_unaligned_le32(tx_hdr1, ptr);
put_unaligned_le32(tx_hdr2, ptr + 4);
diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c
index 66e035c1df88..84b5deeeff3a 100644
--- a/drivers/net/wireless/cnss2/debug.c
+++ b/drivers/net/wireless/cnss2/debug.c
@@ -876,6 +876,24 @@ void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv)
#endif
#if IS_ENABLED(CONFIG_IPC_LOGGING)
+void cnss_debug_ipc_log_print(void *log_ctx, char *process, const char *fn,
+ const char *log_level, char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list va_args;
+
+ va_start(va_args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &va_args;
+
+ if (log_level)
+ printk("%scnss: %pV", log_level, &vaf);
+
+ ipc_log_string(log_ctx, "[%s] %s: %pV", process, fn, &vaf);
+
+ va_end(va_args);
+}
+
static int cnss_ipc_logging_init(void)
{
cnss_ipc_log_context = ipc_log_context_create(CNSS_IPC_LOG_PAGES,
@@ -911,6 +929,21 @@ static void cnss_ipc_logging_deinit(void)
#else
static int cnss_ipc_logging_init(void) { return 0; }
static void cnss_ipc_logging_deinit(void) {}
+void cnss_debug_ipc_log_print(void *log_ctx, char *process, const char *fn,
+ const char *log_level, char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list va_args;
+
+ va_start(va_args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &va_args;
+
+ if (log_level)
+ printk("%scnss: %pV", log_level, &vaf);
+
+ va_end(va_args);
+}
#endif
int cnss_debug_init(void)
diff --git a/drivers/net/wireless/cnss2/debug.h b/drivers/net/wireless/cnss2/debug.h
index da298d7fbf7a..7796d57611b3 100644
--- a/drivers/net/wireless/cnss2/debug.h
+++ b/drivers/net/wireless/cnss2/debug.h
@@ -4,55 +4,58 @@
#ifndef _CNSS_DEBUG_H
#define _CNSS_DEBUG_H
-#if IS_ENABLED(CONFIG_IPC_LOGGING)
-#include
-#endif
#include
#if IS_ENABLED(CONFIG_IPC_LOGGING)
-#define CNSS_IPC_LOG_PAGES 32
+#include
+#include
extern void *cnss_ipc_log_context;
extern void *cnss_ipc_log_long_context;
-#define cnss_ipc_log_string(_x...) \
- ipc_log_string(cnss_ipc_log_context, _x)
-
-#define cnss_ipc_log_long_string(_x...) \
- ipc_log_string(cnss_ipc_log_long_context, _x)
+#ifdef CONFIG_CNSS2_DEBUG
+#define CNSS_IPC_LOG_PAGES 100
#else
-#define cnss_ipc_log_string(_x...)
-#define cnss_ipc_log_long_string(_x...)
+#define CNSS_IPC_LOG_PAGES 50
+#endif
+#define cnss_debug_log_print(_x...) \
+ cnss_debug_ipc_log_print(cnss_ipc_log_context, _x)
+
+#define cnss_debug_log_long_print(_x...) \
+ cnss_debug_ipc_log_print(cnss_ipc_log_long_context, _x)
+#else
+#define cnss_debug_log_print(_x...) \
+ cnss_debug_ipc_log_print((void *)NULL, _x)
+#define cnss_debug_log_long_print(_x...) \
+ cnss_debug_ipc_log_print((void *)NULL, _x)
#endif
-#define cnss_pr_err(_fmt, ...) do { \
- printk("%scnss: " _fmt, KERN_ERR, ##__VA_ARGS__); \
- cnss_ipc_log_string("%scnss: " _fmt, "", ##__VA_ARGS__);\
- } while (0)
+#define proc_name (in_irq() ? "irq" : \
+ (in_softirq() ? "soft_irq" : current->comm))
-#define cnss_pr_warn(_fmt, ...) do { \
- printk("%scnss: " _fmt, KERN_WARNING, ##__VA_ARGS__); \
- cnss_ipc_log_string("%scnss: " _fmt, "", ##__VA_ARGS__);\
- } while (0)
+#define cnss_pr_err(_fmt, ...) \
+ cnss_debug_log_print(proc_name, __func__, \
+ KERN_ERR, _fmt, ##__VA_ARGS__)
-#define cnss_pr_info(_fmt, ...) do { \
- printk("%scnss: " _fmt, KERN_INFO, ##__VA_ARGS__); \
- cnss_ipc_log_string("%scnss: " _fmt, "", ##__VA_ARGS__);\
- } while (0)
+#define cnss_pr_warn(_fmt, ...) \
+ cnss_debug_log_print(proc_name, __func__, \
+ KERN_WARNING, _fmt, ##__VA_ARGS__)
-#define cnss_pr_dbg(_fmt, ...) do { \
- printk("%scnss: " _fmt, KERN_DEBUG, ##__VA_ARGS__); \
- cnss_ipc_log_string("%scnss: " _fmt, "", ##__VA_ARGS__);\
- } while (0)
+#define cnss_pr_info(_fmt, ...) \
+ cnss_debug_log_print(proc_name, __func__, \
+ KERN_INFO, _fmt, ##__VA_ARGS__)
-#define cnss_pr_vdbg(_fmt, ...) do { \
- printk("%scnss: " _fmt, KERN_DEBUG, ##__VA_ARGS__); \
- cnss_ipc_log_long_string("%scnss: " _fmt, "", \
- ##__VA_ARGS__); \
- } while (0)
+#define cnss_pr_dbg(_fmt, ...) \
+ cnss_debug_log_print(proc_name, __func__, \
+ KERN_DEBUG, _fmt, ##__VA_ARGS__)
-#define cnss_pr_buf(_fmt, ...) \
- cnss_ipc_log_long_string("%scnss: " _fmt, "", ##__VA_ARGS__) \
+#define cnss_pr_vdbg(_fmt, ...) \
+ cnss_debug_log_long_print(proc_name, __func__, \
+ KERN_DEBUG, _fmt, ##__VA_ARGS__)
+
+#define cnss_pr_buf(_fmt, ...) \
+ cnss_debug_log_long_print(proc_name, __func__, \
+ NULL, _fmt, ##__VA_ARGS__)
#ifdef CONFIG_CNSS2_DEBUG
#define CNSS_ASSERT(_condition) do { \
@@ -79,5 +82,6 @@ int cnss_debug_init(void);
void cnss_debug_deinit(void);
int cnss_debugfs_create(struct cnss_plat_data *plat_priv);
void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv);
-
+void cnss_debug_ipc_log_print(void *log_ctx, char *process, const char *fn,
+ const char *log_level, char *fmt, ...);
#endif /* _CNSS_DEBUG_H */
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index 90c707279f8f..08635be66660 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -685,6 +685,8 @@ int cnss_pci_check_link_status(struct cnss_pci_data *pci_priv)
static void cnss_pci_select_window(struct cnss_pci_data *pci_priv, u32 offset)
{
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+
u32 window = (offset >> WINDOW_SHIFT) & WINDOW_VALUE_MASK;
u32 window_enable = WINDOW_ENABLE_BIT | window;
u32 val;
@@ -703,7 +705,8 @@ static void cnss_pci_select_window(struct cnss_pci_data *pci_priv, u32 offset)
if (val != window_enable) {
cnss_pr_err("Failed to config window register to 0x%x, current value: 0x%x\n",
window_enable, val);
- if (!cnss_pci_check_link_status(pci_priv))
+ if (!cnss_pci_check_link_status(pci_priv) &&
+ !test_bit(CNSS_IN_PANIC, &plat_priv->driver_state))
CNSS_ASSERT(0);
}
}
@@ -1287,6 +1290,9 @@ static void cnss_pci_handle_linkdown(struct cnss_pci_data *pci_priv)
pci_priv->pci_link_down_ind = true;
spin_unlock_irqrestore(&pci_link_down_lock, flags);
+ /* Notify MHI about link down */
+ mhi_control_error(pci_priv->mhi_ctrl);
+
if (pci_dev->device == QCA6174_DEVICE_ID)
disable_irq(pci_dev->irq);
diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c
index ce014787acbe..79a0af8b47cf 100644
--- a/drivers/pci/controller/pci-msm.c
+++ b/drivers/pci/controller/pci-msm.c
@@ -103,6 +103,7 @@
#define PCIE20_ELBI_SYS_STTS (0x08)
#define PCIE20_CAP (0x70)
+#define PCIE20_CAP_DEVCAP (PCIE20_CAP + 0x04)
#define PCIE20_CAP_DEVCTRLSTATUS (PCIE20_CAP + 0x08)
#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
#define PCIE_CAP_DLL_ACTIVE BIT(29)
@@ -503,7 +504,6 @@ enum msm_pcie_debugfs_option {
MSM_PCIE_DISABLE_LINK,
MSM_PCIE_ENABLE_LINK,
MSM_PCIE_DISABLE_ENABLE_LINK,
- MSM_PCIE_DUMP_SHADOW_REGISTER,
MSM_PCIE_DISABLE_L0S,
MSM_PCIE_ENABLE_L0S,
MSM_PCIE_DISABLE_L1,
@@ -532,7 +532,6 @@ static const char * const
"DISABLE LINK",
"ENABLE LINK",
"DISABLE AND ENABLE LINK",
- "DUMP PCIE SHADOW REGISTER",
"DISABLE L0S",
"ENABLE L0S",
"DISABLE L1",
@@ -640,11 +639,7 @@ struct msm_pcie_sid_info_t {
/* PCIe device info structure */
struct msm_pcie_device_info {
struct list_head pcidev_node;
- u32 bdf;
struct pci_dev *dev;
- short short_bdf;
- u32 sid;
- int domain;
};
/* DRV IPC command type */
@@ -703,6 +698,11 @@ struct msm_pcie_drv_info {
struct completion completion;
};
+struct pcie_i2c_reg_update {
+ u32 offset;
+ u32 val;
+};
+
/* i2c control interface for a i2c client device */
struct pcie_i2c_ctrl {
struct i2c_client *client;
@@ -713,6 +713,8 @@ struct pcie_i2c_ctrl {
u32 ep_reset_gpio_mask;
u32 *dump_regs;
u32 dump_reg_count;
+ struct pcie_i2c_reg_update *reg_update;
+ u32 reg_update_count;
/* client specific callbacks */
int (*client_i2c_read)(struct i2c_client *client, u32 reg_addr,
@@ -721,6 +723,7 @@ struct pcie_i2c_ctrl {
u32 val);
int (*client_i2c_reset)(struct pcie_i2c_ctrl *i2c_ctrl, bool reset);
void (*client_i2c_dump_regs)(struct pcie_i2c_ctrl *i2c_ctrl);
+ void (*client_i2c_de_emphasis_wa)(struct pcie_i2c_ctrl *i2c_ctrl);
};
enum i2c_client_id {
@@ -788,6 +791,8 @@ struct msm_pcie_dev_t {
enum msm_pcie_link_status link_status;
bool user_suspend;
bool disable_pc;
+
+ struct pci_saved_state *default_state;
struct pci_saved_state *saved_state;
struct wakeup_source *ws;
@@ -863,9 +868,6 @@ struct msm_pcie_dev_t {
struct msm_pcie_tcsr_info_t *tcsr_config;
u32 sid_info_len;
struct msm_pcie_sid_info_t *sid_info;
- u32 ep_shadow[MAX_DEVICE_NUM][PCIE_CONF_SPACE_DW];
- u32 rc_shadow[PCIE_CONF_SPACE_DW];
- bool shadow_en;
bool bridge_found;
struct list_head event_reg_list;
spinlock_t evt_reg_list_lock;
@@ -877,7 +879,6 @@ struct msm_pcie_dev_t {
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_sleep;
- struct msm_pcie_device_info pcidev_table[MAX_DEVICE_NUM];
struct msm_pcie_drv_info *drv_info;
struct work_struct drv_enable_pc_work;
@@ -1524,54 +1525,6 @@ static bool msm_pcie_confirm_linkup(struct msm_pcie_dev_t *dev,
return true;
}
-static void msm_pcie_cfg_recover(struct msm_pcie_dev_t *dev, bool rc)
-{
- int i, j;
- u32 val = 0;
- u32 *shadow;
- void __iomem *cfg = dev->conf;
-
- for (i = 0; i < MAX_DEVICE_NUM; i++) {
- if (!rc && !dev->pcidev_table[i].bdf)
- break;
- if (rc) {
- cfg = dev->dm_core;
- shadow = dev->rc_shadow;
- } else {
- if (!msm_pcie_confirm_linkup(dev, false, true,
- dev->pcidev_table[i].dev))
- continue;
-
- shadow = dev->ep_shadow[i];
- PCIE_DBG(dev,
- "PCIe Device: %02x:%02x.%01x\n",
- dev->pcidev_table[i].bdf >> 24,
- dev->pcidev_table[i].bdf >> 19 & 0x1f,
- dev->pcidev_table[i].bdf >> 16 & 0x07);
- }
- for (j = PCIE_CONF_SPACE_DW - 1; j >= 0; j--) {
- val = shadow[j];
- if (val != PCIE_CLEAR) {
- PCIE_DBG3(dev,
- "PCIe: before recovery:cfg 0x%x:0x%x\n",
- j * 4, readl_relaxed(cfg + j * 4));
- PCIE_DBG3(dev,
- "PCIe: shadow_dw[%d]:cfg 0x%x:0x%x\n",
- j, j * 4, val);
- msm_pcie_write_reg(cfg, j * 4, val);
- PCIE_DBG3(dev,
- "PCIe: after recovery:cfg 0x%x:0x%x\n\n",
- j * 4, readl_relaxed(cfg + j * 4));
- }
- }
- if (rc)
- break;
-
- pci_save_state(dev->pcidev_table[i].dev);
- cfg += SZ_4K;
- }
-}
-
static void msm_pcie_write_mask(void __iomem *addr,
uint32_t clear_mask, uint32_t set_mask)
{
@@ -1701,8 +1654,6 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
dev->linkdown_panic);
PCIE_DBG_FS(dev, "the link is %s suspending\n",
dev->suspending ? "" : "not");
- PCIE_DBG_FS(dev, "shadow is %s enabled\n",
- dev->shadow_en ? "" : "not");
PCIE_DBG_FS(dev, "the power of RC is %s on\n",
dev->power_on ? "" : "not");
PCIE_DBG_FS(dev, "smmu_sid_base: 0x%x\n",
@@ -1771,37 +1722,6 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
dev->l23_rdy_poll_timeout);
}
-static void msm_pcie_shadow_dump(struct msm_pcie_dev_t *dev, bool rc)
-{
- int i, j;
- u32 val = 0;
- u32 *shadow;
-
- for (i = 0; i < MAX_DEVICE_NUM; i++) {
- if (!rc && !dev->pcidev_table[i].bdf)
- break;
- if (rc) {
- shadow = dev->rc_shadow;
- } else {
- shadow = dev->ep_shadow[i];
- PCIE_DBG_FS(dev, "PCIe Device: %02x:%02x.%01x\n",
- dev->pcidev_table[i].bdf >> 24,
- dev->pcidev_table[i].bdf >> 19 & 0x1f,
- dev->pcidev_table[i].bdf >> 16 & 0x07);
- }
- for (j = 0; j < PCIE_CONF_SPACE_DW; j++) {
- val = shadow[j];
- if (val != PCIE_CLEAR) {
- PCIE_DBG_FS(dev,
- "PCIe: shadow_dw[%d]:cfg 0x%x:0x%x\n",
- j, j * 4, val);
- }
- }
- if (rc)
- break;
- }
-}
-
static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev,
u32 testcase)
{
@@ -1818,9 +1738,8 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev,
case MSM_PCIE_DISABLE_LINK:
PCIE_DBG_FS(dev,
"\n\nPCIe: RC%d: disable link\n\n", dev->rc_idx);
- ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, 0,
- dev->dev, NULL,
- MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+ ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, 0, dev->dev, NULL,
+ 0);
if (ret)
PCIE_DBG_FS(dev, "PCIe:%s:failed to disable link\n",
__func__);
@@ -1832,50 +1751,28 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev,
PCIE_DBG_FS(dev,
"\n\nPCIe: RC%d: enable link and recover config space\n\n",
dev->rc_idx);
- ret = msm_pcie_pm_control(MSM_PCIE_RESUME, 0,
- dev->dev, NULL,
- MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+ ret = msm_pcie_pm_control(MSM_PCIE_RESUME, 0, dev->dev, NULL,
+ 0);
if (ret)
PCIE_DBG_FS(dev, "PCIe:%s:failed to enable link\n",
__func__);
- else {
- PCIE_DBG_FS(dev, "PCIe:%s:enabled link\n", __func__);
- msm_pcie_recover_config(dev->dev);
- }
break;
case MSM_PCIE_DISABLE_ENABLE_LINK:
PCIE_DBG_FS(dev,
"\n\nPCIe: RC%d: disable and enable link then recover config space\n\n",
dev->rc_idx);
- ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, 0,
- dev->dev, NULL,
- MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+ ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, 0, dev->dev, NULL,
+ 0);
if (ret)
PCIE_DBG_FS(dev, "PCIe:%s:failed to disable link\n",
__func__);
else
PCIE_DBG_FS(dev, "PCIe:%s:disabled link\n", __func__);
- ret = msm_pcie_pm_control(MSM_PCIE_RESUME, 0,
- dev->dev, NULL,
- MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+ ret = msm_pcie_pm_control(MSM_PCIE_RESUME, 0, dev->dev, NULL,
+ 0);
if (ret)
PCIE_DBG_FS(dev, "PCIe:%s:failed to enable link\n",
__func__);
- else {
- PCIE_DBG_FS(dev, "PCIe:%s:enabled link\n", __func__);
- msm_pcie_recover_config(dev->dev);
- }
- break;
- case MSM_PCIE_DUMP_SHADOW_REGISTER:
- PCIE_DBG_FS(dev,
- "\n\nPCIe: RC%d: dumping RC shadow registers\n",
- dev->rc_idx);
- msm_pcie_shadow_dump(dev, true);
-
- PCIE_DBG_FS(dev,
- "\n\nPCIe: RC%d: dumping EP shadow registers\n",
- dev->rc_idx);
- msm_pcie_shadow_dump(dev, false);
break;
case MSM_PCIE_DISABLE_L0S:
PCIE_DBG_FS(dev, "\n\nPCIe: RC%d: disable L0s\n\n",
@@ -2965,29 +2862,6 @@ static void msm_pcie_cfg_bdf(struct msm_pcie_dev_t *dev, u8 bus, u8 devfn)
dev->current_bdf = bdf;
}
-static void msm_pcie_save_shadow(struct msm_pcie_dev_t *dev,
- u32 word_offset, u32 wr_val,
- u32 bdf, bool rc)
-{
- int i;
-
- if (rc) {
- dev->rc_shadow[word_offset / 4] = wr_val;
- } else {
- for (i = 0; i < MAX_DEVICE_NUM; i++) {
- if (!dev->pcidev_table[i].bdf) {
- dev->pcidev_table[i].bdf = bdf;
- if ((!dev->bridge_found) && (i > 0))
- dev->bridge_found = true;
- }
- if (dev->pcidev_table[i].bdf == bdf) {
- dev->ep_shadow[i][word_offset / 4] = wr_val;
- break;
- }
- }
- }
-}
-
static int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,
int where, int size, u32 *val)
{
@@ -3089,18 +2963,6 @@ static int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,
msm_pcie_write_reg(config_base, word_offset, wr_val);
- if (dev->shadow_en) {
- if (rd_val == PCIE_LINK_DOWN &&
- (readl_relaxed(config_base) == PCIE_LINK_DOWN))
- PCIE_ERR(dev,
- "Read of RC%d %d:0x%02x + 0x%04x[%d] is all FFs\n",
- rc_idx, bus->number, devfn,
- where, size);
- else
- msm_pcie_save_shadow(dev, word_offset, wr_val,
- bdf, rc);
- }
-
PCIE_DBG3(dev,
"RC%d %d:0x%02x + 0x%04x[%d] <- 0x%08x; rd 0x%08x val 0x%08x\n",
rc_idx, bus->number, devfn, where, size,
@@ -3793,6 +3655,8 @@ done:
static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
{
+ u32 val;
+
PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
/*
@@ -3815,11 +3679,6 @@ static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
PCIE20_ACK_N_FTS,
dev->n_fts << 8);
- if (dev->shadow_en)
- dev->rc_shadow[PCIE20_ACK_F_ASPM_CTRL_REG / 4] =
- readl_relaxed(dev->dm_core +
- PCIE20_ACK_F_ASPM_CTRL_REG);
-
PCIE_DBG2(dev, "Updated PCIE20_ACK_F_ASPM_CTRL_REG:0x%x\n",
readl_relaxed(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG));
@@ -3833,6 +3692,13 @@ static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
PCIE20_DEVICE_CONTROL2_STATUS2,
0xf, dev->cpl_timeout);
+ /* update RC Max Payload Size based on Max Payload Size Supported */
+ val = readl_relaxed(dev->dm_core + PCIE20_CAP_DEVCAP) &
+ PCI_EXP_DEVCAP_PAYLOAD;
+ msm_pcie_write_reg_field(dev->dm_core,
+ PCIE20_CAP_DEVCTRLSTATUS,
+ PCI_EXP_DEVCTL_PAYLOAD, val);
+
/* Enable AER on RC */
if (dev->aer_enable) {
msm_pcie_write_mask(dev->dm_core + PCIE20_BRIDGE_CTRL, 0,
@@ -4638,6 +4504,28 @@ static void ntn3_dump_regs(struct pcie_i2c_ctrl *i2c_ctrl)
}
}
+static void ntn3_de_emphasis_wa(struct pcie_i2c_ctrl *i2c_ctrl)
+{
+ int i, val;
+ struct msm_pcie_dev_t *pcie_dev = container_of(i2c_ctrl,
+ struct msm_pcie_dev_t,
+ i2c_ctrl);
+
+ PCIE_DBG(pcie_dev, "PCIe: RC%d: NTN3 reg update\n", pcie_dev->rc_idx);
+
+ for (i = 0; i < i2c_ctrl->reg_update_count; i++) {
+ i2c_ctrl->client_i2c_write(i2c_ctrl->client, i2c_ctrl->reg_update[i].offset,
+ i2c_ctrl->reg_update[i].val);
+ /*Read to make sure writes are completed*/
+ i2c_ctrl->client_i2c_read(i2c_ctrl->client, i2c_ctrl->reg_update[i].offset,
+ &val);
+ PCIE_DBG(pcie_dev, "PCIe: RC%d: NTN3 reg off:0x%x wr_val:0x%x rd_val:0x%x\n",
+ pcie_dev->rc_idx, i2c_ctrl->reg_update[i].offset,
+ i2c_ctrl->reg_update[i].val, val);
+ }
+
+}
+
static int msm_pcie_enable(struct msm_pcie_dev_t *dev)
{
int ret = 0;
@@ -4751,6 +4639,9 @@ static int msm_pcie_enable(struct msm_pcie_dev_t *dev)
ep_up_timeout = jiffies + usecs_to_jiffies(EP_UP_TIMEOUT_US);
+ if (dev->i2c_ctrl.client && dev->i2c_ctrl.client_i2c_de_emphasis_wa)
+ dev->i2c_ctrl.client_i2c_de_emphasis_wa(&dev->i2c_ctrl);
+
msm_pcie_config_sid(dev);
msm_pcie_config_controller(dev);
@@ -4876,7 +4767,7 @@ static void msm_pcie_disable(struct msm_pcie_dev_t *dev)
PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
}
-static int msm_pcie_config_device_table(struct pci_dev *pcidev, void *pdev)
+static int msm_pcie_config_device_info(struct pci_dev *pcidev, void *pdev)
{
struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *) pdev;
struct msm_pcie_device_info *dev_info;
@@ -5000,7 +4891,7 @@ int msm_pcie_enumerate(u32 rc_idx)
struct pci_dev *pcidev = NULL;
struct pci_host_bridge *bridge;
bool found = false;
- struct pci_bus *bus;
+ struct pci_bus *bus, *child;
resource_size_t iobase = 0;
u32 ids, vendor_id, device_id;
LIST_HEAD(res);
@@ -5082,6 +4973,9 @@ int msm_pcie_enumerate(u32 rc_idx)
bus = bridge->bus;
pci_assign_unassigned_bus_resources(bus);
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
+
pci_bus_add_devices(bus);
dev->enumerated = true;
@@ -5094,13 +4988,6 @@ int msm_pcie_enumerate(u32 rc_idx)
msm_pcie_write_reg_field(dev->dm_core,
PCIE20_DEVICE_CONTROL2_STATUS2, 0xf, dev->cpl_timeout);
- if (dev->shadow_en) {
- u32 val = readl_relaxed(dev->dm_core + PCIE20_COMMAND_STATUS);
-
- PCIE_DBG(dev, "PCIE20_COMMAND_STATUS:0x%x\n", val);
- dev->rc_shadow[PCIE20_COMMAND_STATUS / 4] = val;
- }
-
do {
pcidev = pci_get_device(vendor_id, device_id, pcidev);
if (pcidev && (dev == (struct msm_pcie_dev_t *)
@@ -5117,8 +5004,10 @@ int msm_pcie_enumerate(u32 rc_idx)
goto out;
}
- pci_walk_bus(dev->dev->bus, msm_pcie_config_device_table, dev);
+ pci_walk_bus(dev->dev->bus, msm_pcie_config_device_info, dev);
+ pci_save_state(pcidev);
+ dev->default_state = pci_store_saved_state(pcidev);
out:
mutex_unlock(&dev->enumerate_lock);
@@ -5387,7 +5276,6 @@ static void msm_pcie_handle_linkdown(struct msm_pcie_dev_t *dev)
return;
dev->link_status = MSM_PCIE_LINK_DOWN;
- dev->shadow_en = false;
if (!dev->suspending) {
/* PCIe registers dump on link down */
@@ -6054,6 +5942,23 @@ static int msm_pcie_i2c_ctrl_init(struct msm_pcie_dev_t *pcie_dev)
i2c_ctrl->dump_reg_count = 0;
}
+ of_get_property(i2c_client_node, "reg_update", &size);
+
+ if (size) {
+ i2c_ctrl->reg_update = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!i2c_ctrl->reg_update)
+ return -ENOMEM;
+
+ i2c_ctrl->reg_update_count = size / sizeof(*i2c_ctrl->reg_update);
+
+ ret = of_property_read_u32_array(i2c_client_node,
+ "reg_update",
+ (unsigned int *)i2c_ctrl->reg_update,
+ size/sizeof(i2c_ctrl->reg_update->offset));
+ if (ret)
+ i2c_ctrl->reg_update_count = 0;
+ }
+
return 0;
}
@@ -6061,7 +5966,7 @@ static int msm_pcie_probe(struct platform_device *pdev)
{
int ret = 0;
int rc_idx = -1;
- int i, j, size;
+ int size;
struct msm_pcie_dev_t *pcie_dev;
struct device_node *of_node;
@@ -6274,7 +6179,6 @@ static int msm_pcie_probe(struct platform_device *pdev)
PCIE_DBG(pcie_dev, "RC%d: clkreq gpio no:%u\n",
pcie_dev->rc_idx, pcie_dev->clkreq_gpio);
- pcie_dev->shadow_en = true;
pcie_dev->aer_enable = true;
memcpy(pcie_dev->vreg, msm_pcie_vreg_info, sizeof(msm_pcie_vreg_info));
@@ -6290,18 +6194,6 @@ static int msm_pcie_probe(struct platform_device *pdev)
memcpy(pcie_dev->pipe_reset, msm_pcie_pipe_reset_info[rc_idx],
sizeof(msm_pcie_pipe_reset_info[rc_idx]));
- for (i = 0; i < PCIE_CONF_SPACE_DW; i++)
- pcie_dev->rc_shadow[i] = PCIE_CLEAR;
- for (i = 0; i < MAX_DEVICE_NUM; i++)
- for (j = 0; j < PCIE_CONF_SPACE_DW; j++)
- pcie_dev->ep_shadow[i][j] = PCIE_CLEAR;
- for (i = 0; i < MAX_DEVICE_NUM; i++) {
- pcie_dev->pcidev_table[i].bdf = 0;
- pcie_dev->pcidev_table[i].dev = NULL;
- pcie_dev->pcidev_table[i].short_bdf = 0;
- pcie_dev->pcidev_table[i].sid = 0;
- }
-
dev_set_drvdata(&pdev->dev, pcie_dev);
ret = msm_pcie_get_resources(pcie_dev, pcie_dev->pdev);
@@ -6438,6 +6330,14 @@ static int msm_pcie_remove(struct platform_device *pdev)
dev_info(&pdev->dev, "PCIe: RC%d: being removed\n", rc_idx);
}
+ if (msm_pcie_dev[rc_idx].saved_state)
+ pci_load_and_free_saved_state(msm_pcie_dev[rc_idx].dev,
+ &msm_pcie_dev[rc_idx].saved_state);
+
+ if (msm_pcie_dev[rc_idx].default_state)
+ pci_load_and_free_saved_state(msm_pcie_dev[rc_idx].dev,
+ &msm_pcie_dev[rc_idx].default_state);
+
msm_pcie_irq_deinit(&msm_pcie_dev[rc_idx]);
msm_pcie_vreg_deinit(&msm_pcie_dev[rc_idx]);
msm_pcie_clk_deinit(&msm_pcie_dev[rc_idx]);
@@ -7132,6 +7032,7 @@ static int pcie_i2c_ctrl_probe(struct i2c_client *client,
i2c_ctrl->client_i2c_write = ntn3_i2c_write;
i2c_ctrl->client_i2c_reset = ntn3_ep_reset_ctrl;
i2c_ctrl->client_i2c_dump_regs = ntn3_dump_regs;
+ i2c_ctrl->client_i2c_de_emphasis_wa = ntn3_de_emphasis_wa;
} else {
dev_err(&client->dev, "invalid client id %d\n", client_id);
}
@@ -7331,16 +7232,26 @@ static int msm_pcie_pm_suspend(struct pci_dev *dev,
return ret;
}
- if (dev && !(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE)
- && msm_pcie_confirm_linkup(pcie_dev, true, true, dev)) {
- ret = pci_save_state(dev);
- pcie_dev->saved_state = pci_store_saved_state(dev);
- }
- if (ret) {
- PCIE_ERR(pcie_dev, "PCIe: fail to save state of RC%d:%d.\n",
- pcie_dev->rc_idx, ret);
- pcie_dev->suspending = false;
- return ret;
+ if (dev) {
+ if (msm_pcie_confirm_linkup(pcie_dev, true, true, dev)) {
+ ret = pci_save_state(dev);
+ if (ret) {
+ PCIE_ERR(pcie_dev,
+ "PCIe: RC%d: fail to save state of RC%d:%d.\n",
+ pcie_dev->rc_idx, ret);
+ pcie_dev->suspending = false;
+ return ret;
+ }
+
+ pcie_dev->saved_state = pci_store_saved_state(dev);
+ } else {
+ pci_load_and_free_saved_state(dev,
+ &pcie_dev->saved_state);
+ pcie_dev->saved_state = pcie_dev->default_state;
+ PCIE_DBG(pcie_dev,
+ "PCIe: RC%d: saved default config space\n",
+ pcie_dev->rc_idx);
+ }
}
spin_lock_irqsave(&pcie_dev->cfg_lock,
@@ -7451,37 +7362,11 @@ static int msm_pcie_pm_resume(struct pci_dev *dev,
"dev->bus->number = %d dev->bus->primary = %d\n",
dev->bus->number, dev->bus->primary);
- if (!(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE)) {
- if (pcie_dev->saved_state) {
- PCIE_DBG(pcie_dev,
- "RC%d: entry of PCI framework restore state\n",
- pcie_dev->rc_idx);
-
- pci_load_and_free_saved_state(dev,
- &pcie_dev->saved_state);
- pci_restore_state(dev);
-
- PCIE_DBG(pcie_dev,
- "RC%d: exit of PCI framework restore state\n",
- pcie_dev->rc_idx);
- } else {
- PCIE_DBG(pcie_dev,
- "RC%d: restore rc config space using shadow recovery\n",
- pcie_dev->rc_idx);
- msm_pcie_cfg_recover(pcie_dev, true);
- }
- }
-
- if (pcie_dev->bridge_found) {
- PCIE_DBG(pcie_dev,
- "RC%d: entry of PCIe recover config\n",
- pcie_dev->rc_idx);
-
- msm_pcie_recover_config(dev);
-
- PCIE_DBG(pcie_dev,
- "RC%d: exit of PCIe recover config\n",
- pcie_dev->rc_idx);
+ if (dev) {
+ PCIE_DBG(pcie_dev, "RC%d: restore config space\n",
+ pcie_dev->rc_idx);
+ pci_load_and_free_saved_state(dev, &pcie_dev->saved_state);
+ pci_restore_state(dev);
}
msm_msi_config_access(dev_get_msi_domain(&pcie_dev->dev->dev), true);
@@ -8205,76 +8090,5 @@ int msm_pcie_deregister_event(struct msm_pcie_register_event *reg)
}
EXPORT_SYMBOL(msm_pcie_deregister_event);
-int msm_pcie_recover_config(struct pci_dev *dev)
-{
- int ret = 0;
- struct msm_pcie_dev_t *pcie_dev;
-
- if (dev) {
- pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
- PCIE_DBG(pcie_dev,
- "Recovery for the link of RC%d\n", pcie_dev->rc_idx);
- } else {
- pr_err("PCIe: the input pci dev is NULL.\n");
- return -ENODEV;
- }
-
- if (msm_pcie_confirm_linkup(pcie_dev, true, true, dev)) {
- PCIE_DBG(pcie_dev,
- "Recover config space of RC%d and its EP\n",
- pcie_dev->rc_idx);
- pcie_dev->shadow_en = false;
- PCIE_DBG(pcie_dev, "Recover RC%d\n", pcie_dev->rc_idx);
- msm_pcie_cfg_recover(pcie_dev, true);
- PCIE_DBG(pcie_dev, "Recover EP of RC%d\n", pcie_dev->rc_idx);
- msm_pcie_cfg_recover(pcie_dev, false);
- PCIE_DBG(pcie_dev,
- "Refreshing the saved config space in PCI framework for RC%d and its EP\n",
- pcie_dev->rc_idx);
- pci_save_state(pcie_dev->dev);
- pci_save_state(dev);
- pcie_dev->shadow_en = true;
- PCIE_DBG(pcie_dev, "Turn on shadow for RC%d\n",
- pcie_dev->rc_idx);
- } else {
- PCIE_ERR(pcie_dev,
- "PCIe: the link of RC%d is not up yet; can't recover config space.\n",
- pcie_dev->rc_idx);
- ret = -ENODEV;
- }
-
- return ret;
-}
-EXPORT_SYMBOL(msm_pcie_recover_config);
-
-int msm_pcie_shadow_control(struct pci_dev *dev, bool enable)
-{
- int ret = 0;
- struct msm_pcie_dev_t *pcie_dev;
-
- if (dev) {
- pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
- PCIE_DBG(pcie_dev,
- "User requests to %s shadow\n",
- enable ? "enable" : "disable");
- } else {
- pr_err("PCIe: the input pci dev is NULL.\n");
- return -ENODEV;
- }
-
- PCIE_DBG(pcie_dev,
- "The shadowing of RC%d is %s enabled currently.\n",
- pcie_dev->rc_idx, pcie_dev->shadow_en ? "" : "not");
-
- pcie_dev->shadow_en = enable;
-
- PCIE_DBG(pcie_dev,
- "Shadowing of RC%d is turned %s upon user's request.\n",
- pcie_dev->rc_idx, enable ? "on" : "off");
-
- return ret;
-}
-EXPORT_SYMBOL(msm_pcie_shadow_control);
-
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. PCIe RC driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c
index 1c6ba978c69f..d7c15cd523bd 100644
--- a/drivers/pinctrl/qcom/pinctrl-qcs404.c
+++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
*/
#include
@@ -411,6 +411,18 @@ enum qcs404_functions {
msm_mux_blsp_i2c_sda_b2,
msm_mux_blsp_i2c_scl_b2,
msm_mux_pwm_led11,
+ msm_mux_i2s_1_sck,
+ msm_mux_i2s_1_ws,
+ msm_mux_i2s_1_data0_dsd0,
+ msm_mux_i2s_1_data0,
+ msm_mux_i2s_1_data1_dsd1,
+ msm_mux_i2s_1_data1,
+ msm_mux_i2s_1_data2_dsd2,
+ msm_mux_i2s_1_data2,
+ msm_mux_i2s_1_data3_dsd3,
+ msm_mux_i2s_1_data3,
+ msm_mux_i2s_1_data4_dsd4,
+ msm_mux_i2s_1_data5_dsd5,
msm_mux_i2s_3_data0_a,
msm_mux_ebi2_lcd,
msm_mux_i2s_3_data1_a,
@@ -419,14 +431,25 @@ enum qcs404_functions {
msm_mux_pwm_led3,
msm_mux_i2s_3_data3_a,
msm_mux_pwm_led4,
- msm_mux_i2s_4,
+ msm_mux_i2s_4_sck,
msm_mux_ebi2_a,
msm_mux_dsd_clk_b,
msm_mux_pwm_led5,
+ msm_mux_i2s_4_ws,
+ msm_mux_i2s_4_data0_dsd0,
msm_mux_pwm_led6,
+ msm_mux_i2s_4_data0,
+ msm_mux_i2s_4_data1_dsd1,
msm_mux_pwm_led7,
+ msm_mux_i2s_4_data1,
+ msm_mux_i2s_4_data2_dsd2,
msm_mux_pwm_led8,
+ msm_mux_i2s_4_data2,
+ msm_mux_i2s_4_data3_dsd3,
msm_mux_pwm_led24,
+ msm_mux_i2s_4_data3,
+ msm_mux_i2s_4_data4_dsd4,
+ msm_mux_i2s_4_data5_dsd5,
msm_mux_spkr_dac0,
msm_mux_blsp_i2c4,
msm_mux_pwm_led9,
@@ -547,25 +570,21 @@ static const char * const gpio_groups[] = {
"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
- "gpio21", "gpio21", "gpio22", "gpio22", "gpio23", "gpio23", "gpio24",
- "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", "gpio30", "gpio31",
- "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", "gpio36", "gpio36",
- "gpio36", "gpio37", "gpio37", "gpio37", "gpio38", "gpio38", "gpio38",
- "gpio39", "gpio39", "gpio40", "gpio40", "gpio41", "gpio41", "gpio41",
- "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48",
- "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55",
- "gpio56", "gpio57", "gpio58", "gpio59", "gpio59", "gpio60", "gpio61",
- "gpio62", "gpio63", "gpio64", "gpio65", "gpio66", "gpio67", "gpio68",
- "gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74", "gpio75",
- "gpio76", "gpio77", "gpio77", "gpio78", "gpio78", "gpio78", "gpio79",
- "gpio79", "gpio79", "gpio80", "gpio81", "gpio81", "gpio82", "gpio83",
- "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90",
- "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97",
- "gpio98", "gpio99", "gpio100", "gpio101", "gpio102", "gpio103",
- "gpio104", "gpio105", "gpio106", "gpio107", "gpio108", "gpio108",
- "gpio108", "gpio109", "gpio109", "gpio110", "gpio111", "gpio112",
- "gpio113", "gpio114", "gpio115", "gpio116", "gpio117", "gpio118",
- "gpio119",
+ "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+ "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+ "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+ "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+ "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70",
+ "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77",
+ "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
+ "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
+ "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+ "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+ "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+ "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116",
+ "gpio117", "gpio118", "gpio119",
};
static const char * const hdmi_tx_groups[] = {
@@ -816,9 +835,56 @@ static const char * const pwm_led4_groups[] = {
"gpio109",
};
-static const char * const i2s_4_groups[] = {
- "gpio110", "gpio111", "gpio111", "gpio112", "gpio112", "gpio113",
- "gpio113", "gpio114", "gpio114", "gpio115", "gpio115", "gpio116",
+static const char * const i2s_1_sck_groups[] = {
+ "gpio87",
+};
+
+static const char * const i2s_1_ws_groups[] = {
+ "gpio88",
+};
+
+static const char * const i2s_1_data0_dsd0_groups[] = {
+ "gpio88",
+};
+
+static const char * const i2s_1_data0_groups[] = {
+ "gpio89",
+};
+
+static const char * const i2s_1_data1_dsd1_groups[] = {
+ "gpio89",
+};
+
+static const char * const i2s_1_data1_groups[] = {
+ "gpio90",
+};
+
+static const char * const i2s_1_data2_dsd2_groups[] = {
+ "gpio90",
+};
+
+static const char * const i2s_1_data2_groups[] = {
+ "gpio91",
+};
+
+static const char * const i2s_1_data3_dsd3_groups[] = {
+ "gpio91",
+};
+
+static const char * const i2s_1_data3_groups[] = {
+ "gpio92",
+};
+
+static const char * const i2s_1_data4_dsd4_groups[] = {
+ "gpio92",
+};
+
+static const char * const i2s_1_data5_dsd5_groups[] = {
+ "gpio93",
+};
+
+static const char * const i2s_4_sck_groups[] = {
+ "gpio110",
};
static const char * const ebi2_a_groups[] = {
@@ -833,22 +899,66 @@ static const char * const pwm_led5_groups[] = {
"gpio110",
};
+static const char * const i2s_4_ws_groups[] = {
+ "gpio111",
+};
+
+static const char * const i2s_4_data0_dsd0_groups[] = {
+ "gpio111",
+};
+
static const char * const pwm_led6_groups[] = {
"gpio111",
};
+static const char * const i2s_4_data0_groups[] = {
+ "gpio112",
+};
+
+static const char * const i2s_4_data1_dsd1_groups[] = {
+ "gpio112",
+};
+
static const char * const pwm_led7_groups[] = {
"gpio112",
};
+static const char * const i2s_4_data1_groups[] = {
+ "gpio113",
+};
+
+static const char * const i2s_4_data2_dsd2_groups[] = {
+ "gpio113",
+};
+
static const char * const pwm_led8_groups[] = {
"gpio113",
};
+static const char * const i2s_4_data2_groups[] = {
+ "gpio114",
+};
+
+static const char * const i2s_4_data3_dsd3_groups[] = {
+ "gpio114",
+};
+
static const char * const pwm_led24_groups[] = {
"gpio114",
};
+static const char * const i2s_4_data3_groups[] = {
+ "gpio115",
+};
+
+static const char * const i2s_4_data4_dsd4_groups[] = {
+ "gpio115",
+};
+
+static const char * const i2s_4_data5_dsd5_groups[] = {
+ "gpio116",
+};
+
static const char * const spkr_dac0_groups[] = {
"gpio116",
};
@@ -1366,14 +1476,37 @@ static const struct msm_function qcs404_functions[] = {
FUNCTION(pwm_led3),
FUNCTION(i2s_3_data3_a),
FUNCTION(pwm_led4),
- FUNCTION(i2s_4),
+ FUNCTION(i2s_1_sck),
+ FUNCTION(i2s_4_sck),
FUNCTION(ebi2_a),
FUNCTION(dsd_clk_b),
FUNCTION(pwm_led5),
+ FUNCTION(i2s_1_ws),
+ FUNCTION(i2s_1_data0_dsd0),
+ FUNCTION(i2s_4_ws),
+ FUNCTION(i2s_4_data0_dsd0),
FUNCTION(pwm_led6),
+ FUNCTION(i2s_1_data0),
+ FUNCTION(i2s_1_data1_dsd1),
+ FUNCTION(i2s_4_data0),
+ FUNCTION(i2s_4_data1_dsd1),
FUNCTION(pwm_led7),
+ FUNCTION(i2s_1_data1),
+ FUNCTION(i2s_1_data2_dsd2),
+ FUNCTION(i2s_4_data1),
+ FUNCTION(i2s_4_data2_dsd2),
FUNCTION(pwm_led8),
+ FUNCTION(i2s_1_data2),
+ FUNCTION(i2s_1_data3_dsd3),
+ FUNCTION(i2s_4_data2),
+ FUNCTION(i2s_4_data3_dsd3),
FUNCTION(pwm_led24),
+ FUNCTION(i2s_1_data3),
+ FUNCTION(i2s_1_data4_dsd4),
+ FUNCTION(i2s_1_data5_dsd5),
+ FUNCTION(i2s_4_data3),
+ FUNCTION(i2s_4_data4_dsd4),
+ FUNCTION(i2s_4_data5_dsd5),
FUNCTION(spkr_dac0),
FUNCTION(blsp_i2c4),
FUNCTION(pwm_led9),
@@ -1582,13 +1715,16 @@ static const struct msm_pingroup qcs404_groups[] = {
[84] = PINGROUP(84, NORTH, blsp_uart3, blsp_i2c3, blsp_spi3, gcc_gp1_clk_a, qdss_cti_trig_in_b1, _, _, _, _),
[85] = PINGROUP(85, NORTH, blsp_uart3, blsp_i2c3, blsp_spi3, gcc_gp2_clk_a, qdss_tracedata_b, _, _, _, _),
[86] = PINGROUP(86, EAST, ext_mclk0, mclk_in1, _, _, _, _, _, _, _),
- [87] = PINGROUP(87, EAST, i2s_1, dsd_clk_a, _, _, _, _, _, _, _),
- [88] = PINGROUP(88, EAST, i2s_1, i2s_1, _, _, _, _, _, _, _),
- [89] = PINGROUP(89, EAST, i2s_1, i2s_1, _, _, _, _, _, _, qdss_tracedata_b),
- [90] = PINGROUP(90, EAST, i2s_1, i2s_1, _, _, _, _, _, _, _),
- [91] = PINGROUP(91, EAST, i2s_1, i2s_1, _, _, _, _, _, _, _),
- [92] = PINGROUP(92, EAST, i2s_1, i2s_1, _, _, _, _, _, qdss_cti_trig_in_a1, _),
- [93] = PINGROUP(93, EAST, i2s_1, pwm_led22, i2s_1, _, _, _, _, _, qdss_tracedata_b),
+ [87] = PINGROUP(87, EAST, i2s_1_sck, dsd_clk_a, _, _, _, _, _, _, _),
+ [88] = PINGROUP(88, EAST, i2s_1_ws, i2s_1_data0_dsd0, _, _, _, _, _, _, _),
+ [89] = PINGROUP(89, EAST, i2s_1_data0, i2s_1_data1_dsd1, _, _, _, _, _, _,
+ qdss_tracedata_b),
+ [90] = PINGROUP(90, EAST, i2s_1_data1, i2s_1_data2_dsd2, _, _, _, _, _, _, _),
+ [91] = PINGROUP(91, EAST, i2s_1_data2, i2s_1_data3_dsd3, _, _, _, _, _, _, _),
+ [92] = PINGROUP(92, EAST, i2s_1_data3, i2s_1_data4_dsd4, _, _, _, _, _,
+ qdss_cti_trig_in_a1, _),
+ [93] = PINGROUP(93, EAST, i2s_1_data5_dsd5, pwm_led22, i2s_1, _, _, _, _, _,
+ qdss_tracedata_b),
[94] = PINGROUP(94, EAST, i2s_1, pwm_led23, i2s_1, _, qdss_cti_trig_out_a0, _, rgmi_dll2, _, _),
[95] = PINGROUP(95, EAST, i2s_1, pwm_led1, i2s_1, _, qdss_cti_trig_out_a1, _, _, _, _),
[96] = PINGROUP(96, EAST, i2s_1, pwm_led2, _, _, _, _, _, _, _),
@@ -1605,13 +1741,13 @@ static const struct msm_pingroup qcs404_groups[] = {
[107] = PINGROUP(107, EAST, i2s_3_data1_a, ebi2_lcd, _, _, ebi_cdc, _, _, _, _),
[108] = PINGROUP(108, EAST, i2s_3_data2_a, ebi2_lcd, atest_char, pwm_led3, ebi_cdc, _, _, _, _),
[109] = PINGROUP(109, EAST, i2s_3_data3_a, ebi2_lcd, pwm_led4, bimc_dte1, _, _, _, _, _),
- [110] = PINGROUP(110, EAST, i2s_4, ebi2_a, dsd_clk_b, pwm_led5, _, _, _, _, _),
- [111] = PINGROUP(111, EAST, i2s_4, i2s_4, pwm_led6, ebi_cdc, _, _, _, _, _),
- [112] = PINGROUP(112, EAST, i2s_4, i2s_4, pwm_led7, _, _, _, _, _, _),
- [113] = PINGROUP(113, EAST, i2s_4, i2s_4, pwm_led8, _, _, _, _, _, _),
- [114] = PINGROUP(114, EAST, i2s_4, i2s_4, pwm_led24, _, _, _, _, _, _),
- [115] = PINGROUP(115, EAST, i2s_4, i2s_4, _, _, _, _, _, _, _),
- [116] = PINGROUP(116, EAST, i2s_4, spkr_dac0, _, _, _, _, _, _, _),
+ [110] = PINGROUP(110, EAST, i2s_4_sck, ebi2_a, dsd_clk_b, pwm_led5, _, _, _, _, _),
+ [111] = PINGROUP(111, EAST, i2s_4_ws, i2s_4_data0_dsd0, pwm_led6, ebi_cdc, _, _, _, _, _),
+ [112] = PINGROUP(112, EAST, i2s_4_data0, i2s_4_data1_dsd1, pwm_led7, _, _, _, _, _, _),
+ [113] = PINGROUP(113, EAST, i2s_4_data1, i2s_4_data2_dsd2, pwm_led8, _, _, _, _, _, _),
+ [114] = PINGROUP(114, EAST, i2s_4_data2, i2s_4_data3_dsd3, pwm_led24, _, _, _, _, _, _),
+ [115] = PINGROUP(115, EAST, i2s_4_data3, i2s_4_data4_dsd4, _, _, _, _, _, _, _),
+ [116] = PINGROUP(116, EAST, i2s_4_data5_dsd5, spkr_dac0, _, _, _, _, _, _, _),
[117] = PINGROUP(117, NORTH, blsp_i2c4, blsp_spi4, pwm_led9, _, _, _, _, _, _),
[118] = PINGROUP(118, NORTH, blsp_i2c4, blsp_spi4, pwm_led10, _, _, _, _, _, _),
[119] = PINGROUP(119, EAST, spdifrx_opt, _, _, _, _, _, _, _, _),
diff --git a/drivers/pinctrl/qcom/pinctrl-sdxlemur.c b/drivers/pinctrl/qcom/pinctrl-sdxlemur.c
index 4a26b0ddd0fe..4576fcb3ab2e 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdxlemur.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdxlemur.c
@@ -1044,12 +1044,13 @@ static const struct msm_gpio_wakeirq_map sdxlemur_pdc_map[] = {
{1, 20}, {2, 21}, {5, 22}, {6, 23}, {9, 24}, {10, 25},
{11, 26}, {12, 27}, {13, 28}, {14, 29}, {15, 30}, {16, 31},
{17, 32}, {18, 33}, {19, 34}, {21, 35}, {22, 36}, {23, 70},
- {24, 37}, {25, 38}, {35, 40}, {43, 41}, {46, 44}, {48, 45},
- {49, 57}, {50, 46}, {52, 47}, {54, 49}, {55, 50}, {60, 53},
+ {24, 37}, {25, 38}, {32, 39}, {35, 40}, {43, 41}, {44, 42},
+ {45, 43}, {46, 44}, {48, 45}, {49, 57}, {50, 46}, {52, 47},
+ {53, 48}, {54, 49}, {55, 50}, {56, 51}, {57, 52}, {60, 53},
{61, 54}, {64, 55}, {65, 81}, {68, 56}, {71, 58}, {73, 59},
- {77, 77}, {81, 65}, {83, 63}, {84, 64}, {86, 66}, {88, 67},
- {89, 68}, {90, 69}, {93, 71}, {94, 72}, {95, 73}, {96, 74},
- {99, 75}, {103, 78}, {104, 79}
+ {76, 60}, {77, 77}, {78, 61}, {79, 62}, {81, 65}, {83, 63},
+ {84, 64}, {86, 66}, {88, 67}, {89, 68}, {90, 69}, {93, 71},
+ {94, 72}, {95, 73}, {96, 74}, {99, 75}, {103, 78}, {104, 79},
};
static const struct msm_pinctrl_soc_data sdxlemur_pinctrl = {
diff --git a/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c b/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c
index dc9702ab169d..d0ebb899fde5 100644
--- a/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c
+++ b/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c
@@ -251,6 +251,9 @@ struct ipa_fmwk_contex {
int (*ipa_usb_xdci_resume)(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot);
+ bool (*ipa_usb_is_teth_prot_connected)(
+ enum ipa_usb_teth_prot usb_teth_prot);
+
/* ipa_wdi3 APIs */
int (*ipa_wdi_init)(struct ipa_wdi_init_in_params *in,
struct ipa_wdi_init_out_params *out);
@@ -309,6 +312,8 @@ struct ipa_fmwk_contex {
int (*ipa_dereg_uc_rdyCB)(void);
+ bool (*ipa_wdi_is_tx1_used)(void);
+
/* ipa_gsb APIs*/
int (*ipa_bridge_init)(struct ipa_bridge_init_params *params, u32 *hdl);
@@ -425,8 +430,15 @@ struct ipa_fmwk_contex {
int (*ipa_eth_client_conn_evt)(struct ipa_ecm_msg *msg);
int (*ipa_eth_client_disconn_evt)(struct ipa_ecm_msg *msg);
+
int (*ipa_get_default_aggr_time_limit)(enum ipa_client_type client,
u32 *default_aggr_time_limit);
+
+ enum ipa_client_type (*ipa_eth_get_ipa_client_type_from_eth_type)(
+ enum ipa_eth_client_type eth_client_type, enum ipa_eth_pipe_direction dir);
+
+ bool (*ipa_eth_client_exist)(
+ enum ipa_eth_client_type eth_client_type, int inst_id);
};
static struct ipa_fmwk_contex *ipa_fmwk_ctx;
@@ -1056,7 +1068,8 @@ int ipa_fmwk_register_ipa_usb(const struct ipa_usb_data *in)
ipa_fmwk_ctx->ipa_usb_xdci_disconnect ||
ipa_fmwk_ctx->ipa_usb_deinit_teth_prot ||
ipa_fmwk_ctx->ipa_usb_xdci_suspend ||
- ipa_fmwk_ctx->ipa_usb_xdci_resume) {
+ ipa_fmwk_ctx->ipa_usb_xdci_resume ||
+ ipa_fmwk_ctx->ipa_usb_is_teth_prot_connected) {
pr_err("ipa_usb APIs were already initialized\n");
return -EPERM;
}
@@ -1066,6 +1079,8 @@ int ipa_fmwk_register_ipa_usb(const struct ipa_usb_data *in)
ipa_fmwk_ctx->ipa_usb_deinit_teth_prot = in->ipa_usb_deinit_teth_prot;
ipa_fmwk_ctx->ipa_usb_xdci_suspend = in->ipa_usb_xdci_suspend;
ipa_fmwk_ctx->ipa_usb_xdci_resume = in->ipa_usb_xdci_resume;
+ ipa_fmwk_ctx->ipa_usb_is_teth_prot_connected =
+ in->ipa_usb_is_teth_prot_connected;
pr_info("ipa_usb registered successfully\n");
@@ -1153,6 +1168,17 @@ int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
}
EXPORT_SYMBOL(ipa_usb_xdci_resume);
+bool ipa_usb_is_teth_prot_connected(enum ipa_usb_teth_prot usb_teth_prot)
+{
+ int ret;
+
+ IPA_FMWK_DISPATCH_RETURN(ipa_usb_is_teth_prot_connected,
+ usb_teth_prot);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_usb_is_teth_prot_connected);
+
/* registration API for IPA wdi3 module */
int ipa_fmwk_register_ipa_wdi3(const struct ipa_wdi3_data *in)
{
@@ -1175,7 +1201,8 @@ int ipa_fmwk_register_ipa_wdi3(const struct ipa_wdi3_data *in)
|| ipa_fmwk_ctx->ipa_wdi_release_smmu_mapping
|| ipa_fmwk_ctx->ipa_wdi_get_stats
|| ipa_fmwk_ctx->ipa_get_wdi_version
- || ipa_fmwk_ctx->ipa_wdi_sw_stats) {
+ || ipa_fmwk_ctx->ipa_wdi_sw_stats
+ || ipa_fmwk_ctx->ipa_wdi_is_tx1_used) {
pr_err("ipa_wdi3 APIs were already initialized\n");
return -EPERM;
}
@@ -1197,6 +1224,7 @@ int ipa_fmwk_register_ipa_wdi3(const struct ipa_wdi3_data *in)
ipa_fmwk_ctx->ipa_wdi_get_stats = in->ipa_wdi_get_stats;
ipa_fmwk_ctx->ipa_wdi_sw_stats = in->ipa_wdi_sw_stats;
ipa_fmwk_ctx->ipa_get_wdi_version = in->ipa_get_wdi_version;
+ ipa_fmwk_ctx->ipa_wdi_is_tx1_used = in->ipa_wdi_is_tx1_used;
pr_info("ipa_wdi3 registered successfully\n");
@@ -1217,6 +1245,16 @@ int ipa_wdi_init(struct ipa_wdi_init_in_params *in,
}
EXPORT_SYMBOL(ipa_wdi_init);
+bool ipa_wdi_is_tx1_used(void)
+{
+ int ret;
+
+ IPA_FMWK_DISPATCH_RETURN(ipa_wdi_is_tx1_used);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_wdi_is_tx1_used);
+
int ipa_wdi_cleanup(void)
{
int ret;
@@ -2125,7 +2163,9 @@ int ipa_fmwk_register_ipa_eth(const struct ipa_eth_data *in)
|| ipa_fmwk_ctx->ipa_eth_client_unreg_intf
|| ipa_fmwk_ctx->ipa_eth_client_set_perf_profile
|| ipa_fmwk_ctx->ipa_eth_client_conn_evt
- || ipa_fmwk_ctx->ipa_eth_client_disconn_evt) {
+ || ipa_fmwk_ctx->ipa_eth_client_disconn_evt
+ || ipa_fmwk_ctx->ipa_eth_get_ipa_client_type_from_eth_type
+ || ipa_fmwk_ctx->ipa_eth_client_exist) {
pr_err("ipa_eth APIs were already initialized\n");
return -EPERM;
}
@@ -2143,6 +2183,10 @@ int ipa_fmwk_register_ipa_eth(const struct ipa_eth_data *in)
ipa_fmwk_ctx->ipa_eth_client_conn_evt = in->ipa_eth_client_conn_evt;
ipa_fmwk_ctx->ipa_eth_client_disconn_evt =
in->ipa_eth_client_disconn_evt;
+ ipa_fmwk_ctx->ipa_eth_get_ipa_client_type_from_eth_type =
+ in->ipa_eth_get_ipa_client_type_from_eth_type;
+ ipa_fmwk_ctx->ipa_eth_client_exist =
+ in->ipa_eth_client_exist;
pr_info("ipa_eth registered successfully\n");
@@ -2275,6 +2319,30 @@ int ipa_get_default_aggr_time_limit(enum ipa_client_type client,
}
EXPORT_SYMBOL(ipa_get_default_aggr_time_limit);
+enum ipa_client_type ipa_eth_get_ipa_client_type_from_eth_type(
+ enum ipa_eth_client_type eth_client_type, enum ipa_eth_pipe_direction dir)
+{
+ int ret;
+
+ IPA_FMWK_DISPATCH_RETURN_DP(ipa_eth_get_ipa_client_type_from_eth_type,
+ eth_client_type, dir);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_eth_get_ipa_client_type_from_eth_type);
+
+bool ipa_eth_client_exist(
+ enum ipa_eth_client_type eth_client_type, int inst_id)
+{
+ int ret;
+
+ IPA_FMWK_DISPATCH_RETURN_DP(ipa_eth_client_exist,
+ eth_client_type, inst_id);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_eth_client_exist);
+
/* module functions */
static int __init ipa_fmwk_init(void)
{
diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c
index 0eeef414892c..b7662d475f95 100644
--- a/drivers/platform/msm/mhi_dev/mhi.c
+++ b/drivers/platform/msm/mhi_dev/mhi.c
@@ -91,6 +91,7 @@ static void mhi_dev_cmd_event_msi_cb(void *req);
static int mhi_dev_alloc_cmd_ack_buf_req(struct mhi_dev *mhi);
+static int mhi_dev_ring_init(struct mhi_dev *dev);
static struct mhi_dev_uevent_info channel_state_info[MHI_MAX_CHANNELS];
static DECLARE_COMPLETION(read_from_host);
@@ -476,6 +477,22 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
if (!ereq->is_cmd_cpl) {
ch = ereq->context;
+ /*
+ * Take Channel ring event lock to prevent sending
+ * completion command while the channel is getting
+ * reset/stopped.
+ * Abort sending completion event if channel has moved to
+ * stopped state.
+ */
+ mutex_lock(&ch->ring->event_lock);
+ if (ch->state == MHI_DEV_CH_STOPPED ||
+ ch->state == MHI_DEV_CH_PENDING_STOP) {
+ mhi_log(MHI_MSG_ERROR,
+ "Ch:%d is in %d state, abort sending completion evnt\n"
+ , ch->ch_id, ch->state);
+ rc = -ENXIO;
+ goto exit;
+ }
mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n",
ereq->num_events, ch->ch_id);
} else {
@@ -487,8 +504,7 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len);
if (rc) {
pr_err("%s(): error in adding element rc %d\n", __func__, rc);
- mutex_unlock(&ring->event_lock);
- return rc;
+ goto exit;
}
ring->ring_ctx_shadow->ev.rp = (ring->rd_offset *
@@ -537,6 +553,9 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
pr_err("%s: error sending in msi\n", __func__);
}
+exit:
+ if (!ereq->is_cmd_cpl)
+ mutex_unlock(&ch->ring->event_lock);
mutex_unlock(&ring->event_lock);
return rc;
}
@@ -597,18 +616,21 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi,
unsigned long flags;
struct event_req *flush_ereq;
- /*
- * Channel got stopped or closed with transfers pending
- * Do not send completion events to host
- */
- if (ch->state == MHI_DEV_CH_CLOSED ||
- ch->state == MHI_DEV_CH_STOPPED) {
- mhi_log(MHI_MSG_DBG, "Ch %d closed with %d writes pending\n",
- ch->ch_id, ch->pend_wr_count + 1);
- return -ENODEV;
- }
-
do {
+
+ /*
+ * Channel got stopped or closed with transfers pending
+ * Do not send completion events to host
+ */
+ if (ch->state == MHI_DEV_CH_CLOSED ||
+ ch->state == MHI_DEV_CH_STOPPED) {
+ mhi_log(MHI_MSG_DBG,
+ "Ch %d closed with %d writes pending\n",
+ ch->ch_id, ch->pend_wr_count + 1);
+ rc = -ENODEV;
+ break;
+ }
+
spin_lock_irqsave(&mhi->lock, flags);
if (list_empty(&ch->flush_event_req_buffers)) {
spin_unlock_irqrestore(&mhi->lock, flags);
@@ -634,7 +656,8 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi,
"Invalid cmpl evt buf - start %pK, end %pK\n",
flush_ereq->tr_events,
flush_ereq->tr_events + flush_ereq->num_events);
- return -EINVAL;
+ rc = -EINVAL;
+ break;
}
rc = mhi_dev_send_multiple_tr_events(mhi,
mhi->ch_ctx_cache[ch->ch_id].err_indx,
@@ -1326,13 +1349,7 @@ static int mhi_enable_int(void)
int rc = 0;
mhi_log(MHI_MSG_VERBOSE,
- "Enable chdb, ctrl and cmdb interrupts\n");
-
- rc = mhi_dev_mmio_enable_chdb_interrupts(mhi_ctx);
- if (rc) {
- pr_err("Failed to enable channel db: %d\n", rc);
- return rc;
- }
+ "Enable ctrl and cmdb interrupts\n");
rc = mhi_dev_mmio_enable_ctrl_interrupt(mhi_ctx);
if (rc) {
@@ -1599,7 +1616,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring,
}
}
- mutex_lock(&mhi->mhi_event_lock);
+ mutex_lock(&ring->event_lock);
/* add the ring element */
mhi_dev_add_element(ring, el, NULL, 0);
@@ -1632,7 +1649,6 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring,
*/
wmb();
- mutex_unlock(&mhi->mhi_event_lock);
mhi_log(MHI_MSG_VERBOSE, "event sent:\n");
mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n", el->evt_tr_comp.ptr);
mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n", el->evt_tr_comp.len);
@@ -1645,6 +1661,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring,
else
rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec);
+ mutex_unlock(&ring->event_lock);
return rc;
}
@@ -1811,7 +1828,57 @@ static int mhi_dev_process_stop_cmd(struct mhi_dev_ring *ring, uint32_t ch_id,
MHI_CMD_COMPL_CODE_SUCCESS);
}
-static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
+static void mhi_dev_process_reset_cmd(struct mhi_dev *mhi, int ch_id)
+{
+ int rc = 0;
+ struct mhi_dev_channel *ch;
+ struct mhi_addr host_addr;
+
+ rc = mhi_dev_mmio_disable_chdb_a7(mhi, ch_id);
+ if (rc) {
+ mhi_log(MHI_MSG_VERBOSE,
+ "Failed to disable chdb for ch %d\n", ch_id);
+ rc = mhi_dev_send_cmd_comp_event(mhi,
+ MHI_CMD_COMPL_CODE_UNDEFINED);
+ if (rc)
+ mhi_log(MHI_MSG_VERBOSE,
+ "Error with compl event\n");
+ return;
+ }
+
+ ch = &mhi->ch[ch_id];
+ /* hard stop and set the channel to stop */
+ mhi->ch_ctx_cache[ch_id].ch_state =
+ MHI_DEV_CH_STATE_DISABLED;
+ mhi->ch[ch_id].state = MHI_DEV_CH_STOPPED;
+
+ if (mhi->use_ipa)
+ host_addr.host_pa =
+ mhi->ch_ctx_shadow.host_pa +
+ (sizeof(struct mhi_dev_ch_ctx) * ch_id);
+ else
+ host_addr.device_va =
+ mhi->ch_ctx_shadow.device_va +
+ (sizeof(struct mhi_dev_ch_ctx) * ch_id);
+
+ host_addr.virt_addr =
+ &mhi->ch_ctx_cache[ch_id].ch_state;
+ host_addr.size = sizeof(enum mhi_dev_ch_ctx_state);
+
+ /* update the channel state in the host */
+ mhi_ctx->write_to_host(mhi, &host_addr, NULL,
+ MHI_DEV_DMA_SYNC);
+
+ /* send the completion event to the host */
+ rc = mhi_dev_send_cmd_comp_event(mhi,
+ MHI_CMD_COMPL_CODE_SUCCESS);
+ if (rc)
+ pr_err("Error sending command completion event\n");
+
+ ch->reset_pending = false;
+}
+
+static int mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
union mhi_dev_ring_element_type *el, void *ctx)
{
int rc = 0;
@@ -1828,7 +1895,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
switch (el->generic.type) {
case MHI_DEV_RING_EL_START:
- mhi_log(MHI_MSG_VERBOSE, "recived start cmd for channel %d\n",
+ mhi_log(MHI_MSG_VERBOSE, "received start cmd for channel %d\n",
ch_id);
if (ch_id >= (HW_CHANNEL_BASE)) {
rc = mhi_hwc_chcmd(mhi, ch_id, el->generic.type);
@@ -1836,13 +1903,26 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
mhi_log(MHI_MSG_ERROR,
"Error with HW channel cmd %d\n", rc);
rc = mhi_dev_send_cmd_comp_event(mhi,
- MHI_CMD_COMPL_CODE_UNDEFINED);
+ MHI_CMD_COMPL_CODE_UNDEFINED);
if (rc)
mhi_log(MHI_MSG_ERROR,
"Error with compl event\n");
- return;
+ return rc;
}
goto send_start_completion_event;
+ } else {
+ rc = mhi_dev_mmio_enable_chdb_a7(mhi, ch_id);
+ if (rc) {
+ mhi_log(MHI_MSG_VERBOSE,
+ "Failed to enable chdb for ch %d\n",
+ ch_id);
+ rc = mhi_dev_send_cmd_comp_event(mhi,
+ MHI_CMD_COMPL_CODE_UNDEFINED);
+ if (rc)
+ mhi_log(MHI_MSG_VERBOSE,
+ "Error with compl event\n");
+ return rc;
+ }
}
/* fetch the channel context from host */
@@ -1860,7 +1940,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
if (rc)
mhi_log(MHI_MSG_ERROR,
"Error with compl event\n");
- return;
+ return rc;
}
mhi->ring[mhi->ch_ring_start + ch_id].state =
@@ -1873,18 +1953,6 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
mhi->ch[ch_id].ring = &mhi->ring[mhi->ch_ring_start + ch_id];
mhi->ch[ch_id].ch_type = mhi->ch_ctx_cache[ch_id].ch_type;
- /* enable DB for event ring */
- rc = mhi_dev_mmio_enable_chdb_a7(mhi, ch_id);
- if (rc) {
- pr_err("Failed to enable channel db\n");
- rc = mhi_dev_send_cmd_comp_event(mhi,
- MHI_CMD_COMPL_CODE_UNDEFINED);
- if (rc)
- mhi_log(MHI_MSG_ERROR,
- "Error with compl event\n");
- return;
- }
-
if (mhi->use_edma || mhi->use_ipa) {
uint32_t evnt_ring_idx = mhi->ev_ring_start +
mhi->ch_ctx_cache[ch_id].err_indx;
@@ -1898,7 +1966,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
mhi_log(MHI_MSG_ERROR,
"error starting event ring %d\n",
mhi->ch_ctx_cache[ch_id].err_indx);
- return;
+ return rc;
}
}
mutex_lock(&mhi->ch[ch_id].ch_lock);
@@ -1953,7 +2021,7 @@ send_start_completion_event:
rc = mhi_dev_flush_cmd_completion_events(mhi, &event);
if (rc) {
pr_err("stop event send failed\n");
- return;
+ return rc;
}
} else {
/*
@@ -1966,12 +2034,13 @@ send_start_completion_event:
ring = &mhi->ring[ch_id + mhi->ch_ring_start];
if (ring->state == RING_STATE_UINT) {
pr_err("Channel not opened for %d\n", ch_id);
- return;
+ return -EINVAL;
}
ch = &mhi->ch[ch_id];
mutex_lock(&ch->ch_lock);
+ mutex_lock(&ch->ring->event_lock);
mhi->ch[ch_id].state = MHI_DEV_CH_PENDING_STOP;
rc = mhi_dev_process_stop_cmd(
@@ -1980,6 +2049,7 @@ send_start_completion_event:
if (rc)
pr_err("stop event send failed\n");
+ mutex_unlock(&ch->ring->event_lock);
mutex_unlock(&ch->ch_lock);
mhi_update_state_info_ch(ch_id, MHI_STATE_DISCONNECTED);
/* Trigger callback to clients */
@@ -2013,7 +2083,7 @@ send_start_completion_event:
rc = mhi_dev_flush_cmd_completion_events(mhi, &event);
if (rc) {
pr_err("stop event send failed\n");
- return;
+ return rc;
}
} else {
@@ -2024,39 +2094,23 @@ send_start_completion_event:
ring = &mhi->ring[ch_id + mhi->ch_ring_start];
if (ring->state == RING_STATE_UINT) {
pr_err("Channel not opened for %d\n", ch_id);
- return;
+ return -EINVAL;
}
-
ch = &mhi->ch[ch_id];
-
mutex_lock(&ch->ch_lock);
-
- /* hard stop and set the channel to stop */
- mhi->ch_ctx_cache[ch_id].ch_state =
- MHI_DEV_CH_STATE_DISABLED;
- mhi->ch[ch_id].state = MHI_DEV_CH_STOPPED;
- if (MHI_USE_DMA(mhi))
- host_addr.host_pa =
- mhi->ch_ctx_shadow.host_pa +
- (sizeof(struct mhi_dev_ch_ctx) * ch_id);
- else
- host_addr.device_va =
- mhi->ch_ctx_shadow.device_va +
- (sizeof(struct mhi_dev_ch_ctx) * ch_id);
-
- host_addr.virt_addr =
- &mhi->ch_ctx_cache[ch_id].ch_state;
- host_addr.size = sizeof(enum mhi_dev_ch_ctx_state);
-
- /* update the channel state in the host */
- mhi_ctx->write_to_host(mhi, &host_addr, NULL,
- MHI_DEV_DMA_SYNC);
-
- /* send the completion event to the host */
- rc = mhi_dev_send_cmd_comp_event(mhi,
- MHI_CMD_COMPL_CODE_SUCCESS);
- if (rc)
- pr_err("Error sending command completion event\n");
+ mutex_lock(&ch->ring->event_lock);
+ if (ch->db_pending) {
+ mhi_log(MHI_MSG_ERROR,
+ "skipping reset cmd ack for channel %d\n",
+ ch_id);
+ ch->reset_pending = true;
+ mutex_unlock(&ch->ring->event_lock);
+ mutex_unlock(&ch->ch_lock);
+ rc = -EBUSY;
+ return rc;
+ }
+ mhi_dev_process_reset_cmd(mhi, ch_id);
+ mutex_unlock(&ch->ring->event_lock);
mutex_unlock(&ch->ch_lock);
mhi_update_state_info_ch(ch_id, MHI_STATE_DISCONNECTED);
mhi_dev_trigger_cb(ch_id);
@@ -2068,9 +2122,10 @@ send_start_completion_event:
pr_err("%s: Invalid command:%d\n", __func__, el->generic.type);
break;
}
+ return rc;
}
-static void mhi_dev_process_tre_ring(struct mhi_dev *mhi,
+static int mhi_dev_process_tre_ring(struct mhi_dev *mhi,
union mhi_dev_ring_element_type *el, void *ctx)
{
struct mhi_dev_ring *ring = (struct mhi_dev_ring *)ctx;
@@ -2081,7 +2136,7 @@ static void mhi_dev_process_tre_ring(struct mhi_dev *mhi,
mhi_log(MHI_MSG_VERBOSE,
"invalid channel ring id (%d), should be < %lu\n",
ring->id, mhi->ch_ring_start);
- return;
+ return -EINVAL;
}
ch = &mhi->ch[ring->id - mhi->ch_ring_start];
@@ -2095,6 +2150,7 @@ static void mhi_dev_process_tre_ring(struct mhi_dev *mhi,
*/
if (ch->active_client && ch->active_client->event_trigger != NULL)
ch->active_client->event_trigger(&reason);
+ return 0;
}
static void mhi_dev_process_ring_pending(struct work_struct *work)
@@ -2104,11 +2160,12 @@ static void mhi_dev_process_ring_pending(struct work_struct *work)
struct list_head *cp, *q;
struct mhi_dev_ring *ring;
struct mhi_dev_channel *ch;
- int rc = 0;
+ int rc = 0, ch_id;
mutex_lock(&mhi_ctx->mhi_lock);
+
rc = mhi_dev_process_ring(&mhi->ring[mhi->cmd_ring_idx]);
- if (rc) {
+ if (rc && rc != -EBUSY) {
mhi_log(MHI_MSG_ERROR, "error processing command ring\n");
goto exit;
}
@@ -2117,12 +2174,6 @@ static void mhi_dev_process_ring_pending(struct work_struct *work)
ring = list_entry(cp, struct mhi_dev_ring, list);
list_del(cp);
mhi_log(MHI_MSG_VERBOSE, "processing ring %d\n", ring->id);
- rc = mhi_dev_process_ring(ring);
- if (rc) {
- mhi_log(MHI_MSG_ERROR,
- "error processing ring %d\n", ring->id);
- goto exit;
- }
if (ring->id < mhi->ch_ring_start) {
mhi_log(MHI_MSG_ERROR,
@@ -2131,10 +2182,40 @@ static void mhi_dev_process_ring_pending(struct work_struct *work)
}
ch = &mhi->ch[ring->id - mhi->ch_ring_start];
+
+ rc = mhi_dev_process_ring(ring);
+ if (rc) {
+ mhi_log(MHI_MSG_ERROR,
+ "error processing ring %d\n", ring->id);
+ goto exit;
+ }
+ mutex_lock(&ch->ch_lock);
+ ch->db_pending = false;
+ mutex_unlock(&ch->ch_lock);
+
+ if (ch->reset_pending) {
+ /*
+ * The channel might be reset asynchronously by the
+ * host, below reset ack is in case the channel
+ * was stopped/reset with pending DB.
+ */
+ ch_id = ch->ch_id;
+ mhi_log(MHI_MSG_VERBOSE,
+ "processing pending ch:%d reset\n", ch_id);
+ rc = mhi_dev_process_ring(
+ &mhi->ring[mhi->cmd_ring_idx]);
+ if (rc) {
+ mhi_log(MHI_MSG_ERROR,
+ "error processing command ring\n");
+ goto exit;
+ }
+ }
+
rc = mhi_dev_mmio_enable_chdb_a7(mhi, ch->ch_id);
if (rc) {
mhi_log(MHI_MSG_ERROR,
"error enabling chdb interrupt for %d\n", ch->ch_id);
+ mutex_unlock(&ch->ch_lock);
goto exit;
}
}
@@ -2169,10 +2250,12 @@ static int mhi_dev_get_event_notify(enum mhi_dev_state state,
return rc;
}
-static void mhi_dev_queue_channel_db(struct mhi_dev *mhi,
+static bool mhi_dev_queue_channel_db(struct mhi_dev *mhi,
uint32_t chintr_value, uint32_t ch_num)
{
struct mhi_dev_ring *ring;
+ struct mhi_dev_channel *ch;
+ bool work_pending = false;
int rc = 0;
for (; chintr_value; ch_num++, chintr_value >>= 1) {
@@ -2184,25 +2267,40 @@ static void mhi_dev_queue_channel_db(struct mhi_dev *mhi,
}
mhi_ring_set_state(ring, RING_STATE_PENDING);
list_add(&ring->list, &mhi->process_ring_list);
+ ch = &mhi->ch[ch_num];
+ mutex_lock(&ch->ch_lock);
+ ch->db_pending = true;
+ work_pending = true;
+ mutex_unlock(&ch->ch_lock);
rc = mhi_dev_mmio_disable_chdb_a7(mhi, ch_num);
if (rc) {
pr_err("Error disabling chdb\n");
- return;
+ return work_pending;
}
- queue_work(mhi->pending_ring_wq, &mhi->pending_work);
}
}
+ return work_pending;
}
-static void mhi_dev_check_channel_interrupt(struct mhi_dev *mhi)
+/*
+ * mhi_dev_check_channel_interrupt () - function called
+ * to check if CH DB interrupts are present to process.
+ *
+ * Return : true if valid interrupts are present
+ * to process, false if not.
+ */
+
+static bool mhi_dev_check_channel_interrupt(struct mhi_dev *mhi)
{
int i, rc = 0;
+ bool pending_work = false;
uint32_t chintr_value = 0, ch_num = 0;
rc = mhi_dev_mmio_read_chdb_status_interrupts(mhi);
if (rc) {
- pr_err("Read channel db\n");
- return;
+ mhi_log(MHI_MSG_ERROR,
+ "Error while reading CH DB\n");
+ return pending_work;
}
for (i = 0; i < MHI_MASK_ROWS_CH_EV_DB; i++) {
@@ -2213,15 +2311,18 @@ static void mhi_dev_check_channel_interrupt(struct mhi_dev *mhi)
mhi_log(MHI_MSG_VERBOSE,
"processing id: %d, ch interrupt 0x%x\n",
i, chintr_value);
- mhi_dev_queue_channel_db(mhi, chintr_value, ch_num);
+ pending_work |= mhi_dev_queue_channel_db(mhi,
+ chintr_value, ch_num);
rc = mhi_dev_mmio_write(mhi, MHI_CHDB_INT_CLEAR_A7_n(i),
mhi->chdb[i].status);
if (rc) {
- pr_err("Error writing interrupt clear for A7\n");
- return;
+ mhi_log(MHI_MSG_ERROR,
+ "Error writing interrupt clear for A7\n");
+ return pending_work;
}
}
}
+ return pending_work;
}
static void mhi_update_state_info_all(enum mhi_ctrl_info info)
@@ -2361,6 +2462,7 @@ static void mhi_dev_transfer_completion_cb(void *mreq)
/* Trigger client call back */
req->client_cb(req);
+ mutex_lock(&ch->ch_lock);
/* Flush read completions to host */
if (snd_cmpl && mhi_ctx->ch_ctx_cache[ch->ch_id].ch_type ==
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL) {
@@ -2371,7 +2473,7 @@ static void mhi_dev_transfer_completion_cb(void *mreq)
"Failed to flush read completions to host\n");
}
}
-
+ mutex_unlock(&ch->ch_lock);
if (ch->state == MHI_DEV_CH_PENDING_STOP) {
ch->state = MHI_DEV_CH_STOPPED;
rc = mhi_dev_process_stop_cmd(ch->ring, ch->ch_id, mhi_ctx);
@@ -2386,6 +2488,7 @@ static void mhi_dev_scheduler(struct work_struct *work)
struct mhi_dev *mhi = container_of(work,
struct mhi_dev, chdb_ctrl_work);
int rc = 0;
+ bool work_pending = false;
uint32_t int_value = 0;
struct mhi_dev_ring *ring;
enum mhi_dev_state state;
@@ -2439,11 +2542,13 @@ static void mhi_dev_scheduler(struct work_struct *work)
"processing cmd db interrupt with %d\n", int_value);
ring = &mhi->ring[MHI_RING_CMD_ID];
ring->state = RING_STATE_PENDING;
- queue_work(mhi->pending_ring_wq, &mhi->pending_work);
+ work_pending = true;
}
/* get the specific channel interrupts */
- mhi_dev_check_channel_interrupt(mhi);
+ work_pending |= mhi_dev_check_channel_interrupt(mhi);
+ if (work_pending)
+ queue_work(mhi->pending_ring_wq, &mhi->pending_work);
fail:
mutex_unlock(&mhi_ctx->mhi_lock);
@@ -2598,6 +2703,25 @@ static int mhi_dev_cache_host_cfg(struct mhi_dev *mhi)
mhi->cfg.event_rings;
mhi->ch_ctx_shadow.size = sizeof(struct mhi_dev_ch_ctx) *
mhi->cfg.channels;
+
+ /* Allocate ring elements, during M0 when host
+ * would have updated MHICFG register
+ */
+
+ if (!mhi->ring) {
+ mhi->ring = devm_kcalloc(&pdev->dev,
+ (mhi->cfg.channels + mhi->cfg.event_rings+1),
+ sizeof(struct mhi_dev_ring),
+ GFP_KERNEL);
+ if (!mhi->ring) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+ mhi_log(MHI_MSG_INFO,
+ "MEM_ALLOC: size:%d RING_ALLOC\n",
+ (sizeof(struct mhi_dev_ring) *
+ (mhi->cfg.channels + mhi->cfg.event_rings + 1)));
+ }
/*
* This func mhi_dev_cache_host_cfg will be called when
* processing mhi device reset as well, do not allocate
@@ -2644,6 +2768,13 @@ static int mhi_dev_cache_host_cfg(struct mhi_dev *mhi)
}
memset(mhi->ch_ctx_cache, 0, sizeof(struct mhi_dev_ch_ctx) *
mhi->cfg.channels);
+
+ rc = mhi_dev_ring_init(mhi);
+ if (rc) {
+ mhi_log(MHI_MSG_VERBOSE, "MHI dev ring init failed\n");
+ goto exit;
+ }
+
if (MHI_USE_DMA(mhi)) {
data_transfer.phy_addr = mhi->cmd_ctx_cache_dma_handle;
data_transfer.host_pa = mhi->cmd_ctx_shadow.host_pa;
@@ -2674,21 +2805,43 @@ static int mhi_dev_cache_host_cfg(struct mhi_dev *mhi)
mhi->ev_ctx_cache->rp,
mhi->ev_ctx_cache->wp);
- return mhi_ring_start(&mhi->ring[0],
+ rc = mhi_ring_start(&mhi->ring[0],
(union mhi_dev_ring_ctx *)mhi->cmd_ctx_cache, mhi);
+ if (rc) {
+ pr_err("MHI ring start failed:%d\n", rc);
+ goto exit;
+ }
+ return 0;
exit:
- if (mhi->cmd_ctx_cache)
+ if (mhi->ring) {
+ devm_kfree(&pdev->dev, mhi->ring);
+ mhi->ring = NULL;
+ mhi_log(MHI_MSG_INFO,
+ "MEM_DEALLOC: size:%d RING_ALLOC\n",
+ (sizeof(struct mhi_dev_ring) *
+ (mhi->cfg.channels + mhi->cfg.event_rings + 1)));
+ }
+ if (mhi->cmd_ctx_cache) {
dma_free_coherent(&pdev->dev,
sizeof(struct mhi_dev_cmd_ctx),
mhi->cmd_ctx_cache,
mhi->cmd_ctx_cache_dma_handle);
- if (mhi->ev_ctx_cache)
+ mhi_log(MHI_MSG_INFO,
+ "MEM_DEALLOC: size:%d CMD_CTX_CACHE\n",
+ sizeof(struct mhi_dev_cmd_ctx));
+ }
+ if (mhi->ev_ctx_cache) {
dma_free_coherent(&pdev->dev,
sizeof(struct mhi_dev_ev_ctx) *
mhi->cfg.event_rings,
mhi->ev_ctx_cache,
mhi->ev_ctx_cache_dma_handle);
+ mhi_log(MHI_MSG_INFO,
+ "MEM_DEALLOC: size:%d EV_CTX_CACHE\n",
+ sizeof(struct mhi_dev_ev_ctx) *
+ mhi->cfg.event_rings);
+ }
return rc;
}
@@ -2839,8 +2992,8 @@ static int mhi_dev_alloc_cmd_ack_buf_req(struct mhi_dev *mhi)
sizeof(*cmd_ctx->ereqs),
GFP_KERNEL);
if (!cmd_ctx->ereqs) {
+ rc = -ENOMEM;
goto free_ereqs;
- return -ENOMEM;
}
/* Allocate buffers to queue transfer completion events */
@@ -2869,9 +3022,13 @@ static int mhi_dev_alloc_cmd_ack_buf_req(struct mhi_dev *mhi)
return 0;
free_ereqs:
- kfree(mhi->cmd_ctx);
kfree(cmd_ctx->ereqs);
cmd_ctx->ereqs = NULL;
+
+ kfree(mhi->cmd_ctx);
+ mhi_log(MHI_MSG_INFO,
+ "MEM_DEALLOC: size:%d CMD_CTX\n",
+ sizeof(struct mhi_cmd_cmpl_ctx));
mhi->cmd_ctx = NULL;
return rc;
}
@@ -3066,7 +3223,8 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle)
ch = handle->channel;
do {
- if (ch->pend_wr_count) {
+ if (ch->pend_wr_count &&
+ !list_empty(&ch->event_req_buffers)) {
usleep_range(MHI_DEV_CH_CLOSE_TIMEOUT_MIN,
MHI_DEV_CH_CLOSE_TIMEOUT_MAX);
} else
@@ -3078,6 +3236,9 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle)
if (ch->pend_wr_count)
mhi_log(MHI_MSG_ERROR, "%d writes pending for channel %d\n",
ch->pend_wr_count, ch->ch_id);
+ if (!list_empty(&ch->event_req_buffers))
+ mhi_log(MHI_MSG_ERROR, "%d pending flush for channel %d\n",
+ ch->pend_wr_count, ch->ch_id);
if (ch->state != MHI_DEV_CH_PENDING_START)
if ((ch->ch_type == MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL &&
@@ -3089,14 +3250,24 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle)
ch->state = MHI_DEV_CH_CLOSED;
ch->active_client = NULL;
kfree(ch->ereqs);
+ mhi_log(MHI_MSG_INFO,
+ "MEM_DEALLOC:ch:%d size:%d EREQ\n",
+ ch->ch_id, ch->evt_req_size);
kfree(ch->tr_events);
+ mhi_log(MHI_MSG_INFO,
+ "MEM_DEALLOC:ch:%d size:%d TR_EVENTS\n",
+ ch->ch_id, ch->evt_buf_size);
ch->evt_buf_size = 0;
ch->evt_req_size = 0;
ch->ereqs = NULL;
ch->tr_events = NULL;
kfree(handle);
+ mhi_log(MHI_MSG_INFO,
+ "MEM_ALLOC:ch:%d size:%d CLNT_HANDLE\n",
+ ch->ch_id, sizeof(struct mhi_dev_client));
mutex_unlock(&ch->ch_lock);
+ return;
}
EXPORT_SYMBOL(mhi_dev_close_channel);
@@ -3175,9 +3346,9 @@ int mhi_dev_read_channel(struct mhi_req *mreq)
mutex_lock(&ch->ch_lock);
do {
- if (ch->state == MHI_DEV_CH_STOPPED) {
+ if (ch->state == MHI_DEV_CH_STOPPED || ch->reset_pending) {
mhi_log(MHI_MSG_VERBOSE,
- "channel (%d) already stopped\n",
+ "channel (%d) already stopped or RST pending\n",
mreq->chan);
bytes_read = -1;
goto exit;
@@ -3358,9 +3529,10 @@ int mhi_dev_write_channel(struct mhi_req *wreq)
}
ch->pend_wr_count++;
- if (ch->state == MHI_DEV_CH_STOPPED) {
+ if (ch->state == MHI_DEV_CH_STOPPED || ch->reset_pending) {
mhi_log(MHI_MSG_ERROR,
- "channel %d already stopped\n", wreq->chan);
+ "channel %d already stopped or RST pending\n",
+ wreq->chan);
bytes_written = -1;
goto exit;
}
@@ -3573,13 +3745,6 @@ static void mhi_dev_enable(struct work_struct *work)
}
}
- rc = mhi_dev_ring_init(mhi);
- if (rc) {
- pr_err("MHI dev ring init failed\n");
- return;
- }
-
-
rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset);
if (rc) {
pr_err("%s: get mhi state failed\n", __func__);
@@ -3893,14 +4058,6 @@ static int mhi_init(struct mhi_dev *mhi)
return rc;
}
- if (!mhi->ring)
- mhi->ring = devm_kzalloc(&pdev->dev,
- (sizeof(struct mhi_dev_ring) *
- (mhi->cfg.channels + mhi->cfg.event_rings + 1)),
- GFP_KERNEL);
- if (!mhi->ring)
- return -ENOMEM;
-
/*
* mhi_init is also called during device reset, in
* which case channel mem will already be allocated.
diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h
index 7fec5e68a001..f703d2b546cf 100644
--- a/drivers/platform/msm/mhi_dev/mhi.h
+++ b/drivers/platform/msm/mhi_dev/mhi.h
@@ -407,7 +407,7 @@ struct mhi_dev_ring {
/* ring_ctx_shadow -> tracking ring_ctx in the host */
union mhi_dev_ring_ctx *ring_ctx_shadow;
struct msi_buf_cb_data msi_buffer;
- void (*ring_cb)(struct mhi_dev *dev,
+ int (*ring_cb)(struct mhi_dev *dev,
union mhi_dev_ring_element_type *el,
void *ctx);
};
@@ -509,6 +509,8 @@ struct mhi_dev_channel {
uint32_t msi_cnt;
uint32_t flush_req_cnt;
bool skip_td;
+ bool db_pending;
+ bool reset_pending;
};
/* Structure device for mhi dev */
@@ -773,7 +775,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
* @ring_cb: callback function.
*/
void mhi_ring_set_cb(struct mhi_dev_ring *ring,
- void (*ring_cb)(struct mhi_dev *dev,
+ int (*ring_cb)(struct mhi_dev *dev,
union mhi_dev_ring_element_type *el, void *ctx));
/**
diff --git a/drivers/platform/msm/mhi_dev/mhi_mmio.c b/drivers/platform/msm/mhi_dev/mhi_mmio.c
index 71fa971dc8eb..a6aebc969a59 100644
--- a/drivers/platform/msm/mhi_dev/mhi_mmio.c
+++ b/drivers/platform/msm/mhi_dev/mhi_mmio.c
@@ -601,13 +601,18 @@ EXPORT_SYMBOL(mhi_dev_mmio_reset);
int mhi_dev_restore_mmio(struct mhi_dev *dev)
{
+ int rc = 0;
uint32_t i, reg_cntl_value;
void *reg_cntl_addr;
if (WARN_ON(!dev))
return -EINVAL;
- mhi_dev_mmio_mask_interrupts(dev);
+ mhi_dev_mmio_disable_ctrl_interrupt(dev);
+
+ mhi_dev_mmio_disable_cmdb_interrupt(dev);
+
+ mhi_dev_mmio_mask_erdb_interrupts(dev);
for (i = 0; i < (MHI_DEV_MMIO_RANGE/4); i++) {
reg_cntl_addr = dev->mmio_base_addr +
@@ -618,12 +623,26 @@ int mhi_dev_restore_mmio(struct mhi_dev *dev)
mhi_dev_mmio_clear_interrupts(dev);
+ for (i = 0; i < MHI_MASK_ROWS_CH_EV_DB; i++) {
+ /* Enable channel interrupt whose mask is enabled */
+ if (dev->chdb[i].mask) {
+ mhi_log(MHI_MSG_VERBOSE,
+ "Enabling id: %d, chdb mask 0x%x\n",
+ i, dev->chdb[i].mask);
+
+ rc = mhi_dev_mmio_write(dev, MHI_CHDB_INT_MASK_A7_n(i),
+ dev->chdb[i].mask);
+ if (rc) {
+ mhi_log(MHI_MSG_VERBOSE,
+ "Error writing enable for A7\n");
+ return rc;
+ }
+ }
+ }
+
/* Mask and enable control interrupt */
mhi_dev_mmio_enable_ctrl_interrupt(dev);
- /*Enable chdb interrupt*/
- mhi_dev_mmio_enable_chdb_interrupts(dev);
-
/*Enable cmdb interrupt*/
mhi_dev_mmio_enable_cmdb_interrupt(dev);
diff --git a/drivers/platform/msm/mhi_dev/mhi_ring.c b/drivers/platform/msm/mhi_dev/mhi_ring.c
index c06e7b36f310..e7b6fb4c389e 100644
--- a/drivers/platform/msm/mhi_dev/mhi_ring.c
+++ b/drivers/platform/msm/mhi_dev/mhi_ring.c
@@ -197,7 +197,7 @@ int mhi_dev_process_ring_element(struct mhi_dev_ring *ring, size_t offset)
el->tre.len, offset);
if (ring->ring_cb)
- ring->ring_cb(ring->mhi_dev, el, (void *)ring);
+ return ring->ring_cb(ring->mhi_dev, el, (void *)ring);
else
mhi_log(MHI_MSG_ERROR, "No callback registered for ring %d\n",
ring->id);
@@ -557,7 +557,7 @@ void mhi_ring_init(struct mhi_dev_ring *ring, enum mhi_dev_ring_type type,
EXPORT_SYMBOL(mhi_ring_init);
void mhi_ring_set_cb(struct mhi_dev_ring *ring,
- void (*ring_cb)(struct mhi_dev *dev,
+ int (*ring_cb)(struct mhi_dev *dev,
union mhi_dev_ring_element_type *el, void *ctx))
{
if (WARN_ON(!ring || !ring_cb))
diff --git a/drivers/platform/msm/mhi_dev/mhi_uci.c b/drivers/platform/msm/mhi_dev/mhi_uci.c
index d33ddfe5ea81..8bcb9530780d 100644
--- a/drivers/platform/msm/mhi_dev/mhi_uci.c
+++ b/drivers/platform/msm/mhi_dev/mhi_uci.c
@@ -375,6 +375,7 @@ struct uci_client {
struct mhi_uci_ctxt_t *uci_ctxt;
struct mutex in_chan_lock;
struct mutex out_chan_lock;
+ struct mutex client_lock;
spinlock_t req_lock;
unsigned int f_flags;
/* Pointer to dynamically allocated mhi_req structs */
@@ -557,15 +558,25 @@ static struct mhi_req *mhi_uci_get_req(struct uci_client *uci_handle)
return req;
}
-static void mhi_uci_put_req(struct uci_client *uci_handle, struct mhi_req *req)
+static int mhi_uci_put_req(struct uci_client *uci_handle, struct mhi_req *req)
{
unsigned long flags;
spin_lock_irqsave(&uci_handle->req_lock, flags);
+ if (req->is_stale) {
+ uci_log(UCI_DBG_VERBOSE,
+ "Got stale completion for ch %d, ignoring\n",
+ req->chan);
+ spin_unlock_irqrestore(&uci_handle->req_lock, flags);
+ return -EINVAL;
+ }
+
/* Remove from in-use list and add back to free list */
list_del_init(&req->list);
list_add_tail(&req->list, &uci_handle->req_list);
spin_unlock_irqrestore(&uci_handle->req_lock, flags);
+
+ return 0;
}
static void mhi_uci_write_completion_cb(void *req)
@@ -581,17 +592,12 @@ static void mhi_uci_write_completion_cb(void *req)
* the stale flag and return. The ureq was added to
* the free list when client called release function.
*/
- if (ureq->is_stale) {
- uci_log(UCI_DBG_VERBOSE,
- "Got stale completion for ch %d\n", ureq->chan);
- ureq->is_stale = false;
+ if (mhi_uci_put_req(uci_handle, ureq))
return;
- }
if (uci_handle->write_done)
complete(uci_handle->write_done);
- mhi_uci_put_req(uci_handle, ureq);
/* Write queue may be waiting for write request structs */
wake_up(&uci_handle->write_wq);
}
@@ -601,19 +607,19 @@ static void mhi_uci_read_completion_cb(void *req)
struct mhi_req *ureq = req;
struct uci_client *uci_handle;
- if (ureq->is_stale) {
- uci_log(UCI_DBG_VERBOSE,
- "Got stale completion for ch %d, ignoring\n",
- ureq->chan);
- return;
- }
-
uci_handle = (struct uci_client *)ureq->context;
uci_handle->pkt_loc = (void *)ureq->buf;
uci_handle->pkt_size = ureq->transfer_len;
- mhi_uci_put_req(uci_handle, ureq);
+ /*
+ * If this is a delayed read completion, just clear
+ * the stale flag and return. The ureq was added to
+ * the free list when client called release function.
+ */
+ if (mhi_uci_put_req(uci_handle, ureq))
+ return;
+
complete(&uci_handle->read_done);
}
@@ -782,6 +788,8 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait)
if (!uci_handle)
return -ENODEV;
+ mutex_lock(&uci_handle->client_lock);
+
poll_wait(file, &uci_handle->read_wq, wait);
poll_wait(file, &uci_handle->write_wq, wait);
/*
@@ -789,8 +797,10 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait)
* to poll are in connected state and return with the
* appropriate mask if channels are disconnected.
*/
- if (!mhi_uci_are_channels_connected(uci_handle)) {
+ if (!atomic_read(&uci_handle->mhi_chans_open) ||
+ !mhi_uci_are_channels_connected(uci_handle)) {
mask = POLLHUP;
+ mutex_unlock(&uci_handle->client_lock);
return mask;
}
mask = uci_handle->at_ctrl_mask;
@@ -810,6 +820,8 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait)
uci_log(UCI_DBG_VERBOSE,
"Client attempted to poll chan %d, returning mask 0x%x\n",
uci_handle->in_chan, mask);
+ mutex_unlock(&uci_handle->client_lock);
+
return mask;
}
@@ -1069,6 +1081,7 @@ static int mhi_uci_client_open(struct inode *mhi_inode,
return -EINVAL;
}
+ mutex_lock(&uci_handle->client_lock);
uci_log(UCI_DBG_DBG,
"Client opened struct device node 0x%x, ref count 0x%x\n",
iminor(mhi_inode), atomic_read(&uci_handle->ref_count));
@@ -1076,6 +1089,7 @@ static int mhi_uci_client_open(struct inode *mhi_inode,
if (!uci_handle) {
atomic_dec(&uci_handle->ref_count);
uci_log(UCI_DBG_DBG, "No memory, returning failure\n");
+ mutex_unlock(&uci_handle->client_lock);
return -ENOMEM;
}
uci_handle->uci_ctxt = &uci_ctxt;
@@ -1093,11 +1107,13 @@ static int mhi_uci_client_open(struct inode *mhi_inode,
uci_log(UCI_DBG_INFO,
"Closing failed channel\n");
}
+ mutex_unlock(&uci_handle->client_lock);
return rc;
}
}
}
file_handle->private_data = uci_handle;
+ mutex_unlock(&uci_handle->client_lock);
return 0;
@@ -1110,14 +1126,17 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
const struct chan_attr *in_chan_attr;
int count = 0, i;
struct mhi_req *ureq;
+ unsigned long flags;
if (!uci_handle)
return -EINVAL;
+ mutex_lock(&uci_handle->client_lock);
in_chan_attr = uci_handle->in_chan_attr;
if (!in_chan_attr) {
uci_log(UCI_DBG_ERROR, "Null channel attributes for chan %d\n",
uci_handle->in_chan);
+ mutex_unlock(&uci_handle->client_lock);
return -EINVAL;
}
@@ -1125,6 +1144,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
uci_log(UCI_DBG_DBG, "Client close chan %d, ref count 0x%x\n",
iminor(mhi_inode),
atomic_read(&uci_handle->ref_count));
+ mutex_unlock(&uci_handle->client_lock);
return 0;
}
@@ -1162,17 +1182,23 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
* to client if the transfer completes later.
*/
count = 0;
- while (!(list_empty(&uci_handle->in_use_list))) {
- ureq = container_of(uci_handle->in_use_list.next,
+
+ spin_lock_irqsave(&uci_handle->req_lock, flags);
+ if (!(uci_handle->f_flags & O_SYNC)) {
+ while (!(list_empty(&uci_handle->in_use_list))) {
+ ureq = container_of(
+ uci_handle->in_use_list.next,
struct mhi_req, list);
- list_del_init(&ureq->list);
- ureq->is_stale = true;
- uci_log(UCI_DBG_VERBOSE,
- "Adding back req for chan %d to free list\n",
- ureq->chan);
- list_add_tail(&ureq->list, &uci_handle->req_list);
- count++;
+ list_del_init(&ureq->list);
+ ureq->is_stale = true;
+ uci_log(UCI_DBG_VERBOSE,
+ "Adding back req for chan %d to free list\n",
+ ureq->chan);
+ list_add_tail(&ureq->list, &uci_handle->req_list);
+ count++;
+ }
}
+ spin_unlock_irqrestore(&uci_handle->req_lock, flags);
if (count)
uci_log(UCI_DBG_DBG,
"Client %d closed with %d transfers pending\n",
@@ -1188,6 +1214,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
atomic_set(&uci_handle->read_data_ready, 0);
atomic_set(&uci_handle->write_data_ready, 0);
file_handle->private_data = NULL;
+ mutex_unlock(&uci_handle->client_lock);
return 0;
}
@@ -1670,6 +1697,7 @@ static int mhi_register_client(struct uci_client *mhi_client, int index)
mutex_init(&mhi_client->in_chan_lock);
mutex_init(&mhi_client->out_chan_lock);
+ mutex_init(&mhi_client->client_lock);
spin_lock_init(&mhi_client->req_lock);
/* Init the completion event for AT ctrl read */
init_completion(&mhi_client->at_ctrl_read_done);
@@ -2069,13 +2097,15 @@ static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data)
mhi_dev_close_channel(client->out_handle);
mhi_dev_close_channel(client->in_handle);
- /* Add back reqs for in-use list, if any, to free list */
- while (!(list_empty(&client->in_use_list))) {
- ureq = container_of(client->in_use_list.next,
- struct mhi_req, list);
- list_del_init(&ureq->list);
- /* Add to in-use list */
- list_add_tail(&ureq->list, &client->req_list);
+ /* Add back reqs from in-use list, if any, to free list */
+ if (!(client->f_flags & O_SYNC)) {
+ while (!(list_empty(&client->in_use_list))) {
+ ureq = container_of(client->in_use_list.next,
+ struct mhi_req, list);
+ list_del_init(&ureq->list);
+ /* Add to in-use list */
+ list_add_tail(&ureq->list, &client->req_list);
+ }
}
for (i = 0; i < (client->in_chan_attr->nr_trbs); i++) {
diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c
index 3c37ffb421df..e99e9e3c2b66 100644
--- a/drivers/platform/msm/sps/bam.c
+++ b/drivers/platform/msm/sps/bam.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2011-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved.
*/
/* Bus-Access-Manager (BAM) Hardware manager. */
@@ -2140,13 +2140,16 @@ void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index, u32 option)
u32 pipe = pipe_index;
u32 desc_fifo_addr;
u32 desc_fifo_size;
- u32 *desc_fifo;
+ u32 __iomem *desc_fifo;
int i;
char desc_info[MAX_MSG_LEN];
+ struct sps_bam *dev;
if (base == NULL)
return;
+ dev = to_sps_bam_dev(virt_addr);
+
desc_fifo_addr = bam_read_reg(base, P_DESC_FIFO_ADDR, pipe);
desc_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES, pipe,
P_DESC_FIFO_SIZE);
@@ -2167,7 +2170,14 @@ void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index, u32 option)
"BAM_P_DESC_FIFO_SIZE: 0x%x (%d)\n\n",
desc_fifo_addr, desc_fifo_size, desc_fifo_size);
- desc_fifo = (u32 *) phys_to_virt(desc_fifo_addr);
+ if (dev->props.options & SPS_BAM_SMMU_EN) {
+ struct sps_pipe *pipe_indx = dev->pipes[pipe_index];
+
+ SPS_DUMP("%s", "SMMU is enabled\n");
+ desc_fifo = pipe_indx->map->desc.base;
+ } else {
+ desc_fifo = (u32 __iomem *) phys_to_virt(desc_fifo_addr);
+ }
if (option == 100) {
SPS_DUMP("%s",
diff --git a/drivers/power/supply/qcom/qbg-battery-profile.c b/drivers/power/supply/qcom/qbg-battery-profile.c
index 14b64f581e97..656a9de75d33 100644
--- a/drivers/power/supply/qcom/qbg-battery-profile.c
+++ b/drivers/power/supply/qcom/qbg-battery-profile.c
@@ -484,7 +484,7 @@ int qbg_batterydata_init(struct device_node *profile_node,
rc = alloc_chrdev_region(&battery->dev_no, 0, 1, "qbg_battery");
if (rc < 0) {
pr_err("Failed to allocate chrdev, rc=%d\n", rc);
- goto free_battery;
+ return rc;
}
cdev_init(&battery->battery_cdev, &qbg_battery_data_fops);
@@ -534,8 +534,6 @@ delete_cdev:
cdev_del(&battery->battery_cdev);
unregister_chrdev:
unregister_chrdev_region(battery->dev_no, 1);
-free_battery:
- kfree(battery);
return rc;
}
diff --git a/drivers/power/supply/qcom/qbg-battery-profile.h b/drivers/power/supply/qcom/qbg-battery-profile.h
index c28c6d5864ac..940dd7d89622 100644
--- a/drivers/power/supply/qcom/qbg-battery-profile.h
+++ b/drivers/power/supply/qcom/qbg-battery-profile.h
@@ -12,6 +12,12 @@
|| ((left) <= (right) && (left) <= (value) \
&& (value) <= (right)))
+enum ttf_calc_mode {
+ TTF_MODE_VBAT_STEP_CHG = 0,
+ TTF_MODE_OCV_STEP_CHG,
+ TTF_MODE_SOC_STEP_CHG,
+};
+
struct battery_data_table0 {
int soc_length;
int ocv_length;
diff --git a/drivers/power/supply/qcom/qbg-core.h b/drivers/power/supply/qcom/qbg-core.h
index 49a619373c08..7c0318500c75 100644
--- a/drivers/power/supply/qcom/qbg-core.h
+++ b/drivers/power/supply/qcom/qbg-core.h
@@ -17,14 +17,13 @@
enum debug_mask {
QBG_DEBUG_BUS_READ = BIT(0),
QBG_DEBUG_BUS_WRITE = BIT(1),
- QBG_DEBUG_FIFO = BIT(2),
+ QBG_DEBUG_SDAM = BIT(2),
QBG_DEBUG_IRQ = BIT(3),
QBG_DEBUG_DEVICE = BIT(4),
QBG_DEBUG_PROFILE = BIT(5),
QBG_DEBUG_SOC = BIT(6),
- QBG_DEBUG_SDAM = BIT(7),
- QBG_DEBUG_STATUS = BIT(8),
- QBG_DEBUG_PON = BIT(9),
+ QBG_DEBUG_STATUS = BIT(7),
+ QBG_DEBUG_PON = BIT(8),
};
enum qbg_sdam {
@@ -37,6 +36,32 @@ enum qbg_sdam {
SDAM_DATA4,
};
+enum qbg_data_tag {
+ QBG_DATA_TAG_FAST_CHAR,
+};
+
+enum QBG_SAMPLE_NUM_TYPE {
+ SAMPLE_NUM_1,
+ SAMPLE_NUM_2,
+ SAMPLE_NUM_4,
+ SAMPLE_NUM_8,
+ SAMPLE_NUM_16,
+ SAMPLE_NUM_32,
+ QBG_SAMPLE_NUM_INVALID,
+};
+
+enum QBG_ACCUM_INTERVAL_TYPE {
+ ACCUM_INTERVAL_100MS,
+ ACCUM_INTERVAL_200MS,
+ ACCUM_INTERVAL_500MS,
+ ACCUM_INTERVAL_1000MS,
+ ACCUM_INTERVAL_2000MS,
+ ACCUM_INTERVAL_5000MS,
+ ACCUM_INTERVAL_10000MS,
+ ACCUM_INTERVAL_100000MS,
+ ACCUM_INTERVAL_INVALID,
+};
+
/**
* struct qti_qbg - Structure for QTI QBG device
* @dev: Pointer to QBG device structure
@@ -59,6 +84,7 @@ enum qbg_sdam {
* @kdata: QBG Kernel space data structure
* @udata: QBG user space data structure
* @battery: Pointer to QBG battery data structure
+ * @step_chg_jeita_params: Jeita step charge parameters structure
* @fifo_lock: Lock for reading FIFO data
* @data_lock: Lock for reading kdata from QBG char device
* @batt_id_chan: IIO channel to read battery ID
@@ -70,8 +96,11 @@ enum qbg_sdam {
* @batt_type_str: String array denoting battery type
* @irq: QBG irq number
* @base: Base address of QBG HW
- * @num_sdams: Number of sdams used for QBG
+ * @num_data_sdams: Number of data sdams used for QBG
* @batt_id_ohm: Battery resistance in ohms
+ * @sdam_batt_id: Battery ID stored and retrieved from SDAM
+ * @essential_param_revid: QBG essential parameters revision ID
+ * @sample_time_us: Array of accumulator sample time in each QBG HW state
* @debug_mask: Debug mask to enable/disable debug prints
* @pon_ocv: Power-on OCV of QBG device
* @pon_ibat: Power-on current of QBG device
@@ -126,6 +155,7 @@ struct qti_qbg {
struct qbg_kernel_data kdata;
struct qbg_user_data udata;
struct qbg_battery_data *battery;
+ struct qbg_step_chg_jeita_params *step_chg_jeita_params;
struct mutex fifo_lock;
struct mutex data_lock;
struct iio_channel *batt_id_chan;
@@ -138,9 +168,11 @@ struct qti_qbg {
int irq;
u32 base;
u32 sdam_base;
- u32 num_sdams;
u32 num_data_sdams;
u32 batt_id_ohm;
+ u32 sdam_batt_id;
+ u32 essential_param_revid;
+ u32 sample_time_us[QBG_STATE_MAX];
u32 *debug_mask;
int pon_ocv;
int pon_ibat;
diff --git a/drivers/power/supply/qcom/qbg-sdam.c b/drivers/power/supply/qcom/qbg-sdam.c
index 4baff57527c7..7afc4c422eea 100644
--- a/drivers/power/supply/qcom/qbg-sdam.c
+++ b/drivers/power/supply/qcom/qbg-sdam.c
@@ -49,22 +49,18 @@ int qbg_sdam_get_fifo_data(struct qti_qbg *chip, struct fifo_data *fifo,
fifo_data_length = fifo_count * QBG_ONE_FIFO_LENGTH;
num_sdams = fifo_data_length / QBG_SDAM_ONE_FIFO_REGION_SIZE;
- if (num_sdams > chip->num_sdams) {
+ if (num_sdams > chip->num_data_sdams) {
pr_err("Fifo count exceeds QBG SDAMs\n");
return -EINVAL;
}
- qbg_dbg(chip, QBG_DEBUG_FIFO, "Completely filled sdams=%d\n",
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Completely filled sdams=%d\n",
num_sdams);
/* First read SDAMs that are completely filled */
for (i = 0; i < num_sdams; i++) {
- if (!chip->sdam[i]) {
- pr_err("Number of SDAMs not sufficient to read FIFOs\n");
- return -ENODEV;
- }
rc = qbg_sdam_read(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_DATA0 + i),
+ QBG_SDAM_DATA_START_OFFSET(chip, (SDAM_DATA0 + i)),
(((u8 *)fifo) + bytes_read),
QBG_SDAM_ONE_FIFO_REGION_SIZE);
if (rc < 0) {
@@ -77,12 +73,12 @@ int qbg_sdam_get_fifo_data(struct qti_qbg *chip, struct fifo_data *fifo,
/* Next read SDAM that is partially filled */
bytes_remaining = fifo_data_length - bytes_read;
- qbg_dbg(chip, QBG_DEBUG_FIFO, "Valid data in partially filled sdam:%d bytes\n",
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Valid data in partially filled sdam:%d bytes\n",
bytes_remaining);
if (bytes_remaining) {
rc = qbg_sdam_read(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_DATA0 + i),
+ QBG_SDAM_DATA_START_OFFSET(chip, (SDAM_DATA0 + i)),
(((u8 *)fifo) + bytes_read),
bytes_remaining);
if (rc < 0) {
@@ -93,7 +89,7 @@ int qbg_sdam_get_fifo_data(struct qti_qbg *chip, struct fifo_data *fifo,
bytes_read += bytes_remaining;
}
- qbg_dbg(chip, QBG_DEBUG_FIFO, "Total bytes read=%d\n", bytes_read);
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Total bytes read=%d\n", bytes_read);
return rc;
}
@@ -106,13 +102,13 @@ int qbg_sdam_get_essential_params(struct qti_qbg *chip, u8 *params)
return -EINVAL;
rc = qbg_sdam_read(chip,
- chip->sdam_base + (SDAM_CTRL1 * 0x100) +
+ chip->sdam_base + (SDAM_CTRL1 * QBG_SINGLE_SDAM_SIZE) +
QBG_ESSENTIAL_PARAMS_START_OFFSET,
(u8 *)params, sizeof(struct qbg_essential_params));
if (rc < 0)
pr_err("Failed to read QBG essential params, rc=%d\n", rc);
else
- qbg_dbg(chip, QBG_DEBUG_FIFO, "Read QBG essential params\n");
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Read QBG essential params\n");
return rc;
}
@@ -125,13 +121,94 @@ int qbg_sdam_set_essential_params(struct qti_qbg *chip, u8 *params)
return -EINVAL;
rc = qbg_sdam_write(chip,
- chip->sdam_base + (SDAM_CTRL1 * 0x100) +
+ chip->sdam_base + (SDAM_CTRL1 * QBG_SINGLE_SDAM_SIZE) +
QBG_ESSENTIAL_PARAMS_START_OFFSET,
(u8 *)params, sizeof(struct qbg_essential_params));
if (rc < 0)
pr_err("Failed to write QBG essential params, rc=%d\n", rc);
else
- qbg_dbg(chip, QBG_DEBUG_FIFO, "Written QBG essential params\n");
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Written QBG essential params\n");
+
+ return rc;
+}
+
+int qbg_sdam_get_essential_param_revid(struct qti_qbg *chip, u8 *revid)
+{
+ int rc;
+
+ if (!chip || !revid)
+ return -EINVAL;
+
+ rc = qbg_sdam_read(chip,
+ chip->sdam_base + (SDAM_CTRL1 * QBG_SINGLE_SDAM_SIZE) +
+ QBG_ESSENTIAL_PARAMS_REVID_OFFSET,
+ revid, 1);
+ if (rc < 0)
+ pr_err("Failed to read QBG essential params revid, rc=%d\n", rc);
+ else
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Read QBG essential params revid:%u\n", *revid);
+
+ return rc;
+}
+
+int qbg_sdam_set_essential_param_revid(struct qti_qbg *chip, u8 revid)
+{
+ int rc;
+
+ if (!chip)
+ return -EINVAL;
+
+ rc = qbg_sdam_write(chip,
+ chip->sdam_base + (SDAM_CTRL1 * QBG_SINGLE_SDAM_SIZE) +
+ QBG_ESSENTIAL_PARAMS_REVID_OFFSET,
+ &revid, 1);
+ if (rc < 0)
+ pr_err("Failed to write QBG essential params revid, rc=%d\n", rc);
+ else
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Store QBG essential params revid:%u\n",
+ revid);
+
+ return rc;
+}
+
+int qbg_sdam_get_battery_id(struct qti_qbg *chip, u32 *battid)
+{
+ int rc;
+ u8 buf[2];
+
+ if (!chip || !battid)
+ return -EINVAL;
+
+ rc = qbg_sdam_read(chip,
+ chip->sdam_base + (SDAM_CTRL1 * QBG_SINGLE_SDAM_SIZE) +
+ QBG_ESSENTIAL_PARAMS_BATTID_OFFSET,
+ buf, 2);
+ if (rc < 0) {
+ pr_err("Failed to read QBG battery ID, rc=%d\n", rc);
+ } else {
+ *battid = buf[0] | (buf[1] << 8);
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Read QBG battery ID:%u\n",
+ *battid);
+ }
+
+ return rc;
+}
+
+int qbg_sdam_set_battery_id(struct qti_qbg *chip, u32 battid)
+{
+ int rc;
+
+ if (!chip)
+ return -EINVAL;
+
+ rc = qbg_sdam_write(chip,
+ chip->sdam_base + (SDAM_CTRL1 * QBG_SINGLE_SDAM_SIZE) +
+ QBG_ESSENTIAL_PARAMS_BATTID_OFFSET,
+ (u8 *)&battid, 2);
+ if (rc < 0)
+ pr_err("Failed to write QBG battery ID, rc=%d\n", rc);
+ else
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "Store QBG battery ID:%u\n", battid);
return rc;
}
diff --git a/drivers/power/supply/qcom/qbg-sdam.h b/drivers/power/supply/qcom/qbg-sdam.h
index db888cc20094..1e9109e99f3f 100644
--- a/drivers/power/supply/qcom/qbg-sdam.h
+++ b/drivers/power/supply/qcom/qbg-sdam.h
@@ -10,7 +10,9 @@
#define QBG_SDAM_FIFO_COUNT_OFFSET 0x46
#define QBG_ESSENTIAL_PARAMS_START_OFFSET 0x47
#define QBG_SDAM_START_OFFSET 0x4b
+#define QBG_ESSENTIAL_PARAMS_BATTID_OFFSET 0x45
#define QBG_SDAM_BHARGER_OCV_HDRM_OFFSET 0x5b
+#define QBG_ESSENTIAL_PARAMS_REVID_OFFSET 0xbb
#define QBG_SDAM_INT_TEST1 0xe0
#define QBG_SDAM_INT_TEST_VAL 0xe1
#define QBG_SDAM_DATA_PUSH_COUNTER_OFFSET 0x46
@@ -19,14 +21,23 @@
#define QBG_SDAM_ONE_FIFO_REGION_SIZE 117 /* 117 bytes for FIFO starting from 0x4B */
#define QBG_SDAM_ESR_PULSE_FIFO_INDEX 11
-#define QBG_SDAM_DATA_START_OFFSET(chip, index) \
- (chip->sdam_base + (index * 0x100) + QBG_SDAM_START_OFFSET)
+#define QBG_SINGLE_SDAM_SIZE 0x100
+#define QBG_SDAM_BASE(chip, index) (chip->sdam_base + (index * QBG_SINGLE_SDAM_SIZE))
-int qbg_sdam_read(struct qti_qbg *chip, int offset, u8 *data, int length);
-int qbg_sdam_write(struct qti_qbg *chip, int offset, u8 *data, int length);
+#define QBG_SDAM_DATA_START_OFFSET(chip, index) \
+ (QBG_SDAM_BASE(chip, index) + QBG_SDAM_START_OFFSET)
+
+int qbg_sdam_read(struct qti_qbg *chip, int offset, u8 *data,
+ int length);
+int qbg_sdam_write(struct qti_qbg *chip, int offset, u8 *data,
+ int length);
int qbg_sdam_get_fifo_data(struct qti_qbg *chip, struct fifo_data *fifo,
u32 fifo_count);
int qbg_sdam_get_essential_params(struct qti_qbg *chip, u8 *params);
int qbg_sdam_set_essential_params(struct qti_qbg *chip, u8 *params);
+int qbg_sdam_get_essential_param_revid(struct qti_qbg *chip, u8 *revid);
+int qbg_sdam_set_essential_param_revid(struct qti_qbg *chip, u8 revid);
+int qbg_sdam_get_battery_id(struct qti_qbg *chip, u32 *battid);
+int qbg_sdam_set_battery_id(struct qti_qbg *chip, u32 battid);
#endif /* __QBG_SDAM_H__ */
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index b0909d23c20c..2796e16c2a11 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
*/
#ifndef __QG_CORE_H__
@@ -154,6 +154,7 @@ struct qpnp_qg {
bool fvss_active;
bool tcss_active;
bool bass_active;
+ bool first_profile_load;
int charge_status;
int charge_type;
int chg_iterm_ma;
diff --git a/drivers/power/supply/qcom/qg-iio.h b/drivers/power/supply/qcom/qg-iio.h
index 46e2232cd74f..b1ee2c4ea3f5 100644
--- a/drivers/power/supply/qcom/qg-iio.h
+++ b/drivers/power/supply/qcom/qg-iio.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
*/
#ifndef __QG_IIO_H
@@ -91,6 +91,7 @@ static const struct qg_iio_channels qg_iio_psy_channels[] = {
QG_CHAN_RES("esr_actual", PSY_IIO_ESR_ACTUAL)
QG_CHAN_RES("esr_nominal", PSY_IIO_ESR_NOMINAL)
QG_CHAN_INDEX("soh", PSY_IIO_SOH)
+ QG_CHAN_INDEX("clear_soh", PSY_IIO_CLEAR_SOH)
QG_CHAN_ENERGY("cc_soc", PSY_IIO_CC_SOC)
QG_CHAN_ACT("fg_reset", PSY_IIO_FG_RESET)
QG_CHAN_VOLT("voltage_avg", PSY_IIO_VOLTAGE_AVG)
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index 96526b23a1c6..4c8839273140 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -2135,6 +2135,9 @@ static int qg_iio_write_raw(struct iio_dev *indio_dev,
if (chip->sp)
soh_profile_update(chip->sp, chip->soh);
break;
+ case PSY_IIO_CLEAR_SOH:
+ chip->first_profile_load = val1;
+ break;
case PSY_IIO_ESR_ACTUAL:
chip->esr_actual = val1;
break;
@@ -2260,6 +2263,9 @@ static int qg_iio_read_raw(struct iio_dev *indio_dev,
case PSY_IIO_SOH:
*val1 = chip->soh;
break;
+ case PSY_IIO_CLEAR_SOH:
+ *val1 = chip->first_profile_load;
+ break;
case PSY_IIO_CC_SOC:
rc = qg_get_cc_soc(chip, val1);
break;
@@ -3470,6 +3476,7 @@ static int qg_sanitize_sdam(struct qpnp_qg *chip)
rc = qg_sdam_write(SDAM_MAGIC, SDAM_MAGIC_NUMBER);
if (!rc)
qg_dbg(chip, QG_DEBUG_PON, "First boot. SDAM initilized\n");
+ chip->first_profile_load = true;
} else {
/* SDAM has invalid value */
rc = qg_sdam_clear();
@@ -3477,6 +3484,7 @@ static int qg_sanitize_sdam(struct qpnp_qg *chip)
pr_err("SDAM uninitialized, SDAM reset\n");
rc = qg_sdam_write(SDAM_MAGIC, SDAM_MAGIC_NUMBER);
}
+ chip->first_profile_load = true;
}
if (rc < 0)
diff --git a/drivers/power/supply/qcom/qti-qbg.c b/drivers/power/supply/qcom/qti-qbg.c
index 952452390639..bdec442c2ae6 100644
--- a/drivers/power/supply/qcom/qti-qbg.c
+++ b/drivers/power/supply/qcom/qti-qbg.c
@@ -67,15 +67,28 @@
#define QBG_MAIN_VBAT_EMPTY_THRESH 0x56
+#define QBG_MAIN_HPM_MEAS_CTL2 0x5d
+
+#define QBG_MAIN_MPM_MEAS_CTL2 0x61
+
+#define QBG_MAIN_LPM_MEAS_CTL2 0x65
+#define QBG_NUM_OF_ACCUM_MASK GENMASK(7, 5)
+#define QBG_NUM_OF_ACCUM_SHIFT 5
+#define QBG_ACCUM_INTERVAL_MASK GENMASK(2, 0)
+
+#define QBG_MAIN_FAST_CHAR_MEAS_CTL2 0x69
+
#define QBG_MAIN_PON_OCV_ACC0_DATA0 0x70
+#define QBG_MAIN_LAST_ADC_ACC0_DATA0 0x9A
+
#define QBG_MAIN_LAST_BURST_AVG_ACC0_DATA0 0xA0
#define QBG_MAIN_LAST_BURST_AVG_ACC2_DATA0 0xA4
#define QBG_FAST_CHAR_DELTA_MS 100000
#define VBATT_1S_LSB 19463
-#define IBATT_10A_LSB 30518
+#define IBATT_10A_LSB 6103
#define VBAT_0PCT_OCV 300000000ULL
#define ICHG_FS_10A 1
#define TBAT_LSB 7500
@@ -86,12 +99,21 @@
#define OCV_BOOST_MAX_UV 5600000
#define OCV_BOOST_STEP_UV 100000
-enum qbg_data_tag {
- QBG_DATA_TAG_FAST_CHAR,
-};
+#define BATT_HOT_DECIDEGREE_MAX 600
+
+#define MILLI_TO_10NANO 100000
+#define MICRO_TO_10NANO 100
+#define MILLI_TO_MICRO 1000
static int qbg_debug_mask;
+static const unsigned int qbg_accum_interval[8] = {
+ 10000, 20000, 50000, 100000, 150000, 200000, 500000, 1000000};
+static const unsigned int qbg_lpm_accum_interval[8] = {
+ 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000};
+static const unsigned int qbg_fast_char_avg_interval[8] = {
+ 6250, 12500, 25000, 50000, 100000, 200000, 400000, 800000};
+
static const struct qbg_iio_channels qbg_iio_psy_channels[] = {
QBG_CHAN_INDEX("debug_battery", PSY_IIO_DEBUG_BATTERY)
QBG_CHAN_ENERGY("capacity", PSY_IIO_CAPACITY)
@@ -100,6 +122,7 @@ static const struct qbg_iio_channels qbg_iio_psy_channels[] = {
QBG_CHAN_VOLT("voltage_max", PSY_IIO_VOLTAGE_MAX)
QBG_CHAN_VOLT("voltage_now", PSY_IIO_VOLTAGE_NOW)
QBG_CHAN_VOLT("voltage_ocv", PSY_IIO_VOLTAGE_OCV)
+ QBG_CHAN_VOLT("voltage_avg", PSY_IIO_VOLTAGE_AVG)
QBG_CHAN_CUR("current_now", PSY_IIO_CURRENT_NOW)
QBG_CHAN_RES("resistance_id", PSY_IIO_RESISTANCE_ID)
QBG_CHAN_TSTAMP("time_to_full_avg", PSY_IIO_TIME_TO_FULL_AVG)
@@ -215,7 +238,7 @@ static int qbg_get_fifo_count(struct qti_qbg *chip, u32 *fifo_count)
u8 val[2];
rc = qbg_sdam_read(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_FIFO_COUNT_OFFSET,
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_FIFO_COUNT_OFFSET,
val, 2);
if (rc < 0) {
pr_err("Failed to read QBG SDAM, rc=%d\n", rc);
@@ -223,7 +246,7 @@ static int qbg_get_fifo_count(struct qti_qbg *chip, u32 *fifo_count)
}
*fifo_count = (val[1] << 8) | val[0];
- qbg_dbg(chip, QBG_DEBUG_FIFO, "FIFO count=%d\n", *fifo_count);
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "FIFO count=%d\n", *fifo_count);
return rc;
}
@@ -248,13 +271,14 @@ static void update_ocv_for_boost(struct qti_qbg *chip)
qbg_dbg(chip, QBG_DEBUG_STATUS, "Boost OCV value written =%d\n", ocv_for_boost);
qbg_sdam_write(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_BHARGER_OCV_HDRM_OFFSET,
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_BHARGER_OCV_HDRM_OFFSET,
(u8 *)&ocv_for_boost, 1);
}
static void process_udata_work(struct work_struct *work)
{
struct qti_qbg *chip = container_of(work, struct qti_qbg, udata_work);
+ int rc;
if (chip->udata.param[QBG_PARAM_BATT_SOC].valid)
chip->batt_soc = chip->udata.param[QBG_PARAM_BATT_SOC].data;
@@ -306,6 +330,20 @@ static void process_udata_work(struct work_struct *work)
if (chip->udata.param[QBG_PARAM_TBAT].valid)
chip->tbat = chip->udata.param[QBG_PARAM_TBAT].data;
+ if (chip->udata.param[QBG_PARAM_ESSENTIAL_PARAM_REVID].valid) {
+ chip->essential_param_revid =
+ chip->udata.param[QBG_PARAM_ESSENTIAL_PARAM_REVID].data;
+
+ rc = qbg_sdam_set_essential_param_revid(chip, chip->essential_param_revid);
+ if (rc < 0)
+ pr_err("Failed to set essential param revid in sdam, rc=%d\n",
+ rc);
+ }
+
+ rc = qbg_sdam_set_battery_id(chip, chip->batt_id_ohm / 1000);
+ if (rc < 0)
+ pr_err("Failed to set battid in sdam, rc=%d\n", rc);
+
qbg_dbg(chip, QBG_DEBUG_STATUS, "udata update: batt_soc=%d sys_soc=%d soc=%d qbg_esr=%d\n",
(chip->batt_soc != INT_MIN) ? chip->batt_soc : -EINVAL,
(chip->sys_soc != INT_MIN) ? chip->sys_soc : -EINVAL,
@@ -382,7 +420,7 @@ static int qbg_process_fifo(struct qti_qbg *chip, u32 fifo_count)
unsigned long timestamp;
if (!fifo_count) {
- qbg_dbg(chip, QBG_DEBUG_FIFO, "No FIFO data\n");
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "No FIFO data\n");
return -EINVAL;
}
@@ -439,7 +477,7 @@ static int qbg_process_fifo(struct qti_qbg *chip, u32 fifo_count)
esr = (vbat1 - vbat1_esr) / ((ibat_esr - ibat) * 10000);
esr *= 10000;
chip->esr = esr;
- qbg_dbg(chip, QBG_DEBUG_FIFO, "ESR:%u, ibat=%d ibat_esr=%d vbat1:%u vbat1_esr:%u\n",
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "ESR:%u, ibat=%d ibat_esr=%d vbat1:%u vbat1_esr:%u\n",
esr, ibat, ibat_esr, vbat1, vbat1_esr);
} else {
pr_err("Couldn't calculate ESR, ibat=%d ibat_esr=%d vbat1:%u vbat1_esr:%u\n",
@@ -456,7 +494,7 @@ static int qbg_process_fifo(struct qti_qbg *chip, u32 fifo_count)
goto ret;
}
- qbg_dbg(chip, QBG_DEBUG_FIFO, "vbat1:%u ibat:%d tbat:%u ibat_t:%u\n",
+ qbg_dbg(chip, QBG_DEBUG_SDAM, "vbat1:%u ibat:%d tbat:%u ibat_t:%u\n",
vbat1, ibat, tbat, ibat_t);
chip->kdata.fifo[i].v1 = vbat1;
@@ -464,6 +502,7 @@ static int qbg_process_fifo(struct qti_qbg *chip, u32 fifo_count)
chip->kdata.fifo[i].i = ibat;
chip->kdata.fifo[i].tbat = tbat;
chip->kdata.fifo[i].ibat = ibat_t;
+ chip->kdata.fifo[i].data_tag = fifo[i].data_tag;
}
ret:
@@ -477,7 +516,7 @@ static int qbg_clear_fifo_data(struct qti_qbg *chip)
u8 val[2] = {0, 0};
rc = qbg_sdam_write(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_FIFO_COUNT_OFFSET,
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_FIFO_COUNT_OFFSET,
val, 2);
if (rc < 0) {
pr_err("Failed to clear QBG FIFO Count, rc=%d\n", rc);
@@ -487,7 +526,7 @@ static int qbg_clear_fifo_data(struct qti_qbg *chip)
for (i = 0; i < 10; i++) {
val[0] = 0;
rc = qbg_sdam_write(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
val, 1);
if (rc < 0) {
pr_err("Failed to SDAM0 test val to 0, rc=%d\n", rc);
@@ -496,7 +535,7 @@ static int qbg_clear_fifo_data(struct qti_qbg *chip)
/* Handshake with PBS to access FIFO data */
rc = qbg_sdam_read(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
val, 1);
if (rc < 0) {
pr_err("Failed to read QBG SDAM, rc=%d\n", rc);
@@ -512,7 +551,7 @@ static int qbg_clear_fifo_data(struct qti_qbg *chip)
val[0] = QBG_SDAM_START_OFFSET;
val[1] = 0x0;
rc = qbg_sdam_write(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_DATA0) + QBG_SDAM_DATA_PUSH_COUNTER_OFFSET,
+ QBG_SDAM_BASE(chip, SDAM_DATA0) + QBG_SDAM_DATA_PUSH_COUNTER_OFFSET,
val, 2);
if (rc < 0) {
pr_err("Failed to configure QBG data push counter, rc=%d\n", rc);
@@ -520,7 +559,7 @@ static int qbg_clear_fifo_data(struct qti_qbg *chip)
}
val[0] = 0x0;
rc = qbg_sdam_write(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_PBS_STATUS_OFFSET,
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_PBS_STATUS_OFFSET,
val, 1);
if (rc < 0) {
pr_err("Failed to set QBG PBS status, rc=%d\n", rc);
@@ -537,7 +576,7 @@ static int qbg_init_sdam(struct qti_qbg *chip)
val[0] = 0x80;
rc = qbg_sdam_write(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST1, val, 1);
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST1, val, 1);
if (rc < 0) {
pr_err("Failed to write QBG SDAM, rc=%d\n", rc);
return rc;
@@ -545,7 +584,7 @@ static int qbg_init_sdam(struct qti_qbg *chip)
val[0] = 0;
rc = qbg_sdam_read(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
val, 1);
if (rc < 0) {
pr_err("Faiiled to read QBG SDAM, rc=%d\n", rc);
@@ -673,6 +712,211 @@ static int qbg_notifier_cb(struct notifier_block *nb,
return NOTIFY_OK;
}
+static int qbg_read_range_data_from_node(struct device_node *node,
+ const char *prop_str, struct ranges *ranges,
+ int max_threshold, u32 max_value)
+{
+ int rc = 0, i, length, per_tuple_length, tuples;
+
+ if (!node || !prop_str || !ranges) {
+ pr_err("Invalid parameters passed\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32));
+ if (rc < 0) {
+ pr_err("Count %s failed, rc=%d\n", prop_str, rc);
+ return rc;
+ }
+
+ length = rc;
+ per_tuple_length = sizeof(struct range_data) / sizeof(u32);
+ if (length % per_tuple_length) {
+ pr_err("%s length (%d) should be multiple of %d\n",
+ prop_str, length, per_tuple_length);
+ return -EINVAL;
+ }
+
+ tuples = length / per_tuple_length;
+ if (tuples > QBG_MAX_STEP_CHG_ENTRIES) {
+ pr_err("too many entries(%d), only %d allowed\n",
+ tuples, QBG_MAX_STEP_CHG_ENTRIES);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32_array(node, prop_str, (u32 *)ranges->data, length);
+ if (rc < 0) {
+ pr_err("Read %s failed, rc=%d\n", prop_str, rc);
+ return rc;
+ }
+
+ for (i = 0; i < tuples; i++) {
+ if (ranges->data[i].low_threshold >
+ ranges->data[i].high_threshold) {
+ pr_err("%s thresholds should be in ascendant ranges data\n",
+ prop_str);
+ rc = -EINVAL;
+ goto clean;
+ }
+
+ if (i != 0) {
+ if (ranges->data[i - 1].high_threshold >
+ ranges->data[i].low_threshold) {
+ pr_err("%s thresholds should be in ascendant ranges data\n",
+ prop_str);
+ rc = -EINVAL;
+ goto clean;
+ }
+ }
+
+ if (ranges->data[i].low_threshold > max_threshold)
+ ranges->data[i].low_threshold = max_threshold;
+ if (ranges->data[i].high_threshold > max_threshold)
+ ranges->data[i].high_threshold = max_threshold;
+ if (ranges->data[i].value > max_value)
+ ranges->data[i].value = max_value;
+ }
+
+ ranges->range_count = tuples;
+ ranges->valid = true;
+ return rc;
+clean:
+ memset(ranges, 0, tuples * sizeof(struct range_data));
+ return rc;
+}
+
+#define QBG_DEFAULT_JEITA_FULL_FV_10NV 420000000
+#define QBG_DEFAULT_BATTERY_BETA 4250
+#define QBG_DEFAULT_BATTERY_THERM_KOHM 100
+#define QBG_DEFAULT_JEITA_WARM_ADC_DATA 0x25e3 /* WARM = 40 DegC */
+#define QBG_DEFAULT_JEITA_COOL_ADC_DATA 0x5314 /* COOL = 5 DegC */
+static int parse_step_chg_jeita_params(struct qti_qbg *chip, struct device_node *profile_node)
+{
+ struct qbg_step_chg_jeita_params *step_chg_jeita;
+ bool soc_based_step_chg = false;
+ bool ocv_based_step_chg = false;
+ int rc;
+ int i = 0;
+ u32 temp[2];
+ bool jeita_thresh_valid = true;
+
+ step_chg_jeita = devm_kzalloc(chip->dev, sizeof(*step_chg_jeita),
+ GFP_KERNEL);
+ if (!step_chg_jeita)
+ return -ENOMEM;
+
+ /* choose the lower of the two soft threaholds as the jeita-full-fv */
+ step_chg_jeita->jeita_full_fv_10nv = QBG_DEFAULT_JEITA_FULL_FV_10NV;
+ rc = of_property_read_u32_array(profile_node, "qcom,jeita-soft-fv-uv",
+ temp, 2);
+ if (!rc)
+ step_chg_jeita->jeita_full_fv_10nv = min(temp[0], temp[1]) * MICRO_TO_10NANO;
+ else
+ pr_debug("Failed to read jeita full fv from battery profile, rc=%d\n", rc);
+
+ step_chg_jeita->jeita_full_iterm_10na = chip->iterm_ma * MILLI_TO_10NANO;
+
+ soc_based_step_chg = of_property_read_bool(profile_node, "qcom,soc-based-step-chg");
+ ocv_based_step_chg = of_property_read_bool(profile_node, "qcom,ocv-based-step-chg");
+ if (soc_based_step_chg)
+ step_chg_jeita->ttf_calc_mode = TTF_MODE_SOC_STEP_CHG;
+ else if (ocv_based_step_chg)
+ step_chg_jeita->ttf_calc_mode = TTF_MODE_OCV_STEP_CHG;
+
+ step_chg_jeita->battery_beta = QBG_DEFAULT_BATTERY_BETA;
+ rc = of_property_read_u32(profile_node, "qcom,battery-beta",
+ &step_chg_jeita->battery_beta);
+ if (rc < 0)
+ pr_debug("Failed to read battery beta form battery profile, rc=%d\n", rc);
+
+ step_chg_jeita->battery_therm_kohm = QBG_DEFAULT_BATTERY_THERM_KOHM;
+ rc = of_property_read_u32(profile_node, "qcom,battery-therm-kohm",
+ &step_chg_jeita->battery_therm_kohm);
+ if (rc < 0)
+ pr_debug("Failed to read battery therm from battery profile, rc=%d\n", rc);
+
+ step_chg_jeita->jeita_cool_adc_value = QBG_DEFAULT_JEITA_COOL_ADC_DATA;
+ step_chg_jeita->jeita_warm_adc_value = QBG_DEFAULT_JEITA_WARM_ADC_DATA;
+ rc = of_property_read_u32_array(profile_node, "qcom,jeita-soft-thresholds",
+ temp, 2);
+ if (!rc) {
+ step_chg_jeita->jeita_cool_adc_value = temp[0];
+ step_chg_jeita->jeita_warm_adc_value = temp[1];
+ } else {
+ jeita_thresh_valid = false;
+ pr_debug("Failed to read jeita cool and warm value from battery profile, rc=%d\n",
+ rc);
+ }
+
+ rc = qbg_read_range_data_from_node(profile_node,
+ "qcom,jeita-fcc-ranges",
+ &step_chg_jeita->jeita_fcc_cfg,
+ BATT_HOT_DECIDEGREE_MAX,
+ chip->fastchg_curr_ma * MILLI_TO_MICRO);
+ if (rc < 0)
+ pr_debug("Failed to read qcom,jeita-fcc-ranges from battery profile, rc=%d\n",
+ rc);
+
+ rc = qbg_read_range_data_from_node(profile_node,
+ "qcom,jeita-fv-ranges",
+ &step_chg_jeita->jeita_fv_cfg,
+ BATT_HOT_DECIDEGREE_MAX, chip->float_volt_uv);
+ if (rc < 0)
+ pr_debug("Failed to read qcom,jeita-fv-ranges from battery profile, rc=%d\n",
+ rc);
+
+ if ((step_chg_jeita->jeita_fcc_cfg.valid != step_chg_jeita->jeita_fv_cfg.valid)
+ || (jeita_thresh_valid != step_chg_jeita->jeita_fcc_cfg.valid)) {
+ pr_err("battery JEITA configuration is not valid\n");
+ return -EINVAL;
+ }
+
+ rc = qbg_read_range_data_from_node(profile_node,
+ "qcom,step-chg-ranges",
+ &step_chg_jeita->step_fcc_cfg,
+ soc_based_step_chg ? 100 : chip->float_volt_uv,
+ chip->fastchg_curr_ma * MILLI_TO_MICRO);
+ if (rc < 0)
+ pr_debug("Failed to read qcom,step-chg-ranges from battery profile, rc=%d\n",
+ rc);
+
+ qbg_dbg(chip, QBG_DEBUG_PROFILE, "jeita_full_fv = %dnv, jeita_full_iterm = %dna, jeita_warm_adc = 0x%x, jeita_cool_adc = 0x%x, battery_beta = %d, battery_therm = %dkohm\n",
+ step_chg_jeita->jeita_full_fv_10nv,
+ step_chg_jeita->jeita_full_iterm_10na,
+ step_chg_jeita->jeita_warm_adc_value,
+ step_chg_jeita->jeita_cool_adc_value,
+ step_chg_jeita->battery_beta,
+ step_chg_jeita->battery_therm_kohm);
+
+ pr_debug("step-chg-ranges:\n");
+ for (i = 0; i < step_chg_jeita->step_fcc_cfg.range_count; i++) {
+ pr_debug("%d %d %d\n",
+ step_chg_jeita->step_fcc_cfg.data[i].low_threshold,
+ step_chg_jeita->step_fcc_cfg.data[i].high_threshold,
+ step_chg_jeita->step_fcc_cfg.data[i].value);
+ }
+
+ pr_debug("jeita-fcc-ranges:\n");
+ for (i = 0; i < step_chg_jeita->jeita_fcc_cfg.range_count; i++) {
+ pr_debug("%d %d %d\n",
+ step_chg_jeita->jeita_fcc_cfg.data[i].low_threshold,
+ step_chg_jeita->jeita_fcc_cfg.data[i].high_threshold,
+ step_chg_jeita->jeita_fcc_cfg.data[i].value);
+ }
+
+ pr_debug("jeita-fv-ranges:\n");
+ for (i = 0; i < step_chg_jeita->jeita_fv_cfg.range_count; i++) {
+ pr_debug("%d %d %d\n",
+ step_chg_jeita->jeita_fv_cfg.data[i].low_threshold,
+ step_chg_jeita->jeita_fv_cfg.data[i].high_threshold,
+ step_chg_jeita->jeita_fv_cfg.data[i].value);
+ }
+
+ chip->step_chg_jeita_params = step_chg_jeita;
+
+ return 0;
+}
+
static int qbg_load_battery_profile(struct qti_qbg *chip)
{
struct device_node *node = chip->dev->of_node;
@@ -687,7 +931,7 @@ static int qbg_load_battery_profile(struct qti_qbg *chip)
}
profile_node = of_batterydata_get_best_profile(chip->batt_node,
- chip->batt_id_ohm, NULL);
+ chip->batt_id_ohm / 1000, NULL);
if (IS_ERR(profile_node)) {
rc = PTR_ERR(profile_node);
pr_err("Failed to detect valid QBG battery profile, rc=%d\n",
@@ -723,7 +967,7 @@ static int qbg_load_battery_profile(struct qti_qbg *chip)
goto out;
}
- rc = of_property_read_u32(profile_node, "qcom,fastchg-curr-ma",
+ rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
&chip->fastchg_curr_ma);
if (rc < 0) {
pr_err("Failed to read battery fastcharge current, rc:%d\n", rc);
@@ -738,6 +982,7 @@ static int qbg_load_battery_profile(struct qti_qbg *chip)
}
chip->nominal_capacity = temp[0];
+ rc = parse_step_chg_jeita_params(chip, profile_node);
out:
of_node_put(chip->batt_node);
return rc;
@@ -868,42 +1113,23 @@ static bool is_battery_present(struct qti_qbg *chip)
return present;
}
-#define BID_RPULL_OHM 100000
-#define BID_VREF_MV 1875
static int get_batt_id_ohm(struct qti_qbg *chip, u32 *batt_id_ohm)
{
- int rc, batt_id_mv;
- int64_t denom;
+ int rc, batt_id;
if (!chip->batt_id_chan)
return -EINVAL;
/* Read battery-id */
- rc = iio_read_channel_processed(chip->batt_id_chan, &batt_id_mv);
+ rc = iio_read_channel_processed(chip->batt_id_chan, &batt_id);
if (rc < 0) {
pr_err("Failed to read BATT_ID over ADC, rc=%d\n", rc);
return rc;
}
- batt_id_mv = div_s64(batt_id_mv, 1000);
- if (batt_id_mv == 0) {
- *batt_id_ohm = 0;
- pr_debug("batt_id_mv = 0 from ADC\n");
- return 0;
- }
+ *batt_id_ohm = (u32)batt_id;
- denom = div64_s64(BID_VREF_MV * 1000, batt_id_mv) - 1000;
- if (denom <= 0) {
- /* batt id connector might be open, return 0 kohms */
- *batt_id_ohm = 0;
- return 0;
- }
-
- *batt_id_ohm = div64_u64(BID_RPULL_OHM * 1000 + denom / 2, denom);
- chip->batt_id_ohm = *batt_id_ohm;
-
- qbg_dbg(chip, QBG_DEBUG_PROFILE, "batt_id_mv=%d, batt_id_ohm=%d\n",
- batt_id_mv, *batt_id_ohm);
+ qbg_dbg(chip, QBG_DEBUG_PROFILE, "batt_id_ohm=%u\n", *batt_id_ohm);
return 0;
}
@@ -934,7 +1160,7 @@ static int qbg_setup_battery(struct qti_qbg *chip)
/* Update OCV boost headroom compensation value to 4.3V in debug battery case */
if (is_debug_batt_id(chip)) {
rc = qbg_sdam_write(chip,
- QBG_SDAM_DATA_START_OFFSET(chip, SDAM_CTRL0) +
+ QBG_SDAM_BASE(chip, SDAM_CTRL0) +
QBG_SDAM_BHARGER_OCV_HDRM_OFFSET, &ocv_boost_val, 1);
}
}
@@ -1034,13 +1260,6 @@ static int qbg_determine_pon_soc(struct qti_qbg *chip)
return 0;
}
-static int qbg_psy_set_prop(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *pval)
-{
- return 0;
-}
-
static int qbg_psy_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *pval)
@@ -1061,7 +1280,6 @@ static struct power_supply_desc qbg_psy_desc = {
.properties = qbg_psy_props,
.num_properties = ARRAY_SIZE(qbg_psy_props),
.get_property = qbg_psy_get_prop,
- .set_property = qbg_psy_set_prop,
};
static int qbg_init_psy(struct qti_qbg *chip)
@@ -1121,6 +1339,9 @@ static int qbg_iio_read_raw(struct iio_dev *indio_dev,
case PSY_IIO_VOLTAGE_OCV:
*val1 = chip->ocv_uv;
break;
+ case PSY_IIO_VOLTAGE_AVG:
+ rc = qbg_get_battery_voltage(chip, val1);
+ break;
case PSY_IIO_VOLTAGE_NOW:
rc = qbg_get_battery_voltage(chip, val1);
break;
@@ -1228,6 +1449,85 @@ static int qbg_init_iio(struct qti_qbg *chip,
return rc;
}
+static int qbg_get_accumulator_properties(struct qti_qbg *chip,
+ enum QBG_STATE state,
+ enum QBG_SAMPLE_NUM_TYPE *num_of_accum,
+ enum QBG_ACCUM_INTERVAL_TYPE *accum_interval)
+{
+ int rc = 0;
+ unsigned int reg = QBG_MAIN_LPM_MEAS_CTL2;
+ unsigned char data = 0;
+
+ if (state >= QBG_STATE_MAX || !num_of_accum || !accum_interval)
+ return -EINVAL;
+
+ switch (state) {
+ case QBG_LPM:
+ reg = QBG_MAIN_LPM_MEAS_CTL2;
+ break;
+ case QBG_MPM:
+ reg = QBG_MAIN_MPM_MEAS_CTL2;
+ break;
+ case QBG_HPM:
+ reg = QBG_MAIN_HPM_MEAS_CTL2;
+ break;
+ case QBG_FAST_CHAR:
+ reg = QBG_MAIN_FAST_CHAR_MEAS_CTL2;
+ break;
+ default:
+ pr_err("Invalid QBG state requested for accumulator properties %u\n",
+ state);
+ return rc;
+ }
+
+ rc = qbg_read(chip, reg, &data, 1);
+ if (rc < 0) {
+ pr_err("Failed to MEAS_CTL2 %u, rc=%d\n", reg, rc);
+ return rc;
+ }
+
+ if (state == QBG_FAST_CHAR)
+ *num_of_accum = 0;
+ else
+ *num_of_accum = (data & QBG_NUM_OF_ACCUM_MASK) >> QBG_NUM_OF_ACCUM_SHIFT;
+
+ *accum_interval = data & QBG_ACCUM_INTERVAL_MASK;
+
+ qbg_dbg(chip, QBG_DEBUG_DEVICE, "state:%u num_of_accum:%u accum_interval:%u\n",
+ state, *num_of_accum, *accum_interval);
+
+ return rc;
+}
+
+static int qbg_get_sample_time_us(struct qti_qbg *chip)
+{
+ int rc = 0, index;
+ unsigned int interval;
+ enum QBG_ACCUM_INTERVAL_TYPE accum_interval = ACCUM_INTERVAL_100MS;
+ enum QBG_SAMPLE_NUM_TYPE num_accum = SAMPLE_NUM_1;
+
+ for (index = 0; index < QBG_STATE_MAX; index++) {
+ if (index == QBG_PON_OCV)
+ continue;
+
+ rc = qbg_get_accumulator_properties(chip, index, &num_accum,
+ &accum_interval);
+ if (rc < 0)
+ return rc;
+
+ if (index == QBG_FAST_CHAR)
+ interval = qbg_fast_char_avg_interval[accum_interval];
+ else if (index == QBG_LPM)
+ interval = qbg_lpm_accum_interval[accum_interval];
+ else
+ interval = qbg_accum_interval[accum_interval];
+
+ chip->sample_time_us[index] = interval;
+ }
+
+ return rc;
+}
+
static ssize_t qbg_device_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
@@ -1360,8 +1660,9 @@ static long qbg_device_ioctl(struct file *file, unsigned int cmd,
struct qbg_config __user *config_user;
struct qbg_config config;
struct qbg_essential_params __user *params_user;
+ struct qbg_step_chg_jeita_params __user *step_chg_params_user;
unsigned long rtc_sec;
- int rc = 0;
+ int rc = 0, i;
if (!chip) {
pr_err("Device private data not set!\n");
@@ -1383,28 +1684,59 @@ static long qbg_device_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case QBGIOCXCFG:
config_user = (struct qbg_config __user *)arg;
+
+ rc = qbg_get_sample_time_us(chip);
+ if (rc < 0) {
+ pr_err("Failed to calculate sample time us, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qbg_sdam_get_battery_id(chip, &chip->sdam_batt_id);
+ if (rc < 0) {
+ pr_err("Failed to get battid from sdam, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qbg_sdam_get_essential_param_revid(chip,
+ (u8 *)&chip->essential_param_revid);
+ if (rc < 0) {
+ pr_err("Failed to get essential param revid, rc=%d\n",
+ rc);
+ return rc;
+ }
+
config.current_time = rtc_sec;
- config.batt_id = chip->batt_id_ohm;
+ config.batt_id = chip->batt_id_ohm / 1000;
config.pon_ocv = chip->pon_ocv;
config.pon_ibat = chip->pon_ibat;
config.pon_tbat = chip->pon_tbat;
config.pon_soc = chip->pon_soc;
config.float_volt_uv = chip->float_volt_uv;
+ config.fastchg_curr_ma = chip->fastchg_curr_ma;
config.vbat_cutoff_mv = chip->vbat_cutoff_mv;
config.ibat_cutoff_ma = chip->ibat_cutoff_ma;
config.vph_min_mv = chip->vph_min_mv;
config.iterm_ma = chip->iterm_ma;
config.rconn_mohm = chip->rconn_mohm;
+ config.sdam_batt_id = chip->sdam_batt_id;
+ config.essential_param_revid = chip->essential_param_revid;
+ for (i = 0; i < QBG_STATE_MAX; i++) {
+ config.sample_time_us[i] = chip->sample_time_us[i];
+ qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBGIOCXCFG: sample_time_us[%d]:%u\n",
+ i, config.sample_time_us[i]);
+ }
if (copy_to_user(config_user, (void *)&config, sizeof(config))) {
pr_err("Failed to copy QBG config to user\n");
return -EFAULT;
}
- qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBGIOCXCFG: battid:%u pon_ocv:%u pon_ibat:%u pon_soc:%u vbatt_cutoff_mv:%u iterm_ma:%u\n",
+ qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBGIOCXCFG: sdam_battid:%u essential param revid:%u battid:%u pon_ocv:%u pon_ibat:%u pon_soc:%u vbatt_cutoff_mv:%u iterm_ma:%u\n",
+ config.sdam_batt_id, config.essential_param_revid,
config.batt_id, config.pon_ocv,
config.pon_ibat, config.pon_soc,
config.vbat_cutoff_mv, config.iterm_ma);
+
break;
case QBGIOCXEPR:
params_user = (struct qbg_essential_params __user *)arg;
@@ -1441,6 +1773,21 @@ static long qbg_device_ioctl(struct file *file, unsigned int cmd,
qbg_dbg(chip, QBG_DEBUG_SDAM, "Essential params written, time:%lu secs\n",
rtc_sec);
+ break;
+ case QBGIOCXSTEPCHGCFG:
+ step_chg_params_user = (struct qbg_step_chg_jeita_params __user *)arg;
+
+ if (copy_to_user(step_chg_params_user, (void *)chip->step_chg_jeita_params,
+ sizeof(struct qbg_step_chg_jeita_params))) {
+ pr_err("Failed to copy QBG step and jeita charge params to user\n");
+ return -EFAULT;
+ }
+ qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBGIOCXSTEPCHGCFG: jeita_full_fv_10nv:%d jeita_warm_adc_value:0x%x jeita_cool_adc_value:0x%x ttf_calc_mode:%u\n",
+ chip->step_chg_jeita_params->jeita_full_fv_10nv,
+ chip->step_chg_jeita_params->jeita_warm_adc_value,
+ chip->step_chg_jeita_params->jeita_cool_adc_value,
+ chip->step_chg_jeita_params->ttf_calc_mode);
+
break;
default:
pr_err("IOCTL %d not supported\n", cmd);
@@ -1538,7 +1885,7 @@ static int qbg_parse_sdam_dt(struct qti_qbg *chip, struct device_node *node)
return rc;
}
- rc = of_property_read_u32(node, "sdam-base", &chip->sdam_base);
+ rc = of_property_read_u32(node, "qcom,sdam-base", &chip->sdam_base);
if (rc < 0) {
pr_err("Failed to read SDAM base address, rc=%d\n", rc);
return rc;
diff --git a/drivers/soc/qcom/eud.c b/drivers/soc/qcom/eud.c
index 91471338a088..597550386965 100644
--- a/drivers/soc/qcom/eud.c
+++ b/drivers/soc/qcom/eud.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
#include
@@ -65,6 +65,7 @@ struct eud_chip {
int eud_irq;
unsigned int extcon_id;
unsigned int int_status;
+ unsigned int int_mask;
bool usb_attach;
bool chgr_enable;
void __iomem *eud_reg_base;
@@ -92,7 +93,7 @@ static inline void msm_eud_enable_irqs(struct eud_chip *chip)
{
/* Enable vbus, chgr & safe mode warning interrupts */
writel_relaxed(EUD_INT_VBUS | EUD_INT_CHGR | EUD_INT_SAFE_MODE,
- chip->eud_reg_base + EUD_REG_INT1_EN_MASK);
+ chip->eud_reg_base + chip->int_mask);
}
static int msm_eud_hw_is_enabled(struct platform_device *pdev)
@@ -506,7 +507,7 @@ static irqreturn_t handle_eud_irq(int irq, void *data)
struct eud_chip *chip = data;
u8 reg;
u32 int_mask_en1 = readl_relaxed(chip->eud_reg_base +
- EUD_REG_INT1_EN_MASK);
+ chip->int_mask);
/* read status register and find out which interrupt triggered */
reg = readb_relaxed(chip->eud_reg_base + EUD_REG_INT_STATUS_1);
@@ -705,6 +706,11 @@ static int msm_eud_probe(struct platform_device *pdev)
if (chip->secure_eud_en && check_eud_mode_mgr2(chip))
enable = 1;
+ ret = of_property_read_u32(pdev->dev.of_node, "interrupt-mask",
+ &chip->int_mask);
+ if (ret)
+ chip->int_mask = EUD_REG_INT1_EN_MASK;
+
ret = uart_add_one_port(&eud_uart_driver, port);
if (ret) {
dev_err(chip->dev, "failed to add uart port!\n");
diff --git a/drivers/soc/qcom/icnss2/main.c b/drivers/soc/qcom/icnss2/main.c
index 49d0d8a74a0f..2f70d96be573 100644
--- a/drivers/soc/qcom/icnss2/main.c
+++ b/drivers/soc/qcom/icnss2/main.c
@@ -2713,11 +2713,18 @@ EXPORT_SYMBOL(icnss_get_mhi_state);
int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
{
int ret;
- struct icnss_priv *priv = dev_get_drvdata(dev);
+ struct icnss_priv *priv;
if (!dev)
return -ENODEV;
+ priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Platform driver not initialized\n");
+ return -EINVAL;
+ }
+
if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
!test_bit(ICNSS_FW_READY, &penv->state)) {
icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
@@ -2737,11 +2744,13 @@ EXPORT_SYMBOL(icnss_set_fw_log_mode);
int icnss_force_wake_request(struct device *dev)
{
- struct icnss_priv *priv = dev_get_drvdata(dev);
+ struct icnss_priv *priv;
if (!dev)
return -ENODEV;
+ priv = dev_get_drvdata(dev);
+
if (!priv) {
icnss_pr_err("Platform driver not initialized\n");
return -EINVAL;
@@ -2764,11 +2773,13 @@ EXPORT_SYMBOL(icnss_force_wake_request);
int icnss_force_wake_release(struct device *dev)
{
- struct icnss_priv *priv = dev_get_drvdata(dev);
+ struct icnss_priv *priv;
if (!dev)
return -ENODEV;
+ priv = dev_get_drvdata(dev);
+
if (!priv) {
icnss_pr_err("Platform driver not initialized\n");
return -EINVAL;
@@ -3336,7 +3347,7 @@ static void icnss_wpss_load(struct work_struct *wpss_load_work)
struct icnss_priv *priv = icnss_get_plat_priv();
priv->subsys = subsystem_get("wpss");
- if (IS_ERR(priv->subsys))
+ if (IS_ERR_OR_NULL(priv->subsys))
icnss_pr_err("Failed to load wpss subsys");
}
@@ -3689,6 +3700,8 @@ static int icnss_smmu_fault_handler(struct iommu_domain *domain,
if (test_bit(ICNSS_FW_READY, &priv->state)) {
fw_down_data.crashed = true;
+ icnss_call_driver_uevent(priv, ICNSS_UEVENT_SMMU_FAULT,
+ &fw_down_data);
icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
&fw_down_data);
}
diff --git a/drivers/soc/qcom/icnss2/qmi.c b/drivers/soc/qcom/icnss2/qmi.c
index b523a357ec95..d9b99c2c485a 100644
--- a/drivers/soc/qcom/icnss2/qmi.c
+++ b/drivers/soc/qcom/icnss2/qmi.c
@@ -2078,6 +2078,14 @@ int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv)
}
req->mem_seg_len = priv->qdss_mem_seg_len;
+
+ if (priv->qdss_mem_seg_len > QMI_WLFW_MAX_NUM_MEM_SEG) {
+ icnss_pr_err("Invalid seg len %u\n",
+ priv->qdss_mem_seg_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
for (i = 0; i < req->mem_seg_len; i++) {
icnss_pr_dbg("Memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n",
qdss_mem[i].va, &qdss_mem[i].pa,
@@ -2469,6 +2477,13 @@ static void wlfw_qdss_trace_req_mem_ind_cb(struct qmi_handle *qmi,
}
priv->qdss_mem_seg_len = ind_msg->mem_seg_len;
+
+ if (priv->qdss_mem_seg_len > QMI_WLFW_MAX_NUM_MEM_SEG) {
+ icnss_pr_err("Invalid seg len %u\n",
+ priv->qdss_mem_seg_len);
+ return;
+ }
+
for (i = 0; i < priv->qdss_mem_seg_len; i++) {
icnss_pr_dbg("QDSS requests for memory, size: 0x%x, type: %u\n",
ind_msg->mem_seg[i].size,
diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c
index 1313f7e5e135..921e2a6ea63b 100644
--- a/drivers/soc/qcom/minidump_log.c
+++ b/drivers/soc/qcom/minidump_log.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#include
@@ -191,9 +191,11 @@ static void __init register_kernel_sections(void)
struct md_region ksec_entry;
char *data_name = "KDATABSS";
char *rodata_name = "KROAIDATA";
+#ifdef CONFIG_SMP
const size_t static_size = __per_cpu_end - __per_cpu_start;
void __percpu *base = (void __percpu *)__per_cpu_start;
unsigned int cpu;
+#endif
strlcpy(ksec_entry.name, data_name, sizeof(ksec_entry.name));
ksec_entry.virt_addr = (uintptr_t)_sdata;
@@ -209,6 +211,7 @@ static void __init register_kernel_sections(void)
if (msm_minidump_add_region(&ksec_entry) < 0)
pr_err("Failed to add rodata section in Minidump\n");
+#ifdef CONFIG_SMP
/* Add percpu static sections */
for_each_possible_cpu(cpu) {
void *start = per_cpu_ptr(base, cpu);
@@ -222,6 +225,7 @@ static void __init register_kernel_sections(void)
if (msm_minidump_add_region(&ksec_entry) < 0)
pr_err("Failed to add percpu sections in Minidump\n");
}
+#endif
}
static inline bool in_stack_range(
@@ -264,7 +268,7 @@ void dump_stack_minidump(u64 sp)
is_vmap_stack = IS_ENABLED(CONFIG_VMAP_STACK);
- if (sp < KIMAGE_VADDR || sp > -256UL)
+ if (sp < MODULES_END || sp > -256UL)
sp = current_stack_pointer;
/*
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 91f680c64450..708ceb3274bd 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -260,7 +260,7 @@ static int modem_ramdump(int enable, const struct subsys_desc *subsys)
static irqreturn_t modem_wdog_bite_intr_handler(int irq, void *dev_data)
{
- struct modem_data *drv = subsys_to_drv(dev_data);
+ struct modem_data *drv = dev_data;
if (drv->ignore_errors)
return IRQ_HANDLED;
diff --git a/drivers/soc/qcom/qcom_wdt_core.c b/drivers/soc/qcom/qcom_wdt_core.c
index b9d16e1eb833..36ab96b56228 100644
--- a/drivers/soc/qcom/qcom_wdt_core.c
+++ b/drivers/soc/qcom/qcom_wdt_core.c
@@ -371,6 +371,8 @@ int qcom_wdt_pet_suspend(struct device *dev)
wdog_dd->freeze_in_progress = true;
spin_unlock(&wdog_dd->freeze_lock);
del_timer_sync(&wdog_dd->pet_timer);
+ if (wdog_dd->user_pet_enabled)
+ del_timer_sync(&wdog_dd->user_pet_timer);
return 0;
}
EXPORT_SYMBOL(qcom_wdt_pet_suspend);
@@ -390,6 +392,11 @@ int qcom_wdt_pet_resume(struct device *dev)
spin_lock(&wdog_dd->freeze_lock);
wdog_dd->pet_timer.expires = jiffies + delay_time;
add_timer(&wdog_dd->pet_timer);
+ if (wdog_dd->user_pet_enabled) {
+ delay_time = msecs_to_jiffies(wdog_dd->bark_time + 3 * 1000);
+ wdog_dd->user_pet_timer.expires = jiffies + delay_time;
+ add_timer(&wdog_dd->user_pet_timer);
+ }
wdog_dd->freeze_in_progress = false;
spin_unlock(&wdog_dd->freeze_lock);
return 0;
@@ -480,6 +487,8 @@ static void qcom_wdt_disable(struct msm_watchdog_data *wdog_dd)
qcom_wdt_unregister_die_notifier(wdog_dd);
unregister_restart_handler(&wdog_dd->restart_blk);
del_timer_sync(&wdog_dd->pet_timer);
+ if (wdog_dd->user_pet_enabled)
+ del_timer_sync(&wdog_dd->user_pet_timer);
wdog_dd->ops->disable_wdt(wdog_dd);
dev_err(wdog_dd->dev, "QCOM Apps Watchdog deactivated\n");
}
@@ -583,6 +592,8 @@ static ssize_t qcom_wdt_user_pet_enabled_set(struct device *dev,
{
struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev);
int ret;
+ unsigned long delay_time = 0;
+ bool already_enabled = wdog_dd->user_pet_enabled;
ret = strtobool(buf, &wdog_dd->user_pet_enabled);
if (ret) {
@@ -590,6 +601,12 @@ static ssize_t qcom_wdt_user_pet_enabled_set(struct device *dev,
return ret;
}
+ delay_time = msecs_to_jiffies(wdog_dd->bark_time + 3 * 1000);
+ if (wdog_dd->user_pet_enabled)
+ mod_timer(&wdog_dd->user_pet_timer, jiffies + delay_time);
+ else if (already_enabled)
+ del_timer_sync(&wdog_dd->user_pet_timer);
+
__qcom_wdt_user_pet(wdog_dd);
return count;
@@ -650,6 +667,15 @@ static void qcom_wdt_pet_task_wakeup(struct timer_list *t)
wdog_dd->timer_fired = sched_clock();
wake_up(&wdog_dd->pet_complete);
}
+static void qcom_wdt_user_pet_bite(struct timer_list *t)
+{
+ struct msm_watchdog_data *wdog_dd =
+ from_timer(wdog_dd, t, user_pet_timer);
+ if (!wdog_dd->user_pet_complete) {
+ dev_info(wdog_dd->dev, "QCOM Apps Watchdog user pet timeout!\n");
+ qcom_wdt_trigger_bite();
+ }
+}
static __ref int qcom_wdt_kthread(void *arg)
{
@@ -751,6 +777,8 @@ int qcom_wdt_remove(struct platform_device *pdev)
irq_dispose_mapping(wdog_dd->bark_irq);
dev_info(wdog_dd->dev, "QCOM Apps Watchdog Exit - Deactivated\n");
del_timer_sync(&wdog_dd->pet_timer);
+ if (wdog_dd->user_pet_enabled)
+ del_timer_sync(&wdog_dd->user_pet_timer);
wdog_dd->timer_expired = true;
wdog_dd->user_pet_complete = true;
kthread_stop(wdog_dd->watchdog_task);
@@ -895,6 +923,7 @@ static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd,
timer_setup(&wdog_dd->pet_timer, qcom_wdt_pet_task_wakeup, 0);
wdog_dd->pet_timer.expires = jiffies + delay_time;
add_timer(&wdog_dd->pet_timer);
+ timer_setup(&wdog_dd->user_pet_timer, qcom_wdt_user_pet_bite, 0);
val = BIT(EN);
if (wdog_dd->wakeup_irq_enable)
val |= BIT(UNMASKED_INT_EN);
diff --git a/drivers/spi/spi-msm-geni.c b/drivers/spi/spi-msm-geni.c
index 0b71df4757e6..ecbe17055e56 100644
--- a/drivers/spi/spi-msm-geni.c
+++ b/drivers/spi/spi-msm-geni.c
@@ -2002,6 +2002,7 @@ static int spi_geni_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "LA-VM usecase\n");
}
+ geni_mas->spi_rsc.wrapper_dev = &wrapper_pdev->dev;
/*
* For LE, clocks, gpio and icb voting will be provided by
* by LA. The SPI operates in GSI mode only for LE usecase,
@@ -2009,7 +2010,6 @@ static int spi_geni_probe(struct platform_device *pdev)
* in SPI LE dt.
*/
if (!geni_mas->is_le_vm) {
- geni_mas->spi_rsc.wrapper_dev = &wrapper_pdev->dev;
ret = geni_se_resources_init(rsc, SPI_CORE2X_VOTE,
(DEFAULT_SE_CLK * DEFAULT_BUS_WIDTH));
if (ret) {
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 21dbdc0075f1..b695777b6ec4 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -232,7 +232,7 @@ struct msm_geni_serial_port {
struct completion s_cmd_timeout;
spinlock_t rx_lock;
bool pm_auto_suspend_disable;
- bool is_clock_off;
+ atomic_t is_clock_off;
enum uart_error_code uart_error;
};
@@ -1268,7 +1268,7 @@ static void msm_geni_serial_start_tx(struct uart_port *uport)
static unsigned int ios_log_limit;
/* when start_tx is called with UART clocks OFF return. */
- if (uart_console(uport) && msm_port->is_clock_off) {
+ if (uart_console(uport) && (uport->suspended || atomic_read(&msm_port->is_clock_off))) {
IPC_LOG_MSG(msm_port->console_log,
"%s. Console in suspend state\n", __func__);
return;
@@ -2166,7 +2166,7 @@ static void msm_geni_serial_handle_isr(struct uart_port *uport,
bool s_cmd_done = false;
bool m_cmd_done = false;
- if (uart_console(uport) && msm_port->is_clock_off) {
+ if (uart_console(uport) && atomic_read(&msm_port->is_clock_off)) {
IPC_LOG_MSG(msm_port->console_log,
"%s. Console in suspend state\n", __func__);
goto exit_geni_serial_isr;
@@ -2380,6 +2380,8 @@ static void msm_geni_serial_shutdown(struct uart_port *uport)
IPC_LOG_MSG(msm_port->ipc_log_misc,
"%s: Error %d pinctrl_select_state\n", __func__, ret);
}
+ /* Reset UART error to default during port_close() */
+ msm_port->uart_error = UART_ERROR_DEFAULT;
}
IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: End\n", __func__);
}
@@ -2991,11 +2993,11 @@ static void msm_geni_serial_cons_pm(struct uart_port *uport,
if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) {
se_geni_resources_on(&msm_port->serial_rsc);
- msm_port->is_clock_off = false;
+ atomic_set(&msm_port->is_clock_off, 0);
} else if (new_state == UART_PM_STATE_OFF &&
old_state == UART_PM_STATE_ON) {
+ atomic_set(&msm_port->is_clock_off, 1);
se_geni_resources_off(&msm_port->serial_rsc);
- msm_port->is_clock_off = true;
}
}
@@ -3620,6 +3622,7 @@ static int msm_geni_serial_sys_suspend(struct device *dev)
struct uart_port *uport = &port->uport;
if (uart_console(uport) || port->pm_auto_suspend_disable) {
+ IPC_LOG_MSG(port->console_log, "%s start\n", __func__);
uart_suspend_port((struct uart_driver *)uport->private_data,
uport);
IPC_LOG_MSG(port->console_log, "%s\n", __func__);
@@ -3652,6 +3655,7 @@ static int msm_geni_serial_sys_resume(struct device *dev)
if ((uart_console(uport) &&
console_suspend_enabled && uport->suspended) ||
port->pm_auto_suspend_disable) {
+ IPC_LOG_MSG(port->console_log, "%s start\n", __func__);
uart_resume_port((struct uart_driver *)uport->private_data,
uport);
IPC_LOG_MSG(port->console_log, "%s\n", __func__);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 6791b4ca95dd..5395c52edf3d 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1485,6 +1485,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,dis-tx-ipgap-linecheck-quirk");
dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
"snps,parkmode-disable-ss-quirk");
+ dwc->usb2_l1_disable = device_property_read_bool(dev,
+ "snps,usb2-l1-disable");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index fee3cf068915..a019b0e6180e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1363,6 +1363,7 @@ struct dwc3 {
unsigned tx_de_emphasis:2;
unsigned err_evt_seen:1;
unsigned enable_bus_suspend:1;
+ unsigned usb2_l1_disable:1;
atomic_t in_lpm;
bool b_suspend;
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 3b439bf49bdf..2d258cd8edcf 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -4070,6 +4070,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
struct dwc3_msm *mdwc = enb->mdwc;
char *eud_str;
const char *edev_name;
+ bool is_cdp;
if (!edev || !mdwc)
return NOTIFY_DONE;
@@ -4094,12 +4095,21 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
mdwc->vbus_active = event;
}
+ /*
+ * In case of ADSP based charger detection driving a pulse on
+ * DP to ensure proper CDP detection will be taken care by
+ * ADSP.
+ */
+ is_cdp = ((mdwc->apsd_source == IIO) &&
+ (get_chg_type(mdwc) == POWER_SUPPLY_TYPE_USB_CDP)) ||
+ ((mdwc->apsd_source == PSY) &&
+ (get_chg_type(mdwc) == POWER_SUPPLY_USB_TYPE_CDP));
+
/*
* Drive a pulse on DP to ensure proper CDP detection
* and only when the vbus connect event is a valid one.
*/
- if (get_chg_type(mdwc) == POWER_SUPPLY_USB_TYPE_CDP &&
- mdwc->vbus_active && !mdwc->check_eud_state) {
+ if (is_cdp && mdwc->vbus_active && !mdwc->check_eud_state) {
dev_dbg(mdwc->dev, "Connected to CDP, pull DP up\n");
mdwc->hs_phy->charger_detect(mdwc->hs_phy);
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index fdc52c8d940e..6452a161c89b 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -971,7 +971,7 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
dbg_event(dep->number, "DISABLE", ret);
spin_unlock_irqrestore(&dwc->lock, flags);
pm_runtime_mark_last_busy(dwc->sysdev);
- pm_runtime_put_sync_autosuspend(dwc->sysdev);
+ pm_runtime_put_autosuspend(dwc->sysdev);
return ret;
}
@@ -4369,7 +4369,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
- dwc->gadget.lpm_capable = true;
+ dwc->gadget.lpm_capable = !dwc->usb2_l1_disable;
/*
* FIXME We might be setting max_speed to desc.bMaxPacketSize0 =
cdev->gadget->ep0->maxpacket;
+ cdev->desc.bcdUSB = cpu_to_le16(0x0200);
if (gadget_is_superspeed(gadget)) {
if (gadget->speed >= USB_SPEED_SUPER) {
cdev->desc.bcdUSB = cpu_to_le16(0x0320);
cdev->desc.bMaxPacketSize0 = 9;
- } else {
+ } else if (gadget->lpm_capable) {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
}
} else {
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 8bb25773b61e..05507606b2b4 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -164,6 +164,14 @@ int usb_assign_descriptors(struct usb_function *f,
{
struct usb_gadget *g = f->config->cdev->gadget;
+ /* super-speed-plus descriptor falls back to super-speed one,
+ * if such a descriptor was provided, thus avoiding a NULL
+ * pointer dereference if a 5gbps capable gadget is used with
+ * a 10gbps capable config (device port + cable + host port)
+ */
+ if (!ssp)
+ ssp = ss;
+
if (fs) {
f->fs_descriptors = usb_copy_descriptors(fs);
if (!f->fs_descriptors)
diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c
index 720e21950e44..29bc22bb2ef5 100644
--- a/drivers/usb/gadget/function/f_cdev.c
+++ b/drivers/usb/gadget/function/f_cdev.c
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#define DEVICE_NAME "at_usb"
#define MODULE_NAME "msm_usb_bridge"
@@ -99,6 +100,7 @@ struct f_cdev {
struct list_head read_pool;
struct list_head read_queued;
struct list_head write_pool;
+ struct list_head write_pending;
/* current active USB RX request */
struct usb_request *current_rx_req;
@@ -127,6 +129,8 @@ struct f_cdev {
struct workqueue_struct *fcdev_wq;
bool is_connected;
bool port_open;
+ bool is_suspended;
+ bool pending_state_notify;
unsigned long nbytes_from_host;
unsigned long nbytes_to_host;
@@ -536,19 +540,56 @@ static int usb_cser_set_alt(struct usb_function *f, unsigned int intf,
return rc;
}
+static int port_notify_serial_state(struct cserial *cser);
+
static void usb_cser_resume(struct usb_function *f)
{
struct f_cdev *port = func_to_port(f);
- struct usb_composite_dev *cdev = f->config->cdev;
+ unsigned long flags;
+ int ret;
- if (cdev->gadget->speed >= USB_SPEED_SUPER && port->func_is_suspended) {
- if (port->func_wakeup_pending) {
- dev_dbg(&cdev->gadget->dev,
- "func_wakeup for port:%s\n", port->name);
- usb_func_wakeup(&port->port_usb.func);
- port->func_wakeup_pending = false;
+ struct usb_request *req, *t;
+ struct usb_ep *in;
+
+ pr_debug("%s\n", __func__);
+ port->is_suspended = false;
+
+ /* process pending state notifications */
+ if (port->pending_state_notify)
+ port_notify_serial_state(&port->port_usb);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ in = port->port_usb.in;
+ /* process any pending requests */
+ list_for_each_entry_safe(req, t, &port->write_pending, list) {
+ list_del_init(&req->list);
+ if (!port->is_connected) {
+ pr_err("%s: cable is disconnected.\n", __func__);
+ list_add(&req->list, &port->write_pool);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = usb_ep_queue(in, req, GFP_KERNEL);
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (ret) {
+ pr_err("EP QUEUE failed:%d\n", ret);
+ list_add(&req->list, &port->write_pool);
+ } else {
+ port->nbytes_from_port_bridge += req->length;
}
}
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void usb_cser_suspend(struct usb_function *f)
+{
+ struct f_cdev *port = func_to_port(f);
+
+ pr_debug("%s\n", __func__);
+ port->is_suspended = true;
}
static int usb_cser_func_suspend(struct usb_function *f, u8 options)
@@ -557,11 +598,31 @@ static int usb_cser_func_suspend(struct usb_function *f, u8 options)
port->func_wakeup_allowed =
!!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8));
- port->func_is_suspended = options & (USB_INTRF_FUNC_SUSPEND_LP >> 8);
+ if (options & (USB_INTRF_FUNC_SUSPEND_LP >> 8)) {
+ if (!port->func_is_suspended) {
+ usb_cser_suspend(f);
+ port->func_is_suspended = true;
+ } else {
+ if (port->func_is_suspended) {
+ port->func_is_suspended = false;
+ usb_cser_resume(f);
+ }
+ }
+ }
return 0;
}
+static int usb_cser_get_remote_wakeup_capable(struct usb_function *f,
+ struct usb_gadget *g)
+{
+ struct f_cdev *port = func_to_port(f);
+
+ return ((g->speed >= USB_SPEED_SUPER && port->func_wakeup_allowed) ||
+ (g->speed < USB_SPEED_SUPER && usb_get_remote_wakeup_status(g)));
+
+}
+
static int usb_cser_get_status(struct usb_function *f)
{
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
@@ -643,7 +704,16 @@ static int port_notify_serial_state(struct cserial *cser)
unsigned long flags;
struct usb_composite_dev *cdev = port->port_usb.func.config->cdev;
+ if (port->is_suspended) {
+ port->pending_state_notify = true;
+ pr_debug("%s: port is suspended\n", __func__);
+ return 0;
+ }
+
spin_lock_irqsave(&port->port_lock, flags);
+ if (port->pending_state_notify)
+ port->pending_state_notify = false;
+
if (!port->port_usb.pending) {
port->port_usb.pending = true;
spin_unlock_irqrestore(&port->port_lock, flags);
@@ -1133,6 +1203,7 @@ static void usb_cser_stop_io(struct f_cdev *port)
usb_cser_free_requests(out, &port->read_queued);
usb_cser_free_requests(out, &port->read_pool);
usb_cser_free_requests(in, &port->write_pool);
+ usb_cser_free_requests(in, &port->write_pending);
spin_unlock_irqrestore(&port->port_lock, flags);
}
@@ -1298,6 +1369,9 @@ ssize_t f_cdev_write(struct file *file,
struct list_head *pool;
unsigned int xfer_size;
struct usb_ep *in;
+ struct cserial *cser;
+ struct usb_function *func;
+ struct usb_gadget *gadget;
port = file->private_data;
if (!port) {
@@ -1305,6 +1379,9 @@ ssize_t f_cdev_write(struct file *file,
return -EINVAL;
}
+ cser = &port->port_usb;
+ func = &cser->func;
+
spin_lock_irqsave(&port->port_lock, flags);
pr_debug("write on port(%s)(%pK)\n", port->name, port);
@@ -1336,9 +1413,40 @@ ssize_t f_cdev_write(struct file *file,
if (ret) {
pr_err("copy_from_user failed: err %d\n", ret);
ret = -EFAULT;
+ goto err_exit;
+ }
+
+ req->length = xfer_size;
+ req->zero = 1;
+ if (port->is_suspended) {
+ gadget = cser->func.config->cdev->gadget;
+ if (!usb_cser_get_remote_wakeup_capable(func, gadget)) {
+ pr_debug("%s remote-wakeup not capable\n",
+ __func__);
+ ret = -EOPNOTSUPP;
+ goto err_exit;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ list_add(&req->list, &port->write_pending);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if (gadget->speed >= USB_SPEED_SUPER
+ && port->func_is_suspended)
+ ret = usb_func_wakeup(func);
+ else
+ ret = usb_gadget_wakeup(gadget);
+
+ if (ret < 0 && ret != -EACCES && ret != -EAGAIN) {
+ pr_err("Remote wakeup failed:%d\n", ret);
+ spin_lock_irqsave(&port->port_lock, flags);
+ req = list_first_entry(&port->write_pending,
+ struct usb_request, list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ goto err_exit;
+ }
} else {
- req->length = xfer_size;
- req->zero = 1;
ret = usb_ep_queue(in, req, GFP_KERNEL);
if (ret) {
pr_err("EP QUEUE failed:%d\n", ret);
@@ -1350,19 +1458,18 @@ ssize_t f_cdev_write(struct file *file,
spin_unlock_irqrestore(&port->port_lock, flags);
}
-err_exit:
- if (ret) {
- spin_lock_irqsave(&port->port_lock, flags);
- /* USB cable is connected, add it back otherwise free request */
- if (port->is_connected)
- list_add(&req->list, &port->write_pool);
- else
- usb_cser_free_req(in, req);
- spin_unlock_irqrestore(&port->port_lock, flags);
- return ret;
- }
-
return xfer_size;
+
+err_exit:
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* USB cable is connected, add it back otherwise free request */
+ if (port->is_connected)
+ list_add(&req->list, &port->write_pool);
+ else
+ usb_cser_free_req(in, req);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return ret;
}
static unsigned int f_cdev_poll(struct file *file, poll_table *wait)
@@ -1598,6 +1705,8 @@ int usb_cser_connect(struct f_cdev *port)
cser->pending = false;
cser->q_again = false;
port->is_connected = true;
+ port->pending_state_notify = false;
+ port->is_suspended = false;
spin_unlock_irqrestore(&port->port_lock, flags);
usb_cser_start_io(port);
@@ -1799,6 +1908,7 @@ static struct f_cdev *f_cdev_alloc(char *func_name, int portno)
INIT_LIST_HEAD(&port->read_pool);
INIT_LIST_HEAD(&port->read_queued);
INIT_LIST_HEAD(&port->write_pool);
+ INIT_LIST_HEAD(&port->write_pending);
port->fcdev_wq = create_singlethread_workqueue(port->name);
if (!port->fcdev_wq) {
@@ -2089,6 +2199,7 @@ static struct usb_function *cser_alloc(struct usb_function_instance *fi)
port->port_usb.func.setup = usb_cser_setup;
port->port_usb.func.func_suspend = usb_cser_func_suspend;
port->port_usb.func.resume = usb_cser_resume;
+ port->port_usb.func.suspend = usb_cser_suspend;
port->port_usb.func.get_status = usb_cser_get_status;
port->port_usb.func.free_func = usb_cser_free_func;
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 0f63755c62fd..6d7a4ef7d2bc 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -28,13 +28,43 @@ struct f_gser {
struct gserial port;
u8 data_id;
u8 port_num;
+ spinlock_t lock;
+
+ struct usb_ep *notify;
+ struct usb_request *notify_req;
+
+ u8 online;
+ u8 pending;
+ struct usb_cdc_line_coding port_line_coding;
+
+ /* SetControlLineState request */
+ u16 port_handshake_bits;
+#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
+#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
+
+ /* SerialState notification */
+ u16 serial_state;
+#define ACM_CTRL_OVERRUN (1 << 6)
+#define ACM_CTRL_PARITY (1 << 5)
+#define ACM_CTRL_FRAMING (1 << 4)
+#define ACM_CTRL_RI (1 << 3)
+#define ACM_CTRL_BRK (1 << 2)
+#define ACM_CTRL_DSR (1 << 1)
+#define ACM_CTRL_DCD (1 << 0)
};
+static inline struct f_gser *port_to_gser(struct gserial *p)
+{
+ return container_of(p, struct f_gser, port);
+}
+
static inline struct f_gser *func_to_gser(struct usb_function *f)
{
return container_of(f, struct f_gser, port.func);
}
+#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
/*-------------------------------------------------------------------------*/
/* interface descriptor: */
@@ -43,15 +73,55 @@ static struct usb_interface_descriptor gser_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
- .bNumEndpoints = 2,
+ .bNumEndpoints = 3,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0x40,
/* .iInterface = DYNAMIC */
};
+static struct usb_cdc_header_desc gser_header_desc = {
+ .bLength = sizeof(gser_header_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+ .bcdCDC = cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+gser_call_mgmt_descriptor = {
+ .bLength = sizeof(gser_call_mgmt_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+ .bmCapabilities = 0,
+ /* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor gser_descriptor = {
+ .bLength = sizeof(gser_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+ .bmCapabilities = USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc gser_union_desc = {
+ .bLength = sizeof(gser_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
/* full speed support: */
+static struct usb_endpoint_descriptor gser_fs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+
static struct usb_endpoint_descriptor gser_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -68,12 +138,25 @@ static struct usb_endpoint_descriptor gser_fs_out_desc = {
static struct usb_descriptor_header *gser_fs_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_header_desc,
+ (struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &gser_descriptor,
+ (struct usb_descriptor_header *) &gser_union_desc,
+ (struct usb_descriptor_header *) &gser_fs_notify_desc,
(struct usb_descriptor_header *) &gser_fs_in_desc,
(struct usb_descriptor_header *) &gser_fs_out_desc,
NULL,
};
/* high speed support: */
+static struct usb_endpoint_descriptor gser_hs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+};
static struct usb_endpoint_descriptor gser_hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
@@ -91,6 +174,11 @@ static struct usb_endpoint_descriptor gser_hs_out_desc = {
static struct usb_descriptor_header *gser_hs_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_header_desc,
+ (struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &gser_descriptor,
+ (struct usb_descriptor_header *) &gser_union_desc,
+ (struct usb_descriptor_header *) &gser_hs_notify_desc,
(struct usb_descriptor_header *) &gser_hs_in_desc,
(struct usb_descriptor_header *) &gser_hs_out_desc,
NULL,
@@ -115,8 +203,33 @@ static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
+static struct usb_endpoint_descriptor gser_ss_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_ss_ep_comp_descriptor gser_ss_notify_comp_desc = {
+ .bLength = sizeof(gser_ss_notify_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+};
+
static struct usb_descriptor_header *gser_ss_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_header_desc,
+ (struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &gser_descriptor,
+ (struct usb_descriptor_header *) &gser_union_desc,
+ (struct usb_descriptor_header *) &gser_ss_notify_desc,
+ (struct usb_descriptor_header *) &gser_ss_notify_comp_desc,
(struct usb_descriptor_header *) &gser_ss_in_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &gser_ss_out_desc,
@@ -127,7 +240,7 @@ static struct usb_descriptor_header *gser_ss_function[] = {
/* string descriptors: */
static struct usb_string gser_string_defs[] = {
- [0].s = "Generic Serial",
+ [0].s = "DUN over Serial",
{ } /* end of list */
};
@@ -142,13 +255,131 @@ static struct usb_gadget_strings *gser_strings[] = {
};
/*-------------------------------------------------------------------------*/
+static void gser_complete_set_line_coding(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct f_gser *gser = ep->driver_data;
+ struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+ if (req->status != 0) {
+ dev_dbg(&cdev->gadget->dev, "gser ttyGS%d completion, err %d\n",
+ gser->port_num, req->status);
+ return;
+ }
+
+ /* normal completion */
+ if (req->actual != sizeof(gser->port_line_coding)) {
+ dev_dbg(&cdev->gadget->dev, "gser ttyGS%d short resp, len %d\n",
+ gser->port_num, req->actual);
+ usb_ep_set_halt(ep);
+ } else {
+ struct usb_cdc_line_coding *value = req->buf;
+
+ gser->port_line_coding = *value;
+ }
+}
+
+static int
+gser_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_gser *gser = func_to_gser(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+ /* SET_LINE_CODING ... just read and save what the host sends */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_LINE_CODING:
+ if (w_length != sizeof(struct usb_cdc_line_coding))
+ goto invalid;
+
+ value = w_length;
+ cdev->gadget->ep0->driver_data = gser;
+ req->complete = gser_complete_set_line_coding;
+ break;
+
+ /* GET_LINE_CODING ... return what host sent, or initial value */
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_GET_LINE_CODING:
+ value = min_t(unsigned int, w_length,
+ sizeof(struct usb_cdc_line_coding));
+ memcpy(req->buf, &gser->port_line_coding, value);
+ break;
+
+ /* SET_CONTROL_LINE_STATE ... save what the host sent */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+
+ value = 0;
+ gser->port_handshake_bits = w_value;
+ pr_debug("%s: USB_CDC_REQ_SET_CONTROL_LINE_STATE: DTR:%d RST:%d\n",
+ __func__, w_value & ACM_CTRL_DTR ? 1 : 0,
+ w_value & ACM_CTRL_RTS ? 1 : 0);
+
+ if (gser->port.notify_modem)
+ gser->port.notify_modem(&gser->port, 0, w_value);
+
+ break;
+
+ default:
+invalid:
+ dev_dbg(&cdev->gadget->dev,
+ "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ dev_dbg(&cdev->gadget->dev,
+ "gser ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+ gser->port_num, ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "gser response on ttyGS%d, err %d\n",
+ gser->port_num, value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
+ int rc = 0;
/* we know alt == 0, so this is an activation or a reset */
+ if (gser->notify->driver_data) {
+ dev_dbg(&cdev->gadget->dev,
+ "reset generic ctl ttyGS%d\n", gser->port_num);
+ usb_ep_disable(gser->notify);
+ }
+
+ if (!gser->notify->desc) {
+ if (config_ep_by_speed(cdev->gadget, f, gser->notify)) {
+ gser->notify->desc = NULL;
+ return -EINVAL;
+ }
+ }
+
+ rc = usb_ep_enable(gser->notify);
+ if (rc) {
+ ERROR(cdev, "can't enable %s, result %d\n",
+ gser->notify->name, rc);
+ return rc;
+ }
+ gser->notify->driver_data = gser;
if (gser->port.in->enabled) {
dev_dbg(&cdev->gadget->dev,
@@ -166,7 +397,9 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
}
gserial_connect(&gser->port, gser->port_num);
- return 0;
+ gser->online = 1;
+
+ return rc;
}
static void gser_disable(struct usb_function *f)
@@ -177,6 +410,178 @@ static void gser_disable(struct usb_function *f)
dev_dbg(&cdev->gadget->dev,
"generic ttyGS%d deactivated\n", gser->port_num);
gserial_disconnect(&gser->port);
+ usb_ep_disable(gser->notify);
+ gser->notify->driver_data = NULL;
+ gser->online = 0;
+}
+
+static int gser_notify(struct f_gser *gser, u8 type, u16 value,
+ void *data, unsigned int length)
+{
+ struct usb_ep *ep = gser->notify;
+ struct usb_request *req;
+ struct usb_cdc_notification *notify;
+ const unsigned int len = sizeof(*notify) + length;
+ void *buf;
+ int status;
+ struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+ req = gser->notify_req;
+ gser->notify_req = NULL;
+ gser->pending = false;
+
+ req->length = len;
+ notify = req->buf;
+ buf = notify + 1;
+
+ notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+ | USB_RECIP_INTERFACE;
+ notify->bNotificationType = type;
+ notify->wValue = cpu_to_le16(value);
+ notify->wIndex = cpu_to_le16(gser->data_id);
+ notify->wLength = cpu_to_le16(length);
+ memcpy(buf, data, length);
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status < 0) {
+ ERROR(cdev, "gser ttyGS%d can't notify serial state, %d\n",
+ gser->port_num, status);
+ gser->notify_req = req;
+ }
+
+ return status;
+}
+
+static int gser_notify_serial_state(struct f_gser *gser)
+{
+ int status;
+ unsigned long flags;
+ struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+ spin_lock_irqsave(&gser->lock, flags);
+ if (gser->notify_req) {
+ DBG(cdev, "gser ttyGS%d serial state %04x\n",
+ gser->port_num, gser->serial_state);
+ status = gser_notify(gser, USB_CDC_NOTIFY_SERIAL_STATE,
+ 0, &gser->serial_state,
+ sizeof(gser->serial_state));
+ } else {
+ gser->pending = true;
+ status = 0;
+ }
+
+ spin_unlock_irqrestore(&gser->lock, flags);
+ return status;
+}
+
+static void gser_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_gser *gser = req->context;
+ u8 doit = false;
+ unsigned long flags;
+
+ /* on this call path we do NOT hold the port spinlock,
+ * which is why ACM needs its own spinlock
+ */
+
+ spin_lock_irqsave(&gser->lock, flags);
+ if (req->status != -ESHUTDOWN)
+ doit = gser->pending;
+
+ gser->notify_req = req;
+ spin_unlock_irqrestore(&gser->lock, flags);
+
+ if (doit && gser->online)
+ gser_notify_serial_state(gser);
+}
+
+static void gser_connect(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ gser->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+ gser_notify_serial_state(gser);
+}
+
+static unsigned int gser_get_dtr(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ if (gser->port_handshake_bits & ACM_CTRL_DTR)
+ return 1;
+ else
+ return 0;
+}
+
+static unsigned int gser_get_rts(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ if (gser->port_handshake_bits & ACM_CTRL_RTS)
+ return 1;
+ else
+ return 0;
+}
+
+static unsigned int gser_send_carrier_detect(struct gserial *port,
+ unsigned int yes)
+{
+ u16 state;
+ struct f_gser *gser = port_to_gser(port);
+
+ state = gser->serial_state;
+ state &= ~ACM_CTRL_DCD;
+ if (yes)
+ state |= ACM_CTRL_DCD;
+
+ gser->serial_state = state;
+ return gser_notify_serial_state(gser);
+}
+
+static unsigned int gser_send_ring_indicator(struct gserial *port,
+ unsigned int yes)
+{
+ u16 state;
+ struct f_gser *gser = port_to_gser(port);
+
+ state = gser->serial_state;
+ state &= ~ACM_CTRL_RI;
+ if (yes)
+ state |= ACM_CTRL_RI;
+
+ gser->serial_state = state;
+ return gser_notify_serial_state(gser);
+}
+
+static void gser_disconnect(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ gser->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+ gser_notify_serial_state(gser);
+}
+
+static int gser_send_break(struct gserial *port, int duration)
+{
+ u16 state;
+ struct f_gser *gser = port_to_gser(port);
+
+ state = gser->serial_state;
+ state &= ~ACM_CTRL_BRK;
+ if (duration)
+ state |= ACM_CTRL_BRK;
+
+ gser->serial_state = state;
+ return gser_notify_serial_state(gser);
+}
+
+static int gser_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ gser->serial_state = ctrl_bits;
+
+ return gser_notify_serial_state(gser);
}
/*-------------------------------------------------------------------------*/
@@ -222,6 +627,21 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
gser->port.out = ep;
+ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_notify_desc);
+ if (!ep)
+ goto fail;
+ gser->notify = ep;
+ ep->driver_data = cdev; /* claim */
+ /* allocate notification */
+ gser->notify_req = gs_alloc_req(ep,
+ sizeof(struct usb_cdc_notification) + 2,
+ GFP_KERNEL);
+ if (!gser->notify_req)
+ goto fail;
+
+ gser->notify_req->complete = gser_notify_complete;
+ gser->notify_req->context = gser;
+
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -232,6 +652,15 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ gser_hs_notify_desc.bEndpointAddress =
+ gser_fs_notify_desc.bEndpointAddress;
+ }
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ gser_ss_notify_desc.bEndpointAddress =
+ gser_fs_notify_desc.bEndpointAddress;
+ }
+
status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
gser_ss_function, NULL);
if (status)
@@ -244,6 +673,18 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
+ if (gser->notify_req)
+ gs_free_req(gser->notify, gser->notify_req);
+
+ /* we might as well release our claims on endpoints */
+ if (gser->notify)
+ gser->notify->driver_data = NULL;
+ /* we might as well release our claims on endpoints */
+ if (gser->port.out)
+ gser->port.out->driver_data = NULL;
+ if (gser->port.in)
+ gser->port.in->driver_data = NULL;
+
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
@@ -324,7 +765,10 @@ static void gser_free(struct usb_function *f)
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_gser *gser = func_to_gser(f);
+
usb_free_all_descriptors(f);
+ gs_free_req(gser->notify, gser->notify_req);
}
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
@@ -339,6 +783,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
opts = container_of(fi, struct f_serial_opts, func_inst);
+ spin_lock_init(&gser->lock);
gser->port_num = opts->port_num;
gser->port.func.name = "gser";
@@ -348,6 +793,15 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
gser->port.func.free_func = gser_free;
+ gser->port.func.setup = gser_setup;
+ gser->port.connect = gser_connect;
+ gser->port.get_dtr = gser_get_dtr;
+ gser->port.get_rts = gser_get_rts;
+ gser->port.send_carrier_detect = gser_send_carrier_detect;
+ gser->port.send_ring_indicator = gser_send_ring_indicator;
+ gser->port.send_modem_ctrl_bits = gser_send_modem_ctrl_bits;
+ gser->port.disconnect = gser_disconnect;
+ gser->port.send_break = gser_send_break;
return &gser->port.func;
}
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index 9acaac1cbb75..74dc864ca050 100644
--- a/drivers/usb/gadget/function/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -42,11 +42,23 @@ struct gserial {
/* REVISIT avoid this CDC-ACM support harder ... */
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
+ u16 serial_state;
+
+ /* control signal callbacks*/
+ unsigned int (*get_dtr)(struct gserial *p);
+ unsigned int (*get_rts)(struct gserial *p);
/* notification callbacks */
void (*connect)(struct gserial *p);
void (*disconnect)(struct gserial *p);
int (*send_break)(struct gserial *p, int duration);
+ unsigned int (*send_carrier_detect)(struct gserial *p,
+ unsigned int yes);
+ unsigned int (*send_ring_indicator)(struct gserial *p,
+ unsigned int yes);
+ int (*send_modem_ctrl_bits)(struct gserial *p, int ctrl_bits);
+ /* notification changes to modem */
+ void (*notify_modem)(void *gser, u8 portno, int ctrl_bits);
};
/* utilities to allocate/free request and buffer */
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index bc618e490068..2756b7a67806 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -204,10 +204,13 @@ static unsigned int fuse_req_hash(u64 unique)
/**
* A new request is available, wake fiq->waitq
*/
-static void fuse_dev_wake_and_unlock(struct fuse_iqueue *fiq)
+static void fuse_dev_wake_and_unlock(struct fuse_iqueue *fiq, bool sync)
__releases(fiq->lock)
{
- wake_up(&fiq->waitq);
+ if (sync)
+ wake_up_sync(&fiq->waitq);
+ else
+ wake_up(&fiq->waitq);
kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
spin_unlock(&fiq->lock);
}
@@ -220,14 +223,14 @@ const struct fuse_iqueue_ops fuse_dev_fiq_ops = {
EXPORT_SYMBOL_GPL(fuse_dev_fiq_ops);
static void queue_request_and_unlock(struct fuse_iqueue *fiq,
- struct fuse_req *req)
+ struct fuse_req *req, bool sync)
__releases(fiq->lock)
{
req->in.h.len = sizeof(struct fuse_in_header) +
fuse_len_args(req->args->in_numargs,
(struct fuse_arg *) req->args->in_args);
list_add_tail(&req->list, &fiq->pending);
- fiq->ops->wake_pending_and_unlock(fiq);
+ fiq->ops->wake_pending_and_unlock(fiq, sync);
}
void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
@@ -242,7 +245,7 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
if (fiq->connected) {
fiq->forget_list_tail->next = forget;
fiq->forget_list_tail = forget;
- fiq->ops->wake_forget_and_unlock(fiq);
+ fiq->ops->wake_forget_and_unlock(fiq, 0);
} else {
kfree(forget);
spin_unlock(&fiq->lock);
@@ -262,7 +265,7 @@ static void flush_bg_queue(struct fuse_conn *fc)
fc->active_background++;
spin_lock(&fiq->lock);
req->in.h.unique = fuse_get_unique(fiq);
- queue_request_and_unlock(fiq, req);
+ queue_request_and_unlock(fiq, req, 0);
}
}
@@ -351,7 +354,7 @@ static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
spin_unlock(&fiq->lock);
return 0;
}
- fiq->ops->wake_interrupt_and_unlock(fiq);
+ fiq->ops->wake_interrupt_and_unlock(fiq, 0);
} else {
spin_unlock(&fiq->lock);
}
@@ -417,7 +420,7 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
/* acquire extra reference, since request is still needed
after fuse_request_end() */
__fuse_get_request(req);
- queue_request_and_unlock(fiq, req);
+ queue_request_and_unlock(fiq, req, 1);
request_wait_answer(fc, req);
/* Pairs with smp_wmb() in fuse_request_end() */
@@ -588,7 +591,7 @@ static int fuse_simple_notify_reply(struct fuse_conn *fc,
spin_lock(&fiq->lock);
if (fiq->connected) {
- queue_request_and_unlock(fiq, req);
+ queue_request_and_unlock(fiq, req, 0);
} else {
err = -ENODEV;
spin_unlock(&fiq->lock);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index bf040533bed3..0b3f6a4ab42a 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -379,19 +379,19 @@ struct fuse_iqueue_ops {
/**
* Signal that a forget has been queued
*/
- void (*wake_forget_and_unlock)(struct fuse_iqueue *fiq)
+ void (*wake_forget_and_unlock)(struct fuse_iqueue *fiq, bool sync)
__releases(fiq->lock);
/**
* Signal that an INTERRUPT request has been queued
*/
- void (*wake_interrupt_and_unlock)(struct fuse_iqueue *fiq)
+ void (*wake_interrupt_and_unlock)(struct fuse_iqueue *fiq, bool sync)
__releases(fiq->lock);
/**
* Signal that a request has been queued
*/
- void (*wake_pending_and_unlock)(struct fuse_iqueue *fiq)
+ void (*wake_pending_and_unlock)(struct fuse_iqueue *fiq, bool sync)
__releases(fiq->lock);
/**
diff --git a/gen_headers_arm.bp b/gen_headers_arm.bp
index acafa0e5d7f1..8569b1909c43 100644
--- a/gen_headers_arm.bp
+++ b/gen_headers_arm.bp
@@ -474,6 +474,8 @@ gen_headers_out_arm = [
"linux/qemu_fw_cfg.h",
"linux/qg.h",
"linux/qg-profile.h",
+ "linux/qbg.h",
+ "linux/qbg-profile.h",
"linux/qnx4_fs.h",
"linux/qnxtypes.h",
"linux/qrng.h",
diff --git a/gen_headers_arm64.bp b/gen_headers_arm64.bp
index 975584a08898..cd9d16532421 100644
--- a/gen_headers_arm64.bp
+++ b/gen_headers_arm64.bp
@@ -469,6 +469,8 @@ gen_headers_out_arm64 = [
"linux/qemu_fw_cfg.h",
"linux/qg.h",
"linux/qg-profile.h",
+ "linux/qbg.h",
+ "linux/qbg-profile.h",
"linux/qnx4_fs.h",
"linux/qnxtypes.h",
"linux/qrng.h",
diff --git a/include/dt-bindings/iio/qti_power_supply_iio.h b/include/dt-bindings/iio/qti_power_supply_iio.h
index c48865098c6a..84611eb3bcc6 100644
--- a/include/dt-bindings/iio/qti_power_supply_iio.h
+++ b/include/dt-bindings/iio/qti_power_supply_iio.h
@@ -151,4 +151,7 @@
/* USB */
#define PSY_IIO_MOISTURE_DETECTION_EN 0x7b
+
+/* BMS */
+#define PSY_IIO_CLEAR_SOH 0x7c
#endif /* __QTI_POWER_SUPPLY_IIO_H__ */
diff --git a/include/dt-bindings/msm/msm-camera.h b/include/dt-bindings/msm/msm-camera.h
index 0a5aadce4eb7..9735e75f7900 100644
--- a/include/dt-bindings/msm/msm-camera.h
+++ b/include/dt-bindings/msm/msm-camera.h
@@ -95,8 +95,8 @@
#define CAM_CPAS_FUSE_FEATURE_MAX 10
/* Flash type*/
-#define CAM_FLASH_TYPE_GPIO 0
+#define CAM_FLASH_TYPE_PMIC 0
#define CAM_FLASH_TYPE_I2C 1
-#define CAM_FLASH_TYPE_PMIC 2
+#define CAM_FLASH_TYPE_GPIO 2
#endif
diff --git a/include/linux/btpower.h b/include/linux/btpower.h
index 57b235733d62..a8bcd48f1623 100644
--- a/include/linux/btpower.h
+++ b/include/linux/btpower.h
@@ -89,7 +89,4 @@ int btpower_get_chipset_version(void);
#define TCS_CMD_IO_ADDR_OFFSET 0x4
-/* total number of power src */
-#define BT_POWER_SRC_SIZE 30
-
#endif /* __LINUX_BLUETOOTH_POWER_H */
diff --git a/include/linux/ipa_eth.h b/include/linux/ipa_eth.h
index a2d013da4a61..ff230cbb466f 100644
--- a/include/linux/ipa_eth.h
+++ b/include/linux/ipa_eth.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _IPA_ETH_H_
@@ -1094,5 +1094,9 @@ int ipa_eth_client_set_perf_profile(struct ipa_eth_client *client,
struct ipa_eth_perf_profile *profile);
int ipa_eth_client_conn_evt(struct ipa_ecm_msg *msg);
int ipa_eth_client_disconn_evt(struct ipa_ecm_msg *msg);
+enum ipa_client_type ipa_eth_get_ipa_client_type_from_eth_type(
+ enum ipa_eth_client_type eth_client_type, enum ipa_eth_pipe_direction dir);
+bool ipa_eth_client_exist(
+ enum ipa_eth_client_type eth_client_type, int inst_id);
#endif // _IPA_ETH_H_
diff --git a/include/linux/ipa_fmwk.h b/include/linux/ipa_fmwk.h
index af65e545507d..28077d869691 100644
--- a/include/linux/ipa_fmwk.h
+++ b/include/linux/ipa_fmwk.h
@@ -162,6 +162,8 @@ struct ipa_usb_data {
int (*ipa_usb_xdci_resume)(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot);
+
+ bool (*ipa_usb_is_teth_prot_connected)(enum ipa_usb_teth_prot usb_teth_prot);
};
struct ipa_wdi3_data {
@@ -199,6 +201,8 @@ struct ipa_wdi3_data {
int (*ipa_wdi_sw_stats)(struct ipa_wdi_tx_info *info);
int (*ipa_get_wdi_version)(void);
+
+ bool (*ipa_wdi_is_tx1_used)(void);
};
struct ipa_qdss_data {
@@ -327,6 +331,13 @@ struct ipa_eth_data {
int (*ipa_eth_client_conn_evt)(struct ipa_ecm_msg *msg);
int (*ipa_eth_client_disconn_evt)(struct ipa_ecm_msg *msg);
+
+ enum ipa_client_type (*ipa_eth_get_ipa_client_type_from_eth_type)(
+ enum ipa_eth_client_type eth_client_type,
+ enum ipa_eth_pipe_direction dir);
+
+ bool (*ipa_eth_client_exist)(
+ enum ipa_eth_client_type eth_client_type, int inst_id);
};
#if IS_ENABLED(CONFIG_IPA3)
diff --git a/include/linux/ipa_uc_offload.h b/include/linux/ipa_uc_offload.h
index 095fcb048ada..2f7474ea7e43 100644
--- a/include/linux/ipa_uc_offload.h
+++ b/include/linux/ipa_uc_offload.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _IPA_UC_OFFLOAD_H_
@@ -19,6 +19,7 @@ enum ipa_uc_offload_proto {
IPA_UC_INVALID = 0,
IPA_UC_WDI = 1,
IPA_UC_NTN = 2,
+ IPA_UC_NTN_V2X = 3,
IPA_UC_MAX_PROT_SIZE
};
@@ -89,6 +90,7 @@ struct ntn_buff_smmu_map {
* @num_buffers: Rx/Tx buffer pool size (in terms of elements)
* @data_buff_size: size of the each data buffer allocated in DDR
* @ntn_reg_base_ptr_pa: physical address of the Tx/Rx NTN Ring's
+ * @u8 db_mode: 0 means irq mode, 1 means db mode
* tail pointer
*/
struct ipa_ntn_setup_info {
@@ -111,6 +113,8 @@ struct ipa_ntn_setup_info {
u32 data_buff_size;
phys_addr_t ntn_reg_base_ptr_pa;
+
+ u8 db_mode;
};
/**
@@ -139,10 +143,14 @@ struct ipa_ntn_conn_in_params {
* @ul_uc_db_pa: physical address of IPA uc doorbell for UL
* @dl_uc_db_pa: physical address of IPA uc doorbell for DL
* @clnt_hdl: opaque handle assigned to offload client
+ * @ul_uc_db_iomem: iomem address of IPA uc doorbell for UL
+ * @dl_uc_db_iomem: iomem address of IPA uc doorbell for DL
*/
struct ipa_ntn_conn_out_params {
phys_addr_t ul_uc_db_pa;
phys_addr_t dl_uc_db_pa;
+ void __iomem *ul_uc_db_iomem;
+ void __iomem *dl_uc_db_iomem;
};
/**
@@ -176,10 +184,12 @@ struct ipa_uc_offload_conn_out_params {
* struct ipa_perf_profile - To set BandWidth profile
*
* @client: type of "client" (IPA_CLIENT_ODU#_PROD/CONS)
+ * @proto: uC offload protocol type
* @max_supported_bw_mbps: maximum bandwidth needed (in Mbps)
*/
struct ipa_perf_profile {
enum ipa_client_type client;
+ enum ipa_uc_offload_proto proto;
u32 max_supported_bw_mbps;
};
diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h
index c0c250e4709a..3fad1a392896 100644
--- a/include/linux/ipa_usb.h
+++ b/include/linux/ipa_usb.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _IPA_USB_H_
@@ -292,6 +292,15 @@ int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot);
+/**
+ * ipa_usb_is_teth_prot_connected - Internal API for checking USB
+ * protocol is connected
+ *
+ * @usb_teth_prot: USB tethering protocol
+ * @Return true if connected, false if not
+ */
+bool ipa_usb_is_teth_prot_connected(enum ipa_usb_teth_prot usb_teth_prot);
+
#else /* IS_ENABLED(CONFIG_IPA3) */
static inline int ipa_usb_init_teth_prot(enum ipa_usb_teth_prot teth_prot,
@@ -337,6 +346,11 @@ static inline int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
return -EPERM;
}
+static inline int ipa_usb_is_teth_prot_connected(enum ipa_usb_teth_prot usb_teth_prot)
+{
+ return -EPERM;
+}
+
#endif /* IS_ENABLED(CONFIG_IPA3) */
diff --git a/include/linux/ipa_wdi3.h b/include/linux/ipa_wdi3.h
index ad31f927adb9..ca07b74877a2 100644
--- a/include/linux/ipa_wdi3.h
+++ b/include/linux/ipa_wdi3.h
@@ -250,6 +250,12 @@ struct ipa_wdi_perf_profile {
int ipa_wdi_init(struct ipa_wdi_init_in_params *in,
struct ipa_wdi_init_out_params *out);
+/** ipa_wdi_is_tx1_used - return if DBS mode is active
+ *
+ * @Return bool
+ */
+bool ipa_wdi_is_tx1_used(void);
+
/**
* ipa_wdi_cleanup - Client should call this function to
* clean up WDI IPA offload data path
@@ -399,6 +405,11 @@ static inline int ipa_get_wdi_version(void)
return -EPERM;
}
+static inline int ipa_wdi_is_tx1_used(void)
+{
+ return -EPERM;
+}
+
static inline int ipa_wdi_cleanup(void)
{
return -EPERM;
diff --git a/include/linux/msm-sps.h b/include/linux/msm-sps.h
index 67247dcaa155..773a0be9302b 100644
--- a/include/linux/msm-sps.h
+++ b/include/linux/msm-sps.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
/* Smart-Peripheral-Switch (SPS) API. */
@@ -485,7 +485,7 @@ struct sps_bam_props {
*
*/
struct sps_mem_buffer {
- void *base;
+ void __iomem *base;
phys_addr_t phys_base;
unsigned long iova;
u32 size;
diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h
index 6910eb4c76cf..44a076807bb5 100644
--- a/include/linux/msm_pcie.h
+++ b/include/linux/msm_pcie.h
@@ -9,11 +9,10 @@
enum msm_pcie_config {
MSM_PCIE_CONFIG_INVALID = 0,
- MSM_PCIE_CONFIG_NO_CFG_RESTORE = 0x1,
- MSM_PCIE_CONFIG_LINKDOWN = 0x2,
- MSM_PCIE_CONFIG_NO_RECOVERY = 0x4,
- MSM_PCIE_CONFIG_NO_L1SS_TO = 0x8,
- MSM_PCIE_CONFIG_NO_DRV_PC = 0x10,
+ MSM_PCIE_CONFIG_LINKDOWN = BIT(0),
+ MSM_PCIE_CONFIG_NO_RECOVERY = BIT(1),
+ MSM_PCIE_CONFIG_NO_L1SS_TO = BIT(2),
+ MSM_PCIE_CONFIG_NO_DRV_PC = BIT(3),
};
enum msm_pcie_pm_opt {
@@ -174,16 +173,6 @@ int msm_pcie_register_event(struct msm_pcie_register_event *reg);
*/
int msm_pcie_deregister_event(struct msm_pcie_register_event *reg);
-/**
- * msm_pcie_recover_config - recover config space.
- * @dev: pci device structure
- *
- * This function recovers the config space of both RC and Endpoint.
- *
- * Return: 0 on success, negative value on error
- */
-int msm_pcie_recover_config(struct pci_dev *dev);
-
/**
* msm_pcie_enumerate - enumerate Endpoints.
* @rc_idx: RC that Endpoints connect to.
@@ -194,28 +183,6 @@ int msm_pcie_recover_config(struct pci_dev *dev);
*/
int msm_pcie_enumerate(u32 rc_idx);
-/**
- * msm_pcie_recover_config - recover config space.
- * @dev: pci device structure
- *
- * This function recovers the config space of both RC and Endpoint.
- *
- * Return: 0 on success, negative value on error
- */
-int msm_pcie_recover_config(struct pci_dev *dev);
-
-/**
- * msm_pcie_shadow_control - control the shadowing of PCIe config space.
- * @dev: pci device structure
- * @enable: shadowing should be enabled or disabled
- *
- * This function gives PCIe endpoint device drivers the control to enable
- * or disable the shadowing of PCIe config space.
- *
- * Return: 0 on success, negative value on error
- */
-int msm_pcie_shadow_control(struct pci_dev *dev, bool enable);
-
/*
* msm_pcie_debug_info - run a PCIe specific debug testcase.
* @dev: pci device structure
@@ -286,21 +253,11 @@ static inline int msm_pcie_deregister_event(struct msm_pcie_register_event *reg)
return -ENODEV;
}
-static inline int msm_pcie_recover_config(struct pci_dev *dev)
-{
- return -ENODEV;
-}
-
static inline int msm_pcie_enumerate(u32 rc_idx)
{
return -ENODEV;
}
-static inline int msm_pcie_shadow_control(struct pci_dev *dev, bool enable)
-{
- return -ENODEV;
-}
-
static inline int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
u32 offset, u32 mask, u32 value)
{
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 0e83233722d7..82ed4137f37a 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -62,6 +62,7 @@ extern unsigned int sysctl_sched_prefer_spread;
extern unsigned int sysctl_walt_rtg_cfs_boost_prio;
extern unsigned int sysctl_walt_low_latency_task_threshold;
extern unsigned int sysctl_sched_sync_hint_enable;
+extern unsigned int sysctl_walt_cpu_high_irqload;
extern int
walt_proc_group_thresholds_handler(struct ctl_table *table, int write,
@@ -87,6 +88,10 @@ extern int sched_boost_handler(struct ctl_table *table, int write,
extern int sched_busy_hyst_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos);
+
+extern int walt_high_irqload_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos);
+
#endif
enum sched_tunable_scaling {
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 3eb7cae8206c..3222e3a2daaf 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -215,6 +215,7 @@ void __wake_up_sync(struct wait_queue_head *wq_head, unsigned int mode, int nr);
#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
#define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
#define wake_up_interruptible_sync(x) __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)
+#define wake_up_sync(x) __wake_up_sync(x, TASK_NORMAL, 1)
/*
* Wakeup macros to be used to report events to the targets.
diff --git a/include/soc/qcom/watchdog.h b/include/soc/qcom/watchdog.h
index f7a7dacb3a93..e38ae008b2be 100644
--- a/include/soc/qcom/watchdog.h
+++ b/include/soc/qcom/watchdog.h
@@ -149,6 +149,7 @@ struct msm_watchdog_data {
struct qcom_irq_info ipi_counts[NR_IPI];
unsigned int tot_irq_count[NR_CPUS];
atomic_t irq_counts_running;
+ struct timer_list user_pet_timer;
};
extern void qcom_wdt_trigger_bite(void);
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index f7e9b59cb304..d4dd11734ef0 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -140,6 +140,7 @@
#define IPA_IOCTL_SET_PKT_THRESHOLD 87
#define IPA_IOCTL_ADD_EoGRE_MAPPING 88
#define IPA_IOCTL_DEL_EoGRE_MAPPING 89
+#define IPA_IOCTL_SET_IPPT_SW_FLT 90
/**
* max size of the header to be inserted
@@ -200,6 +201,12 @@
(IPA_MAX_FLT_RT_CNT_INDEX - IPA_FLT_RT_HW_COUNTER)
#define IPA_MAX_FLT_RT_CLIENTS 60
+/**
+ * Max number of ports/IPs IPPT exception
+ */
+
+#define IPA_MAX_IPPT_NUM_PORT_FLT 5
+
/**
* New feature flag for CV2X config.
*/
@@ -256,6 +263,47 @@
#define IPA_MAX_PDN_NUM 16
#define IPA_MAX_PDN_NUM_v4 5
+/**
+ * Macros duplicated from ipa_lnx_spearhead_stats.h and
+ * ipa_lnx_stats.h. All three macros should match.
+ * This needs to be updated whenever the header file structure
+ * and structure length macros are updated to match exactly
+ * the same. This is done to overcome backward and forward
+ * compatibility between userspace and driver spearhead structures.
+ */
+/* IPA Linux basic stats structure macros */
+#define IPA_LNX_PG_RECYCLE_STATS_STRUCT_LEN 32
+#define IPA_LNX_EXCEPTION_STATS_STRUCT_LEN 40
+#define IPA_LNX_ODL_EP_STATS_STRUCT_LEN 16
+#define IPA_LNX_HOLB_DISCARD_STATS_STRUCT_LEN 16
+#define IPA_LNX_HOLB_MONITOR_STATS_STRUCT_LEN 16
+#define IPA_LNX_HOLB_DROP_AND_MON_STATS_STRUCT_LEN (8 + 16 + 16)
+#define IPA_LNX_GENERIC_STATS_STRUCT_LEN (40 + 32 + 40 + 16 + 40)
+/* IPA Linux clock stats structures */
+#define IPA_LNX_PM_CLIENT_STATS_STRUCT_LEN 24
+#define IPA_LNX_CLOCK_STATS_STRUCT_LEN (24 + 24)
+/* Generic instance structures */
+#define IPA_LNX_GSI_RX_DEBUG_STATS_STRUCT_LEN 48
+#define IPA_LNX_GSI_TX_DEBUG_STATS_STRUCT_LEN 56
+#define IPA_LNX_GSI_DEBUG_STATS_STRUCT_LEN (8 + 48 + 56)
+#define IPA_LNX_PIPE_INFO_STATS_STRUCT_LEN 120
+/* IPA Linux wlan instance stats structures */
+#define IPA_LNX_WLAN_INSTANCE_INFO_STRUCT_LEN (32 + 112 + 120)
+#define IPA_LNX_WLAN_INST_STATS_STRUCT_LEN (8 + 264)
+/* IPA Linux eth instance stats structures */
+#define IPA_LNX_ETH_INSTANCE_INFO_STRUCT_LEN (16 + 112 + 120)
+#define IPA_LNX_ETH_INST_STATS_STRUCT_LEN (8 + 248)
+/* IPA Linux usb instance stats structures */
+#define IPA_LNX_USB_INSTANCE_INFO_STRUCT_LEN (16 + 112 + 120)
+#define IPA_LNX_USB_INST_STATS_STRUCT_LEN (8 + 248)
+/* IPA Linux mhip instance stats structures */
+#define IPA_LNX_MHIP_INSTANCE_INFO_STRUCT_LEN (16 + 112 + 120)
+#define IPA_LNX_MHIP_INST_STATS_STRUCT_LEN (8 + 248)
+/* IPA Linux Instance allocation info structures */
+#define IPA_LNX_EACH_INST_ALLOC_INFO_STRUCT_LEN (24 + 12 + 12 + 16)
+#define IPA_LNX_STATS_ALL_INFO_STRUCT_LEN (32 + 128 + 128 + 128)
+#define IPA_LNX_STATS_SPEARHEAD_CTX_STRUCT_LEN (8 + 4 + 416)
+
/**
* enum ipa_client_type - names for the various IPA "clients"
* these are from the perspective of the clients, for e.g.
@@ -846,7 +894,13 @@ enum ipa_eogre_event {
#define IPA_EoGRE_EVENT_MAX IPA_EoGRE_EVENT_MAX
};
-#define IPA_EVENT_MAX_NUM (IPA_EoGRE_EVENT_MAX)
+enum ipa_ippt_sw_flt_event {
+ IPA_IPPT_SW_FLT_EVENT = IPA_EoGRE_EVENT_MAX,
+ IPA_IPPT_SW_FLT_EVENT_MAX
+#define IPA_IPPT_SW_FLT_EVENT_MAX IPA_IPPT_SW_FLT_EVENT_MAX
+};
+
+#define IPA_EVENT_MAX_NUM (IPA_IPPT_SW_FLT_EVENT_MAX)
#define IPA_EVENT_MAX ((int)IPA_EVENT_MAX_NUM)
/**
@@ -3225,11 +3279,33 @@ struct ipa_sw_flt_list_type {
char iface[IPA_MAX_NUM_IFACE_FLT][IPA_RESOURCE_NAME_MAX];
};
+/**
+ * struct ipa_ippt_sw_flt_list_type- exception list
+ * @ipv4_enable: true to block ipv4 addrs given below and false to clean
+ * up all previous ipv4 addrs
+ * @num_of_ipv4: holds num of ipv4 to SW-exception
+ * @ipv4: an array to hold ipv4 addrs to SW-exception
+ * @port_enable: true to block current ports and false to clean
+ * up all previous ports
+ * @num_of_port: holds num of ports to SW-exception
+ * @port: an array to hold connection ports to SW-exception
+ */
+
+struct ipa_ippt_sw_flt_list_type {
+ uint8_t ipv4_enable;
+ int num_of_ipv4;
+ uint32_t ipv4[IPA_MAX_PDN_NUM];
+ uint8_t port_enable;
+ int num_of_port;
+ uint16_t port[IPA_MAX_IPPT_NUM_PORT_FLT];
+};
+
/**
* struct ipa_ioc_sw_flt_list_type
* @ioctl_ptr: has to be typecasted to (__u64)(uintptr_t)
* @ioctl_data_size:
* Eg: For ipa_sw_flt_list_type = sizeof(ipa_sw_flt_list_type)
+ * Eg: For ipa_ippt_sw_flt_list_type = sizeof(ipa_ippt_sw_flt_list_type)
*/
struct ipa_ioc_sw_flt_list_type {
__u64 ioctl_ptr;
@@ -3536,6 +3612,10 @@ struct ipa_ioc_sw_flt_list_type {
#define IPA_IOC_DEL_EoGRE_MAPPING _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_DEL_EoGRE_MAPPING, \
struct ipa_ioc_eogre_info)
+
+#define IPA_IOC_SET_IPPT_SW_FLT _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_SET_IPPT_SW_FLT, \
+ struct ipa_ioc_sw_flt_list_type)
/*
* unique magic number of the Tethering bridge ioctls
*/
diff --git a/include/uapi/linux/qbg.h b/include/uapi/linux/qbg.h
index f8bbb4e1a90c..48960a505413 100644
--- a/include/uapi/linux/qbg.h
+++ b/include/uapi/linux/qbg.h
@@ -7,6 +7,16 @@
#define __QBG_H__
#define MAX_FIFO_COUNT 36
+#define QBG_MAX_STEP_CHG_ENTRIES 6
+
+enum QBG_STATE {
+ QBG_LPM,
+ QBG_MPM,
+ QBG_HPM,
+ QBG_FAST_CHAR,
+ QBG_PON_OCV,
+ QBG_STATE_MAX,
+};
enum QBG_SDAM_DATA_OFFSET {
QBG_ACC0_OFFSET = 0,
@@ -41,10 +51,7 @@ enum qbg {
QBG_PARAM_SYS_SOC_HOLD_100PCT,
QBG_PARAM_JEITA_COOL_THRESHOLD,
QBG_PARAM_TOTAL_IMPEDANCE,
- QBG_PARAM_PON_OCV,
- QBG_PARAM_PON_IBAT,
- QBG_PARAM_PON_SOC,
- QBG_PARAM_BATT_ID,
+ QBG_PARAM_ESSENTIAL_PARAM_REVID,
QBG_PARAM_FIFO_TIMESTAMP,
QBG_PARAM_MAX,
};
@@ -115,6 +122,9 @@ struct qbg_config {
unsigned int iterm_ma;
unsigned int rconn_mohm;
unsigned long current_time;
+ unsigned int sdam_batt_id;
+ unsigned int essential_param_revid;
+ unsigned long sample_time_us[QBG_STATE_MAX];
} __attribute__ ((__packed__));
struct qbg_param {
@@ -134,9 +144,35 @@ struct qbg_user_data {
struct qbg_param param[QBG_PARAM_MAX];
} __attribute__ ((__packed__));
+struct range_data {
+ int low_threshold;
+ int high_threshold;
+ unsigned int value;
+} __attribute__ ((__packed__));
+
+struct ranges {
+ struct range_data data[QBG_MAX_STEP_CHG_ENTRIES];
+ unsigned char range_count;
+ _Bool valid;
+} __attribute__((__packed__));
+
+struct qbg_step_chg_jeita_params {
+ int jeita_full_fv_10nv;
+ int jeita_full_iterm_10na;
+ int jeita_warm_adc_value;
+ int jeita_cool_adc_value;
+ int battery_beta;
+ int battery_therm_kohm;
+ struct ranges step_fcc_cfg;
+ struct ranges jeita_fcc_cfg;
+ struct ranges jeita_fv_cfg;
+ unsigned char ttf_calc_mode;
+} __attribute__ ((__packed__));
+
/* IOCTLs to read & write QBG config and essential params */
#define QBGIOCXCFG _IOR('B', 0x01, struct qbg_config)
#define QBGIOCXEPR _IOR('B', 0x02, struct qbg_essential_params)
#define QBGIOCXEPW _IOWR('B', 0x03, struct qbg_essential_params)
+#define QBGIOCXSTEPCHGCFG _IOWR('B', 0x04, struct qbg_step_chg_jeita_params)
#endif
diff --git a/kernel/sched/walt/walt.c b/kernel/sched/walt/walt.c
index 99e8a0bf78ab..ddf1c0d98356 100644
--- a/kernel/sched/walt/walt.c
+++ b/kernel/sched/walt/walt.c
@@ -111,6 +111,8 @@ unsigned int sysctl_sched_capacity_margin_up[MAX_MARGIN_LEVELS] = {
[0 ... MAX_MARGIN_LEVELS-1] = 1078}; /* ~5% margin */
unsigned int sysctl_sched_capacity_margin_down[MAX_MARGIN_LEVELS] = {
[0 ... MAX_MARGIN_LEVELS-1] = 1205}; /* ~15% margin */
+
+unsigned int sysctl_walt_cpu_high_irqload = 95;
static unsigned int walt_cpu_high_irqload;
unsigned int sysctl_sched_walt_rotate_big_tasks;
@@ -3666,6 +3668,26 @@ int walt_proc_group_thresholds_handler(struct ctl_table *table, int write,
return ret;
}
+int walt_high_irqload_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret;
+ static DEFINE_MUTEX(mutex);
+
+ mutex_lock(&mutex);
+ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+ if (ret || !write) {
+ mutex_unlock(&mutex);
+ return ret;
+ }
+
+ walt_cpu_high_irqload = div64_u64((u64)sched_ravg_window *
+ sysctl_walt_cpu_high_irqload, (u64) 100);
+ mutex_unlock(&mutex);
+ return ret;
+}
+
static void walt_init_window_dep(void)
{
walt_cpu_util_freq_divisor =
@@ -3678,7 +3700,8 @@ static void walt_init_window_dep(void)
sched_init_task_load_windows_scaled =
scale_demand(sched_init_task_load_windows);
- walt_cpu_high_irqload = div64_u64((u64)sched_ravg_window * 95, (u64) 100);
+ walt_cpu_high_irqload = mult_frac((u64)sched_ravg_window,
+ sysctl_walt_cpu_high_irqload, (u64) 100);
}
static void walt_init_once(void)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 5fa53e98b3eb..d85172a76f3f 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -149,6 +149,7 @@ static int __maybe_unused max_kswapd_threads = MAX_KSWAPD_THREADS;
#ifdef CONFIG_SCHED_WALT
static int neg_three = -3;
static int three = 3;
+static int fifty = 50;
static int two_hundred_fifty_five = 255;
const int sched_user_hint_max = 1000;
static unsigned int ns_per_sec = NSEC_PER_SEC;
@@ -594,6 +595,16 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "sched_cpu_high_irqload",
+ .data = &sysctl_walt_cpu_high_irqload,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = walt_high_irqload_handler,
+ .extra1 = &fifty,
+ .extra2 = &one_hundred,
+ },
+
#endif
{
.procname = "sched_force_lb_enable",
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index ea576b51868e..5efa47cf4f9e 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -482,7 +482,7 @@ static void sysstats_build(struct sys_memstats *stats)
stats->version = SYSSTATS_VERSION;
stats->memtotal = K(i.totalram);
stats->reclaimable =
- global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE) >> 10;
+ K(global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE));
stats->swap_used = K(i.totalswap - i.freeswap);
stats->swap_total = K(i.totalswap);
stats->vmalloc_total = K(vmalloc_nr_pages());
diff --git a/net/core/filter.c b/net/core/filter.c
index aff14a883ffd..65b6ecb07a59 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2861,8 +2861,6 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
shinfo->gso_type |= SKB_GSO_TCPV6;
}
- /* Due to IPv6 header, MSS needs to be downgraded. */
- skb_decrease_gso_size(shinfo, len_diff);
/* Header must be checked, and gso_segs recomputed. */
shinfo->gso_type |= SKB_GSO_DODGY;
shinfo->gso_segs = 0;
@@ -2902,8 +2900,6 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
shinfo->gso_type |= SKB_GSO_TCPV4;
}
- /* Due to IPv4 header, MSS can be upgraded. */
- skb_increase_gso_size(shinfo, len_diff);
/* Header must be checked, and gso_segs recomputed. */
shinfo->gso_type |= SKB_GSO_DODGY;
shinfo->gso_segs = 0;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5e311e6a31d5..713c8c62c638 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2071,6 +2071,9 @@ static bool tcp_can_coalesce_send_queue_head(struct sock *sk, int len)
struct sk_buff *skb, *next;
skb = tcp_send_head(sk);
+ if (!skb)
+ return false;
+
tcp_for_write_queue_from_safe(skb, next, sk) {
if (len <= skb->len)
break;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index d89725d93d6c..99aed1b2571b 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2510,6 +2510,9 @@ void udp_destroy_sock(struct sock *sk)
{
struct udp_sock *up = udp_sk(sk);
bool slow = lock_sock_fast(sk);
+
+ /* protects from races with udp_abort() */
+ sock_set_flag(sk, SOCK_DEAD);
udp_flush_pending_frames(sk);
unlock_sock_fast(sk, slow);
if (static_branch_unlikely(&udp_encap_needed_key)) {
@@ -2765,10 +2768,17 @@ int udp_abort(struct sock *sk, int err)
{
lock_sock(sk);
+ /* udp{v6}_destroy_sock() sets it under the sk lock, avoid racing
+ * with close()
+ */
+ if (sock_flag(sk, SOCK_DEAD))
+ goto out;
+
sk->sk_err = err;
sk->sk_error_report(sk);
__udp_disconnect(sk, 0);
+out:
release_sock(sk);
return 0;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 6762430280f5..3c94b81bb459 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1539,6 +1539,9 @@ void udpv6_destroy_sock(struct sock *sk)
{
struct udp_sock *up = udp_sk(sk);
lock_sock(sk);
+
+ /* protects from races with udp_abort() */
+ sock_set_flag(sk, SOCK_DEAD);
udp_v6_flush_pending_frames(sk);
release_sock(sk);
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 562bd1802c99..896ccad73ee8 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -116,7 +116,8 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_IPC_PERMS, NULL } },
{ "netlink_route_socket",
{ COMMON_SOCK_PERMS,
- "nlmsg_read", "nlmsg_write", "nlmsg_readpriv", NULL } },
+ "nlmsg_read", "nlmsg_write", "nlmsg_readpriv", "nlmsg_getneigh",
+ NULL } },
{ "netlink_tcpdiag_socket",
{ COMMON_SOCK_PERMS,
"nlmsg_read", "nlmsg_write", NULL } },
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 6d6068490466..6f5abff4109b 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -106,6 +106,8 @@ struct selinux_state {
bool initialized;
bool policycap[__POLICYDB_CAPABILITY_MAX];
bool android_netlink_route;
+ bool android_netlink_getneigh;
+
struct selinux_avc *avc;
struct selinux_ss *ss;
};
@@ -185,6 +187,13 @@ static inline bool selinux_android_nlroute_getlink(void)
return state->android_netlink_route;
}
+static inline bool selinux_android_nlroute_getneigh(void)
+{
+ struct selinux_state *state = &selinux_state;
+
+ return state->android_netlink_getneigh;
+}
+
int security_mls_enabled(struct selinux_state *state);
int security_load_policy(struct selinux_state *state,
void *data, size_t len);
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index d1bec5a281db..328f5ffc30a9 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -207,12 +207,12 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
return err;
}
-static void nlmsg_set_getlink_perm(u32 perm)
+static void nlmsg_set_perm_for_type(u32 perm, u16 type)
{
int i;
for (i = 0; i < ARRAY_SIZE(nlmsg_route_perms); i++) {
- if (nlmsg_route_perms[i].nlmsg_type == RTM_GETLINK) {
+ if (nlmsg_route_perms[i].nlmsg_type == type) {
nlmsg_route_perms[i].perm = perm;
break;
}
@@ -222,11 +222,27 @@ static void nlmsg_set_getlink_perm(u32 perm)
/**
* Use nlmsg_readpriv as the permission for RTM_GETLINK messages if the
* netlink_route_getlink policy capability is set. Otherwise use nlmsg_read.
+ * Similarly, use nlmsg_getneigh for RTM_GETNEIGH and RTM_GETNEIGHTBL if the
+ * netlink_route_getneigh policy capability is set. Otherwise use nlmsg_read.
*/
void selinux_nlmsg_init(void)
{
if (selinux_android_nlroute_getlink())
- nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READPRIV);
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READPRIV,
+ RTM_GETLINK);
else
- nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READ);
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+ RTM_GETLINK);
+
+ if (selinux_android_nlroute_getneigh()) {
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_GETNEIGH,
+ RTM_GETNEIGH);
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_GETNEIGH,
+ RTM_GETNEIGHTBL);
+ } else {
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+ RTM_GETNEIGH);
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+ RTM_GETNEIGHTBL);
+ }
}
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 99a207784ce9..b2393555d471 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -2353,6 +2353,10 @@ int policydb_read(struct policydb *p, void *fp)
p->android_netlink_route = 1;
}
+ if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH)) {
+ p->android_netlink_getneigh = 1;
+ }
+
if (p->policyvers >= POLICYDB_VERSION_POLCAP) {
rc = ebitmap_read(&p->policycaps, fp);
if (rc)
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 95fb3607f07c..eefacd40d491 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -235,6 +235,7 @@ struct genfs {
struct policydb {
int mls_enabled;
int android_netlink_route;
+ int android_netlink_getneigh;
/* symbol tables */
struct symtab symtab[SYM_NUM];
@@ -322,6 +323,7 @@ extern int policydb_write(struct policydb *p, void *fp);
#define POLICYDB_CONFIG_MLS 1
#define POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE (1 << 31)
+#define POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH (1 << 30)
/* the config flags related to unknown classes/perms are bits 2 and 3 */
#define REJECT_UNKNOWN 0x00000002
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 5199dd3f3db5..6559767d3444 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2127,6 +2127,7 @@ static void security_load_policycaps(struct selinux_state *state)
}
state->android_netlink_route = p->android_netlink_route;
+ state->android_netlink_getneigh = p->android_netlink_getneigh;
selinux_nlmsg_init();
}