Merge f627bf94a3 on remote branch

Change-Id: I2ab85c61ae88c640e8dce588ae234c707580b89e
This commit is contained in:
Linux Build Service Account 2021-07-23 03:50:08 -07:00
commit 3a6bc9783a
99 changed files with 3078 additions and 860 deletions

View file

@ -1 +1 @@
LTS_5.4.86_7d99cf8c84a7
LTS_5.4.86_fe9e8630bfaf

View file

@ -3967,6 +3967,7 @@
<elf-symbol name='spi_bus_lock' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x1001acda'/>
<elf-symbol name='spi_bus_unlock' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x4861f1cc'/>
<elf-symbol name='spi_busnum_to_master' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x40920415'/>
<elf-symbol name='spi_set_cs_timing' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0xe80f6172'/>
<elf-symbol name='spi_controller_resume' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x3b6b9b70'/>
<elf-symbol name='spi_controller_suspend' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x9e6ee413'/>
<elf-symbol name='spi_finalize_current_message' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes' crc='0x71c101fd'/>
@ -128457,6 +128458,13 @@
<parameter type-id='f22e4524' name='ctlr' filepath='drivers/spi/spi.c' line='2705' column='1'/>
<return type-id='95e97e5e'/>
</function-decl>
<function-decl name='spi_set_cs_timing' mangled-name='spi_set_cs_timing' filepath='drivers/spi/spi.c' line='3225' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='spi_set_cs_timing'>
<parameter type-id='e90f4666' name='spi' filepath='drivers/spi/spi.c' line='3225' column='1'/>
<parameter type-id='f9b06939' name='setup' filepath='drivers/spi/spi.c' line='3225' column='1'/>
<parameter type-id='f9b06939' name='hold' filepath='drivers/spi/spi.c' line='3225' column='1'/>
<parameter type-id='f9b06939' name='inactive_dly' filepath='drivers/spi/spi.c' line='3226' column='1'/>
<return type-id='48b5725f'/>
</function-decl>
<function-decl name='spi_busnum_to_master' mangled-name='spi_busnum_to_master' filepath='drivers/spi/spi.c' line='2741' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='spi_busnum_to_master'>
<parameter type-id='1dc6a898' name='bus_num' filepath='drivers/spi/spi.c' line='2741' column='1'/>
<return type-id='f22e4524'/>

View file

@ -94598,6 +94598,12 @@
<parameter type-id='fa0b179b'/>
<return type-id='48b5725f'/>
</function-decl>
<function-decl name='strscpy' mangled-name='strscpy' filepath='include/linux/string.h' line='32' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='strscpy'>
<parameter type-id='26a90f95'/>
<parameter type-id='80f4b756'/>
<parameter type-id='7359adad'/>
<return type-id='bd54fe1a'/>
</function-decl>
<function-decl name='msleep' mangled-name='msleep' filepath='include/linux/delay.h' line='59' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='msleep'>
<parameter type-id='f0981eeb'/>
<return type-id='48b5725f'/>
@ -128690,15 +128696,15 @@
<parameter type-id='1ce53783'/>
<return type-id='c894953d'/>
</function-decl>
<function-decl name='sched_setscheduler' mangled-name='sched_setscheduler' filepath='kernel/sched/core.c' line='5319' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='sched_setscheduler'>
<parameter type-id='f23e2572' name='p' filepath='kernel/sched/core.c' line='5319' column='1'/>
<parameter type-id='95e97e5e' name='policy' filepath='kernel/sched/core.c' line='5319' column='1'/>
<parameter type-id='36fca399' name='param' filepath='kernel/sched/core.c' line='5320' column='1'/>
<function-decl name='sched_setscheduler' mangled-name='sched_setscheduler' filepath='kernel/sched/core.c' line='5322' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='sched_setscheduler'>
<parameter type-id='f23e2572' name='p' filepath='kernel/sched/core.c' line='5322' column='1'/>
<parameter type-id='95e97e5e' name='policy' filepath='kernel/sched/core.c' line='5322' column='1'/>
<parameter type-id='36fca399' name='param' filepath='kernel/sched/core.c' line='5323' column='1'/>
<return type-id='95e97e5e'/>
</function-decl>
<function-decl name='sched_setattr' mangled-name='sched_setattr' filepath='kernel/sched/core.c' line='5326' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='sched_setattr'>
<parameter type-id='f23e2572' name='p' filepath='kernel/sched/core.c' line='5326' column='1'/>
<parameter type-id='8abbb6c3' name='attr' filepath='kernel/sched/core.c' line='5326' column='1'/>
<function-decl name='sched_setattr' mangled-name='sched_setattr' filepath='kernel/sched/core.c' line='5329' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='sched_setattr'>
<parameter type-id='f23e2572' name='p' filepath='kernel/sched/core.c' line='5329' column='1'/>
<parameter type-id='8abbb6c3' name='attr' filepath='kernel/sched/core.c' line='5329' column='1'/>
<return type-id='95e97e5e'/>
</function-decl>
<function-decl name='strnstr' mangled-name='strnstr' filepath='include/linux/string.h' line='84' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='strnstr'>
@ -130490,12 +130496,6 @@
<parameter type-id='83c1bde6' name='work' filepath='kernel/workqueue.c' line='4553' column='1'/>
<return type-id='f0981eeb'/>
</function-decl>
<function-decl name='strscpy' mangled-name='strscpy' filepath='include/linux/string.h' line='32' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='strscpy'>
<parameter type-id='26a90f95'/>
<parameter type-id='80f4b756'/>
<parameter type-id='7359adad'/>
<return type-id='bd54fe1a'/>
</function-decl>
<function-decl name='ida_alloc_range' mangled-name='ida_alloc_range' filepath='include/linux/idr.h' line='255' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='ida_alloc_range'>
<parameter type-id='a9e764bb'/>
<parameter type-id='f0981eeb'/>

View file

@ -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

View file

@ -198,6 +198,7 @@
remove_proc_subtree
request_firmware
rtc_time64_to_tm
spi_set_cs_timing
spi_sync
strcat
system_wq

View file

@ -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

View file

@ -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

View file

@ -32,12 +32,6 @@
#endif
#include <linux/fs.h>
#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 = {

View file

@ -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];

View file

@ -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);

View file

@ -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), &regval);
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;

View file

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

View file

@ -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,

View file

@ -6,6 +6,7 @@
#include <uapi/linux/sched/types.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dma-buf.h>
#include <linux/fdtable.h>
#include <linux/io.h>
@ -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,

View file

@ -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);

View file

@ -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 <linux/debugfs.h>
@ -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);

View file

@ -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) {

View file

@ -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)

View file

@ -14,6 +14,7 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@ -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;

View file

@ -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)

View file

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

View file

@ -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 {

View file

@ -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;
}

View file

@ -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);

View file

@ -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");

View file

@ -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 */

View file

@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/reset.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/qcom-pinctrl.h>
#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;
}

View file

@ -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);

View file

@ -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)

View file

@ -4,55 +4,58 @@
#ifndef _CNSS_DEBUG_H
#define _CNSS_DEBUG_H
#if IS_ENABLED(CONFIG_IPC_LOGGING)
#include <linux/ipc_logging.h>
#endif
#include <linux/printk.h>
#if IS_ENABLED(CONFIG_IPC_LOGGING)
#define CNSS_IPC_LOG_PAGES 32
#include <linux/ipc_logging.h>
#include <asm/current.h>
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 */

View file

@ -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);

View file

@ -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");

View file

@ -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 <linux/module.h>
@ -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, _, _, _, _, _, _, _, _),

View file

@ -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 = {

View file

@ -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)
{

View file

@ -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.

View file

@ -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));
/**

View file

@ -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);

View file

@ -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))

View file

@ -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++) {

View file

@ -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",

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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;

View file

@ -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)

View file

@ -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)

View file

@ -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;

View file

@ -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 <linux/kernel.h>
@ -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");

View file

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

View file

@ -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,

View file

@ -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 <linux/cache.h>
@ -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;
/*

View file

@ -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;

View file

@ -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);

View file

@ -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) {

View file

@ -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__);

View file

@ -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");

View file

@ -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;

View file

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

View file

@ -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 <SUPER, however versions

View file

@ -1726,11 +1726,12 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
count_configs(cdev, USB_DT_DEVICE);
cdev->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 {

View file

@ -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)

View file

@ -38,6 +38,7 @@
#include <linux/module.h>
#include <asm/ioctls.h>
#include <asm-generic/termios.h>
#include <linux/usb/dwc3-msm.h>
#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;

View file

@ -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;
}

View file

@ -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 */

View file

@ -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);

View file

@ -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);
/**

View file

@ -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",

View file

@ -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",

View file

@ -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__ */

View file

@ -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

View file

@ -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 */

View file

@ -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_

View file

@ -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)

View file

@ -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;
};

View file

@ -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) */

View file

@ -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;

View file

@ -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;

View file

@ -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)
{

View file

@ -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 {

View file

@ -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.

View file

@ -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);

View file

@ -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
*/

View file

@ -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

View file

@ -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)

View file

@ -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",

View file

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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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 } },

View file

@ -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);

View file

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

View file

@ -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)

View file

@ -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

View file

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