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(); }