diff --git a/Androidbp b/Androidbp index 3d5382bfbd47..a1fde7b44556 100644 --- a/Androidbp +++ b/Androidbp @@ -28,6 +28,109 @@ kernel_headers_verbose = "" build = ["gen_headers_arm.bp", "gen_headers_arm64.bp"] +soong_config_module_type { + name: "mmi_camera_legacy_genrule_defaults", + module_type: "genrule_defaults", + config_namespace: "mmi_camera_legacy", + variables: ["board_config_camera_legacy_kernel"], + properties: ["cmd"], +} + +soong_config_string_variable { + name: "board_config_camera_legacy_kernel", + values: ["enabled", "disabled"], +} + +mmi_camera_legacy_genrule_defaults { + name: "mmi_kernel_headers_cmdrules_arm", + soong_config_variables: { + board_config_camera_legacy_kernel: { + disabled: { + cmd: "python3 $(location kernel_headers.py) " + + kernel_headers_verbose + + "--header_arch arm " + + "--gen_dir $(genDir) " + + "--arch_asm_kbuild $(location arch/arm/include/uapi/asm/Kbuild) " + + "--arch_include_uapi $(locations arch/arm/include/uapi/**/*.h) " + + "--techpack_include_uapi $(locations techpack/*/include/uapi/*/**/*.h) " + + "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild) " + + "headers " + + "--old_gen_headers_bp $(location gen_headers_arm.bp) " + + "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm) " + + "--version_makefile $(location Makefile) " + + "--arch_syscall_tool $(location arch/arm/tools/syscallhdr.sh) " + + "--arch_syscall_tbl $(location arch/arm/tools/syscall.tbl) " + + "--headers_install $(location headers_install.sh) " + + "--unifdef $(location unifdef) " + + "--include_uapi $(locations include/uapi/**/*.h)", + }, + enabled: { + cmd: "python3 $(location kernel_headers.py) " + + kernel_headers_verbose + + "--header_arch arm " + + "--gen_dir $(genDir) " + + "--arch_asm_kbuild $(location arch/arm/include/uapi/asm/Kbuild) " + + "--arch_include_uapi $(locations arch/arm/include/uapi/**/*.h) " + + "--techpack_include_uapi $(locations techpack/*/include/uapi/*/**/*.h) " + + "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild) " + + "headers " + + "--old_gen_headers_bp $(location gen_headers_arm.bp) " + + "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm) " + + "--version_makefile $(location Makefile) " + + "--arch_syscall_tool $(location arch/arm/tools/syscallhdr.sh) " + + "--arch_syscall_tbl $(location arch/arm/tools/syscall.tbl) " + + "--headers_install $(location headers_install.sh) " + + "--unifdef $(location unifdef) " + + "--include_uapi $(locations include/uapi/**/*.h) " + + "--camera_legacy ", + }, + }, + }, +} + +mmi_camera_legacy_genrule_defaults { + name: "mmi_kernel_headers_cmdrules_arm64", + soong_config_variables: { + board_config_camera_legacy_kernel: { + disabled: { + cmd: "python3 $(location kernel_headers.py) " + + kernel_headers_verbose + + "--header_arch arm64 " + + "--gen_dir $(genDir) " + + "--arch_asm_kbuild $(location arch/arm64/include/uapi/asm/Kbuild) " + + "--arch_include_uapi $(locations arch/arm64/include/uapi/**/*.h) " + + "--techpack_include_uapi $(locations techpack/*/include/uapi/*/**/*.h) " + + "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild) " + + "headers " + + "--old_gen_headers_bp $(location gen_headers_arm64.bp) " + + "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm64) " + + "--version_makefile $(location Makefile) " + + "--headers_install $(location headers_install.sh) " + + "--unifdef $(location unifdef) " + + "--include_uapi $(locations include/uapi/**/*.h)", + }, + enabled: { + cmd: "python3 $(location kernel_headers.py) " + + kernel_headers_verbose + + "--header_arch arm64 " + + "--gen_dir $(genDir) " + + "--arch_asm_kbuild $(location arch/arm64/include/uapi/asm/Kbuild) " + + "--arch_include_uapi $(locations arch/arm64/include/uapi/**/*.h) " + + "--techpack_include_uapi $(locations techpack/*/include/uapi/*/**/*.h) " + + "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild) " + + "headers " + + "--old_gen_headers_bp $(location gen_headers_arm64.bp) " + + "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm64) " + + "--version_makefile $(location Makefile) " + + "--headers_install $(location headers_install.sh) " + + "--unifdef $(location unifdef) " + + "--include_uapi $(locations include/uapi/**/*.h) " + + "--camera_legacy ", + }, + }, + }, +} + cc_library_headers { name: "qti_kernel_headers", arch: { diff --git a/Makefile b/Makefile index fa2cd06f09e2..972653b48f96 100644 --- a/Makefile +++ b/Makefile @@ -1336,7 +1336,13 @@ PHONY += archheaders archscripts hdr-inst := -f $(srctree)/scripts/Makefile.headersinst obj +ifeq ($(BOARD_CONFIG_CAMERA_KERNEL_NAME),) techpack-dirs := $(shell find $(srctree)/techpack -maxdepth 1 -mindepth 1 -type d -not -name ".*") +else +techpack-dirs := $(shell find $(srctree)/techpack -maxdepth 1 -mindepth 1 -type d -not -name ".*"\ + | grep -v camera) +techpack-dirs += $(shell find $(srctree)/techpack -maxdepth 2 -mindepth 2 -name "$(BOARD_CONFIG_CAMERA_KERNEL_NAME)") +endif techpack-dirs := $(subst $(srctree)/,,$(techpack-dirs)) PHONY += headers diff --git a/android/abi_gki_aarch64_qcom b/android/abi_gki_aarch64_qcom index ac6f0fc833ee..bf40649817b8 100644 --- a/android/abi_gki_aarch64_qcom +++ b/android/abi_gki_aarch64_qcom @@ -103,9 +103,11 @@ blk_status_to_errno blk_update_request blk_verify_command + __blockdev_direct_IO blocking_notifier_call_chain blocking_notifier_chain_register blocking_notifier_chain_unregister + block_write_full_page bpf_prog_add bpf_prog_put bpf_prog_sub @@ -124,6 +126,8 @@ bpf_warn_invalid_xdp_action bprm_change_interp __break_lease + __bread_gfp + __brelse build_skb bus_find_device bus_for_each_dev @@ -182,6 +186,9 @@ __class_create class_destroy class_find_device + class_for_each_device + class_create_file_ns + class_remove_file_ns class_interface_unregister __class_register class_unregister @@ -252,6 +259,7 @@ __const_udelay consume_skb contig_page_data + cont_write_begin _copy_from_iter_full copy_page copy_strings_kernel @@ -382,7 +390,9 @@ device_property_read_u32_array device_property_read_u8_array device_register + device_remove_bin_file device_remove_file + device_set_of_node_from_dev device_set_wakeup_capable device_show_int device_store_int @@ -418,6 +428,7 @@ devm_gpio_request_one devm_iio_channel_get devm_iio_device_alloc + devm_gpio_free __devm_iio_device_register devm_input_allocate_device devm_ioremap @@ -451,6 +462,7 @@ devm_platform_ioremap_resource devm_power_supply_get_by_phandle devm_power_supply_register + devm_pwm_get devm_pwm_put devm_regmap_add_irq_chip devm_regmap_del_irq_chip @@ -514,6 +526,7 @@ dev_uc_sync_multiple dev_uc_unsync _dev_warn + d_find_alias dget_parent d_instantiate disable_irq @@ -594,11 +607,13 @@ dma_resv_wait_timeout_rcu dma_set_coherent_mask dma_set_mask + d_move do_exit do_wait_intr down down_read down_write + d_path dput drain_workqueue driver_find_device @@ -891,6 +906,7 @@ dump_stack enable_irq enable_percpu_irq + end_buffer_write_sync eth_commit_mac_addr_change ether_setup eth_mac_addr @@ -916,7 +932,11 @@ fasync_helper fd_install fget + filemap_fdatawait_range + filemap_fdatawrite_range file_path + filp_close + filp_open find_last_bit find_next_bit find_next_zero_bit @@ -961,17 +981,25 @@ fwnode_property_read_u32_array fwnode_usb_role_switch_get gcd + generic_block_bmap + generic_cont_expand_simple generic_delete_inode generic_device_group + generic_fh_to_dentry + generic_fh_to_parent generic_file_llseek generic_file_mmap generic_file_read_iter generic_file_splice_read + generic_fillattr generic_handle_irq generic_mii_ioctl generic_permission generic_read_dir generic_shutdown_super + generic_write_end + generic_file_fsync + generic_file_write_iter genlmsg_put genl_register_family genl_unregister_family @@ -1009,7 +1037,10 @@ get_random_bytes get_random_u32 __get_task_comm + get_task_exe_file + get_task_mm get_task_pid + __getblk_gfp get_tree_single get_unmapped_area get_unused_fd_flags @@ -1105,10 +1136,12 @@ i2c_smbus_read_byte_data i2c_smbus_read_byte_data i2c_smbus_read_i2c_block_data + i2c_smbus_read_word_data i2c_smbus_write_byte i2c_smbus_write_byte_data i2c_smbus_write_byte_data i2c_smbus_write_i2c_block_data + i2c_smbus_write_word_data __i2c_smbus_xfer i2c_smbus_xfer __i2c_transfer @@ -1149,12 +1182,16 @@ iio_channel_release iio_read_channel_processed iio_write_channel_raw + ilookup import_iovec in_egroup_p + inc_nlink init_dummy_netdev init_net + in_group_p __init_rwsem init_srcu_struct + init_task init_timer_key init_uts_ns init_wait_entry @@ -1171,6 +1208,7 @@ input_mt_init_slots input_mt_report_pointer_emulation input_mt_report_slot_state + input_mt_sync_frame input_open_device input_register_device input_register_handle @@ -1180,12 +1218,15 @@ input_unregister_device input_unregister_handle input_unregister_handler + __insert_inode_hash interval_tree_insert interval_tree_iter_first interval_tree_iter_next interval_tree_remove int_to_scsilun invalidate_mapping_pages + invalidate_bdev + invalidate_inode_buffers iomem_resource iommu_alloc_resv_region iommu_attach_device @@ -1282,6 +1323,7 @@ irq_work_queue iterate_dir iterate_fd + iunique jiffies jiffies_to_msecs jiffies_to_usecs @@ -1290,6 +1332,7 @@ kernel_connect kernel_getsockname kernel_kobj + kernel_power_off kernel_read kernel_recvmsg kernel_restart @@ -1312,6 +1355,7 @@ kfree_skb kfree_skb_list kill_anon_super + kill_block_super kill_fasync kill_litter_super kimage_vaddr @@ -1328,6 +1372,7 @@ kmemdup_nul kobject_create_and_add kobject_del + kobject_get_path kobject_init_and_add kobject_put kobject_uevent @@ -1375,6 +1420,7 @@ ktime_add_safe ktime_get ktime_get_mono_fast_ns + ktime_get_raw_ts64 ktime_get_real_seconds ktime_get_real_ts64 ktime_get_ts64 @@ -1399,16 +1445,22 @@ list_sort llist_add_batch llist_reverse_order + load_nls __local_bh_enable_ip lockref_get + __lock_buffer lock_rename lock_sock_nested lookup_one_len LZ4_decompress_safe mac_pton + mark_buffer_dirty + __mark_inode_dirty mark_page_accessed match_int + match_octal match_string + match_strdup match_token mbox_chan_received_data mbox_chan_txdone @@ -1453,9 +1505,12 @@ mempool_free_slab memremap memset + memset32 __memset_io memunmap memweight + mfd_add_devices + mfd_remove_devices mii_check_media mii_ethtool_get_link_ksettings mii_ethtool_gset @@ -1472,12 +1527,17 @@ mmc_cqe_request_done mmc_of_parse mmc_send_tuning + mmput mod_delayed_work_on mod_node_page_state mod_timer __module_get module_layout module_put + mount_bdev + mpage_readpage + mpage_readpages + mpage_writepages __msecs_to_jiffies msleep msleep_interruptible @@ -1665,6 +1725,7 @@ of_usb_host_tpl_support oops_in_progress override_creds + orderly_poweroff pagecache_get_page panic panic_notifier_list @@ -1673,6 +1734,7 @@ param_get_int param_get_string param_get_ullong + param_get_uint param_ops_bool param_ops_byte param_ops_charp @@ -1685,6 +1747,7 @@ param_set_bool param_set_copystring param_set_int + param_set_uint passthru_features_check path_get path_put @@ -1797,6 +1860,7 @@ platform_get_irq platform_get_irq_byname platform_get_irq_byname_optional + platform_get_irq_optional platform_get_resource platform_get_resource_byname platform_irq_count @@ -1849,8 +1913,10 @@ power_supply_get_property power_supply_put power_supply_reg_notifier + power_supply_register power_supply_set_property power_supply_unreg_notifier + power_supply_unregister prandom_u32 preempt_schedule preempt_schedule_notrace @@ -1938,6 +2004,7 @@ __rcu_read_lock __rcu_read_unlock rdev_get_drvdata + rdev_get_id reboot_mode refcount_dec_and_lock refcount_dec_and_test_checked @@ -1979,9 +2046,11 @@ regmap_field_read regmap_field_update_bits_base __regmap_init + __regmap_init_i2c regmap_irq_get_virq regmap_mmio_detach_clk regmap_raw_read + regmap_raw_write regmap_read regmap_register_patch regmap_update_bits_base @@ -1990,17 +2059,22 @@ regulator_count_voltages regulator_disable regulator_disable_deferred + regulator_disable_regmap regulator_enable + regulator_enable_regmap regulator_get regulator_get_current_limit regulator_get_drvdata regulator_get_mode regulator_get_voltage + regulator_get_voltage_sel_regmap regulator_is_enabled + regulator_is_enabled_regmap regulator_is_supported_voltage regulator_list_voltage regulator_list_voltage_linear regulator_lock + regulator_map_voltage_linear regulator_notifier_call_chain regulator_put regulator_register @@ -2009,17 +2083,21 @@ regulator_set_load regulator_set_mode regulator_set_voltage + regulator_set_voltage_sel_regmap regulator_unlock regulator_unregister regulator_unregister_notifier regulatory_set_wiphy_regd release_firmware + release_pages __release_region release_sock remap_pfn_range remap_vmalloc_range remove_arg_zero + __remove_inode_hash remove_proc_entry + remove_proc_subtree remove_wait_queue report_iommu_fault request_firmware @@ -2073,8 +2151,8 @@ rtc_update_irq rtc_valid_tm rt_mutex_lock - rt_mutex_trylock rt_mutex_unlock + rt_mutex_trylock rtnl_is_locked rtnl_link_register rtnl_link_unregister @@ -2086,6 +2164,8 @@ rtnl_unregister_all save_stack_trace save_stack_trace_tsk + sb_min_blocksize + sb_set_blocksize sched_clock sched_setattr sched_setscheduler @@ -2166,6 +2246,7 @@ serial8250_suspend_port serial8250_unregister_port set_anon_super + setattr_copy set_cpus_allowed_ptr set_disk_ro set_freezable @@ -2284,6 +2365,7 @@ snd_pcm_stop _snd_pcm_stream_lock_irqsave snd_pcm_stream_unlock_irqrestore + snd_soc_add_card_controls snd_soc_add_component_controls snd_soc_bytes_tlv_callback snd_soc_card_get_kcontrol @@ -2399,6 +2481,7 @@ __stack_chk_guard stack_trace_print strcasecmp + strcat strchr strchrnul strcmp @@ -2412,6 +2495,7 @@ strlcpy strlen strncasecmp + strncat strnchr strncmp strncpy @@ -2425,13 +2509,17 @@ strscpy_pad strsep strstr + submit_bh __sw_hweight32 __sw_hweight64 __sw_hweight8 swiotlb_max_segment + sync_blockdev + sync_dirty_buffer sync_file_create sync_file_get_fence sync_filesystem + sync_mapping_buffers synchronize_hardirq synchronize_irq synchronize_net @@ -2473,6 +2561,7 @@ thermal_cooling_device_unregister thermal_of_cooling_device_register thermal_zone_device_register + thermal_zone_device_unregister thermal_zone_device_update thermal_zone_get_temp thermal_zone_get_zone_by_name @@ -2482,6 +2571,7 @@ timer_unstable_counter_workaround timespec64_to_jiffies _totalram_pages + touch_buffer trace_array_create trace_array_destroy __trace_bprintk @@ -2503,6 +2593,7 @@ trace_seq_printf tracing_off truncate_inode_pages + truncate_pagecache try_module_get try_wait_for_completion tty_flip_buffer_push @@ -2515,6 +2606,7 @@ typec_altmode_vdm typec_get_drvdata typec_partner_register_altmode + typec_partner_set_identity typec_port_register_altmode typec_register_partner typec_register_port @@ -2568,6 +2660,8 @@ __uio_register_device uhid_hid_driver uio_unregister_device + unload_nls + unlock_buffer unlock_new_inode unlock_page unlock_rename @@ -2752,6 +2846,8 @@ __usecs_to_jiffies user_path_at_empty usleep_range + utf16s_to_utf8s + utf8s_to_utf16s v4l2_ctrl_find v4l2_ctrl_g_ctrl v4l2_ctrl_get_name @@ -2904,9 +3000,13 @@ wait_for_completion_killable wait_for_completion_timeout wait_woken + __wait_on_buffer __wake_up __wake_up_locked wake_up_process + wakeup_source_add + wakeup_source_create + wakeup_source_remove wakeup_source_register wakeup_source_unregister __warn_printk @@ -2918,6 +3018,7 @@ woken_wake_function work_busy would_dump + write_inode_now ww_mutex_lock ww_mutex_lock_interruptible ww_mutex_unlock diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 43c0cfe6d405..d2ae4d1345bc 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1918,3 +1918,103 @@ source "arch/arm64/kvm/Kconfig" if CRYPTO source "arch/arm64/crypto/Kconfig" endif + +config AF_NOISE_ELIMINATION + bool "For elimnating AF resonance noise" + default n + help + Enable it only for produts that suffering AF resonance noise. + +config AW882XX_PARAMS_FROM_BIN + bool "params is stored in bin file" + default n + help + Enable it only for params of smartPA is stored in bin file. + +config AW882XX_STEREO_SMARTPA + bool "aw dual smart pa" + default n + help + Enable it only for dual smart pa. + +config AW882XX_MAPSIZE_16K + bool "aw afe mapsize 16k" + default n + help + Enable it only for use 16k mapsize. + +config AW882XX_STEREO_SMARTPA_PRI + bool "aw dual smart pa pri i2s" + default n + help + Enable it only for dual smart pa. + +config SND_SOC_AW87XXX + tristate "SoC Audio for awinic AW87XXX Smart K PA" + depends on I2C + help + This option enables support for AW87XXX Smart K PA. + +config SND_SOC_FS1815 + tristate "SoC Audio for fs1815 Smart K PA" + depends on I2C + help + This option enables support for fs1815 K PA. + +config SND_SOC_AW8838 + bool "aw8838 for hac" + default n + help + Enable it only for hac on. + +config CAMERA_FLASH_PWM + bool "control flash" + default n + help + Enable it only for produts that control flash. + +config CAMERA_DISABLE_VSYNC_IRQ + bool "For cam disable vsync irq" + default n + help + Enable it to disable cam vsync irq. + +config CAM_SENSOR_PROBE_RETRY + bool "For sensor probe fail" + default n + help + Add retries for sensor id matching + +config CAMERA_CCI_ADDR_SWITCH + bool "For cam switch i2c address" + default n + help + Enable it to switch i2c address. + +config CAMERA_FLASH_IIC_COMPATIBLE + bool "auto select iic flash suppliers" + default n + help + Enable it, this is automatically selected flash supplers. + +config MOT_DONGWOON_OIS_AF_DRIFT + bool "dongwoon ois af drift" + default n + help + Enable it will support dongwoon ois af drift. + +config MOT_SENSOR_PRE_POWERUP + bool "For sensor power up and initialization early" + default n + help + Enable it to power up and initialise early. + +config MOT_SECURE_CAMERA + bool "For switch secure mode successfully" + default n + help + Enable it to fix switch secure mode error. + +config MOT_USB_DEBUG_LOG + bool "For usb debug log in factiry SW" + default n diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-bangkk.config b/arch/arm64/configs/vendor/ext_config/debug-holi-bangkk.config new file mode 100644 index 000000000000..95c989c10a1b --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-bangkk.config @@ -0,0 +1,28 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set + +# CONFIG_SLUB_DEBUG_ON is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-corfup.config b/arch/arm64/configs/vendor/ext_config/debug-holi-corfup.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-corfup.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-corfuq.config b/arch/arm64/configs/vendor/ext_config/debug-holi-corfuq.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-corfuq.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-corfur.config b/arch/arm64/configs/vendor/ext_config/debug-holi-corfur.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-corfur.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-cypfg.config b/arch/arm64/configs/vendor/ext_config/debug-holi-cypfg.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-cypfg.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-cypfq.config b/arch/arm64/configs/vendor/ext_config/debug-holi-cypfq.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-cypfq.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-cypfr.config b/arch/arm64/configs/vendor/ext_config/debug-holi-cypfr.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-cypfr.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-denver.config b/arch/arm64/configs/vendor/ext_config/debug-holi-denver.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-denver.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-fogo.config b/arch/arm64/configs/vendor/ext_config/debug-holi-fogo.config new file mode 100644 index 000000000000..95c989c10a1b --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-fogo.config @@ -0,0 +1,28 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set + +# CONFIG_SLUB_DEBUG_ON is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-fogor.config b/arch/arm64/configs/vendor/ext_config/debug-holi-fogor.config new file mode 100644 index 000000000000..95c989c10a1b --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-fogor.config @@ -0,0 +1,28 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set + +# CONFIG_SLUB_DEBUG_ON is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-fogos.config b/arch/arm64/configs/vendor/ext_config/debug-holi-fogos.config new file mode 100644 index 000000000000..95c989c10a1b --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-fogos.config @@ -0,0 +1,28 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set + +# CONFIG_SLUB_DEBUG_ON is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-ibiza.config b/arch/arm64/configs/vendor/ext_config/debug-holi-ibiza.config new file mode 100644 index 000000000000..c207497d403c --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-ibiza.config @@ -0,0 +1,32 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_SM_DEBUGCC_BLAIR is not set +# CONFIG_SM_DISPCC_BLAIR is not set +# CONFIG_SM_GCC_BLAIR is not set +# CONFIG_SM_GPUCC_BLAIR is not set +# CONFIG_PINCTRL_BLAIR is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_ARCH_BLAIR is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-miami.config b/arch/arm64/configs/vendor/ext_config/debug-holi-miami.config new file mode 100644 index 000000000000..2d6354f8a3a9 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-miami.config @@ -0,0 +1,31 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_CAMERA_FLASH_PWM is not set +# CONFIG_UFSTW is not set +# CONFIG_DUMP_GPIO_ON_SUSPEND is not set +# CONFIG_DUMP_CLK_ON_SUSPEND is not set +# CONFIG_DUMP_REGULATOR_ON_SUSPEND is not set + diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-milanf.config b/arch/arm64/configs/vendor/ext_config/debug-holi-milanf.config new file mode 100644 index 000000000000..3076d485f8d8 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-milanf.config @@ -0,0 +1,30 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_PANIC_ON_BUG is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-penang.config b/arch/arm64/configs/vendor/ext_config/debug-holi-penang.config new file mode 100644 index 000000000000..41a60c8502ac --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-penang.config @@ -0,0 +1,31 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set + +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_DUMP_GPIO_ON_SUSPEND is not set +# CONFIG_DUMP_CLK_ON_SUSPEND is not set +# CONFIG_DUMP_REGULATOR_ON_SUSPEND is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-penangb.config b/arch/arm64/configs/vendor/ext_config/debug-holi-penangb.config new file mode 100644 index 000000000000..95c989c10a1b --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-penangb.config @@ -0,0 +1,28 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set + +# CONFIG_SLUB_DEBUG_ON is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-pnangn.config b/arch/arm64/configs/vendor/ext_config/debug-holi-pnangn.config new file mode 100644 index 000000000000..a77035638ae1 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-pnangn.config @@ -0,0 +1,29 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_UFSTW is not set + +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_PANIC_ON_RT_THROTTLING is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-rhodec.config b/arch/arm64/configs/vendor/ext_config/debug-holi-rhodec.config new file mode 100644 index 000000000000..88417720a117 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-rhodec.config @@ -0,0 +1,29 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_PANIC_ON_BUG is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-rhodei.config b/arch/arm64/configs/vendor/ext_config/debug-holi-rhodei.config new file mode 100644 index 000000000000..13f71e03c466 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-rhodei.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi-rhodep.config b/arch/arm64/configs/vendor/ext_config/debug-holi-rhodep.config new file mode 100644 index 000000000000..f6674734aa68 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi-rhodep.config @@ -0,0 +1,27 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA_ALLOW_WRITE_DEBUGFS is not set +# CONFIG_CMA_DEBUG is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_TEST_USER_COPY is not set +CONFIG_CAMERA_FLASH_PWM=y +# CONFIG_UFSTW is not set diff --git a/arch/arm64/configs/vendor/ext_config/debug-holi.config b/arch/arm64/configs/vendor/ext_config/debug-holi.config new file mode 100644 index 000000000000..f7ee34778e71 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/debug-holi.config @@ -0,0 +1,69 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_DEBUG_FS=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_EDAC_KRYO_ARM64_PANIC_ON_CE is not set +CONFIG_SLUB_DEBUG_ON=y +# CONFIG_SLUB_DEBUG_PANIC_ON is not set +# CONFIG_CORESIGHT is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT is not set +# CONFIG_DEBUG_PANIC_ON_OOM is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_TOUCHSCREEN_FTS is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_DEVICE is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TESTING is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_REFLASH is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_RECOVERY is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_ZEROFLASH is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_DIAGNOSTICS is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C is not set +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_CTI_SAVE_DISABLE=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_DUMMY=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_TGU=y +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_DEBUG_LOCKDEP is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_STACKDEPOT is not set +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_PAGE_OWNER is not set +# CONFIG_DM_DEBUG is not set +# CONFIG_EXT4_DEBUG is not set +# CONFIG_LOCK_STAT is not set diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-bangkk.config b/arch/arm64/configs/vendor/ext_config/factory-holi-bangkk.config new file mode 100644 index 000000000000..059b41766762 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-bangkk.config @@ -0,0 +1,18 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y +CONFIG_MOT_USB_DEBUG_LOG=y +# CONFIG_MOT_DONGWOON_OIS_AF_DRIFT is not set diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-corfup.config b/arch/arm64/configs/vendor/ext_config/factory-holi-corfup.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-corfup.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-corfuq.config b/arch/arm64/configs/vendor/ext_config/factory-holi-corfuq.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-corfuq.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-corfur.config b/arch/arm64/configs/vendor/ext_config/factory-holi-corfur.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-corfur.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-cypfg.config b/arch/arm64/configs/vendor/ext_config/factory-holi-cypfg.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-cypfg.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-cypfq.config b/arch/arm64/configs/vendor/ext_config/factory-holi-cypfq.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-cypfq.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-cypfr.config b/arch/arm64/configs/vendor/ext_config/factory-holi-cypfr.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-cypfr.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-denver.config b/arch/arm64/configs/vendor/ext_config/factory-holi-denver.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-denver.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-fogo.config b/arch/arm64/configs/vendor/ext_config/factory-holi-fogo.config new file mode 100644 index 000000000000..0de1c6cc1d60 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-fogo.config @@ -0,0 +1,22 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y +# CONFIG_MOT_DONGWOON_OIS_AF_DRIFT is not set +# Enable minimal coresight configuration for modem qdss log +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_DUMMY=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-fogor.config b/arch/arm64/configs/vendor/ext_config/factory-holi-fogor.config new file mode 100644 index 000000000000..0de1c6cc1d60 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-fogor.config @@ -0,0 +1,22 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y +# CONFIG_MOT_DONGWOON_OIS_AF_DRIFT is not set +# Enable minimal coresight configuration for modem qdss log +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_DUMMY=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-fogos.config b/arch/arm64/configs/vendor/ext_config/factory-holi-fogos.config new file mode 100644 index 000000000000..0de1c6cc1d60 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-fogos.config @@ -0,0 +1,22 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y +# CONFIG_MOT_DONGWOON_OIS_AF_DRIFT is not set +# Enable minimal coresight configuration for modem qdss log +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_DUMMY=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-ibiza.config b/arch/arm64/configs/vendor/ext_config/factory-holi-ibiza.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-ibiza.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-miami.config b/arch/arm64/configs/vendor/ext_config/factory-holi-miami.config new file mode 100644 index 000000000000..1a5f2dfdc5e6 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-miami.config @@ -0,0 +1,17 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +# CONFIG_CAMERA_FLASH_PWM is not set +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-milanf.config b/arch/arm64/configs/vendor/ext_config/factory-holi-milanf.config new file mode 100644 index 000000000000..cc7be9152bdd --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-milanf.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-penang.config b/arch/arm64/configs/vendor/ext_config/factory-holi-penang.config new file mode 100644 index 000000000000..cc7be9152bdd --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-penang.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-penangb.config b/arch/arm64/configs/vendor/ext_config/factory-holi-penangb.config new file mode 100644 index 000000000000..cc7be9152bdd --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-penangb.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-pnangn.config b/arch/arm64/configs/vendor/ext_config/factory-holi-pnangn.config new file mode 100644 index 000000000000..cc7be9152bdd --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-pnangn.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-rhodec.config b/arch/arm64/configs/vendor/ext_config/factory-holi-rhodec.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-rhodec.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-rhodei.config b/arch/arm64/configs/vendor/ext_config/factory-holi-rhodei.config new file mode 100644 index 000000000000..830e8a22f610 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-rhodei.config @@ -0,0 +1,16 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# + diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi-rhodep.config b/arch/arm64/configs/vendor/ext_config/factory-holi-rhodep.config new file mode 100644 index 000000000000..ebca30acfe4d --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi-rhodep.config @@ -0,0 +1,17 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CAMERA_FLASH_PWM=y +CONFIG_CAMERA_DISABLE_VSYNC_IRQ=y diff --git a/arch/arm64/configs/vendor/ext_config/factory-holi.config b/arch/arm64/configs/vendor/ext_config/factory-holi.config new file mode 100644 index 000000000000..caf4e8c59e4e --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/factory-holi.config @@ -0,0 +1,19 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_SERIAL_MSM_GENI_CONSOLE=y +CONFIG_QCOM_RTB=y +CONFIG_QCOM_RTB_QGKI=y +CONFIG_QCOM_RTB_SEPARATE_CPUS=y diff --git a/arch/arm64/configs/vendor/ext_config/holi_consolidate.config b/arch/arm64/configs/vendor/ext_config/holi_consolidate.config new file mode 120000 index 000000000000..e6f8e868a91d --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/holi_consolidate.config @@ -0,0 +1 @@ +../holi_consolidate.config \ No newline at end of file diff --git a/arch/arm64/configs/vendor/ext_config/holi_debug.config b/arch/arm64/configs/vendor/ext_config/holi_debug.config new file mode 120000 index 000000000000..c17d67945c2c --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/holi_debug.config @@ -0,0 +1 @@ +../holi_debug.config \ No newline at end of file diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-bangkk.config b/arch/arm64/configs/vendor/ext_config/moto-holi-bangkk.config new file mode 100644 index 000000000000..e3350de19055 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-bangkk.config @@ -0,0 +1,46 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_BANGKK_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_MOT_DONGWOON_OIS_AF_DRIFT=y +CONFIG_SND_SOC_AW87XXX=m +#CONFIG_NFC_QTI_I2C=m +CONFIG_MSM_TZ_QSEE_LOG=y +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_TOUCHSCREEN_FTS is not set +CONFIG_FELICA_MOUNT_BLOCK=y +# CONFIG_UFSFEATURE is not set +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set +CONFIG_NFC=y +CONFIG_SEC_NFC_PRODUCT_N5=y +CONFIG_SEC_NFC=m +CONFIG_SEC_NFC_IF_I2C=y +CONFIG_QUP0_SE2_AP=y +CONFIG_CAMERA_FLASH_IIC_COMPATIBLE=y +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y +CONFIG_MOT_SENSOR_PRE_POWERUP=y +CONFIG_MMAP_READAROUND_LIMIT=y + diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-corfup.config b/arch/arm64/configs/vendor/ext_config/moto-holi-corfup.config new file mode 100644 index 000000000000..4a26a09a25e7 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-corfup.config @@ -0,0 +1,24 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CORFUP_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_TOUCHSCREEN_FTS is not set +# CONFIG_TOUCHSCREEN_FTS_DIRECTORY is not set +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_MICRON_UFSHID is not set diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-corfuq.config b/arch/arm64/configs/vendor/ext_config/moto-holi-corfuq.config new file mode 100644 index 000000000000..82b9d5a96b57 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-corfuq.config @@ -0,0 +1,24 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CORFUQ_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_TOUCHSCREEN_FTS is not set +# CONFIG_TOUCHSCREEN_FTS_DIRECTORY is not set +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_MICRON_UFSHID is not set diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-corfur.config b/arch/arm64/configs/vendor/ext_config/moto-holi-corfur.config new file mode 100644 index 000000000000..b556bd07998d --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-corfur.config @@ -0,0 +1,26 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CORFUR_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_TOUCHSCREEN_FTS is not set +# CONFIG_TOUCHSCREEN_FTS_DIRECTORY is not set +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_TOUCHSCREEN_NT36XXX is not set +# CONFIG_MICRON_UFSHID is not set +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-cypfg.config b/arch/arm64/configs/vendor/ext_config/moto-holi-cypfg.config new file mode 100644 index 000000000000..ecc6c0c9be42 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-cypfg.config @@ -0,0 +1,24 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CYPFG_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_UFSHID is not set +# CONFIG_UFSHID_POC is not set +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_MICRON_UFSHID is not set diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-cypfq.config b/arch/arm64/configs/vendor/ext_config/moto-holi-cypfq.config new file mode 100644 index 000000000000..e8fab4e7c8b8 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-cypfq.config @@ -0,0 +1,24 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CYPFQ_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_MICRON_UFSHID is not set +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-cypfr.config b/arch/arm64/configs/vendor/ext_config/moto-holi-cypfr.config new file mode 100644 index 000000000000..393c195abb51 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-cypfr.config @@ -0,0 +1,24 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_CYPFR_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_MICRON_UFSHID is not set +CONFIG_FELICA_MOUNT_BLOCK=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y \ No newline at end of file diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-denver.config b/arch/arm64/configs/vendor/ext_config/moto-holi-denver.config new file mode 100644 index 000000000000..65d13ff498cc --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-denver.config @@ -0,0 +1,35 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_DENVER_DTB=y +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX is not set +# CONFIG_TOUCHSCREEN_FTS is not set +# CONFIG_UFSFEATURE is not set +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set +# CONFIG_UFSTW_IGNORE_GUARANTEE_BIT is not set +# CONFIG_UFSTW_BOOT_ENABLED is not set +# CONFIG_MICRON_HPB is not set +# CONFIG_UFSHID is not set +# CONFIG_UFSHID_POC is not set +# CONFIG_SCSI_SKHID is not set +# CONFIG_MICRON_UFSHID is not set +CONFIG_HZ_300=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-fogo.config b/arch/arm64/configs/vendor/ext_config/moto-holi-fogo.config new file mode 100644 index 000000000000..0710193ab5c6 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-fogo.config @@ -0,0 +1,50 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_FOGO_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_MOT_DONGWOON_OIS_AF_DRIFT=y +CONFIG_AW882XX_STEREO_SMARTPA=y +CONFIG_AW882XX_STEREO_SMARTPA_PRI=y +CONFIG_AW882XX_PARAMS_FROM_BIN=y +CONFIG_AW882XX_MAPSIZE_16K=y +CONFIG_SND_SOC_AW8838=y +#CONFIG_NFC_QTI_I2C=m +CONFIG_MSM_TZ_QSEE_LOG=y +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_TOUCHSCREEN_FTS is not set +CONFIG_FELICA_MOUNT_BLOCK=y +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set +# CONFIG_MICRON_HPB is not set +CONFIG_NFC=y +CONFIG_SEC_NFC_PRODUCT_N5=y +CONFIG_SEC_NFC=m +CONFIG_SEC_NFC_IF_I2C=y +CONFIG_QUP0_SE2_AP=y +# CONFIG_CAMERA_FLASH_IIC_COMPATIBLE is not set +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y +CONFIG_MOT_SENSOR_PRE_POWERUP=y +CONFIG_MMAP_READAROUND_LIMIT=y +CONFIG_MOT_SECURE_CAMERA=y \ No newline at end of file diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-fogor.config b/arch/arm64/configs/vendor/ext_config/moto-holi-fogor.config new file mode 100644 index 000000000000..a5f9aec4290f --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-fogor.config @@ -0,0 +1,45 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_FOGOR_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_MOT_DONGWOON_OIS_AF_DRIFT=y +CONFIG_SND_SOC_FS1815=m +#CONFIG_NFC_QTI_I2C=m +CONFIG_MSM_TZ_QSEE_LOG=y +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_TOUCHSCREEN_FTS is not set +CONFIG_FELICA_MOUNT_BLOCK=y +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set +# CONFIG_MICRON_HPB is not set +CONFIG_NFC=y +CONFIG_SEC_NFC_PRODUCT_N5=y +CONFIG_SEC_NFC=m +CONFIG_SEC_NFC_IF_I2C=y +CONFIG_QUP0_SE2_AP=y +CONFIG_CAMERA_FLASH_IIC_COMPATIBLE=y +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y +CONFIG_MOT_SENSOR_PRE_POWERUP=y +CONFIG_MMAP_READAROUND_LIMIT=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-fogos.config b/arch/arm64/configs/vendor/ext_config/moto-holi-fogos.config new file mode 100644 index 000000000000..9eaafd22de67 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-fogos.config @@ -0,0 +1,46 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_FOGOS_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_MOT_DONGWOON_OIS_AF_DRIFT=y +CONFIG_SND_SOC_FS1815=m +#CONFIG_NFC_QTI_I2C=m +CONFIG_MSM_TZ_QSEE_LOG=y +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_TOUCHSCREEN_FTS is not set +CONFIG_FELICA_MOUNT_BLOCK=y +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set +# CONFIG_MICRON_HPB is not set +CONFIG_NFC=y +CONFIG_SEC_NFC_PRODUCT_N5=y +CONFIG_SEC_NFC=m +CONFIG_SEC_NFC_IF_I2C=y +CONFIG_QUP0_SE2_AP=y +CONFIG_CAMERA_FLASH_IIC_COMPATIBLE=y +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y +CONFIG_MMAP_READAROUND_LIMIT=y +CONFIG_MOT_SENSOR_PRE_POWERUP=y +CONFIG_MOT_SECURE_CAMERA=y \ No newline at end of file diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-ibiza-cmcc-extra.config b/arch/arm64/configs/vendor/ext_config/moto-holi-ibiza-cmcc-extra.config new file mode 100644 index 000000000000..8bef06574d05 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-ibiza-cmcc-extra.config @@ -0,0 +1,6 @@ +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_UFSHID is not set +# CONFIG_UFSHID_POC is not set +# CONFIG_SCSI_SKHID is not set +# CONFIG_MICRON_UFSHID is not set +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-ibiza.config b/arch/arm64/configs/vendor/ext_config/moto-holi-ibiza.config new file mode 100644 index 000000000000..4d32e413cb40 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-ibiza.config @@ -0,0 +1,35 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_IBIZA_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_UFSFEATURE is not set +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set +# CONFIG_UFSTW_IGNORE_GUARANTEE_BIT is not set +# CONFIG_UFSTW_BOOT_ENABLED is not set +# CONFIG_MICRON_HPB is not set +# CONFIG_UFSHID is not set +# CONFIG_UFSHID_POC is not set +# CONFIG_SCSI_SKHID is not set +# CONFIG_MICRON_UFSHID is not set +# CONFIG_TOUCHSCREEN_NT36XXX is not set +CONFIG_HZ_300=y +CONFIG_PROCESS_RECLAIM=n +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-miami.config b/arch/arm64/configs/vendor/ext_config/moto-holi-miami.config new file mode 100644 index 000000000000..e3262cd2ae6f --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-miami.config @@ -0,0 +1,31 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_MIAMI_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +CONFIG_AW882XX_STEREO_SMARTPA=y +CONFIG_AW882XX_PARAMS_FROM_BIN=y +CONFIG_AW882XX_MAPSIZE_16K=y +# CONFIG_UFSTW is not set +CONFIG_QUP0_SE2_AP=y +# CONFIG_CAMERA_FLASH_PWM is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_CAMERA_CCI_ADDR_SWITCH=y +CONFIG_IS_XFER_IN_PROGRESS=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-milanf.config b/arch/arm64/configs/vendor/ext_config/moto-holi-milanf.config new file mode 100644 index 000000000000..1b765d98d027 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-milanf.config @@ -0,0 +1,25 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_MILANF_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_AW882XX_PARAMS_FROM_BIN is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-penang.config b/arch/arm64/configs/vendor/ext_config/moto-holi-penang.config new file mode 100644 index 000000000000..9059e025d4c2 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-penang.config @@ -0,0 +1,36 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_PENANG_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_SND_SOC_AW87XXX=m +CONFIG_NFC_QTI_I2C=m +CONFIG_MSM_TZ_QSEE_LOG=y +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_TOUCHSCREEN_FTS is not set +CONFIG_FELICA_MOUNT_BLOCK=y +# CONFIG_UFSFEATURE is not set +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-penangb.config b/arch/arm64/configs/vendor/ext_config/moto-holi-penangb.config new file mode 100644 index 000000000000..15e3b9f5dbfc --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-penangb.config @@ -0,0 +1,34 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_PENANGB_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_SND_SOC_AW87XXX=m +CONFIG_NFC_QTI_I2C=m +CONFIG_MSM_TZ_QSEE_LOG=y +CONFIG_AF_NOISE_ELIMINATION=y +# CONFIG_TOUCHSCREEN_FTS is not set +CONFIG_FELICA_MOUNT_BLOCK=y +# CONFIG_UFSFEATURE is not set +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-pnangn.config b/arch/arm64/configs/vendor/ext_config/moto-holi-pnangn.config new file mode 100644 index 000000000000..c13ef95eb93d --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-pnangn.config @@ -0,0 +1,37 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_PNANGN_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX is not set +# CONFIG_TOUCHSCREEN_FTS is not set +# CONFIG_TOUCHSCREEN_FTS_DIRECTORY is not set +# CONFIG_TOUCHSCREEN_NT36XXX is not set +# CONFIG_UFSTW is not set +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +# CONFIG_SND_SOC_AW87XXX is not set +CONFIG_AW882XX_STEREO_SMARTPA=y +CONFIG_AW882XX_PARAMS_FROM_BIN=y +CONFIG_AW882XX_MAPSIZE_16K=y +# CONFIG_UFSFEATURE is not set +# CONFIG_UFSHPB is not set +# CONFIG_UFSTW is not set +# CONFIG_SCSI_SKHPB is not set diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-rhodec.config b/arch/arm64/configs/vendor/ext_config/moto-holi-rhodec.config new file mode 100644 index 000000000000..b47d0ac19b55 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-rhodec.config @@ -0,0 +1,27 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_RHODEC_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +CONFIG_AF_NOISE_ELIMINATION=y +CONFIG_AW882XX_STEREO_SMARTPA=y +CONFIG_AW882XX_PARAMS_FROM_BIN=y +CONFIG_AW882XX_MAPSIZE_16K=y +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-rhodei.config b/arch/arm64/configs/vendor/ext_config/moto-holi-rhodei.config new file mode 100644 index 000000000000..4313d73778d7 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-rhodei.config @@ -0,0 +1,29 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_RHODEI_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +CONFIG_AF_NOISE_ELIMINATION=y +CONFIG_AW882XX_STEREO_SMARTPA=y +CONFIG_AW882XX_PARAMS_FROM_BIN=y +CONFIG_AW882XX_MAPSIZE_16K=y +# CONFIG_TOUCHSCREEN_NT36XXX is not set +CONFIG_QUP0_SE2_AP=y +CONFIG_CAM_SENSOR_PROBE_RETRY=y +CONFIG_CRYPTO_CHACHA20POLY1305=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi-rhodep.config b/arch/arm64/configs/vendor/ext_config/moto-holi-rhodep.config new file mode 100644 index 000000000000..8ad3bb171e47 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi-rhodep.config @@ -0,0 +1,30 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_RHODEP_DTB=y +# CONFIG_NFC_QTI_I2C is not set +CONFIG_FS_EPOLL_WAKEUP_DEBUG=y +CONFIG_ZRAM_WRITEBACK=y +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_REGULATOR_QPNP_LCDB is not set +CONFIG_AW882XX_STEREO_SMARTPA=y +CONFIG_AW882XX_PARAMS_FROM_BIN=y +CONFIG_AW882XX_MAPSIZE_16K=y +# CONFIG_MICRON_UFSHID is not set +CONFIG_QUP0_SE2_AP=y +CONFIG_CAMERA_FLASH_PWM=y +CONFIG_CRYPTO_CHACHA20POLY1305=y +# CONFIG_UFSTW is not set +CONFIG_CAM_SENSOR_PROBE_RETRY=y diff --git a/arch/arm64/configs/vendor/ext_config/moto-holi.config b/arch/arm64/configs/vendor/ext_config/moto-holi.config new file mode 100644 index 000000000000..8c7923319548 --- /dev/null +++ b/arch/arm64/configs/vendor/ext_config/moto-holi.config @@ -0,0 +1,41 @@ +# +# Please avoid changing the Qualcomm defconfig files and use this file to +# add any Motorola-specific configuration changes that are applicable to +# userdebug, eng, and user builds. +# +# After building, a list of all of the config files that are used can be found +# at the top of: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/mapphone_defconfig +# +# The actual output of the kernel configuration system can be found at: +# out/target/product/$(PRODUCT)/obj/KERNEL_OBJ/.config +# +# You can check the .config file to verify that your configuration option is +# being set as expected. +# +CONFIG_LOCALVERSION="-moto" +CONFIG_MMI_DEVICE_DTBS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_PSTORE_PMSG=y +CONFIG_SERIAL_MSM_GENI_CONSOLE=y +# CONFIG_SMB1398_CHARGER is not set +# CONFIG_SMB1355_SLAVE_CHARGER is not set +CONFIG_SLUB_DEBUG=y +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_DEBUG_PANIC_ON is not set +CONFIG_BUG_ON_DATA_CORRUPTION=y +CONFIG_SCHED_STACK_END_CHECK=y + +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=y +# CONFIG_CORESIGHT_LINK_AND_SINK_TMC is not set +# CONFIG_CORESIGHT_SOURCE_ETM4X is not set +# CONFIG_CORESIGHT_CTI is not set +# CONFIG_CORESIGHT_TPDA is not set +# CONFIG_CORESIGHT_TPDM is not set +# CONFIG_CORESIGHT_HWEVENT is not set +# CONFIG_CORESIGHT_DUMMY is not set +# CONFIG_CORESIGHT_REMOTE_ETM is not set +# CONFIG_CORESIGHT_TGU is not set diff --git a/arch/arm64/configs/vendor/holi_QGKI.config b/arch/arm64/configs/vendor/holi_QGKI.config index c33b461c436c..0625eae6e6bc 100644 --- a/arch/arm64/configs/vendor/holi_QGKI.config +++ b/arch/arm64/configs/vendor/holi_QGKI.config @@ -20,6 +20,7 @@ CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_QCOM_LLCC_PERFMON=m CONFIG_SCHED_WALT=y CONFIG_QGKI=y +CONFIG_ZRAM=m CONFIG_REGULATOR_QTI_DEBUG=y CONFIG_REGMAP_QTI_DEBUG=y CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y @@ -199,3 +200,15 @@ CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_QTI_CX_IPEAK_COOLING_DEVICE=y CONFIG_TLB_CONF_HANDLER=y CONFIG_QTI_DEVFREQ_VDD_COOLING_DEVICE=y +CONFIG_UFSFEATURE=y +CONFIG_UFSHPB=y +CONFIG_UFSTW=y +CONFIG_UFSTW_IGNORE_GUARANTEE_BIT=y +CONFIG_UFSTW_BOOT_ENABLED=y +CONFIG_SCSI_SKHPB=y +CONFIG_MICRON_HPB=y +CONFIG_UFSHID=y +CONFIG_UFSHID_POC=y +CONFIG_SCSI_SKHID=y +# CONFIG_DM_VERITY_AVB is not set +CONFIG_MICRON_UFSHID=y diff --git a/arch/arm64/configs/vendor/holi_consolidate.config b/arch/arm64/configs/vendor/holi_consolidate.config new file mode 100644 index 000000000000..b373865fe005 --- /dev/null +++ b/arch/arm64/configs/vendor/holi_consolidate.config @@ -0,0 +1,51 @@ +CONFIG_LOCALVERSION="-qgki-consolidate" +CONFIG_IOMMU_TLBSYNC_DEBUG=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_TESTS=y +CONFIG_IO_PGTABLE_PAGE_ACCOUNTING=y +CONFIG_CMA_DEBUGFS=y +CONFIG_CMA_DEBUG=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y +CONFIG_PAGE_OWNER=y +CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y +CONFIG_PAGE_EXTENSION_PAGE_FREE=y +CONFIG_PSI_FTRACE=y +# CONFIG_DEBUG_DMA_BUF_REF is not set +CONFIG_EDAC_KRYO_ARM64_PANIC_ON_CE=y +CONFIG_QMP_DEBUGFS_CLIENT=y +CONFIG_ATOMIC64_SELFTEST=m +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_TEST_USER_COPY=m +CONFIG_OF_RESERVED_MEM_CHECK=y +CONFIG_BUG_ON_HW_MEM_ONLINE_FAIL=y +CONFIG_PANIC_ON_SCHED_BUG=y +CONFIG_PANIC_ON_RT_THROTTLING=y +CONFIG_LKDTM=m +CONFIG_RUNTIME_TESTING_MENU=y +CONFIG_PREEMPTIRQ_EVENTS=y +CONFIG_SERIAL_MSM_GENI_CONSOLE=y +CONFIG_QCOM_RTB=y +CONFIG_QCOM_RTB_SEPARATE_CPUS=y +CONFIG_QCOM_RTB_QGKI=y +#CONFIG_DEBUG_CONSOLE_UNHASHED_POINTERS is not set +CONFIG_COMMON_CLK_QCOM_DEBUG=y +CONFIG_CNSS2_DEBUG=y +CONFIG_NL80211_TESTMODE=y +CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE=y +CONFIG_LOCK_TORTURE_TEST=m +CONFIG_RCU_TORTURE_TEST=m +# CONFIG_CDNS_I3C_MASTER is not set +# CONFIG_DW_I3C_MASTER is not set +CONFIG_CORESIGHT_SOURCE_ETM4X=y +# CONFIG_CORESIGHT_CTI_SAVE_DISABLE is not set +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_MHI_DEBUG=y +CONFIG_SM_DEBUGCC_HOLI=y +CONFIG_DEBUG_PANIC_ON_OOM=y +CONFIG_USB_F_FS_IPC_LOGGING=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG=y +# CONFIG_HH_DISABLE_UART is not set +CONFIG_DEBUG_MODULE_LOAD_INFO=y diff --git a/arch/arm64/configs/vendor/holi_debug.config b/arch/arm64/configs/vendor/holi_debug.config index 74a3f1b4b1d5..fe2e00c5079e 100644 --- a/arch/arm64/configs/vendor/holi_debug.config +++ b/arch/arm64/configs/vendor/holi_debug.config @@ -29,7 +29,7 @@ CONFIG_QCOM_RTB_QGKI=y CONFIG_IPA_DEBUG=y CONFIG_IPA_UT=y CONFIG_MSM_ADSPRPC=y -CONFIG_DEBUG_CONSOLE_UNHASHED_POINTERS=y +#CONFIG_DEBUG_CONSOLE_UNHASHED_POINTERS is not set CONFIG_COMMON_CLK_QCOM_DEBUG=y CONFIG_I2C_MSM_GENI=y CONFIG_SPI_MSM_GENI=y @@ -140,3 +140,6 @@ CONFIG_MMC_IPC_LOGGING=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_DEBUG_PAGEALLOC=y CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y +CONFIG_DUMP_GPIO_ON_SUSPEND=y +CONFIG_DUMP_CLK_ON_SUSPEND=y +CONFIG_DUMP_REGULATOR_ON_SUSPEND=y diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 7a6c74313727..608cd880700b 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -406,6 +406,9 @@ enum binder_prio_state { * (invariant after initialized) * @tsk task_struct for group_leader of process * (invariant after initialized) + * @files files_struct for process + * (protected by @files_lock) + * @files_lock mutex to protect @files * @deferred_work_node: element for binder_deferred_list * (protected by binder_deferred_lock) * @deferred_work: bitmap of deferred work to perform @@ -468,6 +471,8 @@ struct binder_proc { struct list_head waiting_threads; int pid; struct task_struct *tsk; + struct files_struct *files; + struct mutex files_lock; struct hlist_node deferred_work_node; int deferred_work; int outstanding_txns; diff --git a/drivers/android/vendor_hooks.c b/drivers/android/vendor_hooks.c index 0830f821d49b..0b4a3ae830f3 100644 --- a/drivers/android/vendor_hooks.c +++ b/drivers/android/vendor_hooks.c @@ -17,11 +17,13 @@ #include #include #include +#include #include #include #include #include #include +#include /* * Export tracepoints that act as a bare tracehook (ie: have no trace event @@ -71,5 +73,14 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_sysrq_crash); EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_find_busiest_group); EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_map_util_freq); EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_em_pd_energy); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_rmqueue); EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_cgroup_set_task); EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_syscall_prctl_finished); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_tune_scan_type); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_tune_swappiness); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_tune_inactive_ratio); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mem_cgroup_id_remove); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mem_cgroup_css_offline); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mem_cgroup_css_online); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mem_cgroup_free); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mem_cgroup_alloc); diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 656fadddb35b..b62640bffaa7 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1636,6 +1637,27 @@ static void zram_slot_free_notify(struct block_device *bdev, zram_slot_unlock(zram, index); } +/* Moto huangzq2: check sync_io state on swap entry, + * return 0 on wb page, else return 1. + */ +#ifdef CONFIG_ZRAM_WRITEBACK +static int zram_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long index) +{ + struct zram *zram; + int has_sync_io = 1; + + if (cmd != SWP_SYNCHRONOUS_IO) return -EINVAL; + + zram = bdev->bd_disk->private_data; + zram_slot_lock(zram, index); + has_sync_io = zram_test_flag(zram, index, ZRAM_WB) ? 0 : 1; + zram_slot_unlock(zram, index); + + return has_sync_io; +} +#endif + static int zram_rw_page(struct block_device *bdev, sector_t sector, struct page *page, unsigned int op) { @@ -1828,6 +1850,9 @@ static int zram_open(struct block_device *bdev, fmode_t mode) static const struct block_device_operations zram_devops = { .open = zram_open, .swap_slot_free_notify = zram_slot_free_notify, +#ifdef CONFIG_ZRAM_WRITEBACK + .ioctl = zram_ioctl, +#endif .rw_page = zram_rw_page, .owner = THIS_MODULE }; diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c index 46202a5ef1c0..3ada61a65f9c 100644 --- a/drivers/bluetooth/btfm_slim.c +++ b/drivers/bluetooth/btfm_slim.c @@ -145,7 +145,7 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, if (!btfmslim || !ch) return -EINVAL; - BTFMSLIM_DBG("port: %d ch: %d", ch->port, ch->ch); + BTFMSLIM_INFO("port: %d ch: %d", ch->port, ch->ch); /* Define the channel with below parameters */ prop.prot = ((rates == 44100) || (rates == 88200)) ? diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0678dad58262..8778e376c873 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3489,7 +3489,7 @@ static const struct file_operations clk_enabled_list_fops = { .release = seq_release, }; -static u32 debug_suspend; +static u32 debug_suspend = IS_ENABLED(CONFIG_DUMP_CLK_ON_SUSPEND); /* * Print the names of all enabled clocks and their parents if diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 50564ec5ef3f..5a9c715868bb 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -123,6 +123,10 @@ static void cluster_prepare(struct lpm_cluster *cluster, static bool sleep_disabled; module_param_named(sleep_disabled, sleep_disabled, bool, 0664); +#ifdef CONFIG_DEBUG_FS +extern void gpio_debug_print_enabled(void); +#endif + #ifdef CONFIG_SMP static int lpm_cpu_qos_notify(struct notifier_block *nb, unsigned long val, void *ptr); @@ -1119,6 +1123,9 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, if (!from_idle) { clock_debug_print_enabled(); regulator_debug_print_enabled(); +#ifdef CONFIG_DEBUG_FS + gpio_debug_print_enabled(); +#endif } cpu = get_next_online_cpu(from_idle); diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d60b7ced21cb..9b774277b75e 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -433,6 +433,11 @@ static long dma_buf_ioctl(struct file *file, ret = dma_buf_begin_cpu_access(dmabuf, direction); return ret; + #ifdef CONFIG_DEBUG_FS + case DMA_BUF_IOCTL_IMPORT_BUF_ADD_BY_MOTO: + dmabuf->ops->import_buf_add_by_moto(dmabuf); + return 0; + #endif case DMA_BUF_SET_NAME_A: case DMA_BUF_SET_NAME_B: diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index 9a4c4f4f47fa..b24d680cd618 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -1,8 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only config QTI_TZ_LOG tristate "QTI Trust Zone (TZ) Log Driver" - depends on DEBUG_FS help This option enables a driver with a debugfs interface for messages produced by the Secure code (Trust zone). These messages provide diagnostic information about TZ operation. +config MSM_TZ_QSEE_LOG + bool "enable print QSEE log to kernel log" + depends on QTI_TZ_LOG + help + Set to enable print MSM Trust Zone (TZ) QSEE log to kernel log \ No newline at end of file diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c index c14d513a6708..f98e09cb0d5b 100644 --- a/drivers/firmware/qcom/tz_log.c +++ b/drivers/firmware/qcom/tz_log.c @@ -19,6 +19,15 @@ #include #include #include +#ifdef CONFIG_MSM_TZ_QSEE_LOG +#include +#endif + +#ifndef CONFIG_DEBUG_FS +#include +static struct proc_dir_entry *tzdbg_root; +#define TZDBG_ROOT_DIR "tzdbg" +#endif /* QSEE_LOG_BUF_SIZE = 32K */ #define QSEE_LOG_BUF_SIZE 0x8000 @@ -382,7 +391,16 @@ struct tzdbg_stat { char *name; char *data; }; - +//print qsee log to kernel log +#ifdef CONFIG_MSM_TZ_QSEE_LOG +#ifdef CONFIG_DEBUG_FS +static bool enable_printk_qseelog = false; +#else +static bool enable_printk_qseelog = false; +#endif +#define PRINTK_LIMIT 1024 +static char *b_str = NULL; +#endif struct tzdbg { void __iomem *virt_iobase; void __iomem *hyp_virt_iobase; @@ -396,6 +414,10 @@ struct tzdbg { uint32_t tz_version; bool is_encrypted_log_enabled; bool is_enlarged_buf; +#ifdef CONFIG_MSM_TZ_QSEE_LOG + struct workqueue_struct *heartbeat_work_q; + struct work_struct heartbeat_work; +#endif }; struct tzbsp_encr_log_t { @@ -1086,7 +1108,11 @@ static ssize_t tzdbgfs_read_unencrypted(struct file *file, char __user *buf, size_t count, loff_t *offp) { int len = 0; +#ifdef CONFIG_DEBUG_FS int tz_id = *(int *)(file->private_data); +#else + int tz_id = *(int *)(long)PDE_DATA(file_inode(file)); +#endif if (tz_id == TZDBG_BOOT || tz_id == TZDBG_RESET || tz_id == TZDBG_INTERRUPT || tz_id == TZDBG_GENERAL || @@ -1150,7 +1176,11 @@ static ssize_t tzdbgfs_read_encrypted(struct file *file, char __user *buf, size_t count, loff_t *offp) { int len = 0, ret = 0; +#ifdef CONFIG_DEBUG_FS int tz_id = *(int *)(file->private_data); +#else + int tz_id = *(int *)(long)PDE_DATA(file_inode(file)); +#endif struct tzdbg_stat *stat = &(tzdbg.stat[tz_id]); pr_debug("%s: tz_id = %d\n", __func__, tz_id); @@ -1190,7 +1220,11 @@ static ssize_t tzdbgfs_read_encrypted(struct file *file, char __user *buf, static ssize_t tzdbgfs_read(struct file *file, char __user *buf, size_t count, loff_t *offp) { +#ifdef CONFIG_DEBUG_FS int tz_id = *(int *)(file->private_data); +#else + int tz_id = *(int *)(long)PDE_DATA(file_inode(file)); +#endif if (!tzdbg.is_encrypted_log_enabled || (tz_id == TZDBG_HYP_GENERAL || tz_id == TZDBG_HYP_LOG)) @@ -1348,6 +1382,7 @@ static void tzdbg_free_encrypted_log_buf(struct platform_device *pdev) static int tzdbgfs_init(struct platform_device *pdev) { +#ifdef CONFIG_DEBUG_FS int rc = 0; int i; struct dentry *dent_dir; @@ -1376,13 +1411,47 @@ err: debugfs_remove_recursive(dent_dir); return rc; +#else + int rc = 0; + int i; + struct proc_dir_entry *proc_d_entry = NULL; + + tzdbg_root = proc_mkdir(TZDBG_ROOT_DIR, NULL); + if (NULL == tzdbg_root) { + dev_err(&pdev->dev, "Created dir /proc/%s error!\n", TZDBG_ROOT_DIR); + return -1; + } + + dev_info(&pdev->dev, "Created dir /proc/%s \n", TZDBG_ROOT_DIR); + + for (i = 0; i < TZDBG_STATS_MAX; i++) { + tzdbg.debug_tz[i] = i; + proc_d_entry = proc_create_data(tzdbg.stat[i].name, 0444, tzdbg_root, &tzdbg_fops, &tzdbg.debug_tz[i]); + if (proc_d_entry == NULL) { + dev_err(&pdev->dev, "TZ proc_create_data %s failed\n", tzdbg.stat[i].name); + rc = -ENOMEM; + goto err; + } + } + + platform_set_drvdata(pdev, tzdbg_root); + + return 0; +err: + remove_proc_subtree(TZDBG_ROOT_DIR, NULL); + return rc; +#endif } static void tzdbgfs_exit(struct platform_device *pdev) { +#ifdef CONFIG_DEBUG_FS struct dentry *dent_dir; dent_dir = platform_get_drvdata(pdev); debugfs_remove_recursive(dent_dir); +#else + remove_proc_subtree(TZDBG_ROOT_DIR, NULL); +#endif } static int __update_hypdbg_base(struct platform_device *pdev, @@ -1479,6 +1548,263 @@ static void tzdbg_query_encrypted_log(void) } } +#ifdef CONFIG_MSM_TZ_QSEE_LOG +static int splitline_printk(const char *str, int len) +{ + int cnt = 0; + char *s = "\n"; + char *b_str_tmp = NULL; + char *buf = NULL; + + if (b_str == NULL && debug_rw_buf_size >0 ) { + b_str = (char *)vmalloc(debug_rw_buf_size +1); + } + if(len> debug_rw_buf_size) { + pr_err("[TEE-AP]:log is more than max len. trancate \n"); + len = debug_rw_buf_size; + } + b_str_tmp = b_str; + memcpy(b_str_tmp, str, len); + b_str_tmp[len] = 0; + + + buf = strstr(b_str_tmp, s); + + while (buf != NULL) { + cnt++; + buf[0] = '\0'; + pr_err("[TEE]: %s \n",b_str_tmp); + b_str_tmp = buf + strlen(s); + buf = strstr(b_str_tmp, s); + } + + return 0; +} + +static int _print_qsee_log_stats(struct tzdbg_log_t *log, + struct tzdbg_log_pos_t *log_start, uint32_t log_len + ) +{ + uint32_t wrap_start; + uint32_t wrap_end; + uint32_t wrap_cnt; + int max_len; + int len = 0; + int i = 0; + + wrap_start = log_start->wrap; + wrap_end = log->log_pos.wrap; + + /* Calculate difference in # of buffer wrap-arounds */ + if (wrap_end >= wrap_start) { + wrap_cnt = wrap_end - wrap_start; + } else { + /* wrap counter has wrapped around, invalidate start position */ + wrap_cnt = 2; + } + + if (wrap_cnt > 1) { + /* end position has wrapped around more than once, */ + /* current start no longer valid */ + log_start->wrap = log->log_pos.wrap - 1; + log_start->offset = (log->log_pos.offset + 1) % log_len; + } else if ((wrap_cnt == 1) && + (log->log_pos.offset > log_start->offset)) { + /* end position has overwritten start */ + log_start->offset = (log->log_pos.offset + 1) % log_len; + } + while (log_start->offset == log->log_pos.offset) { + /* + * No data in ring buffer, + * so we'll hang around until something happens + */ + unsigned long t = msleep_interruptible(50); + + if (t != 0) { + /* Some event woke us up, so let's quit */ + return 0; + } + } + + max_len = debug_rw_buf_size; + + /* + * Read from ring buff while there is data and space in return buff + */ + while ((log_start->offset != log->log_pos.offset) && (len < max_len)) { + tzdbg.disp_buf[i++] = log->log_buf[log_start->offset]; + log_start->offset = (log_start->offset + 1) % log_len; + if (log_start->offset == 0) + ++log_start->wrap; + ++len; + } + + /* + * return buffer to caller + */ + tzdbg.stat[TZDBG_QSEE_LOG].data = tzdbg.disp_buf; + /* + * print the display buffer + */ + splitline_printk(tzdbg.stat[TZDBG_QSEE_LOG].data,len); + return len; +} + +static int _print_qsee_log_v2_stats(struct tzdbg_log_v2_t *log, + struct tzdbg_log_pos_v2_t *log_start, uint32_t log_len) +{ + uint32_t wrap_start; + uint32_t wrap_end; + uint32_t wrap_cnt; + int max_len; + int len = 0; + int i = 0; + + wrap_start = log_start->wrap; + wrap_end = log->log_pos.wrap; + + /* Calculate difference in # of buffer wrap-arounds */ + if (wrap_end >= wrap_start) + wrap_cnt = wrap_end - wrap_start; + else { + /* wrap counter has wrapped around, invalidate start position */ + wrap_cnt = 2; +} + + if (wrap_cnt > 1) { + /* end position has wrapped around more than once, */ + /* current start no longer valid */ + log_start->wrap = log->log_pos.wrap - 1; + log_start->offset = (log->log_pos.offset + 1) % log_len; + } else if ((wrap_cnt == 1) && + (log->log_pos.offset > log_start->offset)) { + /* end position has overwritten start */ + log_start->offset = (log->log_pos.offset + 1) % log_len; + } + pr_debug("diag_buf wrap = %u, offset = %u\n", + log->log_pos.wrap, log->log_pos.offset); + + while (log_start->offset == log->log_pos.offset) { + /* + * No data in ring buffer, + * so we'll hang around until something happens + */ + unsigned long t = msleep_interruptible(50); + + if (t != 0) { + /* Some event woke us up, so let's quit */ + return 0; + } + } + + max_len = debug_rw_buf_size; + + pr_debug("diag_buf wrap = %u, offset = %u\n", + log->log_pos.wrap, log->log_pos.offset); + + /* + * Read from ring buff while there is data and space in return buff + */ + while ((log_start->offset != log->log_pos.offset) && (len < max_len)) { + tzdbg.disp_buf[i++] = log->log_buf[log_start->offset]; + log_start->offset = (log_start->offset + 1) % log_len; + if (log_start->offset == 0) + ++log_start->wrap; + ++len; + } + + /* + * return buffer to caller + */ + tzdbg.stat[TZDBG_QSEE_LOG].data = tzdbg.disp_buf; + /* + * print the display buffer + */ + splitline_printk(tzdbg.stat[TZDBG_QSEE_LOG].data,len); + return len; +} +static _print_encrpted_qsee_log_stats(struct encrypted_log_info *enc_log_info) +{ + int ret = 0, len = 0; + struct tzbsp_encr_log_t *encr_log_head; + uint32_t size = 0; + + ret = qcom_scm_request_encrypted_log(enc_log_info->paddr, + enc_log_info->size, ENCRYPTED_QSEE_LOG_ID); + if (ret) + return 0; + encr_log_head = (struct tzbsp_encr_log_t *)(enc_log_info->vaddr); + pr_debug("display_buf_size = %d, encr_log_buff_size = %d\n", + display_buf_size, encr_log_head->encr_log_buff_size); + size = encr_log_head->encr_log_buff_size; + + len += scnprintf(tzdbg.disp_buf + len, + (display_buf_size - 1) - len, + "\n-------- New Encrypted QSEE Log --------\n"); + + len += scnprintf(tzdbg.disp_buf + len, + (display_buf_size - 1) - len, + "\nMagic_Num :\n0x%x\n" + "\nVerion :\n%d\n" + "\nEncr_Log_Buff_Size :\n%d\n" + "\nWrap_Count :\n%d\n", + encr_log_head->magic_num, + encr_log_head->version, + encr_log_head->encr_log_buff_size, + encr_log_head->wrap_count); + + len += print_text("\nKey : ", encr_log_head->key, + TZBSP_AES_256_ENCRYPTED_KEY_SIZE, + tzdbg.disp_buf + len, display_buf_size); + len += print_text("\nNonce : ", encr_log_head->nonce, + TZBSP_NONCE_LEN, + tzdbg.disp_buf + len, display_buf_size - len); + len += print_text("\nTag : ", encr_log_head->tag, + TZBSP_TAG_LEN, + tzdbg.disp_buf + len, display_buf_size - len); + + if (len > display_buf_size - size) + pr_warn("Cannot fit all info into the buffer\n"); + + pr_debug("encrypted log size %d, disply buffer size %d, used len %d\n", + size, display_buf_size, len); + + len += print_text("\nLog : ", encr_log_head->log_buf, size, + tzdbg.disp_buf + len, display_buf_size - len); + memset(enc_log_info->vaddr, 0, enc_log_info->size); + tzdbg.stat[TZDBG_QSEE_LOG].data = tzdbg.disp_buf; + /* + * print the display buffer + */ + splitline_printk(tzdbg.stat[TZDBG_QSEE_LOG].data,len); + return len; +} + +static void tzdbg_heartbeat_work(struct work_struct *work) +{ + static struct tzdbg_log_pos_t log_start = {0}; + static struct tzdbg_log_pos_v2_t log_start_v2 = {0}; + if(enable_printk_qseelog) { + pr_debug("[TEE] qsee log -------------begin---\n"); + if (!tzdbg.is_encrypted_log_enabled) { + if (!tzdbg.is_enlarged_buf) + _print_qsee_log_stats(g_qsee_log, &log_start, + QSEE_LOG_BUF_SIZE - sizeof(struct tzdbg_log_pos_t)); + else + _print_qsee_log_v2_stats(g_qsee_log_v2, &log_start_v2, + QSEE_LOG_BUF_SIZE_V2 - sizeof(struct tzdbg_log_pos_v2_t)); + }else{ + _print_encrpted_qsee_log_stats(&enc_qseelog_info); + } + + pr_debug("[TEE] qsee log -------------end---\n"); + + queue_work(tzdbg.heartbeat_work_q , + &tzdbg.heartbeat_work + ); + } +} +#endif /* * Driver functions */ @@ -1596,6 +1922,15 @@ static int tz_log_probe(struct platform_device *pdev) if (tzdbgfs_init(pdev)) goto exit_free_disp_buf; +#ifdef CONFIG_MSM_TZ_QSEE_LOG + if(enable_printk_qseelog) { + tzdbg.heartbeat_work_q = create_singlethread_workqueue("tz_heartbeat"); + INIT_WORK(&tzdbg.heartbeat_work, + tzdbg_heartbeat_work); + queue_work(tzdbg.heartbeat_work_q, &tzdbg.heartbeat_work); + dev_err(&pdev->dev, "%s: enable_printk_qseelog is set, is_enlarged=%d is_encrypted=%d\n", __func__,tzdbg.is_enlarged_buf,tzdbg.is_encrypted_log_enabled); + } +#endif return 0; exit_free_disp_buf: @@ -1613,6 +1948,12 @@ exit_free_diag_buf: static int tz_log_remove(struct platform_device *pdev) { +#ifdef CONFIG_MSM_TZ_QSEE_LOG + if(enable_printk_qseelog) { + enable_printk_qseelog = false; + flush_work(&tzdbg.heartbeat_work); + } +#endif tzdbgfs_exit(pdev); dma_free_coherent(&pdev->dev, display_buf_size, (void *)tzdbg.disp_buf, disp_buf_paddr); @@ -1639,6 +1980,14 @@ static struct platform_driver tz_log_driver = { }; module_platform_driver(tz_log_driver); +#ifdef CONFIG_MSM_TZ_QSEE_LOG +static int __init setup_enable_printk_qseelog(char *buf) +{ + enable_printk_qseelog = true; + return 0; +} +early_param("printk_qseelog", setup_enable_printk_qseelog); +#endif MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("TZ Log driver"); diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c index 66af8140ef2d..46a64458e51b 100644 --- a/drivers/firmware/qcom_scm-smc.c +++ b/drivers/firmware/qcom_scm-smc.c @@ -28,6 +28,10 @@ #define MAX_QCOM_SCM_ARGS 10 #define MAX_QCOM_SCM_RETS 3 +#define SCM_SVC_MOBICORE 250 +#define SCM_CMD_MOBICORE 1 +#define ARM_SMCCC_OWNER_MOBI_OS 51 + #define QCOM_SCM_ATOMIC BIT(0) #define QCOM_SCM_NORETRY BIT(1) @@ -2388,6 +2392,70 @@ int __qcom_scm_invoke_smc(struct device *dev, phys_addr_t in_buf, return ret; } +int __trustonic_smc_fastcall(void *fc_generic, size_t size) +{ + int ret = 0; + + struct qcom_scm_desc desc = { + .svc = SCM_SVC_MOBICORE, + .cmd = SCM_CMD_MOBICORE, + .owner = ARM_SMCCC_OWNER_MOBI_OS + }; + + void *scm_buf_va = NULL; + phys_addr_t scm_buf_pa = 0; + struct qtee_shm scm_shm = {0}; + + if (qtee_shmbridge_is_enabled()) { + + ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(size), &scm_shm); + if (ret) + return ret; + } + else { + scm_shm.vaddr = kzalloc(PAGE_ALIGN(size), GFP_KERNEL); + if (!scm_shm.vaddr) + return -ENOMEM; + + scm_shm.paddr = dma_map_single(NULL, scm_shm.vaddr, size, DMA_TO_DEVICE); + if (dma_mapping_error(NULL, scm_shm.paddr)) { + kfree(scm_shm.vaddr); + return -ENOMEM; + } + } + + scm_buf_va = scm_shm.vaddr; + scm_buf_pa = scm_shm.paddr; + + if (!scm_buf_va) + return -ENOMEM; + + memset(scm_buf_va, 0, size); + memcpy(scm_buf_va, fc_generic, size); + qtee_shmbridge_flush_shm_buf(&scm_shm); + + desc.args[0] = scm_buf_pa; + desc.args[1] = (u32)size; + desc.args[2] = scm_buf_pa; + desc.args[3] = (u32)size; + desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL); + + ret = qcom_scm_call(NULL, &desc); + qtee_shmbridge_inv_shm_buf(&scm_shm); + memset(fc_generic, 0x00, size); + memcpy(fc_generic, scm_buf_va, size); + + if (qtee_shmbridge_is_enabled()) { + qtee_shmbridge_free_shm(&scm_shm); + } + else { + dma_unmap_single(NULL, scm_shm.paddr, size, DMA_TO_DEVICE); + kfree(scm_shm.vaddr); + } + + return ret; +} + int __qcom_scm_invoke_callback_response(struct device *dev, phys_addr_t out_buf, size_t out_buf_size, int32_t *result, u64 *response_type, unsigned int *data) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 96b71bc4cd99..a99547e709b7 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -1084,6 +1084,12 @@ int qcom_scm_invoke_smc(phys_addr_t in_buf, size_t in_buf_size, } EXPORT_SYMBOL(qcom_scm_invoke_smc); +int trustonic_smc_fastcall(void *fc_generic, size_t size) +{ + return __trustonic_smc_fastcall(fc_generic, size); +} +EXPORT_SYMBOL(trustonic_smc_fastcall); + int qcom_scm_invoke_callback_response(phys_addr_t out_buf, size_t out_buf_size, int32_t *result, u64 *response_type, unsigned int *data) diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 445790592780..e784405794a5 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -297,6 +297,7 @@ extern int __qcom_scm_invoke_smc_legacy(struct device *dev, phys_addr_t in_buf, extern int __qcom_scm_invoke_smc(struct device *dev, phys_addr_t in_buf, size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size, int32_t *result, u64 *response_type, unsigned int *data); +extern int __trustonic_smc_fastcall(void *fc_generic, size_t size); extern int __qcom_scm_invoke_callback_response(struct device *dev, phys_addr_t out_buf, size_t out_buf_size, int32_t *result, u64 *response_type, unsigned int *data); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f83b2214d704..c39c1a36dee6 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5064,6 +5064,104 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) return 0; } +static u32 debug_gpio = IS_ENABLED(CONFIG_DUMP_GPIO_ON_SUSPEND); +void gpio_debug_print_enabled(void) +{ + unsigned long flags; + struct gpio_device *gdev = NULL; + struct seq_file *s; + int back; + + if (likely(!debug_gpio)) + { + return; + } + + printk("======================= Dump GPIO status begin ======================="); + spin_lock_irqsave(&gpio_lock, flags); + s = kzalloc(sizeof(struct seq_file), GFP_KERNEL); + if(s == NULL) + { + printk("hxy struct seq file failed to alloc memory"); + return; + } + s->index = 0; + s->version = 0; + s->count = s->from = 0; + s->read_pos= 0; + s->private = NULL; + s->file = NULL; + s->op = NULL; + s->private = ""; + + s->buf = kzalloc(s->size = PAGE_SIZE,GFP_KERNEL_ACCOUNT); + if (!s->buf) + { + printk("hxy seq file buffer failed to alloc memory %d bytes",s->size); + kfree(s ); + return ; + } + + list_for_each_entry(gdev, &gpio_devices, list) + { + try_again: + s->count = 0; + back=s->count; + + gpiolib_seq_show(s,gdev); + + if(s->count >= s->size) + { + kfree(s->buf ); + s->buf = kzalloc(s->size = (s->size*2),GFP_KERNEL_ACCOUNT); + if (!s->buf) + { + printk("hxy seq file buffer failed to alloc memory %d bytes",s->size); + kfree(s ); + return ; + } + goto try_again; + } + s->buf[s->count] = '\0'; + { + char tt[256]; + char *ptr; + char *ptr1; + int n; + int loop; + + ptr1=s->buf+back; + ptr=strstr(ptr1,"\x0a"); + loop=0; + while(ptr != NULL && (loop++ < 500) && (ptr < (s->buf+ s->size))) + { + n=ptr-ptr1; + strncpy(tt,ptr1,n); + tt[n]='\0'; + printk(" %s",tt); + ptr1 = ptr+strlen("\x0a"); + ptr=strstr(ptr1,"\x0a"); + } + } + } + + if(s->buf != NULL) + { + kfree(s->buf ); + s->buf = NULL; + } + if(s != NULL) + { + kfree(s ); + s = NULL; + } + spin_unlock_irqrestore(&gpio_lock, flags); + printk("======================= Dump GPIO status end ======================="); + +} + +EXPORT_SYMBOL(gpio_debug_print_enabled); + static const struct seq_operations gpiolib_seq_ops = { .start = gpiolib_seq_start, .next = gpiolib_seq_next, @@ -5089,6 +5187,7 @@ static int __init gpiolib_debugfs_init(void) /* /sys/kernel/debug/gpio */ debugfs_create_file("gpio", S_IFREG | S_IRUGO, NULL, NULL, &gpiolib_operations); + debugfs_create_u32("debug_gpio", 0644, NULL, &debug_gpio); return 0; } subsys_initcall(gpiolib_debugfs_init); diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 1474e57ecafc..77ec4e6ea998 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -145,6 +145,14 @@ config I2C_DEBUG_BUS a problem with I2C support and want to see more of what is going on. +config QCOM_I2C_NACK_ERR_WA + bool "I2C Bus nack err for fac" + default 'n' + help + Say Y here if you want the I2C bus drivers to produce a bunch of + debug messages to the system log. Select this if you are having + a problem with I2C support and want to see more of what is going + on. endif # I2C endmenu diff --git a/drivers/i2c/busses/i2c-msm-geni.c b/drivers/i2c/busses/i2c-msm-geni.c index e29a96949536..f6326852fe60 100644 --- a/drivers/i2c/busses/i2c-msm-geni.c +++ b/drivers/i2c/busses/i2c-msm-geni.c @@ -79,6 +79,10 @@ #define MAX_SE 20 +#if IS_ENABLED(CONFIG_QCOM_I2C_NACK_ERR_WA) +u8 i2c_wt6670_dl_mode = 0; +#endif + enum i2c_se_mode { UNINITIALIZED, FIFO_SE_DMA, @@ -135,7 +139,6 @@ struct geni_i2c_dev { bool first_resume; bool gpi_reset; bool disable_dma_mode; - bool prev_cancel_pending; //Halt cancel till IOS in good state bool is_i2c_rtl_based; /* doing pending cancel only for rtl based SE's */ atomic_t is_xfer_in_progress; /* Used to maintain xfer inprogress status */ }; @@ -246,58 +249,6 @@ err_ret: gi2c->err = gi2c_log[err].err; } -static int do_pending_cancel(struct geni_i2c_dev *gi2c) -{ - int timeout = 0; - u32 geni_ios = 0; - - /* doing pending cancel only rtl based SE's */ - if (!gi2c->is_i2c_rtl_based) - return 0; - - geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS); - if ((geni_ios & 0x3) != 0x3) { - /* Try to restore IOS with FORCE_DEFAULT */ - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, - "%s: IOS:0x%x, bad state\n", __func__, geni_ios); - - geni_write_reg(FORCE_DEFAULT, - gi2c->base, GENI_FORCE_DEFAULT_REG); - geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS); - if ((geni_ios & 0x3) != 0x3) { - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, - "%s: IOS:0x%x, Fix from Slave side\n", - __func__, geni_ios); - return -EINVAL; - } - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, - "%s: IOS:0x%x restored properly\n", __func__, geni_ios); - } - - if (gi2c->se_mode == GSI_ONLY) { - dmaengine_terminate_all(gi2c->tx_c); - gi2c->cfg_sent = 0; - } else { - reinit_completion(&gi2c->xfer); - geni_cancel_m_cmd(gi2c->base); - timeout = wait_for_completion_timeout(&gi2c->xfer, HZ); - if (!timeout) { - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, - "%s:Pending Cancel failed\n", __func__); - reinit_completion(&gi2c->xfer); - geni_abort_m_cmd(gi2c->base); - timeout = wait_for_completion_timeout(&gi2c->xfer, HZ); - if (!timeout) - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, - "%s:Abort failed\n", __func__); - } - } - gi2c->prev_cancel_pending = false; - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, - "%s: Pending Cancel done\n", __func__); - return timeout; -} - static int geni_i2c_prepare(struct geni_i2c_dev *gi2c) { if (gi2c->se_mode == UNINITIALIZED) { @@ -346,6 +297,15 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN); struct i2c_msg *cur = gi2c->cur; +#if IS_ENABLED(CONFIG_QCOM_I2C_NACK_ERR_WA) + //wt6670 download mode i2c addr + if(cur[0].addr == 0x2B){ + i2c_wt6670_dl_mode = 1; + } else if(cur[0].addr == 0x35){ + i2c_wt6670_dl_mode = 0; + } +#endif + if (!cur) { geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl); GENI_SE_ERR(gi2c->ipcl, false, gi2c->dev, "Spurious irq\n"); @@ -358,8 +318,15 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) (m_stat & M_CMD_ABORT_EN) || (m_stat & M_GP_IRQ_1_EN)) { +#if IS_ENABLED(CONFIG_QCOM_I2C_NACK_ERR_WA) + if(i2c_wt6670_dl_mode == 0){ + if (m_stat & M_GP_IRQ_1_EN) + geni_i2c_err(gi2c, I2C_NACK); + } +#else if (m_stat & M_GP_IRQ_1_EN) geni_i2c_err(gi2c, I2C_NACK); +#endif if (m_stat & M_GP_IRQ_3_EN) geni_i2c_err(gi2c, I2C_BUS_PROTO); if (m_stat & M_GP_IRQ_4_EN) @@ -994,8 +961,6 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], timeout = wait_for_completion_timeout(&gi2c->xfer, gi2c->xfer_timeout); if (!timeout) { - u32 geni_ios = 0; - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, "I2C gsi xfer timeout:%u flags:%d addr:0x%x\n", gi2c->xfer_timeout, gi2c->cur->flags, @@ -1003,18 +968,6 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl); gi2c->err = -ETIMEDOUT; - - /* WAR: Set flag to mark cancel pending if IOS stuck */ - geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS); - if ((geni_ios & 0x3) != 0x3) { //SCL:b'1, SDA:b'0 - GENI_SE_DBG(gi2c->ipcl, true, gi2c->dev, - "%s: IO lines not in good state\n", __func__); - /* doing pending cancel only rtl based SE's */ - if (gi2c->is_i2c_rtl_based) { - gi2c->prev_cancel_pending = true; - goto geni_i2c_gsi_cancel_pending; - } - } } geni_i2c_err_prep_sg: if (gi2c->err) { @@ -1037,7 +990,6 @@ geni_i2c_err_prep_sg: /* Resend cfg tre for every new message on shared se */ gi2c->cfg_sent = 0; -geni_i2c_gsi_cancel_pending: if (msgs[i].flags & I2C_M_RD) geni_se_iommu_unmap_buf(rx_dev, &gi2c->rx_ph, msgs[i].len, DMA_FROM_DEVICE); @@ -1065,6 +1017,14 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, gi2c->err = 0; atomic_set(&gi2c->is_xfer_in_progress, 1); +#if IS_ENABLED(CONFIG_QCOM_I2C_NACK_ERR_WA) + //wt6670 download mode i2c addr + if(msgs[0].addr == 0x2B){ + i2c_wt6670_dl_mode = 1; + } else if( msgs[0].addr == 0x35){ + i2c_wt6670_dl_mode = 0; + } +#endif /* Client to respect system suspend */ if (!pm_runtime_enabled(gi2c->dev)) { GENI_SE_ERR(gi2c->ipcl, false, gi2c->dev, @@ -1086,17 +1046,6 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, } } - // WAR : Complete previous pending cancel cmd - if (gi2c->prev_cancel_pending) { - ret = do_pending_cancel(gi2c); - if (ret) { - pm_runtime_mark_last_busy(gi2c->dev); - pm_runtime_put_autosuspend(gi2c->dev); - atomic_set(&gi2c->is_xfer_in_progress, 0); - return ret; //Don't perform xfer is cancel failed - } - } - GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "n:%d addr:0x%x\n", num, msgs[0].addr); @@ -1214,23 +1163,9 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, timeout = wait_for_completion_timeout(&gi2c->xfer, gi2c->xfer_timeout); if (!timeout) { - u32 geni_ios = 0; - GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, "I2C xfer timeout: %d\n", gi2c->xfer_timeout); geni_i2c_err(gi2c, GENI_TIMEOUT); - - /* WAR: Set flag to mark cancel pending if IOS bad */ - geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS); - if ((geni_ios & 0x3) != 0x3) { //SCL:b'1, SDA:b'0 - GENI_SE_DBG(gi2c->ipcl, true, gi2c->dev, - "%s: IO lines not in good state\n", __func__); - /* doing pending cancel only rtl based SE's */ - if (gi2c->is_i2c_rtl_based) { - gi2c->prev_cancel_pending = true; - goto geni_i2c_txn_ret; - } - } } if (gi2c->err) { @@ -1272,6 +1207,15 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, if (gi2c->err) { GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, "i2c error :%d\n", gi2c->err); +#if IS_ENABLED(CONFIG_QCOM_I2C_NACK_ERR_WA) + if(i2c_wt6670_dl_mode == 1){ + if(gi2c->err == gi2c_log[I2C_NACK].err) { + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,"wt6670 nack continue! :%d\n", gi2c->err); + continue; + } + } + +#endif break; } } diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 20fe16b1d4c8..c049f48cc02c 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -19,6 +19,7 @@ #define SE_I2C_TX_TRANS_LEN 0x26c #define SE_I2C_RX_TRANS_LEN 0x270 #define SE_I2C_SCL_COUNTERS 0x278 +#define SE_I2C_NOISE_CANCEL_CTL (0x234) #define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\ M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN) @@ -90,6 +91,8 @@ struct geni_i2c_dev { void *dma_buf; size_t xfer_len; dma_addr_t dma_addr; + u32 noise_rjct_scl; + u32 noise_rjct_sda; }; struct geni_i2c_err_log { @@ -164,6 +167,9 @@ static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c) val |= itr->t_low_cnt << LOW_COUNTER_SHFT; val |= itr->t_cycle_cnt; writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS); + if (gi2c->noise_rjct_scl || gi2c->noise_rjct_sda) + geni_write_reg(gi2c->noise_rjct_scl << 1 | + gi2c->noise_rjct_sda << 3, gi2c->base, SE_I2C_NOISE_CANCEL_CTL); } static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c) @@ -556,6 +562,14 @@ static int geni_i2c_probe(struct platform_device *pdev) if (has_acpi_companion(dev)) ACPI_COMPANION_SET(&gi2c->adap.dev, ACPI_COMPANION(dev)); + gi2c->noise_rjct_scl = 0; + gi2c->noise_rjct_sda = 0; + + of_property_read_u32(pdev->dev.of_node, "qcom,noise-rjct-scl", + &gi2c->noise_rjct_scl); + of_property_read_u32(pdev->dev.of_node, "qcom,noise-rjct-sda", + &gi2c->noise_rjct_sda); + gi2c->irq = platform_get_irq(pdev, 0); if (gi2c->irq < 0) return gi2c->irq; diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index d4c1e58c0d21..aa17f046d7ca 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -991,6 +991,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0, SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_GPIO3_DIV3] = ADC5_CHAN_VOLT("gpio3_div3", 1, + SCALE_HW_CALIB_DEFAULT) }; static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = { diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c index 49c00750dfe4..f51965839d1b 100644 --- a/drivers/input/misc/qpnp-power-on.c +++ b/drivers/input/misc/qpnp-power-on.c @@ -26,6 +26,7 @@ #include #include #include +#include #define PMIC_VER_8941 0x01 #define PMIC_VERSION_REG 0x0105 @@ -99,6 +100,8 @@ enum qpnp_pon_version { #define QPNP_PON_S3_DBC_CTL(pon) ((pon)->base + 0x75) #define QPNP_PON_SMPL_CTL(pon) ((pon)->base + 0x7F) #define QPNP_PON_TRIGGER_EN(pon) ((pon)->base + 0x80) +#define QPNP_PON_PERPH_RB_SPARE(pon) ((pon)->base + 0x8C) +#define QPNP_PON_DVDD_RB_SPARE(pon) ((pon)->base + 0x8D) #define QPNP_PON_XVDD_RB_SPARE(pon) ((pon)->base + 0x8E) #define QPNP_PON_SOFT_RB_SPARE(pon) ((pon)->base + 0x8F) #define QPNP_PON_SEC_ACCESS(pon) ((pon)->base + 0xD0) @@ -451,6 +454,50 @@ int qpnp_pon_set_restart_reason(enum pon_restart_reason reason) } EXPORT_SYMBOL(qpnp_pon_set_restart_reason); +/** + * qpnp_pon_store_extra_reset_info - Store extra reset info in PMIC register. + * + * Returns = 0 if PMIC feature is not available or store restart reason + * successfully. + * Returns > 0 for errors + * + * This function is used to store extra reset info in PMIC spare register + * which can be preserved during reset. + */ +int qpnp_pon_store_extra_reset_info(u16 mask, u16 val) +{ + int rc = 0; + struct qpnp_pon *pon = sys_reset_dev; + + if (!pon) + return 0; + + if (mask & 0xFF) { + rc = qpnp_pon_masked_write(pon, QPNP_PON_DVDD_RB_SPARE(pon), + (mask & 0xFF), (val & 0xFF)); + if (rc) { + dev_err(pon->dev, + "Failed to store extra reset info to 0x%x\n", + QPNP_PON_DVDD_RB_SPARE(pon)); + return rc; + } + } + + if (mask & 0xFF00) { + rc = qpnp_pon_masked_write(pon, QPNP_PON_XVDD_RB_SPARE(pon), + ((mask >> 8) & 0xFF), ((val >> 8) & 0xFF)); + if (rc) { + dev_err(pon->dev, + "Failed to store extra reset info to 0x%x\n", + QPNP_PON_XVDD_RB_SPARE(pon)); + return rc; + } + } + + return rc; +} +EXPORT_SYMBOL(qpnp_pon_store_extra_reset_info); + /* * qpnp_pon_check_hard_reset_stored() - Checks if the PMIC need to * store hard reset reason. @@ -986,6 +1033,10 @@ static int qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) uint pon_rt_sts; u64 elapsed_us; int rc; + struct timespec64 now; + struct timeval timestamp; + struct tm tm; + char buff[255]; cfg = qpnp_get_cfg(pon, pon_type); if (!cfg) @@ -1014,6 +1065,19 @@ static int qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) pon_rt_bit = is_pon_gen3(pon) ? QPNP_PON_GEN3_KPDPWR_N_SET : QPNP_PON_KPDPWR_N_SET; + + /* get the time stamp in readable format to print*/ + ktime_get_real_ts64(&now); + timestamp.tv_sec = now.tv_sec; + timestamp.tv_usec = now.tv_nsec/1000; + time64_to_tm((time64_t)(timestamp.tv_sec), 0, &tm); + snprintf(buff, sizeof(buff), + "%u-%02d-%02d %02d:%02d:%02d UTC", + (int) tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + pr_warn("Report pwrkey %s event at: %s\n", pon_rt_bit & + pon_rt_sts ? "press" : "release", buff); break; case PON_RESIN: pon_rt_bit = is_pon_gen3(pon) @@ -1030,7 +1094,7 @@ static int qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) return -EINVAL; } - pr_debug("PMIC input: code=%d, status=0x%02X\n", cfg->key_code, + pr_info("PMIC input: code=%d, status=0x%02X\n", cfg->key_code, pon_rt_sts); key_status = pon_rt_sts & pon_rt_bit; @@ -2085,6 +2149,8 @@ static int qpnp_pon_read_gen2_pon_off_reason(struct qpnp_pon *pon, u16 *reason, rc = qpnp_pon_read(pon, QPNP_PON_OFF_REASON(pon), ®); if (rc) return rc; + dev_info(pon->dev, "PON Register , addr=0x%04X, val=0x%x\n", + QPNP_PON_OFF_REASON(pon), (u8)reg); if (reg & QPNP_GEN2_POFF_SEQ) { rc = qpnp_pon_read(pon, QPNP_POFF_REASON1(pon), ®1); @@ -2092,6 +2158,8 @@ static int qpnp_pon_read_gen2_pon_off_reason(struct qpnp_pon *pon, u16 *reason, return rc; *reason = (u8)reg1; *reason_index_offset = 0; + dev_info(pon->dev, "PON Register , addr=0x%04X, val=0x%x\n", + QPNP_POFF_REASON1(pon), (u8)buf[0]); } else if (reg & QPNP_GEN2_FAULT_SEQ) { rc = regmap_bulk_read(pon->regmap, QPNP_FAULT_REASON1(pon), buf, 2); @@ -2102,12 +2170,18 @@ static int qpnp_pon_read_gen2_pon_off_reason(struct qpnp_pon *pon, u16 *reason, } *reason = buf[0] | (u16)(buf[1] << 8); *reason_index_offset = POFF_REASON_FAULT_OFFSET; + dev_info(pon->dev, "PON Register , addr=0x%04X, val=0x%x\n", + QPNP_FAULT_REASON1(pon), (u8)buf[0]); + dev_info(pon->dev, "PON Register , addr=0x%04X, val=0x%x\n", + QPNP_FAULT_REASON1(pon), (u8)buf[1]); } else if (reg & QPNP_GEN2_S3_RESET_SEQ) { rc = qpnp_pon_read(pon, QPNP_S3_RESET_REASON(pon), ®1); if (rc) return rc; *reason = (u8)reg1; *reason_index_offset = POFF_REASON_S3_RESET_OFFSET; + dev_info(pon->dev, "PON Register , addr=0x%04X, val=0x%x\n", + QPNP_S3_RESET_REASON(pon), (u8)buf[0]); } return 0; diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c index 799d0b58cb12..fc80b2ed9635 100644 --- a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c @@ -70,7 +70,7 @@ /***************************************************************************** * Private constant and macro definitions using #define *****************************************************************************/ -#define FTS_DRIVER_NAME "fts_ts" +#define FTS_DRIVER_NAME "fts_tp" #define INTERVAL_READ_REG 200 /* unit:ms */ #define TIMEOUT_READ_REG 1000 /* unit:ms */ #if FTS_POWER_SOURCE_CUST_EN diff --git a/drivers/leds/leds-qpnp-vibrator-ldo.c b/drivers/leds/leds-qpnp-vibrator-ldo.c index d7d0c6112ed0..373c150e2f48 100644 --- a/drivers/leds/leds-qpnp-vibrator-ldo.c +++ b/drivers/leds/leds-qpnp-vibrator-ldo.c @@ -54,6 +54,10 @@ struct vib_ldo_chip { u64 vib_play_ms; bool vib_enabled; bool disable_overdrive; + + bool dis_short_long; + int dis_long_ms; + int vmax_uV_long; }; static inline int qpnp_vib_ldo_poll_status(struct vib_ldo_chip *chip) @@ -63,7 +67,7 @@ static inline int qpnp_vib_ldo_poll_status(struct vib_ldo_chip *chip) ret = regmap_read_poll_timeout(chip->regmap, chip->base + QPNP_VIB_LDO_REG_STATUS1, val, - val & QPNP_VIB_LDO_VREG_READY, 100, 1000); + val & QPNP_VIB_LDO_VREG_READY, 100, 2000); if (ret < 0) { pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n", val, ret); @@ -144,6 +148,15 @@ static int qpnp_vibrator_play_on(struct vib_ldo_chip *chip) int ret; volt_uV = chip->vmax_uV; + + if (chip->dis_short_long) { + pr_debug("vib in dis short and long, play ms=%d, dis_longms=%d, vmax_uV=%d, vmax_uV_long=%d\n", + chip->vib_play_ms, chip->dis_long_ms, chip->vmax_uV, chip->vmax_uV_long); + if (chip->vib_play_ms > chip->dis_long_ms) { + volt_uV = chip->vmax_uV_long; + } + } + if (!chip->disable_overdrive) volt_uV = chip->overdrive_volt_uV ? chip->overdrive_volt_uV : min(chip->vmax_uV * 2, QPNP_VIB_LDO_VMAX_UV); @@ -386,6 +399,28 @@ static int qpnp_vib_parse_dt(struct device *dev, struct vib_ldo_chip *chip) return ret; } + chip->dis_short_long = of_property_read_bool(dev->of_node, + "qcom,vib-dis-short-long"); + + if (chip->dis_short_long) { + pr_warn("read dis_short_long true"); + ret = of_property_read_u32(dev->of_node, "qcom,vib-dis-short-long-val", + &chip->dis_long_ms); + if (ret < 0) { + pr_err("qcom,vib-dis-short-long-val property read failed, ret=%d\n", ret); + return ret; + } + pr_warn("read dis_long_ms=%d\n", chip->dis_long_ms); + + ret = of_property_read_u32(dev->of_node, "qcom,vib-ldo-volt-uv-long", + &chip->vmax_uV_long); + if (ret < 0) { + pr_err("qcom,vib-ldo-volt-uv-long property read failed, ret=%d\n", ret); + return ret; + } + pr_warn("read vmax_uV_long=%d\n", chip->vmax_uV_long); + } + chip->disable_overdrive = of_property_read_bool(dev->of_node, "qcom,disable-overdrive"); diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index ef1722c4165f..943037fe50be 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -3139,6 +3139,11 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data, goto unload_exit; } + if (!memcmp(data->client.app_name, "prov", strlen("prov"))) { + pr_debug("Do not unload prov app from tz\n"); + goto unload_exit; + } + __qseecom_cleanup_app(data); __qseecom_reentrancy_check_if_no_app_blocked(TZ_OS_APP_SHUTDOWN_ID); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e04f116cd39a..c65106e229db 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2932,6 +2932,12 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card) struct mmc_host *host = card->host; unsigned int max_discard, max_trim; + if (!host->max_busy_timeout || + (host->caps2 & MMC_CAP2_MAX_DISCARD_SIZE)) + { + pr_err("mmc_calc_max_discard UINT_MAX = %d \n",UINT_MAX); + return UINT_MAX; + } /* * Without erase_group_def set, MMC erase timeout depends on clock * frequence which can change. In that case, the best choice is diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9607ff927ea4..5c5bc269916a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1289,7 +1289,7 @@ static int mmc_sd_suspend(struct mmc_host *host) int err; err = _mmc_sd_suspend(host); - if (!err) { + if (!err && host->card) { pm_runtime_disable(&host->card->dev); pm_runtime_set_suspended(&host->card->dev); } diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 302a77938787..d474e1c3fb07 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -475,6 +475,7 @@ struct sdhci_msm_host { int sdiowakeup_irq; bool is_sdiowakeup_enabled; bool sdio_pending_processing; + bool first_power_on; }; static struct sdhci_msm_host *sdhci_slot[2]; @@ -1718,11 +1719,11 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, /* * Ensure larger discard size by always setting max_busy_timeout to zero. * This will always return max_busy_timeout as zero to the sdhci layer. - */ +*/ -static unsigned int sdhci_msm_get_max_timeout_count(struct sdhci_host *host) -{ - return 0; +static unsigned int sdhci_msm_get_max_timeout_count(struct sdhci_host +*host) { + return 0; } #define MAX_PROP_SIZE 32 @@ -2271,8 +2272,14 @@ static int sdhci_msm_setup_vreg(struct sdhci_msm_host *msm_host, for (i = 0; i < ARRAY_SIZE(vreg_table); i++) { if (vreg_table[i]) { - if (enable) + if (enable) { ret = sdhci_msm_vreg_enable(vreg_table[i]); + if (msm_host->first_power_on && !(mmc->caps & MMC_CAP_NONREMOVABLE)) { + ret = sdhci_msm_vreg_disable(vreg_table[i]); + msleep(1); + ret = sdhci_msm_vreg_enable(vreg_table[i]); + } + } else ret = sdhci_msm_vreg_disable(vreg_table[i]); if (ret) @@ -2280,6 +2287,9 @@ static int sdhci_msm_setup_vreg(struct sdhci_msm_host *msm_host, } } + if (enable && !(mmc->caps & MMC_CAP_NONREMOVABLE)) + msm_host->first_power_on = false; + if (enable && !(mmc->caps & MMC_CAP_NONREMOVABLE)) { /* @@ -3571,6 +3581,7 @@ static const struct sdhci_ops sdhci_msm_ops = { .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, .get_max_timeout_count = sdhci_msm_get_max_timeout_count, + #if defined(CONFIG_SDC_QTI) .dump_vendor_regs = sdhci_msm_dump_vendor_regs, #endif @@ -4308,6 +4319,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host = sdhci_pltfm_priv(pltfm_host); msm_host->mmc = host->mmc; msm_host->pdev = pdev; + msm_host->first_power_on = true; ret = mmc_of_parse(host->mmc); if (ret) @@ -4555,6 +4567,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) } sdhci_msm_set_caps(msm_host); + msm_host->mmc->caps2 |= MMC_CAP2_MAX_DISCARD_SIZE; #if defined(CONFIG_SDC_QTI) host->timeout_clk_div = 4; diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 36d414f63dbf..2ab2e795295e 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -59,6 +59,7 @@ source "drivers/nfc/st-nci/Kconfig" source "drivers/nfc/nxp-nci/Kconfig" source "drivers/nfc/s3fwrn5/Kconfig" source "drivers/nfc/st95hf/Kconfig" +source "drivers/nfc/samsung/Kconfig" endmenu config NFC_QTI_I2C diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 7aa7c077b4a0..9c5c77308bc5 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -17,4 +17,5 @@ obj-$(CONFIG_NFC_ST_NCI) += st-nci/ obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/ obj-$(CONFIG_NFC_ST95HF) += st95hf/ +obj-$(CONFIG_SEC_NFC) += samsung/ obj-y += qti/ diff --git a/drivers/nfc/qti/nfc_common.c b/drivers/nfc/qti/nfc_common.c index 9b593f8bd51d..38a8578a3b6b 100644 --- a/drivers/nfc/qti/nfc_common.c +++ b/drivers/nfc/qti/nfc_common.c @@ -651,6 +651,40 @@ unsigned int nfc_ioctl_nfcc_info(struct file *filp, unsigned long arg) * * @return 0 on success, error code for failures. */ + +#ifdef CONFIG_COMPAT +long nfc_compat_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct nfc_dev *nfc_dev = pfile->private_data; + + arg = (compat_u64)arg; + pr_debug("%s cmd = %x arg = %zx\n", __func__, cmd, arg); + + switch (cmd) { + case NFC_SET_PWR: + ret = nfc_ioctl_power_states(nfc_dev, arg); + break; + case ESE_SET_PWR: + ret = nfc_ese_pwr(nfc_dev, arg); + break; + case ESE_GET_PWR: + ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE); + break; + case NFCC_GET_INFO: + ret = nfc_ioctl_nfcc_info(pfile, arg); + break; + case NFC_GET_PLATFORM_TYPE: + ret = nfc_dev->interface; + break; + default: + pr_err("%s bad cmd %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} +#endif + long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) { int ret = 0; diff --git a/drivers/nfc/qti/nfc_common.h b/drivers/nfc/qti/nfc_common.h index 2dc31036c1e7..a4ed5348ab0d 100644 --- a/drivers/nfc/qti/nfc_common.h +++ b/drivers/nfc/qti/nfc_common.h @@ -254,6 +254,9 @@ struct nfc_dev { int nfc_dev_open(struct inode *inode, struct file *filp); int nfc_dev_close(struct inode *inode, struct file *filp); long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long nfc_compat_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +#endif int nfc_parse_dt(struct device *dev, struct platform_gpio *nfc_gpio, struct platform_ldo *ldo, uint8_t interface); int nfc_misc_probe(struct nfc_dev *nfc_dev, diff --git a/drivers/nfc/qti/nfc_i2c_drv.c b/drivers/nfc/qti/nfc_i2c_drv.c index fee83a9f9a2e..e5480a1afd58 100644 --- a/drivers/nfc/qti/nfc_i2c_drv.c +++ b/drivers/nfc/qti/nfc_i2c_drv.c @@ -266,6 +266,9 @@ static const struct file_operations nfc_i2c_dev_fops = { .open = nfc_dev_open, .release = nfc_dev_close, .unlocked_ioctl = nfc_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = nfc_compat_dev_ioctl, +#endif }; int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id) diff --git a/drivers/nfc/samsung/Kconfig b/drivers/nfc/samsung/Kconfig new file mode 100644 index 000000000000..3c66f3b24baf --- /dev/null +++ b/drivers/nfc/samsung/Kconfig @@ -0,0 +1,67 @@ +# +# Near Field Communication (NFC) devices +# + +menu "Near Field Communication (NFC) devices" + depends on NFC + +config SEC_NFC + tristate "Samsung NFC driver" + default m + help + SAMSUNG NFC Driver. + This driver provides support for SAMSUNG NFC products. + You should select your NFC product and interface. + + Say Y here to compile support for SAMSUNG NFC driver into the + kernel. + +choice + prompt "SEC NFC Products" + depends on SEC_NFC + default SEC_NFC_PRODUCT_N5 +config SEC_NFC_PRODUCT_N3 + bool "RN3" +config SEC_NFC_PRODUCT_N5 + bool "RN5/RNx" +endchoice +choice + prompt "SEC NFC Interface" + depends on SEC_NFC + default SEC_NFC_IF_I2C +config SEC_NFC_IF_UART + bool "UART" +config SEC_NFC_IF_I2C + bool "I2C" +config SEC_NFC_IF_I2C_GPIO + bool "I2C-GPIO (S/W-I2C)" + select I2C_CHARDEV + select I2C_MUX + select I2C_MUX_GPIO + select I2C_ARB_GPIO_CHALLENGE + select I2C_ALGOBIT + select I2C_GPIO +endchoice +config SEC_NFC_GPIO_CLK + bool "SEC NFC GPIO CLK" + default n + help + SAMSUNG NFC CLR REQ feature. + This feature provides support for SAMSUNG NFC products. + You should select this feature if your NFC product uses AP CLK. + + Say Y here to compile support for SAMSUNG NFC CLK REQ feature into the + kernel. +config SEC_NFC_DEDICATED_CLK + tristate "SEC NFC DEDICATED CLK" + default m +config SEC_NFC_LDO_EN + tristate "SEC NFC LDO" + default m +config SEC_NFC_PMIC_LDO + tristate "SEC NFC PMIC LDO" + default m +config SEC_ESE_COLDRESET + tristate "eSE Cold reset" + default m +endmenu diff --git a/drivers/nfc/samsung/Makefile b/drivers/nfc/samsung/Makefile new file mode 100644 index 000000000000..950cde363a53 --- /dev/null +++ b/drivers/nfc/samsung/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Samsung NRNx NFC driver +# + +obj-$(CONFIG_SEC_NFC) += sec_nfc.o + +ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/samsung/sec_nfc.c b/drivers/nfc/samsung/sec_nfc.c new file mode 100644 index 000000000000..fc5206ad333e --- /dev/null +++ b/drivers/nfc/samsung/sec_nfc.c @@ -0,0 +1,1154 @@ +/* + * SAMSUNG NFC Controller + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Author: Woonki Lee + * Heejae Kim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program + * + */ +#ifdef CONFIG_SEC_NFC_IF_I2C_GPIO +#define CONFIG_SEC_NFC_IF_I2C +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SEC_NFC_GPIO_CLK +#include +#endif +#ifdef CONFIG_SEC_NFC_WAKE_LOCK +#include +#endif +#include +#include + +#ifndef CONFIG_SEC_NFC_IF_I2C +struct sec_nfc_i2c_info {}; +#define sec_nfc_read NULL +#define sec_nfc_write NULL +#define sec_nfc_poll NULL +#define sec_nfc_i2c_irq_clear(x) + +#define SEC_NFC_GET_INFO(dev) platform_get_drvdata(to_platform_device(dev)) + +#else /* CONFIG_SEC_NFC_IF_I2C */ +#include +#include +#include +#include +#include "sec_nfc.h" + +#define SEC_NFC_GET_INFO(dev) i2c_get_clientdata(to_i2c_client(dev)) +enum sec_nfc_irq { + SEC_NFC_SKIP = -1, + SEC_NFC_NONE, + SEC_NFC_INT, + SEC_NFC_READ_TIMES, +}; + +struct sec_nfc_i2c_info { + struct i2c_client *i2c_dev; + struct mutex read_mutex; + enum sec_nfc_irq read_irq; + wait_queue_head_t read_wait; + size_t buflen; + u8 *buf; +}; +#endif + +struct sec_nfc_info { + struct miscdevice miscdev; + struct mutex mutex; + enum sec_nfc_mode mode; + struct device *dev; + struct sec_nfc_platform_data *pdata; + struct sec_nfc_i2c_info i2c_info; +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + struct wake_lock nfc_wake_lock; +#endif +#ifdef CONFIG_SEC_NFC_GPIO_CLK + bool clk_ctl; + bool clk_state; +#endif +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + void __iomem *clkctrl; +#endif +}; + +#ifdef CONFIG_SEC_ESE_COLDRESET +struct mutex coldreset_mutex; +struct mutex sleep_wake_mutex; +bool sleep_wakeup_state[2]; +u8 disable_combo_reset_cmd[4] = {0x2F, 0x30, 0x01, 0x00}; +bool is_shutdown = false; +enum sec_nfc_mode cur_mode; +#endif + +#ifdef CONFIG_SEC_NFC_IF_I2C +static irqreturn_t sec_nfc_irq_thread_fn(int irq, void *dev_id) +{ + struct sec_nfc_info *info = dev_id; + struct sec_nfc_platform_data *pdata = info->pdata; + + dev_dbg(info->dev, "[NFC] Read Interrupt is occurred!\n"); + + if (gpio_get_value(pdata->irq) == 0) { + dev_err(info->dev, "[NFC] Warning,irq-gpio state is low!\n"); + return IRQ_HANDLED; + } + + mutex_lock(&info->i2c_info.read_mutex); + /* Skip interrupt during power switching + * It is released after first write */ + if (info->i2c_info.read_irq == SEC_NFC_SKIP) { + dev_dbg(info->dev, "%s: Now power swiching. Skip this IRQ\n", __func__); + mutex_unlock(&info->i2c_info.read_mutex); + return IRQ_HANDLED; + } + + info->i2c_info.read_irq += SEC_NFC_READ_TIMES; + mutex_unlock(&info->i2c_info.read_mutex); + + wake_up_interruptible(&info->i2c_info.read_wait); +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + wake_lock_timeout(&info->nfc_wake_lock, 2 * HZ); +#endif + + return IRQ_HANDLED; +} + +static ssize_t sec_nfc_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + enum sec_nfc_irq irq; + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p, count: %zu\n", __func__, + info, count); + + mutex_lock(&info->mutex); + if (info->mode == SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is not enabled\n"); + ret = -ENODEV; + goto out; + } + + mutex_lock(&info->i2c_info.read_mutex); + if (count == 0) { + if (info->i2c_info.read_irq >= SEC_NFC_INT) + info->i2c_info.read_irq--; + mutex_unlock(&info->i2c_info.read_mutex); + goto out; + } + + irq = info->i2c_info.read_irq; + mutex_unlock(&info->i2c_info.read_mutex); + if (irq == SEC_NFC_NONE) { + if (file->f_flags & O_NONBLOCK) { + dev_err(info->dev, "it is nonblock\n"); + ret = -EAGAIN; + goto out; + } + } + + /* i2c recv */ + if (count > info->i2c_info.buflen) + count = info->i2c_info.buflen; + + if (count > SEC_NFC_MSG_MAX_SIZE) { + dev_err(info->dev, "user required wrong size :%d\n", (u32)count); + ret = -EINVAL; + goto out; + } + + mutex_lock(&info->i2c_info.read_mutex); + memset(info->i2c_info.buf, 0, count); + ret = i2c_master_recv(info->i2c_info.i2c_dev, info->i2c_info.buf, (u32)count); + dev_dbg(info->dev, "recv size : %d\n", ret); + + if (ret == -EREMOTEIO) { + ret = -ERESTART; + goto read_error; + } else if (ret != count) { + dev_err(info->dev, "read failed: return: %d count: %d\n", + ret, (u32)count); + /* ret = -EREMOTEIO; */ + goto read_error; + } + + if (info->i2c_info.read_irq >= SEC_NFC_INT) + info->i2c_info.read_irq--; + + if (info->i2c_info.read_irq == SEC_NFC_READ_TIMES) + wake_up_interruptible(&info->i2c_info.read_wait); + + mutex_unlock(&info->i2c_info.read_mutex); + + if (copy_to_user(buf, info->i2c_info.buf, ret)) { + dev_err(info->dev, "copy failed to user\n"); + ret = -EFAULT; + } + + goto out; + +read_error: + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_unlock(&info->i2c_info.read_mutex); +out: + mutex_unlock(&info->mutex); + + return ret; +} + +static ssize_t sec_nfc_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p, count %d\n", __func__, + info, (u32)count); + + mutex_lock(&info->mutex); + + if (info->mode == SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is not enabled\n"); + ret = -ENODEV; + goto out; + } + + if (count > info->i2c_info.buflen) + count = info->i2c_info.buflen; + + if (count > SEC_NFC_MSG_MAX_SIZE) { + dev_err(info->dev, "user required wrong size :%d\n", (u32)count); + ret = -EINVAL; + goto out; + } + + if (copy_from_user(info->i2c_info.buf, buf, count)) { + dev_err(info->dev, "copy failed from user\n"); + ret = -EFAULT; + goto out; + } + + /* Skip interrupt during power switching + * It is released after first write + */ + mutex_lock(&info->i2c_info.read_mutex); + ret = i2c_master_send(info->i2c_info.i2c_dev, info->i2c_info.buf, count); + if (info->i2c_info.read_irq == SEC_NFC_SKIP) + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_unlock(&info->i2c_info.read_mutex); + + if (ret == -EREMOTEIO) { + dev_err(info->dev, "send failed: return: %d count: %d\n", + ret, (u32)count); + ret = -ERESTART; + goto out; + } + + if (ret != count) { + dev_err(info->dev, "send failed: return: %d count: %d\n", + ret, (u32)count); + ret = -EREMOTEIO; + } + +out: + mutex_unlock(&info->mutex); + + return ret; +} + +static unsigned int sec_nfc_poll(struct file *file, poll_table *wait) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + enum sec_nfc_irq irq; + + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p\n", __func__, info); + + mutex_lock(&info->mutex); + + if (info->mode == SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is not enabled\n"); + ret = -ENODEV; + goto out; + } + + poll_wait(file, &info->i2c_info.read_wait, wait); + + mutex_lock(&info->i2c_info.read_mutex); + irq = info->i2c_info.read_irq; + if (irq == SEC_NFC_READ_TIMES) + ret = (POLLIN | POLLRDNORM); + mutex_unlock(&info->i2c_info.read_mutex); + +out: + mutex_unlock(&info->mutex); + + return ret; +} + +void sec_nfc_i2c_irq_clear(struct sec_nfc_info *info) +{ + /* clear interrupt. Interrupt will be occurred at power off */ + mutex_lock(&info->i2c_info.read_mutex); + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_unlock(&info->i2c_info.read_mutex); +} + +int sec_nfc_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sec_nfc_info *info = dev_get_drvdata(dev); + struct sec_nfc_platform_data *pdata = info->pdata; + int ret; + + pr_info("%s: start: %p\n", __func__, info); + + info->i2c_info.buflen = SEC_NFC_MAX_BUFFER_SIZE; + info->i2c_info.buf = kzalloc(SEC_NFC_MAX_BUFFER_SIZE, GFP_KERNEL); + if (!info->i2c_info.buf) { + dev_err(dev, "failed to allocate memory for sec_nfc_info->buf\n"); + return -ENOMEM; + } + info->i2c_info.i2c_dev = client; + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_init(&info->i2c_info.read_mutex); + init_waitqueue_head(&info->i2c_info.read_wait); + i2c_set_clientdata(client, info); + + info->dev = dev; + + ret = gpio_request(pdata->irq, "nfc_int"); + if (ret) { + pr_err("GPIO request is failed to register IRQ\n"); + goto err_irq_req; + } + client->irq = gpio_to_irq(pdata->irq); + pr_info("%s: push interrupt no = %d\n", __func__, client->irq); + + ret = request_threaded_irq(client->irq, NULL, sec_nfc_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, SEC_NFC_DRIVER_NAME, + info); + if (ret < 0) { + pr_err("failed to register IRQ handler\n"); + kfree(info->i2c_info.buf); + return ret; + } + + pr_info("%s success\n", __func__); + return 0; + +err_irq_req: + return ret; +} + +void sec_nfc_i2c_remove(struct device *dev) +{ + struct sec_nfc_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->i2c_info.i2c_dev; + struct sec_nfc_platform_data *pdata = info->pdata; + free_irq(client->irq, info); + gpio_free(pdata->irq); +} +#endif /* CONFIG_SEC_NFC_IF_I2C */ + +#ifdef CONFIG_SEC_NFC_GPIO_CLK +static irqreturn_t sec_nfc_clk_irq_thread(int irq, void *dev_id) +{ + struct sec_nfc_info *info = dev_id; + struct sec_nfc_platform_data *pdata = info->pdata; + bool value; + + dev_dbg(info->dev, "[NFC]Clock Interrupt is occurred!\n"); + value = gpio_get_value(pdata->clk_req) > 0 ? true : false; + + if (value == info->clk_state) + return IRQ_HANDLED; + + if (value) + clk_prepare_enable(pdata->clk); + else + clk_disable_unprepare(pdata->clk); + + info->clk_state = value; + + return IRQ_HANDLED; +} + +void sec_nfc_clk_ctl_enable(struct sec_nfc_info *info) +{ + struct sec_nfc_platform_data *pdata = info->pdata; + unsigned int irq; + int ret; + + irq = gpio_to_irq(pdata->clk_req); + + if (info->clk_ctl) + return; + + info->clk_state = false; + ret = request_threaded_irq(irq, NULL, sec_nfc_clk_irq_thread, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + SEC_NFC_DRIVER_NAME, info); + if (ret < 0) + dev_err(info->dev, "failed to register CLK REQ IRQ handler\n"); + + info->clk_ctl = true; +} + +void sec_nfc_clk_ctl_disable(struct sec_nfc_info *info) +{ + struct sec_nfc_platform_data *pdata = info->pdata; + unsigned int irq; + + if (pdata->clk_req) + irq = gpio_to_irq(pdata->clk_req); + else + return; + + if (!info->clk_ctl) + return; + + free_irq(irq, info); + if (info->clk_state) + clk_disable_unprepare(pdata->clk); + info->clk_state = false; + info->clk_ctl = false; +} +#else +#define sec_nfc_clk_ctl_enable(x) +#define sec_nfc_clk_ctl_disable(x) +#endif /* CONFIG_SEC_NFC_GPIO_CLK */ + +static void sec_nfc_set_mode(struct sec_nfc_info *info, + enum sec_nfc_mode mode) +{ + struct sec_nfc_platform_data *pdata = info->pdata; +#ifdef CONFIG_SEC_ESE_COLDRESET + int alreadFirmHigh = 0; + int ret ; + enum sec_nfc_mode oldmode = info->mode; +#endif +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + unsigned int val = readl(info->clkctrl); +#endif + + /* intfo lock is aleady gotten before calling this function */ + if (info->mode == mode) { + pr_err("Power mode is already %d", mode); + return; + } + info->mode = mode; + +#ifdef CONFIG_SEC_NFC_IF_I2C + /* Skip interrupt during power switching + * It is released after first write */ + mutex_lock(&info->i2c_info.read_mutex); +#ifdef CONFIG_SEC_ESE_COLDRESET + cur_mode = mode; + memset(sleep_wakeup_state, false, sizeof(sleep_wakeup_state)); + if (oldmode == SEC_NFC_MODE_OFF) { + if (gpio_get_value(pdata->firm) == 1) { + alreadFirmHigh = 1; + pr_err("Firm is already high; do not anything"); + } else { /*Firm pin is low*/ + gpio_set_value(pdata->firm, SEC_NFC_FW_ON); + msleep(SEC_NFC_VEN_WAIT_TIME); + } + + if (gpio_get_value(pdata->ven) == SEC_NFC_PW_ON) { + ret = i2c_master_send(info->i2c_info.i2c_dev, disable_combo_reset_cmd, + sizeof(disable_combo_reset_cmd)/sizeof(u8)); + pr_err("disable combo_reset_command ret: %d", ret); + } else { + pr_err("skip disable combo_reset_command"); + } + + if (alreadFirmHigh == 1) { + pr_err("Firm is already high; do not anything2"); + } else { /*Firm pin is low*/ + mdelay(3); /*wait for FW*/ + gpio_set_value(pdata->firm, SEC_NFC_FW_OFF); + } + } +#endif + info->i2c_info.read_irq = SEC_NFC_SKIP; + mutex_unlock(&info->i2c_info.read_mutex); +#endif + +#ifdef CONFIG_SEC_ESE_COLDRESET + mdelay(1); + pr_err("FIRMWARE_GUARD_TIME(+1ms) in PW_OFF(total:4ms)\n"); +#endif + + gpio_set_value(pdata->ven, SEC_NFC_PW_OFF); + if (pdata->firm) + gpio_set_value(pdata->firm, SEC_NFC_FW_OFF); + + if (mode == SEC_NFC_MODE_BOOTLOADER) + if (pdata->firm) + gpio_set_value(pdata->firm, SEC_NFC_FW_ON); + + if (mode != SEC_NFC_MODE_OFF) { + msleep(SEC_NFC_VEN_WAIT_TIME); + sec_nfc_clk_ctl_enable(info); + gpio_set_value(pdata->ven, SEC_NFC_PW_ON); +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + val |= 0xC0000000; + writel(val, info->clkctrl); +#endif +#ifdef CONFIG_SEC_NFC_IF_I2C + enable_irq_wake(info->i2c_info.i2c_dev->irq); +#endif + msleep(SEC_NFC_VEN_WAIT_TIME/2); + } else { +#ifdef CONFIG_SEC_ESE_COLDRESET + int PW_OFF_DURATION = 20; + struct timespec t0, t1; + getnstimeofday(&t0); + mdelay(PW_OFF_DURATION); + + if (is_shutdown == false) + gpio_set_value(pdata->ven, SEC_NFC_PW_ON); + + getnstimeofday(&t1); + pr_err("DeepStby: PW_OFF duration (%d)ms, real PW_OFF duration is (%ld-%ld)ms\n", + PW_OFF_DURATION ,t0.tv_nsec*1000, t1.tv_nsec*1000); + pr_err("DeepStby: enter DeepStby(PW_ON)\n"); +#endif + sec_nfc_clk_ctl_disable(info); +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + val &= ~0xC0000000; + writel(val, info->clkctrl); +#endif +#ifdef CONFIG_SEC_NFC_IF_I2C + disable_irq_wake(info->i2c_info.i2c_dev->irq); +#endif + } + +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + if (wake_lock_active(&info->nfc_wake_lock)) + wake_unlock(&info->nfc_wake_lock); +#endif + + pr_info("%s NFC mode is : %d\n", __func__, mode); +} + +#ifdef CONFIG_SEC_ESE_COLDRESET +struct cold_reset_gpio { + int firm_gpio; + int coldreset_gpio; +}; + +struct cold_reset_gpio cold_reset_gpio_data; + +void init_coldreset_mutex(void) +{ + mutex_init(&coldreset_mutex); +} + +void init_sleep_wake_mutex(void) +{ + mutex_init(&sleep_wake_mutex); +} + +void check_and_sleep_nfc(unsigned int gpio, int value) { + if (sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] == true || + sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] == true) { + pr_info("%s keep wake up state\n", __func__); + return; + } + gpio_set_value(gpio, value); +} + +int trig_cold_reset_id(int id) +{ + int wakeup_delay = 20; + int duration = 18; + struct timespec t0, t1, t2; + int isFirmHigh = 0; + + if (id == ESE_ID) + mutex_lock(&coldreset_mutex); + + getnstimeofday(&t0); + if (gpio_get_value(cold_reset_gpio_data.firm_gpio) == 1) { + isFirmHigh = 1; + } else { + gpio_set_value(cold_reset_gpio_data.firm_gpio, SEC_NFC_FW_ON); + mdelay(wakeup_delay); + } + + getnstimeofday(&t1); + gpio_set_value(cold_reset_gpio_data.coldreset_gpio, SEC_NFC_COLDRESET_ON); + mdelay(duration); + gpio_set_value(cold_reset_gpio_data.coldreset_gpio, SEC_NFC_COLDRESET_OFF); + getnstimeofday(&t2); + + if (isFirmHigh == 1) + pr_err("COLDRESET: FW_PIN already high, do not FW_OFF\n"); + else + gpio_set_value(cold_reset_gpio_data.firm_gpio, SEC_NFC_FW_OFF); + + pr_err("COLDRESET: FW_ON time (%ld-%ld)\n", t0.tv_nsec*1000, t1.tv_nsec*1000); + pr_err("COLDRESET: GPIO3 ON time (%ld-%ld)\n", t1.tv_nsec*1000, t2.tv_nsec*1000); + + if (id == ESE_ID) + mutex_unlock(&coldreset_mutex); + + pr_err("COLDRESET: exit"); + return 0; +} + +extern int trig_cold_reset(void) // Called from eSE driver +{ + pr_info("%s trig_cold_reset\n", __func__); + return trig_cold_reset_id(ESE_ID); +} +EXPORT_SYMBOL(trig_cold_reset); + +extern int trig_nfc_wakeup(void) +{ + pr_info("%s trig_nfc_wakeup\n", __func__); + + if (cur_mode == SEC_NFC_MODE_OFF || cur_mode == SEC_NFC_MODE_BOOTLOADER) { + pr_err("nfc mode not support to wake up"); + return -EPERM;; + } + mutex_lock(&sleep_wake_mutex); + gpio_set_value(cold_reset_gpio_data.firm_gpio, SEC_NFC_WAKE_UP); + sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] = true; + mutex_unlock(&sleep_wake_mutex); + return 0; +} +EXPORT_SYMBOL(trig_nfc_wakeup); + +extern int trig_nfc_sleep(void) +{ + pr_info("%s trig_nfc_sleep\n", __func__); + if (cur_mode == SEC_NFC_MODE_OFF || cur_mode == SEC_NFC_MODE_BOOTLOADER) { + pr_err("nfc mode not support to sleep"); + return -EPERM;; + } + mutex_lock(&sleep_wake_mutex); + sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] = false; + check_and_sleep_nfc(cold_reset_gpio_data.firm_gpio, SEC_NFC_WAKE_SLEEP); + mutex_unlock(&sleep_wake_mutex); + return 0; +} +EXPORT_SYMBOL(trig_nfc_sleep); +#endif + +static long sec_nfc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + struct sec_nfc_platform_data *pdata = info->pdata; + unsigned int new = (unsigned int)arg; + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p, cmd: 0x%x\n", + __func__, info, cmd); + + mutex_lock(&info->mutex); +#ifdef CONFIG_SEC_ESE_COLDRESET + mutex_lock(&coldreset_mutex); +#endif + + switch (cmd) { + case SEC_NFC_SET_MODE: + if (info->mode == new) + break; + + if (new >= SEC_NFC_MODE_COUNT) { + dev_err(info->dev, "wrong mode (%d)\n", new); + ret = -EFAULT; + break; + } + sec_nfc_set_mode(info, new); + break; + +#if defined(CONFIG_SEC_NFC_PRODUCT_N3) + case SEC_NFC_SLEEP: + case SEC_NFC_WAKEUP: + break; + +#elif defined(CONFIG_SEC_NFC_PRODUCT_N5) || defined(CONFIG_SEC_NFC_PRODUCT_N7) + case SEC_NFC_SLEEP: + if (info->mode != SEC_NFC_MODE_BOOTLOADER) { +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + if (wake_lock_active(&info->nfc_wake_lock)) + wake_unlock(&info->nfc_wake_lock); +#endif +#ifdef CONFIG_SEC_ESE_COLDRESET + mutex_lock(&sleep_wake_mutex); + sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] = false; + check_and_sleep_nfc(pdata->wake, SEC_NFC_WAKE_SLEEP); + mutex_unlock(&sleep_wake_mutex); +#else + gpio_set_value(pdata->wake, SEC_NFC_WAKE_SLEEP); +#endif + } + break; + + case SEC_NFC_WAKEUP: + if (info->mode != SEC_NFC_MODE_BOOTLOADER) { + gpio_set_value(pdata->wake, SEC_NFC_WAKE_UP); +#ifdef CONFIG_SEC_ESE_COLDRESET + sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] = true; +#endif +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + if (!wake_lock_active(&info->nfc_wake_lock)) + wake_lock(&info->nfc_wake_lock); +#endif + } + break; +#endif +#ifdef CONFIG_SEC_ESE_COLDRESET + case SEC_NFC_COLD_RESET: + trig_cold_reset_id(DEVICEHOST_ID); + break; + case SEC_NFC_SHUTDOWN: + is_shutdown = true; + break; +#endif + default: + dev_err(info->dev, "Unknown ioctl 0x%x\n", cmd); + ret = -ENOIOCTLCMD; + break; + } + +#ifdef CONFIG_SEC_ESE_COLDRESET + mutex_unlock(&coldreset_mutex); +#endif + mutex_unlock(&info->mutex); + + return ret; +} + +static int sec_nfc_open(struct inode *inode, struct file *file) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + int ret = 0; + + pr_info("%s info : %p\n", __func__, info); + + mutex_lock(&info->mutex); + if (info->mode != SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is busy\n"); + ret = -EBUSY; + goto out; + } + + sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); + +out: + mutex_unlock(&info->mutex); + return ret; +} + +static int sec_nfc_close(struct inode *inode, struct file *file) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + + pr_info("%s: info : %p\n", __func__, info); + + mutex_lock(&info->mutex); + sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); + mutex_unlock(&info->mutex); + + return 0; +} + +static const struct file_operations sec_nfc_fops = { + .owner = THIS_MODULE, + .read = sec_nfc_read, + .write = sec_nfc_write, + .poll = sec_nfc_poll, + .open = sec_nfc_open, + .release = sec_nfc_close, + .unlocked_ioctl = sec_nfc_ioctl, +}; + +#ifdef CONFIG_PM +static int sec_nfc_suspend(struct device *dev) +{ + struct sec_nfc_info *info = SEC_NFC_GET_INFO(dev); + struct sec_nfc_platform_data *pdata = info->pdata; + int ret = 0; + int value = 0; + + mutex_lock(&info->mutex); + + if (info->mode == SEC_NFC_MODE_BOOTLOADER) + ret = -EPERM; + + value = gpio_get_value(pdata->wake); + if(value > 0){ + pr_info("%s: hal is still active, block suspend.\n", __func__); + ret = -EPERM; + } + mutex_unlock(&info->mutex); + + return ret; +} + +static int sec_nfc_resume(struct device *dev) +{ + return 0; +} + +static SIMPLE_DEV_PM_OPS(sec_nfc_pm_ops, sec_nfc_suspend, sec_nfc_resume); +#endif + +#ifdef CONFIG_OF +/*device tree parsing*/ +static int sec_nfc_parse_dt(struct device *dev, + struct sec_nfc_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + + pdata->ven = of_get_named_gpio(np, "sec-nfc,ven-gpio", 0); + pdata->firm = of_get_named_gpio(np, "sec-nfc,firm-gpio", 0); + pdata->wake = pdata->firm; +#ifdef CONFIG_SEC_NFC_IF_I2C + pdata->irq = of_get_named_gpio(np, "sec-nfc,irq-gpio", 0); +#endif + +#ifdef CONFIG_SEC_ESE_COLDRESET + pdata->coldreset = of_get_named_gpio(np, "sec-nfc,coldreset-gpio",0); + cold_reset_gpio_data.firm_gpio = pdata->firm; + cold_reset_gpio_data.coldreset_gpio = pdata->coldreset; +#endif + +#ifdef CONFIG_SEC_NFC_LDO_EN + pdata->pvdd_en = of_get_named_gpio(np, "sec-nfc,ldo_en",0); +#endif +#ifdef CONFIG_SEC_NFC_PMIC_LDO + if (of_property_read_string(np, "sec-nfc,pmic-ldo", &pdata->pvdd_regulator_name)) + pr_err("%s: Failed to get %s regulator.\n", __func__, pdata->pvdd_regulator_name); +#endif + +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + if(of_property_read_u32(np, "clkctrl-reg", (u32 *)&pdata->clkctrl_addr)) + return -EINVAL; +#endif +#ifdef CONFIG_SEC_NFC_GPIO_CLK + pdata->clk_req = of_get_named_gpio(np, "sec-nfc,clk_req-gpio", 0); + pdata->clk = clk_get(dev, "OSC_NFC"); +#endif + + pr_info("%s: irq : %d, ven : %d, firm : %d\n", + __func__, pdata->irq, pdata->ven, pdata->firm); + return 0; +} +#else /* CONFIG_OF */ +static int sec_nfc_parse_dt(struct device *dev, + struct sec_nfc_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int __sec_nfc_probe(struct device *dev) +{ + struct sec_nfc_info *info; + struct sec_nfc_platform_data *pdata = NULL; + int ret = 0; + + pr_info("%s : start\n", __func__); + if (dev->of_node) { + pdata = devm_kzalloc(dev, sizeof(struct sec_nfc_platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = sec_nfc_parse_dt(dev, pdata); + if (ret) + return ret; + } else { + pdata = dev->platform_data; + } + + if (!pdata) { + dev_err(dev, "No platform data\n"); + ret = -ENOMEM; + goto err_pdata; + } + + info = kzalloc(sizeof(struct sec_nfc_info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto err_info_alloc; + } + info->dev = dev; + info->pdata = pdata; + info->mode = SEC_NFC_MODE_OFF; + + mutex_init(&info->mutex); + dev_set_drvdata(dev, info); + + info->miscdev.minor = MISC_DYNAMIC_MINOR; + info->miscdev.name = SEC_NFC_DRIVER_NAME; + info->miscdev.fops = &sec_nfc_fops; + info->miscdev.parent = dev; + ret = misc_register(&info->miscdev); + if (ret < 0) { + dev_err(dev, "failed to register Device\n"); + goto err_dev_reg; + } + +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + if (pdata->clkctrl_addr != 0) { + info->clkctrl = ioremap_nocache(pdata->clkctrl_addr, 0x80); + if (!info->clkctrl) { + dev_err(dev, "cannot remap nfc clock register\n"); + ret = -ENXIO; + goto err_iomap; + } + } +#endif +#ifdef CONFIG_SEC_NFC_LDO_EN + { + ret = gpio_request(pdata->pvdd_en, "ldo_en"); + if (ret < 0) { + pr_err("failed to request about pvdd_en pin\n"); + goto err_dev_reg; + } + if (!is_charging_mode) + gpio_direction_output(pdata->pvdd_en, NFC_I2C_LDO_ON); + pr_info("pvdd en: %d\n", gpio_get_value(pdata->pvdd_en)); + } +#endif +#ifdef CONFIG_SEC_NFC_PMIC_LDO + if (!is_charging_mode) { + pdata->pvdd_regulator = regulator_get(NULL, pdata->pvdd_regulator_name); + if (IS_ERR(pdata->pvdd_regulator)) + pr_err("%s: Failed to get %s pmic regulator.\n", __func__, pdata->pvdd_regulator); + ret = regulator_enable(pdata->pvdd_regulator); + pr_err("%s: Failed to enable pmic regulator: %d\n", __func__, ret); + } +#endif + + ret = gpio_request(pdata->ven, "nfc_ven"); + if (ret) { + dev_err(dev, "failed to get gpio ven\n"); + goto err_gpio_ven; + } + if (!is_charging_mode) + gpio_direction_output(pdata->ven, SEC_NFC_PW_OFF); + + if (pdata->firm) { + ret = gpio_request(pdata->firm, "nfc_firm"); + if (ret) { + dev_err(dev, "failed to get gpio firm\n"); + goto err_gpio_firm; + } + gpio_direction_output(pdata->firm, SEC_NFC_FW_OFF); + } +#ifdef CONFIG_SEC_ESE_COLDRESET + init_coldreset_mutex(); + init_sleep_wake_mutex(); + memset(sleep_wakeup_state, false, sizeof(sleep_wakeup_state)); + ret = gpio_request(pdata->coldreset, "nfc_coldreset"); + if (ret) { + dev_err(dev, "failed to get gpio coldreset(NFC-GPIO3)\n"); + goto err_gpio_coldreset; + } + gpio_direction_output(pdata->coldreset, SEC_NFC_COLDRESET_OFF); +#endif +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + wake_lock_init(&info->nfc_wake_lock, WAKE_LOCK_SUSPEND, "nfc_wake_lock"); +#endif + + dev_info(dev, "%s: success info: %p, pdata %p\n", __func__, info, pdata); + + return 0; + +#ifdef CONFIG_SEC_ESE_COLDRESET +err_gpio_coldreset: + gpio_free(pdata->coldreset); +#endif +err_gpio_firm: + gpio_free(pdata->ven); +err_gpio_ven: +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + iounmap(info->clkctrl); +err_iomap: +#endif +err_dev_reg: + mutex_destroy(&info->mutex); + kfree(info); +err_info_alloc: + devm_kfree(dev, pdata); +err_pdata: + return ret; +} + +static int __sec_nfc_remove(struct device *dev) +{ + struct sec_nfc_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->i2c_info.i2c_dev; + struct sec_nfc_platform_data *pdata = info->pdata; + + dev_dbg(info->dev, "%s\n", __func__); + +#ifdef CONFIG_SEC_NFC_GPIO_CLK + if (pdata->clk) + clk_unprepare(pdata->clk); +#endif + + misc_deregister(&info->miscdev); + sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); + free_irq(client->irq, info); + gpio_free(pdata->irq); + gpio_set_value(pdata->firm, 0); + gpio_free(pdata->ven); + if (pdata->firm) + gpio_free(pdata->firm); + +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + wake_lock_destroy(&info->nfc_wake_lock); +#endif + + kfree(info); + + return 0; +} + +#ifdef CONFIG_SEC_NFC_IF_I2C +static struct i2c_device_id sec_nfc_id_table[] = { + { SEC_NFC_DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sec_nfc_id_table); +typedef struct i2c_driver sec_nfc_driver_type; +#define SEC_NFC_INIT(driver) i2c_add_driver(driver) +#define SEC_NFC_EXIT(driver) i2c_del_driver(driver) + +static int sec_nfc_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + + ret = __sec_nfc_probe(&client->dev); + if (ret) + return ret; + + if (sec_nfc_i2c_probe(client)) + __sec_nfc_remove(&client->dev); + + return ret; +} + +static int sec_nfc_remove(struct i2c_client *client) +{ + sec_nfc_i2c_remove(&client->dev); + return __sec_nfc_remove(&client->dev); +} +#else /* CONFIG_SEC_NFC_IF_I2C */ +MODULE_DEVICE_TABLE(platform, sec_nfc_id_table); +typedef struct platform_driver sec_nfc_driver_type; +#define SEC_NFC_INIT(driver) platform_driver_register(driver); +#define SEC_NFC_EXIT(driver) platform_driver_unregister(driver); + +static int sec_nfc_probe(struct platform_device *pdev) +{ + return __sec_nfc_probe(&pdev->dev); +} + +static int sec_nfc_remove(struct platform_device *pdev) +{ + return __sec_nfc_remove(&pdev->dev); +} + +static struct platform_device_id sec_nfc_id_table[] = { + { SEC_NFC_DRIVER_NAME, 0 }, + { } +}; +#endif /* CONFIG_SEC_NFC_IF_I2C */ + +#ifdef CONFIG_OF +static const struct of_device_id nfc_match_table[] = { + { .compatible = SEC_NFC_DRIVER_NAME,}, + {}, +}; +#else /* CONFIG_OF */ +#define nfc_match_table NULL +#endif + +static struct i2c_driver sec_nfc_driver = { + .probe = sec_nfc_probe, + .id_table = sec_nfc_id_table, + .remove = sec_nfc_remove, + .driver = { + .name = SEC_NFC_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &sec_nfc_pm_ops, +#endif + .of_match_table = nfc_match_table, + }, +}; + +#if 0 +static int __init is_poweroff_charging_mode(char *str) +{ + if (strncmp("charger", str, 7) == 0) + is_charging_mode = true; + else + is_charging_mode = false; + + return 0; +} early_param("androidboot.mode", is_poweroff_charging_mode); +#endif + +static int __init sec_nfc_init(void) +{ + return SEC_NFC_INIT(&sec_nfc_driver); +} + +static void __exit sec_nfc_exit(void) +{ + SEC_NFC_EXIT(&sec_nfc_driver); +} + +module_init(sec_nfc_init); +module_exit(sec_nfc_exit); + +MODULE_DESCRIPTION("Samsung sec_nfc driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/samsung/sec_nfc.h b/drivers/nfc/samsung/sec_nfc.h new file mode 100644 index 000000000000..78d525115686 --- /dev/null +++ b/drivers/nfc/samsung/sec_nfc.h @@ -0,0 +1,142 @@ +/* + * SAMSUNG NFC Controller + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Author: Woonki Lee + * Author: Heejae Kim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Last update: 2016-01-14 + */ + +#include +#ifdef CONFIG_SEC_NFC_GPIO_CLK +#include +#endif +#ifdef CONFIG_SEC_NFC_PMIC_LDO +#include +#include +#endif + +#ifdef CONFIG_SEC_NFC_I2C /* support old driver configuration */ + +#ifndef CONFIG_SEC_NFC +#define CONFIG_SEC_NFC +#endif + +#if !defined(CONFIG_SEC_NFC_IF_I2C) && !defined(CONFIG_SEC_NFC_IF_UART) +#define CONFIG_SEC_NFC_IF_I2C +#endif + +#if !defined(CONFIG_SEC_NFC_PRODUCT_N3) && !defined(CONFIG_SEC_NFC_PRODUCT_N5) +#define CONFIG_SEC_NFC_PRODUCT_N3 +#endif + +#endif /* CONFIG_SEC_NFC_I2C */ + +#define SEC_NFC_DRIVER_NAME "sec-nfc" + +#define SEC_NFC_MAX_BUFFER_SIZE 512 + +/* ioctl */ +#define SEC_NFC_MAGIC 'S' +#define SEC_NFC_GET_MODE _IOW(SEC_NFC_MAGIC, 0, unsigned int) +#define SEC_NFC_SET_MODE _IOW(SEC_NFC_MAGIC, 1, unsigned int) +#define SEC_NFC_SLEEP _IOW(SEC_NFC_MAGIC, 2, unsigned int) +#define SEC_NFC_WAKEUP _IOW(SEC_NFC_MAGIC, 3, unsigned int) +#ifdef CONFIG_SEC_ESE_COLDRESET +#define SEC_NFC_COLD_RESET _IOW(SEC_NFC_MAGIC, 5, unsigned int) +#define SEC_NFC_SHUTDOWN _IOW(SEC_NFC_MAGIC, 6, unsigned int) +#endif + +/* size */ +#define SEC_NFC_MSG_MAX_SIZE (256 + 4) + +/* wait for device stable */ +#ifdef CONFIG_SEC_NFC_MARGINTIME +#define SEC_NFC_VEN_WAIT_TIME (150) +#else +#define SEC_NFC_VEN_WAIT_TIME (20) +#endif + +/* gpio pin configuration */ +struct sec_nfc_platform_data { + int irq; + int ven; + int firm; + int wake; + int pvdd_en; + unsigned int tvdd; + unsigned int avdd; +#ifdef CONFIG_SEC_ESE_COLDRESET + unsigned int coldreset; +#endif +#ifdef CONFIG_SEC_NFC_GPIO_CLK + unsigned int clk_req; + struct clk *clk; +#endif + + void (*cfg_gpio)(void); + u32 ven_gpio_flags; + u32 firm_gpio_flags; + u32 irq_gpio_flags; + const char *nfc_pvdd; +#ifdef CONFIG_SEC_NFC_PMIC_LDO + const char *pvdd_regulator_name; + struct regulator *pvdd_regulator; +#endif +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + phys_addr_t clkctrl_addr; +#endif +}; + +enum sec_nfc_mode { + SEC_NFC_MODE_OFF = 0, + SEC_NFC_MODE_FIRMWARE, + SEC_NFC_MODE_BOOTLOADER, + SEC_NFC_MODE_COUNT, +}; + +#ifdef CONFIG_SEC_NFC_PRODUCT_N3 +enum sec_nfc_power { + SEC_NFC_PW_OFF = 0, + SEC_NFC_PW_ON, +}; +#else /* CONFIG_SEC_NFC_PRODUCT_N3 */ +enum sec_nfc_power { + SEC_NFC_PW_ON = 0, + SEC_NFC_PW_OFF, +}; +#endif + +enum sec_nfc_firmpin { + SEC_NFC_FW_OFF = 0, + SEC_NFC_FW_ON, +}; + +enum sec_nfc_wake { + SEC_NFC_WAKE_SLEEP = 0, + SEC_NFC_WAKE_UP, +}; + +#ifdef CONFIG_SEC_ESE_COLDRESET +enum sec_nfc_coldreset{ + SEC_NFC_COLDRESET_OFF = 0, + SEC_NFC_COLDRESET_ON, +}; + +#define FIRMWARE_GUARD_TIME (4) +#define DEVICEHOST_ID (0x00) +#define ESE_ID (0x02) +#define IDX_SLEEP_WAKEUP_NFC 0 +#define IDX_SLEEP_WAKEUP_ESE 1 +#endif + +bool is_charging_mode; + +#define NFC_I2C_LDO_ON 1 +#define NFC_I2C_LDO_OFF 0 diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index d20e59378909..e374c49d01e2 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -343,4 +343,7 @@ config PINCTRL_BLAIR Say Y here to compile statically, or M here to compile it as a module. If unsure, say N. +config QUP0_SE2_AP + bool "Set GPIO of QUP0_SE2 as AP side" + endif diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 1ba4ba27a173..24ad4cb8a9f7 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Qualcomm pin control drivers +ccflags-$(CONFIG_QUP0_SE2_AP) += -DQUP0_SE2_AP + obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o obj-$(CONFIG_PINCTRL_APQ8084) += pinctrl-apq8084.o diff --git a/drivers/pinctrl/qcom/pinctrl-blair.c b/drivers/pinctrl/qcom/pinctrl-blair.c index 39ee6e4674c0..648e457c3974 100644 --- a/drivers/pinctrl/qcom/pinctrl-blair.c +++ b/drivers/pinctrl/qcom/pinctrl-blair.c @@ -1593,7 +1593,11 @@ static const struct msm_pingroup blair_groups[] = { }; static const int blair_reserved_gpios[] = { - 13, 14, 15, 16, 17, 45, 46, 48, 56, 57, -1 +#ifdef QUP0_SE2_AP + 13, 14, 15, 16, -1 +#else + 13, 14, 15, 16, 45, 46, 56, 57, -1 +#endif }; static const struct msm_gpio_wakeirq_map blair_mpm_map[] = { diff --git a/drivers/pinctrl/qcom/pinctrl-holi.c b/drivers/pinctrl/qcom/pinctrl-holi.c index c2125c904876..73f7eb9145b9 100644 --- a/drivers/pinctrl/qcom/pinctrl-holi.c +++ b/drivers/pinctrl/qcom/pinctrl-holi.c @@ -1538,7 +1538,11 @@ static const struct msm_pingroup holi_groups[] = { }; static const int holi_reserved_gpios[] = { +#if defined(CONFIG_RHODEC_DTB)||defined(CONFIG_PNANGN_DTB) + 13, 14, 15, 16, -1 +#else 13, 14, 15, 16, 45, 46, 56, 57, -1 +#endif }; static const struct msm_gpio_wakeirq_map holi_mpm_map[] = { diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 707183744178..4bd5187f90cf 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1324,6 +1324,11 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) chip = &pctrl->chip; chip->base = -1; + if (!of_property_read_u32(pctrl->dev->of_node, "qcom,base-value", &ret)) { + chip->base = ret; + dev_info(pctrl->dev, "set base value as %d\n", chip->base); + } + chip->ngpio = ngpio; chip->label = dev_name(pctrl->dev); chip->parent = pctrl->dev; diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 8db5b7286ac3..002eb49200dc 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -132,6 +132,10 @@ static int panic_prep_restart(struct notifier_block *this, unsigned long event, void *ptr) { in_panic = 1; + __raw_writel(0x77665505, restart_reason); + qpnp_pon_store_extra_reset_info(RESET_EXTRA_PANIC_REASON, + RESET_EXTRA_PANIC_REASON); + pr_info("panic_prep_restart store extra reset info\n"); return NOTIFY_DONE; } @@ -162,6 +166,8 @@ static bool get_dload_mode(void) static void enable_emergency_dload_mode(void) { +#if 0 +// MOTO: Disabled for security hardening if (emergency_dload_mode_addr) { __raw_writel(EMERGENCY_DLOAD_MAGIC1, emergency_dload_mode_addr); @@ -181,6 +187,9 @@ static void enable_emergency_dload_mode(void) } qcom_scm_set_download_mode(SCM_EDLOAD_MODE, tcsr_boot_misc_detect ?: 0); +#else + pr_err("EDL mode disabled\n"); +#endif } static int dload_set(const char *val, const struct kernel_param *kp) @@ -395,6 +404,23 @@ void msm_set_restart_mode(int mode) } EXPORT_SYMBOL(msm_set_restart_mode); +#define DEBUG_SYS_RESETART_WARM 1 +#define DEBUG_SYS_RESETART_PANIC 2 +static int debug_sys_restart_mode; + +#ifdef CONFIG_QGKI +static int __init set_sys_restart_mode(char *str) +{ + if (!strcmp(str, "warm")) + debug_sys_restart_mode = DEBUG_SYS_RESETART_WARM; + else if (!strcmp(str, "panic")) + debug_sys_restart_mode = DEBUG_SYS_RESETART_PANIC; + pr_info("sys_restart_mode is set to %d\n", debug_sys_restart_mode); + return 1; +} + +__setup("sys_restart_mode=", set_sys_restart_mode); +#endif static void msm_restart_prepare(const char *cmd) { @@ -405,6 +431,11 @@ static void msm_restart_prepare(const char *cmd) * Kill download mode if master-kill switch is set */ + if (debug_sys_restart_mode == DEBUG_SYS_RESETART_PANIC) { + in_panic = 1; + pr_info("force system enter into ramdump for debug\n"); + } + if (cmd != NULL && !strcmp(cmd, "qcom_dload")) restart_mode = RESTART_DLOAD; @@ -426,7 +457,7 @@ static void msm_restart_prepare(const char *cmd) pr_info("Forcing a warm reset of the system\n"); /* Hard reset the PMIC unless memory contents must be maintained. */ - if (force_warm_reboot || need_warm_reset) + if (force_warm_reboot || need_warm_reset || in_panic) qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET); else qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); @@ -435,6 +466,14 @@ static void msm_restart_prepare(const char *cmd) if (!strncmp(cmd, "bootloader", 10)) { reason = PON_RESTART_REASON_BOOTLOADER; __raw_writel(0x77665500, restart_reason); + /* set reboot_bl flag in PMIC for cold reset */ + qpnp_pon_store_extra_reset_info(RESET_EXTRA_REBOOT_BL_REASON, + RESET_EXTRA_REBOOT_BL_REASON); + /* + * force cold reboot here to avoid unexpected + * warm boot from bootloader. + */ + qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); } else if (!strncmp(cmd, "recovery", 8)) { reason = PON_RESTART_REASON_RECOVERY; __raw_writel(0x77665502, restart_reason); @@ -460,6 +499,34 @@ static void msm_restart_prepare(const char *cmd) restart_reason); } else if (!strncmp(cmd, "edl", 3)) { enable_emergency_dload_mode(); + } else if (!strncmp(cmd, "post-wdt", 8)) { + /* set flag in PMIC to nofity BL post watchdog reboot */ + qpnp_pon_store_extra_reset_info(RESET_EXTRA_POST_REBOOT_MASK, + RESET_EXTRA_POST_WDT_REASON); + /* force cold reboot */ + qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); + } else if (!strncmp(cmd, "post-pmicwdt", 12)) { + /* set flag in PMIC to nofity BL post pmic watchdog reboot */ + qpnp_pon_store_extra_reset_info(RESET_EXTRA_POST_REBOOT_MASK, + RESET_EXTRA_POST_PMICWDT_REASON); + /* force cold reboot */ + qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); + } else if (!strncmp(cmd, "post-panic", 10)) { + /* set flag in PMIC to nofity BL post panic reboot */ + qpnp_pon_store_extra_reset_info(RESET_EXTRA_POST_REBOOT_MASK, + RESET_EXTRA_POST_PANIC_REASON); + /* force cold reboot */ + qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); + } else if (!strncmp(cmd, "hw_warmreset", 13)) { + reason = PON_RESTART_REASON_HW_WARMRESET; + qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET); + pr_info("set system hw warmreset mode\n"); + } else if (!strncmp(cmd, "post-hw_warmreset", 17)) { + /* set flag in PMIC to nofity BL post hw_warmreset reboot */ + qpnp_pon_store_extra_reset_info(RESET_EXTRA_POST_REBOOT_MASK, + RESET_EXTRA_POST_HWWARM_RESET_REASON); + /* force cold reboot */ + qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); } else { __raw_writel(0x77665501, restart_reason); } @@ -469,6 +536,17 @@ static void msm_restart_prepare(const char *cmd) else qpnp_pon_set_restart_reason( (enum pon_restart_reason)reason); + } else if (in_panic == 1) { + __raw_writel(0x77665505, restart_reason); + qpnp_pon_store_extra_reset_info(RESET_EXTRA_PANIC_REASON, + RESET_EXTRA_PANIC_REASON); + } else { + __raw_writel(0x77665501, restart_reason); + } + + if (debug_sys_restart_mode == DEBUG_SYS_RESETART_WARM) { + qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET); + pr_info("set system warmreset mode for debug\n"); } /*outer_flush_all is not supported by 64bit kernel*/ diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index f08dd44c9dba..8f632f9d3ca0 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_QPNP_SMB5) += qpnp-smb5-main.o -qpnp-smb5-main-y += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o battery-profile-loader.o smb5-iio.o +qpnp-smb5-main-y += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o mmi-pmic-voter.o storm-watch.o schgm-flash.o battery-profile-loader.o smb5-iio.o obj-$(CONFIG_QPNP_QG) += qcom-qpnp-qg.o qcom-qpnp-qg-y += qpnp-qg.o battery-profile-loader.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o obj-$(CONFIG_SMB1398_CHARGER) += qcom-smb1398-charger.o diff --git a/drivers/power/supply/qcom/battery-profile-loader.c b/drivers/power/supply/qcom/battery-profile-loader.c index 287ddb9e5403..5dfe2ebb966d 100644 --- a/drivers/power/supply/qcom/battery-profile-loader.c +++ b/drivers/power/supply/qcom/battery-profile-loader.c @@ -42,6 +42,88 @@ static int of_batterydata_read_batt_id_kohm(const struct device_node *np, return 0; } +static const char *get_battery_serialnumber(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + battsn_buf = NULL; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + pr_err("Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + pr_err("Battsn = %s\n", battsn_buf); + + of_node_put(np); + + return battsn_buf; +} + +static struct device_node *get_profile_by_serialnumber( + const struct device_node *np) +{ + struct device_node *node, *df_node, *sn_node; + const char *sn_buf, *df_sn, *dev_sn; + int rc; + + if (!np) + return NULL; + + dev_sn = NULL; + df_sn = NULL; + sn_buf = NULL; + df_node = NULL; + sn_node = NULL; + + dev_sn = get_battery_serialnumber(); + + rc = of_property_read_string(np, "df-serialnum", + &df_sn); + if (rc) + pr_warn("No Default Serial Number defined\n"); + else if (df_sn) + pr_info("Default Serial Number %s\n", df_sn); + + for_each_child_of_node(np, node) { + rc = of_property_read_string(node, "serialnum", + &sn_buf); + if (!rc && sn_buf) { + if (dev_sn) + if (strnstr(dev_sn, sn_buf, 32)) + sn_node = node; + if (df_sn) + if (strnstr(df_sn, sn_buf, 32)) + df_node = node; + } + } + + if (sn_node) { + node = sn_node; + df_node = NULL; + pr_info("Battery Match Found using %s\n", sn_node->name); + } else if (df_node) { + node = df_node; + sn_node = NULL; + pr_info("Battery Match Found using default %s\n", + df_node->name); + } else { + pr_warn("No Battery Match Found!\n"); + return NULL; + } + + return node; +} + struct device_node *of_batterydata_get_best_profile( const struct device_node *batterydata_container_node, int batt_id_kohm, const char *batt_type) @@ -66,6 +148,10 @@ struct device_node *of_batterydata_get_best_profile( } } + best_node = get_profile_by_serialnumber(batterydata_container_node); + if (best_node) + return best_node; + /* * Find the battery data with a battery id resistor closest to this one */ diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 5ba4510aad39..c2fd21158acc 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -1390,12 +1390,13 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, } /* rerun AICL if new ICL is above settled ICL */ - if (icl_ua > val) + if (icl_ua != INT_MAX && icl_ua > val) rerun_aicl = true; if (rerun_aicl && (chip->wa_flags & AICL_RERUN_WA_BIT)) { /* set a lower ICL */ val = max(val - ICL_STEP_UA, ICL_STEP_UA); + pr_debug("****initial step icl setting %d\n", val); rc = chip->chg_param->iio_write(chip->dev, PSY_IIO_CURRENT_MAX, val); if (rc < 0) @@ -1404,6 +1405,7 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, /* set the effective ICL */ val = icl_ua; + pr_debug("****final icl setting %d\n", val); rc = chip->chg_param->iio_write(chip->dev, PSY_IIO_CURRENT_MAX, val); if (rc < 0) pr_err("Couldn't set main current_max, rc=%d\n", rc); diff --git a/drivers/power/supply/qcom/mmi-pmic-voter.c b/drivers/power/supply/qcom/mmi-pmic-voter.c new file mode 100644 index 000000000000..cac27fa884f4 --- /dev/null +++ b/drivers/power/supply/qcom/mmi-pmic-voter.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2017, 2019-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include + +int mmi_get_effective_result(struct votable *votable) +{ + if(votable) + return get_effective_result(votable); + + return -EINVAL; +} +EXPORT_SYMBOL(mmi_get_effective_result); + +int mmi_vote(struct votable *votable, const char *client_str, bool enabled, int val) +{ + if(votable) + return vote(votable, client_str, enabled, val); + + return -EINVAL; +} +EXPORT_SYMBOL(mmi_vote); + +int mmi_rerun_election(struct votable *votable) +{ + if(votable) + return rerun_election(votable); + + return -EINVAL; +} +EXPORT_SYMBOL(mmi_rerun_election); + +struct votable *mmi_find_votable(const char *name) +{ + return find_votable(name); +} +EXPORT_SYMBOL(mmi_find_votable); + +int mmi_pmic_vote_force_val_set(struct votable *votable, u32 val) +{ + return pmic_vote_force_val_set(votable, val); +} +EXPORT_SYMBOL(mmi_pmic_vote_force_val_set); + +int mmi_pmic_vote_force_active_get(struct votable *votable, u64 *val) +{ + return pmic_vote_force_active_get(votable, val); +} +EXPORT_SYMBOL(mmi_pmic_vote_force_active_get); + +int mmi_pmic_vote_force_active_set(struct votable *votable, u64 val) +{ + return pmic_vote_force_active_set(votable, val); +} +EXPORT_SYMBOL(mmi_pmic_vote_force_active_set); + diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index b10c2a060b10..257771842183 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -645,6 +645,29 @@ out: DEFINE_DEBUGFS_ATTRIBUTE(votable_force_ops, force_active_get, force_active_set, "%lld\n"); +int pmic_vote_force_val_set(struct votable *votable, u32 val) { + if(votable) { + votable->force_val = val; + return 0; + } + + return -EINVAL; +} + +int pmic_vote_force_active_get(struct votable *votable, u64 *val) { + if(votable) + return force_active_get(votable, val); + + return -EINVAL; +} + +int pmic_vote_force_active_set(struct votable *votable, u64 val) { + if(votable) + return force_active_set(votable, val); + + return -EINVAL; +} + static int show_votable_clients(struct seq_file *m, void *data) { struct votable *votable = m->private; diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h index 2796e16c2a11..9ee46f79ed4a 100644 --- a/drivers/power/supply/qcom/qg-core.h +++ b/drivers/power/supply/qcom/qg-core.h @@ -21,6 +21,7 @@ struct qg_batt_props { int vbatt_full_mv; int fastchg_curr_ma; int qg_profile_version; + u32 sdam_magic_number; }; struct qg_irq_info { @@ -65,6 +66,7 @@ struct qg_dt { int fvss_vbat_mv; int tcss_entry_soc; int esr_low_temp_threshold; + int cp_chrg_soc_limit; bool hold_soc_while_full; bool linearize_soc; bool cl_disable; @@ -80,6 +82,7 @@ struct qg_dt { bool multi_profile_load; bool tcss_enable; bool bass_enable; + bool cp_use_internal_qg; }; struct qg_esr_data { diff --git a/drivers/power/supply/qcom/qg-util.c b/drivers/power/supply/qcom/qg-util.c index 2e2f61225ee4..61f91c1c088a 100644 --- a/drivers/power/supply/qcom/qg-util.c +++ b/drivers/power/supply/qcom/qg-util.c @@ -419,7 +419,7 @@ int qg_get_battery_voltage(struct qpnp_qg *chip, int *vbat_uv) u64 last_vbat = 0; if (chip->battery_missing) { - *vbat_uv = 3700000; + *vbat_uv = 4200000; return 0; } diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c index 4c8839273140..165f19ef6a22 100644 --- a/drivers/power/supply/qcom/qpnp-qg.c +++ b/drivers/power/supply/qcom/qpnp-qg.c @@ -1103,6 +1103,15 @@ static int qg_esr_estimate(struct qpnp_qg *chip) goto done; } + if ((chip->dt.cp_use_internal_qg) && (chip->msoc < chip->dt.cp_chrg_soc_limit)) { + rc = qg_write_iio_chan(chip, CP_CHARGING_ENABLED, 0); + if (rc < 0) + pr_err("Failed to force disable cp rc=%d\n", rc); + else + qg_dbg(chip, QG_DEBUG_STATUS, "disable cp, msoc =%d\n",chip->msoc); + msleep(1000); + } + for (i = 0; i < qg_esr_count; i++) { /* Fire ESR measurement */ rc = qg_masked_write(chip, @@ -1191,6 +1200,14 @@ static int qg_esr_estimate(struct qpnp_qg *chip) msleep(200); } + if ((chip->dt.cp_use_internal_qg) && (chip->msoc < chip->dt.cp_chrg_soc_limit)) { + rc = qg_write_iio_chan(chip, CP_CHARGING_ENABLED, 1); + if (rc < 0) + pr_err("Failed to force enable cp rc=%d\n", rc); + else + qg_dbg(chip, QG_DEBUG_STATUS, "enable cp, msoc =%d\n",chip->msoc); + } + rc = qg_process_esr_data(chip); if (rc < 0) pr_err("Failed to process ESR data rc=%d\n", rc); @@ -1768,7 +1785,7 @@ static const char *qg_get_battery_type(struct qpnp_qg *chip) } #define DEBUG_BATT_SOC 67 -#define BATT_MISSING_SOC 50 +#define BATT_MISSING_SOC 10 #define EMPTY_SOC 0 #define FULL_SOC 100 static int qg_get_battery_capacity(struct qpnp_qg *chip, int *soc) @@ -1828,8 +1845,10 @@ static int qg_get_charge_counter(struct qpnp_qg *chip, int *charge_counter) return rc; } - cc_soc = CAP(0, 100, DIV_ROUND_CLOSEST(chip->cc_soc, 100)); - *charge_counter = div_s64(temp * cc_soc, 100); + cc_soc = CAP(0, 100*100, chip->cc_soc); + *charge_counter = div_s64(temp * cc_soc, 10000); + + pr_info("charge-counter:%d\n",*charge_counter); return 0; } @@ -2150,6 +2169,10 @@ static int qg_iio_write_raw(struct iio_dev *indio_dev, case PSY_IIO_BATT_AGE_LEVEL: rc = qg_setprop_batt_age_level(chip, val1); break; + case PSY_IIO_BATT_FULL_CURRENT: + chip->dt.iterm_ma = val1; + qg_dbg(chip, QG_DEBUG_STATUS, "set bat full current = %d\n", chip->dt.iterm_ma); + break; default: pr_debug("Unsupported QG IIO chan %d\n", chan->channel); rc = -EINVAL; @@ -2717,14 +2740,23 @@ static int qg_psy_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *pval) { + struct qpnp_qg *chip = power_supply_get_drvdata(psy); + int rc = 0; + if (psp == POWER_SUPPLY_PROP_TYPE) pval->intval = POWER_SUPPLY_TYPE_MAINS; + else if (psp == POWER_SUPPLY_PROP_CHARGE_NOW) + pval->intval = chip->cl->init_cap_uah; + else if (psp == POWER_SUPPLY_PROP_VOLTAGE_OCV) + rc = qg_sdam_read(SDAM_OCV_UV, &pval->intval); - return 0; + return rc; } static enum power_supply_property qg_psy_props[] = { POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_OCV, }; static const struct power_supply_desc qg_psy_desc = { @@ -3037,6 +3069,7 @@ static int get_batt_id_ohm(struct qpnp_qg *chip, u32 *batt_id_ohm) return 0; } +#define SDAM_MAGIC_NUMBER 0x12345678 static int qg_load_battery_profile(struct qpnp_qg *chip) { struct device_node *node = chip->dev->of_node; @@ -3106,6 +3139,13 @@ static int qg_load_battery_profile(struct qpnp_qg *chip) chip->bp.fastchg_curr_ma = -EINVAL; } + rc = of_property_read_u32(profile_node, "qcom,sdam-magic-number", + &chip->bp.sdam_magic_number); + if (rc < 0) { + pr_err("Failed to read sdam magic number rc:%d\n", rc); + chip->bp.sdam_magic_number = SDAM_MAGIC_NUMBER; + } + /* * Update the max fcc values based on QG subtype including * error margins. @@ -3458,7 +3498,6 @@ static int qg_set_wa_flags(struct qpnp_qg *chip) return 0; } -#define SDAM_MAGIC_NUMBER 0x12345678 static int qg_sanitize_sdam(struct qpnp_qg *chip) { int rc = 0; @@ -3470,10 +3509,10 @@ static int qg_sanitize_sdam(struct qpnp_qg *chip) return rc; } - if (data == SDAM_MAGIC_NUMBER) { + if (data == chip->bp.sdam_magic_number) { qg_dbg(chip, QG_DEBUG_PON, "SDAM valid\n"); } else if (data == 0) { - rc = qg_sdam_write(SDAM_MAGIC, SDAM_MAGIC_NUMBER); + rc = qg_sdam_write(SDAM_MAGIC, chip->bp.sdam_magic_number); if (!rc) qg_dbg(chip, QG_DEBUG_PON, "First boot. SDAM initilized\n"); chip->first_profile_load = true; @@ -3482,7 +3521,7 @@ static int qg_sanitize_sdam(struct qpnp_qg *chip) rc = qg_sdam_clear(); if (!rc) { pr_err("SDAM uninitialized, SDAM reset\n"); - rc = qg_sdam_write(SDAM_MAGIC, SDAM_MAGIC_NUMBER); + rc = qg_sdam_write(SDAM_MAGIC, chip->bp.sdam_magic_number); } chip->first_profile_load = true; } @@ -4353,6 +4392,14 @@ static int qg_parse_dt(struct qpnp_qg *chip) chip->dt.esr_discharge_enable = of_property_read_bool(node, "qcom,esr-discharge-enable"); + chip->dt.cp_use_internal_qg = of_property_read_bool(node, + "mmi,cp-use-internal-qg"); + + rc = of_property_read_u32(node, "mmi,cp-chrg-soc-limit", &temp); + if (rc < 0) + chip->dt.cp_chrg_soc_limit = 100; + else + chip->dt.cp_chrg_soc_limit = temp; rc = of_property_read_u32(node, "qcom,esr-qual-current-ua", &temp); if (rc < 0) @@ -4698,6 +4745,10 @@ static int qpnp_qg_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct qg_config *config; + if (!alarmtimer_get_rtcdev()) { + pr_err("Failed:qpnp qg probe failed, wait rtc prob\n"); + return -EPROBE_DEFER; + } indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); if (!indio_dev) return -ENOMEM; diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index 8a25745698a3..d50f82d5b8fc 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -229,7 +229,7 @@ struct smb5 { struct iio_chan_spec *iio_chan_ids; }; -static int __debug_mask; +static int __debug_mask = PR_MISC | PR_INTERRUPT; static ssize_t pd_disabled_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -281,9 +281,38 @@ static ssize_t weak_chg_icl_ua_store(struct device *dev, struct device_attribute } static DEVICE_ATTR_RW(weak_chg_icl_ua); +static ssize_t usb_dcp_icl_ua_show(struct device *dev, struct device_attribute + *attr, char *buf) +{ + struct smb5 *chip = dev_get_drvdata(dev); + struct smb_charger *chg = &chip->chg; + + return snprintf(buf, PAGE_SIZE, "%d\n", chg->usb_dcp_curr_max); +} + +static ssize_t usb_dcp_icl_ua_store(struct device *dev, struct device_attribute + *attr, const char *buf, size_t count) +{ + int val; + struct smb5 *chip = dev_get_drvdata(dev); + struct smb_charger *chg = &chip->chg; + + if (kstrtos32(buf, 0, &val)) + return -EINVAL; + + chg->usb_dcp_curr_max = val; + pr_info("USB dcp icl was rewritten as %dua\n", chg->usb_dcp_curr_max); + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_DCP) + smblib_rerun_apsd_if_required(chg); + + return count; +} +static DEVICE_ATTR_RW(usb_dcp_icl_ua); + static struct attribute *smb5_attrs[] = { &dev_attr_pd_disabled.attr, &dev_attr_weak_chg_icl_ua.attr, + &dev_attr_usb_dcp_icl_ua.attr, NULL, }; ATTRIBUTE_GROUPS(smb5); @@ -597,6 +626,8 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) &chg->chg_param.qc4_max_icl_ua); if (chg->chg_param.qc4_max_icl_ua <= 0) chg->chg_param.qc4_max_icl_ua = MICRO_4PA; + chg->mmi_qc3p_support = of_property_read_bool(node, + "mmi,qc3p-support"); return 0; } @@ -763,6 +794,53 @@ static int smb5_parse_sdam(struct smb5 *chip, struct device_node *node) return 0; } +static int smb5_parse_dt_mmi(struct smb5 *chip, struct device_node *node) +{ + struct smb_charger *chg = &chip->chg; + struct device_node *npoint = chg->dev->of_node; + struct device_node *np; + const char *dcp_curr = NULL; + int retval; + + chg->usb_dcp_curr_max = 0; + np = of_find_node_by_path("/chosen"); + + retval = of_property_read_u32(npoint, "qcom,cdp-curr-max", + &chg->usb_cdp_curr_max); + if (retval) { + chg->usb_cdp_curr_max = 1500000; + } + + retval = of_property_read_u32(npoint, "qcom,dcp-curr-max", + &chg->usb_dcp_curr_max); + if (retval) { + chg->usb_dcp_curr_max = 1500000; + goto out; + } + + if (!np) + goto out; + + retval = of_property_read_string(np, "mmi,usb_dcp", + &dcp_curr); + + if ((retval == -EINVAL) || !dcp_curr) { + pr_info("mmi,usb_dcp unused\n"); + goto out; + } else + pr_info("usb_dcp = %s\n", dcp_curr); + + if (strstr(dcp_curr, "1.5A")) + chg->usb_dcp_curr_max = 1500000; + else if (strstr(dcp_curr, "2A")) + chg->usb_dcp_curr_max = 2000000; + +out: + if (np) + of_node_put(np); + return 0; +} + static int smb5_parse_dt(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; @@ -794,6 +872,10 @@ static int smb5_parse_dt(struct smb5 *chip) if (rc < 0) return rc; + rc = smb5_parse_dt_mmi(chip, node); + if (rc < 0) + return rc; + return 0; } @@ -871,7 +953,7 @@ static int smb5_usb_get_prop(struct power_supply *psy, rc = smblib_get_prop_usb_current_now(chg, val); break; case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = smblib_get_prop_input_current_max(chg, val); + val->intval = get_effective_result(chg->usb_icl_votable); break; case POWER_SUPPLY_PROP_TYPE: val->intval = POWER_SUPPLY_TYPE_USB_PD; @@ -1366,8 +1448,8 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy, } static const struct power_supply_desc batt_psy_desc = { - .name = "battery", - .type = POWER_SUPPLY_TYPE_BATTERY, + .name = "qcom_battery", + .type = POWER_SUPPLY_TYPE_MAINS, .properties = smb5_batt_props, .num_properties = ARRAY_SIZE(smb5_batt_props), .get_property = smb5_batt_get_prop, @@ -1469,6 +1551,9 @@ static int smb5_init_vconn_regulator(struct smb5 *chip) chg->vconn_vreg->rdesc.of_match = "qcom,smb5-vconn"; chg->vconn_vreg->rdesc.name = "qcom,smb5-vconn"; + if (of_get_property(chg->dev->of_node, "vconn-parent-supply", NULL)) + chg->vconn_vreg->rdesc.supply_name = "vconn-parent"; + chg->vconn_vreg->rdev = devm_regulator_register(chg->dev, &chg->vconn_vreg->rdesc, &cfg); if (IS_ERR(chg->vconn_vreg->rdev)) { @@ -1583,6 +1668,19 @@ static int smb5_configure_typec(struct smb_charger *chg) } } + /* + * Config SCHG_P_TYPEC_TYPE_C_CCOUT_CONTROL to 0x03 + * Enable detection of debug accessory in sink mode + */ + rc = smblib_masked_write(chg, TYPE_C_DEBUG_ACCESS_SINK_REG, + TYPEC_DEBUG_ACCESS_SINK_MASK, 0x03); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure TYPE_C_DEBUG_ACCESS_SINK_REG rc=%d\n", + rc); + return rc; + } + if (chg->chg_param.smb_version != PMI632) { /* * Enable detection of unoriented debug @@ -2422,7 +2520,7 @@ static struct smb_irq_info smb5_irqs[] = { }, [INPUT_CURRENT_LIMITING_IRQ] = { .name = "input-current-limiting", - .handler = smb5_default_irq_handler, + .handler = smb5_icl_irq_handler, }, [CONCURRENT_MODE_DISABLE_IRQ] = { .name = "concurrent-mode-disable", @@ -2883,6 +2981,49 @@ static int smb5_init_typec_class(struct smb5 *chip) return rc; } +static int smb5_typec_cable_check(struct smb5 *chip) +{ + struct smb_charger *chg = &chip->chg; + int rc = 0; + int i = 0; + u8 stat = 0; + bool usb_input_present = false; + + if (chg->pd_not_supported) + return rc; + + rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + pr_err("%s Couldn't read USBIN_RT_STS rc=%d\n", __func__, rc); + return rc; + } + usb_input_present = stat & USBIN_PLUGIN_RT_STS_BIT; + + rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat); + if (rc < 0) { + pr_err("%s Couldn't read TYPE_C_STATUS_4 rc=%d\n", __func__, rc); + return rc; + } + pr_info("%s usb_input_present=%d TYPE_C_STATUS_4=0x%02X\n", __func__, usb_input_present, stat); + + if (usb_input_present || (stat & TYPEC_VBUS_STATUS)) { + for (i = 0; i < 10; i++) { + rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat); + if (rc < 0) { + pr_err("%s Couldn't read TYPE_C_STATE_MACHINE_STATUS rc=%d\n", __func__, rc); + return rc; + } + pr_info("%s index=%d stat=0x%02X\n", __func__, i, stat); + if (stat & TYPEC_ATTACH_DETACH_STATE_BIT) { + break; + } + msleep(50); + } + } + + return rc; +} + static int smb5_of_xlate(struct iio_dev *indio_dev, const struct of_phandle_args *iiospec) { @@ -3164,6 +3305,8 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; } + smb5_typec_cable_check(chip); + rc = smb5_determine_initial_status(chip); if (rc < 0) { pr_err("Couldn't determine initial status rc=%d\n", diff --git a/drivers/power/supply/qcom/smb5-iio.c b/drivers/power/supply/qcom/smb5-iio.c index 071f4df29b83..a9aebd5e1bca 100644 --- a/drivers/power/supply/qcom/smb5-iio.c +++ b/drivers/power/supply/qcom/smb5-iio.c @@ -267,6 +267,12 @@ int smb5_iio_get_prop(struct smb_charger *chg, int channel, int *val) case PSY_IIO_TYPEC_ACCESSORY_MODE: rc = smblib_get_usb_prop_typec_accessory_mode(chg, val); break; + case PSY_IIO_CHARGING_ENABLED: + *val = !get_effective_result(chg->chg_disable_votable); + break; + case PSY_IIO_MMI_QC3P_POWER: + *val = chg->mmi_qc3p_power; + break; default: pr_err("get prop %d is not supported\n", channel); rc = -EINVAL; @@ -315,10 +321,14 @@ int smb5_iio_set_prop(struct smb_charger *chg, int channel, int val) rc = smblib_set_prop_pr_swap_in_progress(chg, val); break; case PSY_IIO_PD_VOLTAGE_MAX: +#ifdef QCOM_BASE rc = smblib_set_prop_pd_voltage_max(chg, val); +#endif break; case PSY_IIO_PD_VOLTAGE_MIN: +#ifdef QCOM_BASE rc = smblib_set_prop_pd_voltage_min(chg, val); +#endif break; case PSY_IIO_CONNECTOR_HEALTH: chg->connector_health = val; @@ -465,7 +475,12 @@ int smb5_iio_set_prop(struct smb_charger *chg, int channel, int val) rc = smblib_run_aicl(chg, RERUN_AICL); break; case PSY_IIO_DP_DM: - if (!chg->flash_active) + if (chg->mmi_qc3p_support) { + if ((chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + ||(!chg->flash_active && !chg->mmi_is_qc3p_authen + && chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3)) + rc = smblib_dp_dm(chg, val); + } else if (!chg->flash_active) rc = smblib_dp_dm(chg, val); break; case PSY_IIO_INPUT_CURRENT_LIMITED: @@ -487,10 +502,14 @@ int smb5_iio_set_prop(struct smb_charger *chg, int channel, int val) msleep(50); vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER, false, 0); + chg->chg_done = 0; break; case PSY_IIO_FCC_STEPPER_ENABLE: chg->fcc_stepper_enable = val; break; + case PSY_IIO_CHARGING_ENABLED: + vote(chg->chg_disable_votable, USER_VOTER, !!!val, 0); + break; default: pr_err("get prop %d is not supported\n", channel); rc = -EINVAL; diff --git a/drivers/power/supply/qcom/smb5-iio.h b/drivers/power/supply/qcom/smb5-iio.h index 40753d0d28a2..bbd90bf7367d 100644 --- a/drivers/power/supply/qcom/smb5-iio.h +++ b/drivers/power/supply/qcom/smb5-iio.h @@ -14,6 +14,7 @@ enum iio_type { QG, CP, SMB_PARALLEL, + MMI_CP, }; /* For qpnp-smb5.c and smb5-lib.c */ @@ -34,6 +35,10 @@ enum qg_chg_iio_channels { SMB5_QG_TIME_TO_FULL_NOW, }; +enum mmi_cp_iio_channels { + CP_INPUT_VOLTAGE_NOW, +}; + enum cp_iio_channels { CP_PARALLEL_OUTPUT_MODE, CP_MASTER_ENABLE, @@ -201,6 +206,8 @@ static const struct smb5_iio_prop_channels smb5_chans_pmic[] = { SMB5_CHAN_ACTIVITY("battery_force_recharge", FORCE_RECHARGE) SMB5_CHAN_ACTIVITY("battery_fcc_stepper_enable", FCC_STEPPER_ENABLE) SMB5_CHAN_INDEX("usb_typec_accessory_mode", TYPEC_ACCESSORY_MODE) + SMB5_CHAN_INDEX("charging_enabled", CHARGING_ENABLED) + SMB5_CHAN_INDEX("usb_qc3p_power", MMI_QC3P_POWER) }; struct iio_channel **get_ext_channels(struct device *dev, diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index 538bd7116830..e236b761be31 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,9 @@ #include "storm-watch.h" #include "smb5-iio.h" #include "battery-profile-loader.h" +#include +#include + #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ @@ -795,7 +799,7 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, int min_allowed_uv, int max_allowed_uv) { int rc, aicl_threshold; - u8 vbus_allowance; + u8 vbus_allowance = FORCE_NULL; if (chg->chg_param.smb_version == PMI632) return 0; @@ -875,6 +879,10 @@ int smblib_set_aicl_cont_threshold(struct smb_chg_param *param, /******************** * HELPER FUNCTIONS * ********************/ +/* MMI CP channels */ +static const char * const smblib_mmi_cp_ext_iio_chan[] = { + [CP_INPUT_VOLTAGE_NOW] = "cp_input_voltage_now", +}; /* CP channels */ static const char * const smblib_cp_ext_iio_chan[] = { @@ -931,6 +939,11 @@ static int smblib_read_iio_prop(struct smb_charger *chg, return -ENODEV; iio_chan_list = chg->iio_chan_list_smb_parallel[iio_chan]; break; + case MMI_CP: + if (IS_ERR_OR_NULL(chg->iio_chan_list_mmi_cp)) + return -ENODEV; + iio_chan_list = chg->iio_chan_list_mmi_cp[iio_chan]; + break; default: pr_err_ratelimited("iio_type %d is not supported\n", type); return -EINVAL; @@ -961,6 +974,11 @@ static int smblib_write_iio_prop(struct smb_charger *chg, return -ENODEV; iio_chan_list = chg->iio_chan_list_smb_parallel[iio_chan]; break; + case MMI_CP: + if (IS_ERR_OR_NULL(chg->iio_chan_list_mmi_cp)) + return -ENODEV; + iio_chan_list = chg->iio_chan_list_mmi_cp[iio_chan]; + break; default: pr_err_ratelimited("iio_type %d is not supported\n", type); return -EINVAL; @@ -969,6 +987,33 @@ static int smblib_write_iio_prop(struct smb_charger *chg, return iio_write_channel_raw(iio_chan_list, val); } +static bool is_mmi_cp_available(struct smb_charger *chg) +{ + int rc; + struct iio_channel **iio_list; + + if (IS_ERR(chg->iio_chan_list_mmi_cp)) + return false; + + if (!chg->iio_chan_list_mmi_cp) { + iio_list = get_ext_channels(chg->dev, + smblib_mmi_cp_ext_iio_chan, + ARRAY_SIZE(smblib_mmi_cp_ext_iio_chan)); + if (IS_ERR(iio_list)) { + rc = PTR_ERR(iio_list); + if (rc != -EPROBE_DEFER) { + dev_err(chg->dev, "Failed to get channels, rc=%d\n", + rc); + chg->iio_chan_list_mmi_cp = ERR_PTR(-EINVAL); + } + return false; + } + chg->iio_chan_list_mmi_cp = iio_list; + } + + return true; +} + static bool is_cp_available(struct smb_charger *chg) { int rc; @@ -1194,6 +1239,8 @@ void smblib_rerun_apsd(struct smb_charger *chg) APSD_RERUN_BIT, APSD_RERUN_BIT); if (rc < 0) smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc); + else + chg->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; } static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) @@ -1293,6 +1340,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, false, 0); vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0); vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); vote(chg->usb_icl_votable, THERMAL_THROTTLE_VOTER, false, 0); @@ -1313,6 +1361,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); chg->dbc_usbov = false; + chg->chg_done = 0; chg->voltage_min_uv = MICRO_5V; chg->voltage_max_uv = MICRO_5V; @@ -1524,6 +1573,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc); goto out; } + goto unsuspend; } else { /* * Try USB 2.0/3,0 option first on USB path when maximum input @@ -2146,7 +2196,10 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, * charging path is un-suspended. */ if (pval.intval && !suspend) { - val->intval = POWER_SUPPLY_STATUS_CHARGING; + if (chg->chg_done) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; return 0; } } @@ -2419,6 +2472,32 @@ int smblib_get_prop_batt_iterm(struct smb_charger *chg, return rc; } +static bool is_mmi_charge_full(struct smb_charger *chg) +{ int rc = 0; + union power_supply_propval prop = {0, }; + static struct power_supply *mmi_batt_psy = NULL; + + if (!mmi_batt_psy) { + mmi_batt_psy = power_supply_get_by_name("battery"); + if (!mmi_batt_psy) { + smblib_err(chg, "Couldn't get mmi batt prop\n"); + return false; + } + } + + rc = power_supply_get_property(mmi_batt_psy, + POWER_SUPPLY_PROP_STATUS, &prop); + if (rc < 0) { + smblib_err(chg, "Couldn't get mmi batt status, rc=%d\n", rc); + return false; + } + + if (POWER_SUPPLY_STATUS_FULL == prop.intval) + return true; + else + return false; +} + int smblib_get_prop_batt_charge_done(struct smb_charger *chg, int *val) { @@ -2434,6 +2513,9 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, stat = stat & BATTERY_CHARGER_STATUS_MASK; *val = (stat == TERMINATE_CHARGE); + + if (is_mmi_charge_full(chg)) + *val = true; return 0; } @@ -2572,6 +2654,10 @@ int smblib_run_aicl(struct smb_charger *chg, int type) if (stat & USBIN_SUSPEND_STS_BIT) return rc; + /* Neither usbin nor dcin exists so skip re-running AICL */ + if (!(stat & (USE_USBIN_BIT | USE_DCIN_BIT))) + return rc; + smblib_dbg(chg, PR_MISC, "re-running AICL\n"); stat = (type == RERUN_AICL) ? RERUN_AICL_BIT : RESTART_AICL_BIT; @@ -2823,8 +2909,10 @@ int smblib_dp_dm(struct smb_charger *chg, int val) pr_err("Failed to force 12V\n"); break; case QTI_POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3P5: - chg->qc3p5_detected = true; - smblib_update_usb_type(chg); + if (!chg->mmi_qc3p_support) { + chg->qc3p5_detected = true; + smblib_update_usb_type(chg); + } break; case QTI_POWER_SUPPLY_DP_DM_ICL_UP: default: @@ -3445,6 +3533,7 @@ int smblib_get_prop_usb_voltage_max_design(struct smb_charger *chg, { switch (chg->real_charger_type) { case QTI_POWER_SUPPLY_TYPE_USB_HVDCP: + if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_9V) { val->intval = MICRO_5V; break; @@ -3475,6 +3564,7 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, { switch (chg->real_charger_type) { case QTI_POWER_SUPPLY_TYPE_USB_HVDCP: +#ifdef QCOM_BASE if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_9V) { val->intval = MICRO_5V; break; @@ -3484,13 +3574,22 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, break; } /* else, fallthrough */ +#else + val->intval = MICRO_5V; + break; +#endif case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5: case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3: +#ifdef QCOM_BASE if (chg->chg_param.smb_version == PMI632) val->intval = MICRO_9V; else val->intval = MICRO_12V; break; +#else + val->intval = MICRO_5V; + break; +#endif case POWER_SUPPLY_TYPE_USB_PD: val->intval = chg->voltage_max_uv; break; @@ -3814,18 +3913,17 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat); switch (stat & DETECTED_SRC_TYPE_MASK) { + case SNK_DAM_500MA_BIT: case SNK_RP_STD_BIT: return QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + case SNK_DAM_1500MA_BIT: case SNK_RP_1P5_BIT: return QTI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM; + case SNK_DAM_3000MA_BIT: case SNK_RP_3P0_BIT: return QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH; case SNK_RP_SHORT_BIT: return QTI_POWER_SUPPLY_TYPEC_NON_COMPLIANT; - case SNK_DAM_500MA_BIT: - case SNK_DAM_1500MA_BIT: - case SNK_DAM_3000MA_BIT: - return QTI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY; default: break; } @@ -4378,6 +4476,8 @@ static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) /* fall through */ default: rp_ua = DCP_CURRENT_UA; + if (chg->usb_dcp_curr_max > 0) + rp_ua = chg->usb_dcp_curr_max; } return rp_ua; @@ -4393,6 +4493,7 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, int rc, icl; if (chg->pd_active) { + smblib_err(chg, "set pd current %d\n", val); icl = get_client_vote(chg->usb_icl_votable, PD_VOTER); rc = vote(chg->usb_icl_votable, PD_VOTER, true, val); if (val != icl) @@ -4467,6 +4568,13 @@ static int smblib_handle_usb_current(struct smb_charger *chg, if (!rc && !val.intval) return 0; + typec_mode = smblib_get_prop_typec_mode(chg); + if (typec_rp_med_high(chg, typec_mode)) { + vote(chg->usb_icl_votable, USB_PSY_VOTER, + false, 0); + return 0; + } + /* if flash is active force 500mA */ if ((usb_current < SDP_CURRENT_UA) && is_flash_active(chg)) usb_current = SDP_CURRENT_UA; @@ -4808,8 +4916,10 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, } } +#ifdef QCOM_BASE smblib_usb_pd_adapter_allowance_override(chg, !!chg->pd_active ? FORCE_5V : FORCE_NULL); +#endif smblib_update_usb_type(chg); if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB && @@ -5051,7 +5161,13 @@ int smblib_get_charge_current(struct smb_charger *chg, typec_source_rd = smblib_get_prop_ufp_mode(chg); /* QC 2.0/3.0 adapter */ - if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) { + if (chg->mmi_qc3p_rerun_done){ + *total_current_ua = HVDCP_CURRENT_UA; + return 0; + } else if (apsd_result->bit & QC_2P0_BIT) { + *total_current_ua = chg->chg_param.hvdcp2_max_icl_ua; + return 0; + } else if (apsd_result->bit & QC_3P0_BIT){ *total_current_ua = HVDCP_CURRENT_UA; return 0; } @@ -5066,12 +5182,15 @@ int smblib_get_charge_current(struct smb_charger *chg, case FLOAT_CHARGER_BIT: current_ua = DCP_CURRENT_UA; break; + case SDP_CHARGER_BIT: + current_ua = SDP_CURRENT_UA; + break; default: current_ua = 0; break; } - *total_current_ua = max(current_ua, val.intval); + *total_current_ua = current_ua; return 0; } @@ -5086,6 +5205,9 @@ int smblib_get_charge_current(struct smb_charger *chg, case FLOAT_CHARGER_BIT: current_ua = chg->default_icl_ua; break; + case SDP_CHARGER_BIT: + current_ua = SDP_CURRENT_UA; + break; default: current_ua = 0; break; @@ -5104,7 +5226,7 @@ int smblib_get_charge_current(struct smb_charger *chg, break; } - *total_current_ua = max(current_ua, val.intval); + *total_current_ua = current_ua; return 0; } @@ -5148,6 +5270,14 @@ irqreturn_t smb5_default_irq_handler(int irq, void *data) return IRQ_HANDLED; } +irqreturn_t smb5_icl_irq_handler(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + + pr_debug("%s IRQ: %s\n",__func__, irq_data->name); + return IRQ_HANDLED; +} + irqreturn_t smb5_smb_en_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -5219,12 +5349,13 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status) * battery. Trigger the charge termination WA once charging is completed * to prevent overcharing. */ - if ((batt_status == TERMINATE_CHARGE) && (pval.intval == 100) && + if (((batt_status == TERMINATE_CHARGE) || is_mmi_charge_full(chg)) && (pval.intval == 100) && (ktime_to_ms(alarm_expires_remaining(/* alarm not pending */ &chg->chg_termination_alarm)) <= 0)) { chg->cc_soc_ref = 0; chg->last_cc_soc = 0; chg->term_vbat_uv = 0; + chg->chg_done = 1; alarm_start_relative(&chg->chg_termination_alarm, ms_to_ktime(CHG_TERM_WA_ENTRY_DELAY_MS)); } else if (pval.intval < 100) { @@ -5680,6 +5811,7 @@ static void smblib_usb_plugin_locked(struct smb_charger *chg) schedule_delayed_work(&chg->pl_enable_work, msecs_to_jiffies(PL_DELAY_MS)); } else { + chg->mmi_qc3p_rerun_done = false; /* Disable SW Thermal Regulation */ rc = smblib_set_sw_thermal_regulation(chg, false); if (rc < 0) @@ -5738,7 +5870,8 @@ static void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_update_usb_type(chg); } - if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB) + if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB || + chg->typec_mode == QTI_POWER_SUPPLY_TYPEC_NONE) smblib_micro_usb_plugin(chg, vbus_rising); vote(chg->temp_change_irq_disable_votable, DEFAULT_VOTER, @@ -5776,6 +5909,157 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +static int mmi_get_vbus(struct smb_charger *chg) +{ + int rc = 0, val; + + if (!is_mmi_cp_available(chg)) + return 0; + + rc = smblib_read_iio_prop(chg, MMI_CP, CP_INPUT_VOLTAGE_NOW, + &val); + if (!rc) + return val; + else { + smblib_err(chg, "Couldn't read mmi cp vbus rc=%d\n", rc); + return 0; + } +} + +bool mmi_rerun_qc3p_det(struct smb_charger *chg) +{ + union power_supply_propval val; + int rc; + + if (!chg->mmi_qc3p_rerun_done) { + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return false; + } else if (!val.intval) { + smblib_err(chg, "usb is not present\n"); + return false; + } + + smblib_dbg(chg, PR_MISC, "rerun qcep detect\n"); + rc = smblib_request_dpdm(chg, true); + if (rc < 0) { + smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); + return false; + } + rc = smblib_masked_write(chg, CMD_APSD_REG, + APSD_RERUN_BIT, APSD_RERUN_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc); + return false; + } + chg->mmi_qc3p_rerun_done = true; + chg->apsd_ext_timeout = false; + + return true; + }else + return false; +} + + +#define QC3P_AUTHEN_LOW_THR_MV 5500 +#define QC3P_AUTHEN_HIGH_THR_MV 6500 +#define QC3P_AUTHEN_NONE_THR_MV 9500 +#define QC3P_AUTHEN_45W_THR_MV 8500 +#define QC3P_AUTHEN_27W_THR_MV 7500 +#define QC3P_AUTHEN_18W_THR_MV 6500 +static int mmi_qc3p_kthread_handler(void *param) +{ + struct smb_charger *chg = param; + int i; + int v_aut,v_power; + unsigned long j1,j2; + struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO-1}; + + sched_setscheduler(current, SCHED_FIFO, &sch_param); + do { + start: + wait_event_interruptible(chg->mmi_timer_wait_que, chg->mmi_timer_trig_flag || kthread_should_stop()); + if (kthread_should_stop()) + break; + chg->mmi_timer_trig_flag = false; + + for (i = 0; i < 16; i++) { + smblib_dp_pulse(chg); + udelay(5000); + } + mdelay(95); + for (i = 0; i < 16; i++) { + smblib_dm_pulse(chg); + udelay(5000); + } + mdelay(100); + + j1 = jiffies; + v_aut = mmi_get_vbus(chg); + j1 = jiffies - j1; + if (v_aut < QC3P_AUTHEN_LOW_THR_MV || v_aut > QC3P_AUTHEN_HIGH_THR_MV) { + smblib_err(chg, "is qc3.0 charge adatper vbus=%d\n",v_aut); + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + if (mmi_rerun_qc3p_det(chg)) + goto start; + else + goto out; + } + + for (i = 0;i < 3; i++) { + smblib_dp_pulse(chg); + udelay(5000); + smblib_dm_pulse(chg); + udelay(5000); + } + mdelay(30); + + j2 = jiffies; + v_power = mmi_get_vbus(chg); + j2 = jiffies - j2; + + for (i = 0; i < 2; i++) { + smblib_dp_pulse(chg); + udelay(5000); + } + for (i = 0; i < 2; i++) { + smblib_dm_pulse(chg); + udelay(5000); + } + msleep(30); + + if (v_power > QC3P_AUTHEN_NONE_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + else if (v_power > QC3P_AUTHEN_45W_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_45W; + else if (v_power > QC3P_AUTHEN_27W_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_27W; + else if (v_power > QC3P_AUTHEN_18W_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_18W; + else + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + + if (chg->mmi_qc3p_power != QTI_POWER_SUPPLY_QC3P_NONE) { + chg->qc3p5_detected = true; + smblib_update_usb_type(chg); + power_supply_changed(chg->usb_psy); + } else { + smblib_err(chg, "qc3p power is invalid, rerun qc3p detect\n"); + mmi_rerun_qc3p_det(chg); + } + + smblib_dbg(chg, PR_MISC, "V_aut:%dms %dmV,V_power=%dms %dmV,Power =%d\n", + jiffies_to_msecs(j1), v_aut, jiffies_to_msecs(j2), v_power, chg->mmi_qc3p_power); + out: + chg->mmi_is_qc3p_authen = false; + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, false, 0); + }while (!kthread_should_stop()); + + smblib_dbg(chg, PR_MISC, "qc3p kthread stop\n"); + return 0; +} + #define APSD_EXTENDED_TIMEOUT_MS 400 /* triggers when HVDCP 3.0 authentication has finished */ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, @@ -5812,6 +6096,13 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, "APSD Extented timer started at %lld\n", jiffies_to_msecs(jiffies)); + if (chg->mmi_qc3p_support) { + chg->mmi_is_qc3p_authen = true; + chg->qc3p5_detected = false; + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, true, 500000); + } + mod_timer(&chg->apsd_timer, msecs_to_jiffies(APSD_EXTENDED_TIMEOUT_MS) + jiffies); @@ -5865,6 +6156,7 @@ static void update_sw_icl_max(struct smb_charger *chg, int val) { int typec_mode; int rp_ua; + const struct apsd_result *apsd = NULL; /* while PD is active it should have complete ICL control */ if (chg->pd_active) @@ -5887,40 +6179,39 @@ static void update_sw_icl_max(struct smb_charger *chg, int val) if (typec_rp_med_high(chg, typec_mode)) { rp_ua = get_rp_based_dcp_current(chg, typec_mode); vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); return; } /* rp-std or legacy, USB BC 1.2 */ switch (val) { case POWER_SUPPLY_TYPE_USB: - /* - * USB_PSY will vote to increase the current to 500/900mA once - * enumeration is done. - */ - if (!is_client_vote_enabled(chg->usb_icl_votable, - USB_PSY_VOTER)) { - /* if flash is active force 500mA */ + if (get_client_vote(chg->usb_icl_votable, + USB_PSY_VOTER) < SDP_CURRENT_UA) { vote(chg->usb_icl_votable, USB_PSY_VOTER, true, - is_flash_active(chg) ? - SDP_CURRENT_UA : SDP_100_MA); + SDP_CURRENT_UA); } vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); break; case POWER_SUPPLY_TYPE_USB_CDP: vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, - CDP_CURRENT_UA); + (chg->usb_cdp_curr_max > 0) ? chg->usb_cdp_curr_max : CDP_CURRENT_UA); + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); break; case POWER_SUPPLY_TYPE_USB_DCP: - rp_ua = get_rp_based_dcp_current(chg, typec_mode); - vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); + apsd = smblib_get_apsd_result(chg); + if (apsd->bit == OCP_CHARGER_BIT) { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + OCP_CURRENT_UA); + } else { + rp_ua = get_rp_based_dcp_current(chg, typec_mode); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); + } + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); break; case QTI_POWER_SUPPLY_TYPE_USB_FLOAT: - /* - * limit ICL to 100mA, the USB driver will enumerate to check - * if this is a SDP and appropriately set the current - */ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, - SDP_100_MA); + SDP_CURRENT_UA); break; case POWER_SUPPLY_TYPE_UNKNOWN: default: @@ -5933,6 +6224,7 @@ static void update_sw_icl_max(struct smb_charger *chg, int val) static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { const struct apsd_result *apsd_result; + int typec_mode; if (!rising) return; @@ -5949,6 +6241,12 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) smblib_notify_device_mode(chg, true); break; case OCP_CHARGER_BIT: + typec_mode = smblib_get_prop_typec_mode(chg); + if (!typec_rp_med_high(chg, typec_mode)) { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + OCP_CURRENT_UA); + } + break; case DCP_CHARGER_BIT: break; default: @@ -5989,6 +6287,26 @@ irqreturn_t smb5_usb_source_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } + if ((chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_TYPEC) + && (stat & APSD_DTC_STATUS_DONE_BIT) + && !chg->typec_apsd_rerun_done) { + const struct apsd_result *apsd_result = + smblib_update_usb_type(chg); + /* + * Force re-run APSD to handle slow insertion related + * charger-mis-detection. + */ + if (apsd_result->bit == SDP_CHARGER_BIT || + apsd_result->bit == OCP_CHARGER_BIT || + apsd_result->bit == FLOAT_CHARGER_BIT) { + smblib_dbg(chg, PR_INTERRUPT, "IRQ: rerun apsd for %s\n", + apsd_result->name); + chg->typec_apsd_rerun_done = true; + smblib_rerun_apsd_if_required(chg); + return IRQ_HANDLED; + } + } + smblib_handle_apsd_done(chg, (bool)(stat & APSD_DTC_STATUS_DONE_BIT)); @@ -6371,6 +6689,7 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, DCP_VOTER, false, 0); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, false, 0); vote(chg->usb_icl_votable, CTM_VOTER, false, 0); vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0); vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); @@ -6402,6 +6721,7 @@ static void typec_src_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); chg->dbc_usbov = false; + chg->chg_done = 0; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; @@ -6409,6 +6729,7 @@ static void typec_src_removal(struct smb_charger *chg) chg->voltage_max_uv = MICRO_5V; chg->usbin_forced_max_uv = 0; chg->chg_param.forced_main_fcc = 0; + chg->typec_apsd_rerun_done = false; /* Reset all CC mode votes */ vote(chg->fcc_main_votable, MAIN_FCC_VOTER, false, 0); @@ -7211,17 +7532,12 @@ irqreturn_t smb5_usbin_ov_irq_handler(int irq, void *data) * differentiate between OV IRQ glitch and real OV_IRQ, add a debounce * period for evaluation. */ - if (stat & USBIN_OV_RT_STS_BIT) { - chg->dbc_usbov = true; - vote(chg->awake_votable, USBOV_DBC_VOTER, true, 0); - schedule_delayed_work(&chg->usbov_dbc_work, - msecs_to_jiffies(USB_OV_DBC_PERIOD_MS)); - } else { - cancel_delayed_work_sync(&chg->usbov_dbc_work); - chg->dbc_usbov = false; - vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); - } + chg->dbc_usbov = true; + vote(chg->awake_votable, USBOV_DBC_VOTER, true, 0); + schedule_delayed_work(&chg->usbov_dbc_work, + msecs_to_jiffies(USB_OV_DBC_PERIOD_MS)); + power_supply_changed(chg->batt_psy); smblib_dbg(chg, PR_MISC, "USBOV debounce status %d\n", chg->dbc_usbov); return IRQ_HANDLED; @@ -7815,6 +8131,11 @@ static void apsd_timer_cb(struct timer_list *tm) jiffies_to_msecs(jiffies)); chg->apsd_ext_timeout = true; + + if (chg->mmi_qc3p_support) { + chg->mmi_timer_trig_flag = true; + wake_up_interruptible(&chg->mmi_timer_wait_que); + } } #define SOFT_JEITA_HYSTERESIS_OFFSET 0x200 @@ -8147,10 +8468,27 @@ relax: pm_relax(chg->dev); } +static bool mmi_factory_mode; +static void smblib_mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + + if (np) + mmi_factory_mode = of_property_read_bool(np, + "mmi,factory-cable"); + + of_node_put(np); + + return; +} + static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; + mmi_factory_mode = false; + smblib_mmi_factory_check(); + chg->fcc_votable = find_votable("FCC"); if (chg->fcc_votable == NULL) { rc = -EINVAL; @@ -8282,6 +8620,27 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + if (mmi_factory_mode) { + smblib_err(chg, "MMI Factory Mode Force Charge Values!\n"); + if(chg->chg_disable_votable) { + pmic_vote_force_val_set(chg->chg_disable_votable, 1); + pmic_vote_force_active_set(chg->chg_disable_votable, 1); + } + if(chg->fcc_votable) { + pmic_vote_force_val_set(chg->fcc_votable, 100000); + pmic_vote_force_active_set(chg->fcc_votable, 1); + } + if(chg->fv_votable) { + pmic_vote_force_val_set(chg->fv_votable, 4400000); + pmic_vote_force_active_set(chg->fv_votable, 1); + } + if(chg->usb_icl_votable) { + pmic_vote_force_val_set(chg->usb_icl_votable, + 3000000); + pmic_vote_force_active_set(chg->usb_icl_votable, 1); + } + } + return rc; } @@ -8350,6 +8709,17 @@ int smblib_init(struct smb_charger *chg) smblib_pr_swap_detach_work); INIT_DELAYED_WORK(&chg->pr_lock_clear_work, smblib_pr_lock_clear_work); + if (chg->mmi_qc3p_support) { + chg->mmi_qc3p_authen_task = kthread_create(mmi_qc3p_kthread_handler, chg, + "mmi_qc3p_authen", "mmi_qc3p_authen_task", chg); + if (IS_ERR(chg->mmi_qc3p_authen_task)) { + rc = PTR_ERR(chg->mmi_qc3p_authen_task); + smblib_err(chg, "Failed to create mmi_qc3p_authen_task rc = %d\n", rc); + return rc; + } + init_waitqueue_head(&chg->mmi_timer_wait_que); + wake_up_process(chg->mmi_qc3p_authen_task); + } timer_setup(&chg->apsd_timer, apsd_timer_cb, 0); INIT_DELAYED_WORK(&chg->role_reversal_check, @@ -8520,6 +8890,8 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->role_reversal_check); cancel_delayed_work_sync(&chg->pr_swap_detach_work); + if (chg->mmi_qc3p_support && chg->mmi_qc3p_authen_task) + kthread_stop(chg->mmi_qc3p_authen_task); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_step_chg_deinit(); diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h index f3c38894650a..6618d8b6fb73 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -83,6 +83,7 @@ enum print_reason { #define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER" #define OVERHEAT_LIMIT_VOTER "OVERHEAT_LIMIT_VOTER" #define TYPEC_SWAP_VOTER "TYPEC_SWAP_VOTER" +#define SW_QC3P_AUTHEN_VOTER "SW_QC3P_AUTHEN_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 @@ -95,6 +96,7 @@ enum print_reason { #define SDP_100_MA 100000 #define SDP_CURRENT_UA 500000 +#define OCP_CURRENT_UA 1000000 #define CDP_CURRENT_UA 1500000 #define DCP_CURRENT_UA 1500000 #define HVDCP_CURRENT_UA 3000000 @@ -531,6 +533,7 @@ struct smb_charger { int default_icl_ua; int otg_cl_ua; bool uusb_apsd_rerun_done; + bool typec_apsd_rerun_done; bool typec_present; int fake_input_current_limited; int typec_mode; @@ -629,6 +632,18 @@ struct smb_charger { int dcin_uv_count; ktime_t dcin_uv_last_time; int last_wls_vout; + int usb_dcp_curr_max; + int usb_cdp_curr_max; + + /* mmi qc3p */ + struct iio_channel **iio_chan_list_mmi_cp; + struct task_struct *mmi_qc3p_authen_task; + wait_queue_head_t mmi_timer_wait_que; + enum mmi_qc3p_power mmi_qc3p_power; + bool mmi_is_qc3p_authen; + bool mmi_qc3p_support; + bool mmi_timer_trig_flag; + bool mmi_qc3p_rerun_done; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -657,6 +672,7 @@ int smblib_vconn_regulator_disable(struct regulator_dev *rdev); int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev); irqreturn_t smb5_default_irq_handler(int irq, void *data); +irqreturn_t smb5_icl_irq_handler(int irq, void *data); irqreturn_t smb5_smb_en_irq_handler(int irq, void *data); irqreturn_t smb5_chg_state_change_irq_handler(int irq, void *data); irqreturn_t smb5_batt_temp_changed_irq_handler(int irq, void *data); diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h index 71b4de85c159..6b58cfd65e76 100644 --- a/drivers/power/supply/qcom/smb5-reg.h +++ b/drivers/power/supply/qcom/smb5-reg.h @@ -359,6 +359,7 @@ enum { #define TYPE_C_MISC_STATUS_REG (TYPEC_BASE + 0x0B) #define TYPEC_WATER_DETECTION_STATUS_BIT BIT(7) #define SNK_SRC_MODE_BIT BIT(6) +#define TYPEC_VBUS_STATUS BIT(5) #define TYPEC_VBUS_ERROR_STATUS_BIT BIT(4) #define TYPEC_TCCDEBOUNCE_DONE_STATUS_BIT BIT(3) #define CC_ORIENTATION_BIT BIT(1) @@ -395,6 +396,9 @@ enum { #define TYPEC_CCOUT_VALUE_BIT BIT(1) #define TYPEC_CCOUT_SRC_BIT BIT(0) +#define TYPE_C_DEBUG_ACCESS_SINK_REG (TYPEC_BASE + 0x4A) +#define TYPEC_DEBUG_ACCESS_SINK_MASK GENMASK(4, 0) + #define DEBUG_ACCESS_SRC_CFG_REG (TYPEC_BASE + 0x4C) #define EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index eeda444ddd3e..be7429bc33e8 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -55,7 +55,7 @@ static LIST_HEAD(regulator_supply_alias_list); static LIST_HEAD(regulator_coupler_list); static bool has_full_constraints; #ifdef CONFIG_DEBUG_FS -static bool debug_suspend; +static bool debug_suspend = IS_ENABLED(CONFIG_DUMP_REGULATOR_ON_SUSPEND); #endif static struct dentry *debugfs_root; diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index f25d1491a4d4..b9a0f6728fd5 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -196,3 +196,89 @@ config SCSI_UFS_CRYPTO_QTI Enable Vendor Crypto Engine Support in UFS Enabling this allows kernel to use UFS crypto operations defined and implemented by QTI. + +config UFSFEATURE + bool "UFS feature activate" + depends on SCSI_UFSHCD + ---help--- + UFS feature activate such as hpb, tw and etc. + +config UFSHPB + bool "UFS Host Performance Booster support" + depends on SCSI_UFSHCD && UFSFEATURE + ---help--- + UFS HPB Feature Enable + +config HPB_DEBUG + bool "HPB Debug Log Enable" + depends on SCSI_UFSHCD && UFSFEATURE && UFSHPB + default n + ---help--- + UFS HPB Debug Enable (dmesg log[=if set debug sysfs] & BLK_TRACE msg) + +config HPB_ERR_INJECTION + bool "HPB ERROR INJECTION" + depends on SCSI_UFSHCD && UFSFEATURE && UFSHPB + ---help--- + error injection for checking HPB entry integrity + +config UFSTW + bool "UFS Turbo Write support" + depends on SCSI_UFSHCD && UFSFEATURE + ---help--- + UFS TW Feature Enable + +config UFSTW_IGNORE_GUARANTEE_BIT + bool "Ignore UFS Turbo Write Life Time Guarantee bit for POC" + default n + depends on SCSI_UFSHCD && UFSFEATURE && UFSTW + ---help--- + ignore the guarantee bit[31] of dTurboWriteBufferLifeTimeEst for PoC + +config UFSTW_BOOT_ENABLED + bool "Turbo Write enabled at boot time" + default y + depends on SCSI_UFSHCD && UFSFEATURE && UFSTW + ---help--- + fTurboWriteEn and fTurboWriteBufferFlushDuringHibnerEnter flags + are enabled at boot time. + +config UFSHID + bool "UFS Host Initiated Defrag support" + depends on SCSI_UFSHCD && UFSFEATURE + ---help--- + UFS HID Feature Enable + +config UFSHID_POC + bool "UFSHID_POC test" + depends on SCSI_UFSHCD && UFSFEATURE && UFSHID + ---help--- + UFS HID POC test + 1. block to enter suspend state + 2. auto_hibern8 enable/disable (HCI) + 3. set flag bDefragOperation + 4. debug msg enable (default : disable) + +config SCSI_SKHPB + bool "Activate HPB Host-aware Performance Booster" + depends on (SCSI_UFSHCD || SCSI_UFSHCD_QTI) + help + Activate or deactive SKHPB driver + +config SCSI_SKHID + bool "UFS Host Initiated Defrag supportr" + depends on (SCSI_UFSHCD || SCSI_UFSHCD_QTI) + help + Activate or deactive SKHID driver + +config MICRON_HPB + bool "Activate micron HPB Host-aware Performance Booster" + depends on SCSI_UFSHCD && UFSFEATURE && UFSHPB + help + Activate or deactive micron HPB driver + +config MICRON_UFSHID + bool "UFS Host Initiated Defrag supportr" + depends on (SCSI_UFSHCD || SCSI_UFSHCD_QTI) + help + Activate or deactive micron hid driver diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile index 8a0421a1b569..f1e3eaa10a05 100644 --- a/drivers/scsi/ufs/Makefile +++ b/drivers/scsi/ufs/Makefile @@ -5,8 +5,13 @@ obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-d obj-$(CONFIG_SCSI_UFS_CDNS_PLATFORM) += cdns-pltfrm.o obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o +obj-$(CONFIG_UFSFEATURE) += ufsfeature.o +obj-$(CONFIG_UFSHPB) += ufshpb.o +obj-$(CONFIG_UFSTW) += ufstw.o +obj-$(CONFIG_UFSHID) += ufshid.o ufshcd-core-y += ufshcd.o ufs-sysfs.o ufshcd-core-$(CONFIG_SCSI_UFS_BSG) += ufs_bsg.o +obj-$(CONFIG_SCSI_SKHPB) += ufshpb_skh.o obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 562ac279de39..5ade5e502898 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -2257,6 +2257,14 @@ ufs_qcom_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer) goto out_release_mem; } +#if defined(CONFIG_UFSFEATURE) + if (ufsf_check_query(ioctl_data->opcode)) { + err = ufsf_query_ioctl(&hba->ufsf, lun, buffer, ioctl_data, + UFSFEATURE_SELECTOR); + goto out_release_mem; + } +#endif + /* verify legal parameters & send query */ switch (ioctl_data->opcode) { case UPIU_QUERY_OPCODE_READ_DESC: diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index 1d10866df1f6..c1ee9fbd4684 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -7,6 +7,9 @@ #include #include "ufs.h" +#if defined(CONFIG_SCSI_SKHID) +#include "ufshcd.h" +#endif #include "ufs-sysfs.h" static const char *ufschd_uic_link_state_to_string( @@ -182,6 +185,129 @@ static ssize_t auto_hibern8_store(struct device *dev, return count; } +#if defined(CONFIG_SCSI_SKHID) +static ssize_t manual_gc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 status = MANUAL_GC_OFF; + + if (hba->manual_gc.state == MANUAL_GC_DISABLE) + return scnprintf(buf, PAGE_SIZE, "%s", "disabled\n"); + + pm_runtime_get_sync(hba->dev); + + //down_read(&hba->query_lock); + if (hba->manual_gc.hagc_support) { + int err = ufshcd_query_attr_retry(hba, + UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_MANUAL_GC_STATUS, 0, 0, &status); + + hba->manual_gc.hagc_support = err ? false: true; + } + //up_read(&hba->query_lock); + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + + if (!hba->manual_gc.hagc_support) + return scnprintf(buf, PAGE_SIZE, "%s", "bkops\n"); + return scnprintf(buf, PAGE_SIZE, "%s", + status == MANUAL_GC_OFF ? "off\n" : "on\n"); +} + +static int manual_gc_enable(struct ufs_hba *hba, u32 *value) +{ + return ufshcd_query_attr_retry(hba, + UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_MANUAL_GC_CONT, 0, 0, + value); +} + +static ssize_t manual_gc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 value; + int err = 0; + + if (kstrtou32(buf, 0, &value)) + return -EINVAL; + + if (value >= MANUAL_GC_MAX) + return -EINVAL; + + if (value == MANUAL_GC_DISABLE || value == MANUAL_GC_ENABLE) { + hba->manual_gc.state = value; + return count; + } + if (hba->manual_gc.state == MANUAL_GC_DISABLE) + return count; + + pm_runtime_get_sync(hba->dev); + + if (hba->manual_gc.hagc_support) + hba->manual_gc.hagc_support = + manual_gc_enable(hba, &value) ? false : true; + + if (!hba->manual_gc.hagc_support) { + enum query_opcode opcode = (value == MANUAL_GC_ON) ? + UPIU_QUERY_OPCODE_SET_FLAG: + UPIU_QUERY_OPCODE_CLEAR_FLAG; + + err = ufshcd_bkops_ctrl(hba, (value == MANUAL_GC_ON) ? + BKOPS_STATUS_NON_CRITICAL: + BKOPS_STATUS_CRITICAL); + if (!hba->auto_bkops_enabled) + err = -EAGAIN; + + /* flush wb buffer */ + if (hba->dev_info.wspecversion >= 0x0310) { + u8 index = ufshcd_wb_get_query_index(hba); + + ufshcd_query_flag_retry(hba, opcode, + QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8, + index, NULL); + ufshcd_query_flag_retry(hba, opcode, + QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN, index, NULL); + } + } + + if (err || hrtimer_active(&hba->manual_gc.hrtimer)) { + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + return count; + } else { + /* pm_runtime_put_sync in delay_ms */ + hrtimer_start(&hba->manual_gc.hrtimer, + ms_to_ktime(hba->manual_gc.delay_ms), + HRTIMER_MODE_REL); + } + return count; +} + +static ssize_t manual_gc_hold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%lu\n", hba->manual_gc.delay_ms); +} + +static ssize_t manual_gc_hold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + unsigned long value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + hba->manual_gc.delay_ms = value; + return count; +} +#endif + static DEVICE_ATTR_RW(rpm_lvl); static DEVICE_ATTR_RO(rpm_target_dev_state); static DEVICE_ATTR_RO(rpm_target_link_state); @@ -189,6 +315,20 @@ static DEVICE_ATTR_RW(spm_lvl); static DEVICE_ATTR_RO(spm_target_dev_state); static DEVICE_ATTR_RO(spm_target_link_state); static DEVICE_ATTR_RW(auto_hibern8); +#if defined(CONFIG_SCSI_SKHID) +static DEVICE_ATTR_RW(manual_gc); +static DEVICE_ATTR_RW(manual_gc_hold); + +static struct attribute *ufs_sysfs_ufshcd_hagc_attrs[] = { + &dev_attr_manual_gc.attr, + &dev_attr_manual_gc_hold.attr, + NULL +}; + +static const struct attribute_group ufs_sysfs_hagc_default_group = { + .attrs = ufs_sysfs_ufshcd_hagc_attrs, +}; +#endif static struct attribute *ufs_sysfs_ufshcd_attrs[] = { &dev_attr_rpm_lvl.attr, @@ -735,6 +875,19 @@ UFS_ATTRIBUTE(wb_flush_status, _WB_FLUSH_STATUS); UFS_ATTRIBUTE(wb_avail_buf, _AVAIL_WB_BUFF_SIZE); UFS_ATTRIBUTE(wb_life_time_est, _WB_BUFF_LIFE_TIME_EST); UFS_ATTRIBUTE(wb_cur_buf, _CURR_WB_BUFF_SIZE); +#if defined(CONFIG_SCSI_SKHID) +UFS_ATTRIBUTE(manual_gc_status, _MANUAL_GC_STATUS); + +static struct attribute *ufs_sysfs_hagc_attributes[] = { + &dev_attr_manual_gc_status.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_hagc_attributes_group = { + .name = "attributes_hid", + .attrs = ufs_sysfs_hagc_attributes, +}; +#endif static struct attribute *ufs_sysfs_attributes[] = { @@ -779,6 +932,14 @@ static const struct attribute_group *ufs_sysfs_groups[] = { NULL, }; +#if defined(CONFIG_SCSI_SKHID) +static const struct attribute_group *ufs_sysfs_hagc_groups[] = { + &ufs_sysfs_hagc_default_group, + &ufs_sysfs_hagc_attributes_group, + NULL, +}; +#endif + #define UFS_LUN_DESC_PARAM(_pname, _puname, _duname, _size) \ static ssize_t _pname##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ @@ -873,9 +1034,25 @@ void ufs_sysfs_add_nodes(struct device *dev) dev_err(dev, "%s: sysfs groups creation failed (err = %d)\n", __func__, ret); + + #if defined(CONFIG_SCSI_SKHID) + if (IS_SKHYNIX_DEVICE(storage_mfrid)) { + ret = sysfs_create_groups(&dev->kobj, ufs_sysfs_hagc_groups); + if (ret) + dev_err(dev, + "%s: sysfs hagc groups creation failed (err = %d)\n", + __func__, ret); + } + #endif } void ufs_sysfs_remove_nodes(struct device *dev) { sysfs_remove_groups(&dev->kobj, ufs_sysfs_groups); + + #if defined(CONFIG_SCSI_SKHID) + if (IS_SKHYNIX_DEVICE(storage_mfrid)) { + sysfs_remove_groups(&dev->kobj, ufs_sysfs_hagc_groups); + } + #endif } diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 22f03719b419..c2ba85d07aa8 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -152,6 +152,17 @@ enum flag_idn { QUERY_FLAG_IDN_WB_EN = 0x0E, QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN = 0x0F, QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8 = 0x10, +#if defined(CONFIG_UFSHPB) || defined(CONFIG_SCSI_SKHPB) + QUERY_FLAG_IDN_HPB_RESET = 0x11, +#endif +#if defined(CONFIG_UFSTW) + QUERY_FLAG_IDN_TW_EN = 0x0E, + QUERY_FLAG_IDN_TW_BUF_FLUSH_EN = 0x0F, + QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN = 0x10, +#endif +#if defined(CONFIG_MICRON_UFSHID) + QUERY_FLAG_IDN_HID_EN = 0x13 +#endif }; /* Attribute idn for Query requests */ @@ -184,8 +195,32 @@ enum attr_idn { QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE = 0x1D, QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E, QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F, +#if defined(CONFIG_UFSTW) + QUERY_ATTR_IDN_TW_FLUSH_STATUS = 0x1C, + QUERY_ATTR_IDN_TW_AVAIL_BUF_SIZE = 0x1D, + QUERY_ATTR_IDN_TW_BUF_LIFETIME_EST = 0x1E, + QUERY_ATTR_IDN_TW_CURR_BUF_SIZE = 0x1F, +#endif +#if defined(CONFIG_UFSHID) + QUERY_ATTR_IDN_HID_OPERATION = 0x20, + QUERY_ATTR_IDN_HID_FRAG_LEVEL = 0x21, +#endif +#if defined(CONFIG_SCSI_SKHID) + /* use one reserved bit */ + QUERY_ATTR_IDN_MANUAL_GC_CONT = 0x12, + QUERY_ATTR_IDN_MANUAL_GC_STATUS = 0x13, +#endif +#if defined(CONFIG_UFSFEATURE) + QUERY_ATTR_IDN_SUP_VENDOR_OPTIONS = 0xFF, +#endif +#if defined(CONFIG_MICRON_UFSHID) + QUERY_ATTR_IDN_HID_FRAG_STATUS = 0x31, + QUERY_ATTR_IDN_HID_PROGRESS = 0x32, +#endif }; +#define QUERY_ATTR_IDN_BOOT_LU_EN_MAX 0x02 + /* Descriptor idn for Query requests */ enum desc_idn { QUERY_DESC_IDN_DEVICE = 0x0, @@ -207,11 +242,11 @@ enum desc_header_offset { }; enum ufs_desc_def_size { - QUERY_DESC_DEVICE_DEF_SIZE = 0x59, - QUERY_DESC_CONFIGURATION_DEF_SIZE = 0x90, + QUERY_DESC_DEVICE_DEF_SIZE = 0x5F, + QUERY_DESC_CONFIGURATION_DEF_SIZE = 0xE6, QUERY_DESC_UNIT_DEF_SIZE = 0x2D, QUERY_DESC_INTERCONNECT_DEF_SIZE = 0x06, - QUERY_DESC_GEOMETRY_DEF_SIZE = 0x48, + QUERY_DESC_GEOMETRY_DEF_SIZE = 0x59, QUERY_DESC_POWER_DEF_SIZE = 0x62, QUERY_DESC_HEALTH_DEF_SIZE = 0x25, }; @@ -236,6 +271,15 @@ enum unit_desc_param { UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20, UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS = 0x29, +#if defined(CONFIG_UFSHPB) || defined(CONFIG_SCSI_SKHPB) + UNIT_DESC_HPB_LU_MAX_ACTIVE_REGIONS = 0x23, + UNIT_DESC_HPB_LU_PIN_REGION_START_OFFSET = 0x25, + UNIT_DESC_HPB_LU_NUM_PIN_REGIONS = 0x27, +#endif +#if defined(CONFIG_UFSTW) + UNIT_DESC_TW_LU_WRITE_BUFFER_ALLOC_UNIT = 0x29, + +#endif }; /* Device descriptor parameters offsets in bytes*/ @@ -279,6 +323,22 @@ enum device_desc_param { DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53, DEVICE_DESC_PARAM_WB_TYPE = 0x54, DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55, +#if defined(CONFIG_UFSHPB) || defined(CONFIG_SCSI_SKHPB) + DEVICE_DESC_PARAM_HPB_VER = 0x40, + DEVICE_DESC_PARAM_HPB_CONTROL = 0x42, +#endif +#if defined(CONFIG_UFSFEATURE) + DEVICE_DESC_PARAM_EX_FEAT_SUP = 0x4F, +#endif +#if defined(CONFIG_UFSTW) + DEVICE_DESC_PARAM_TW_VER = 0x4D, + DEVICE_DESC_PARAM_TW_RETURN_TO_USER = 0x53, + DEVICE_DESC_PARAM_TW_BUF_TYPE = 0x54, + DEVICE_DESC_PARAM_TW_SHARED_BUF_ALLOC_UNITS = 0x55, +#endif +#if defined(CONFIG_UFSHID) + DEVICE_DESC_PARAM_HID_VER = 0x59, +#endif }; /* Interconnect descriptor parameters offsets in bytes*/ @@ -328,8 +388,32 @@ enum geometry_desc_param { GEOMETRY_DESC_PARAM_WB_BUFF_CAP_ADJ = 0x54, GEOMETRY_DESC_PARAM_WB_SUP_RED_TYPE = 0x55, GEOMETRY_DESC_PARAM_WB_SUP_WB_TYPE = 0x56, +#if defined(CONFIG_UFSHPB) || defined(CONFIG_SCSI_SKHPB) + GEOMETRY_DESC_HPB_REGION_SIZE = 0x48, + GEOMETRY_DESC_HPB_NUMBER_LU = 0x49, + GEOMETRY_DESC_HPB_SUBREGION_SIZE = 0x4A, + GEOMETRY_DESC_HPB_DEVICE_MAX_ACTIVE_REGIONS = 0x4B, +#endif +#if defined(CONFIG_UFSTW) + GEOMETRY_DESC_TW_GROUP_NUM_CAP = 0x4E, + GEOMETRY_DESC_TW_MAX_SIZE = 0x4F, + GEOMETRY_DESC_TW_NUMBER_LU = 0x53, + GEOMETRY_DESC_TW_CAP_ADJ_FAC = 0x54, + GEOMETRY_DESC_TW_SUPPORT_USER_REDUCTION_TYPES = 0x55, + GEOMETRY_DESC_TW_SUPPORT_BUF_TYPE = 0x56, +#endif }; +#if defined(CONFIG_SCSI_SKHID) +enum { + MANUAL_GC_OFF = 0, + MANUAL_GC_ON, + MANUAL_GC_DISABLE, + MANUAL_GC_ENABLE, + MANUAL_GC_MAX, +}; +#endif + /* Health descriptor parameters offsets in bytes*/ enum health_desc_param { HEALTH_DESC_PARAM_LEN = 0x0, @@ -391,6 +475,9 @@ enum power_desc_param_offset { enum { MASK_EE_STATUS = 0xFFFF, MASK_EE_URGENT_BKOPS = (1 << 2), +#if defined(CONFIG_UFSTW) + MASK_EE_TW = (1 << 5), +#endif }; /* Background operation status */ @@ -469,6 +556,9 @@ enum { MASK_RSP_EXCEPTION_EVENT = 0x10000, MASK_TM_SERVICE_RESP = 0xFF, MASK_TM_FUNC = 0xFF, +#if defined(CONFIG_SCSI_SKHPB) + MASK_RSP_UPIU_HPB_UPDATE_ALERT = 0x20000, +#endif }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufsfeature.c b/drivers/scsi/ufs/ufsfeature.c new file mode 100755 index 000000000000..c706ffcd1390 --- /dev/null +++ b/drivers/scsi/ufs/ufsfeature.c @@ -0,0 +1,751 @@ +/* + * Universal Flash Storage Feature Support + * + * Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jinyoung Choi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#include "ufsfeature.h" +#include "ufshcd.h" +#include "ufs-qcom.h" + +#define QUERY_REQ_TIMEOUT 1500 /* msec */ + +static inline void ufsf_init_query(struct ufs_hba *hba, + struct ufs_query_req **request, + struct ufs_query_res **response, + enum query_opcode opcode, u8 idn, + u8 index, u8 selector) +{ + *request = &hba->dev_cmd.query.request; + *response = &hba->dev_cmd.query.response; + memset(*request, 0, sizeof(struct ufs_query_req)); + memset(*response, 0, sizeof(struct ufs_query_res)); + (*request)->upiu_req.opcode = opcode; + (*request)->upiu_req.idn = idn; + (*request)->upiu_req.index = index; + (*request)->upiu_req.selector = selector; +} + +/* + * ufs feature common functions. + */ +static int ufsf_query_flag(struct ufs_hba *hba, enum query_opcode opcode, + enum flag_idn idn, u8 index, bool *flag_res) +{ + struct ufs_query_req *request = NULL; + struct ufs_query_res *response = NULL; + int err; + + BUG_ON(!hba); + + ufshcd_hold(hba, false); + mutex_lock(&hba->dev_cmd.lock); + + /* + * Init the query response and request parameters + */ + ufsf_init_query(hba, &request, &response, opcode, idn, index, + UFSFEATURE_SELECTOR); + + switch (opcode) { + case UPIU_QUERY_OPCODE_SET_FLAG: + case UPIU_QUERY_OPCODE_CLEAR_FLAG: + case UPIU_QUERY_OPCODE_TOGGLE_FLAG: + request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST; + break; + case UPIU_QUERY_OPCODE_READ_FLAG: + request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST; + if (!flag_res) { + /* No dummy reads */ + dev_err(hba->dev, "%s: Invalid argument for read request\n", + __func__); + err = -EINVAL; + goto out_unlock; + } + break; + default: + dev_err(hba->dev, + "%s: Expected query flag opcode but got = %d\n", + __func__, opcode); + err = -EINVAL; + goto out_unlock; + } + + /* Send query request */ + err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, QUERY_REQ_TIMEOUT); + if (err) { + dev_err(hba->dev, + "%s: Sending flag query for idn %d failed, err = %d\n", + __func__, idn, err); + goto out_unlock; + } + + if (flag_res) + *flag_res = (be32_to_cpu(response->upiu_res.value) & + MASK_QUERY_UPIU_FLAG_LOC) & 0x1; + +out_unlock: + mutex_unlock(&hba->dev_cmd.lock); + ufshcd_release(hba); + return err; +} + +int ufsf_query_flag_retry(struct ufs_hba *hba, enum query_opcode opcode, + enum flag_idn idn, u8 idx, bool *flag_res) +{ + int ret; + int retries; + + BUG_ON(idx > 7); + + for (retries = 0; retries < UFSF_QUERY_REQ_RETRIES; retries++) { + ret = ufsf_query_flag(hba, opcode, idn, idx, flag_res); + if (ret) + dev_dbg(hba->dev, + "%s: failed with error %d, retries %d\n", + __func__, ret, retries); + else + break; + } + if (ret) + dev_err(hba->dev, + "%s: query flag, opcode %d, idn %d, failed with error %d after %d retires\n", + __func__, opcode, idn, ret, retries); + return ret; +} + +int ufsf_query_attr_retry(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 idx, u32 *attr_val) +{ + int ret; + int retries; + + for (retries = 0; retries < UFSF_QUERY_REQ_RETRIES; retries++) { + ret = ufshcd_query_attr(hba, opcode, idn, idx, + UFSFEATURE_SELECTOR, attr_val); + if (ret) + dev_dbg(hba->dev, + "%s: failed with error %d, retries %d\n", + __func__, ret, retries); + else + break; + } + if (ret) + dev_err(hba->dev, + "%s: query attr, opcode %d, idn %d, failed with error %d after %d retires\n", + __func__, opcode, idn, ret, retries); + return ret; +} + +static int ufsf_read_desc(struct ufs_hba *hba, u8 desc_id, u8 desc_index, + u8 selector, u8 *desc_buf, u32 size) +{ + int err = 0; + + pm_runtime_get_sync(hba->dev); + + err = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC, + desc_id, desc_index, + selector, + desc_buf, &size); + if (err) + ERR_MSG("reading Device Desc failed. err = %d", err); + + pm_runtime_put_sync(hba->dev); + + return err; +} + +static int ufsf_read_dev_desc(struct ufsf_feature *ufsf, u8 selector) +{ + u8 desc_buf[UFSF_QUERY_DESC_DEVICE_MAX_SIZE]; + int ret; + + ret = ufsf_read_desc(ufsf->hba, QUERY_DESC_IDN_DEVICE, 0, selector, + desc_buf, UFSF_QUERY_DESC_DEVICE_MAX_SIZE); + if (ret) + return ret; + + ufsf->num_lu = desc_buf[DEVICE_DESC_PARAM_NUM_LU]; + INFO_MSG("device lu count %d", ufsf->num_lu); + + INFO_MSG("sel=%u length=%u(0x%x) bSupport=0x%.2x, extend=0x%.2x_%.2x", + selector, desc_buf[DEVICE_DESC_PARAM_LEN], + desc_buf[DEVICE_DESC_PARAM_LEN], + desc_buf[DEVICE_DESC_PARAM_UFS_FEAT], + desc_buf[DEVICE_DESC_PARAM_EX_FEAT_SUP+2], + desc_buf[DEVICE_DESC_PARAM_EX_FEAT_SUP+3]); + + INFO_MSG("Driver Feature Version : (%.6X%s)", UFSFEATURE_DD_VER, + UFSFEATURE_DD_VER_POST); + + +#if defined(CONFIG_UFSHPB) + if (IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) + ufshpb_get_dev_info(ufsf, desc_buf); +#endif +#if defined(CONFIG_UFSTW) + ufstw_get_dev_info(ufsf, desc_buf); +#endif + +#if defined(CONFIG_UFSHID) + ufshid_get_dev_info(ufsf, desc_buf); +#endif + return 0; +} + +static int ufsf_read_geo_desc(struct ufsf_feature *ufsf, u8 selector) +{ + u8 geo_buf[UFSF_QUERY_DESC_GEOMETRY_MAX_SIZE]; + int ret; + + ret = ufsf_read_desc(ufsf->hba, QUERY_DESC_IDN_GEOMETRY, 0, selector, + geo_buf, UFSF_QUERY_DESC_GEOMETRY_MAX_SIZE); + if (ret) + return ret; + +#if defined(CONFIG_UFSHPB) + if ((ufshpb_get_state(ufsf) == HPB_NEED_INIT) && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) + ufshpb_get_geo_info(ufsf, geo_buf); +#endif + +#if defined(CONFIG_UFSTW) + if (ufstw_get_state(ufsf) == TW_NEED_INIT) + ufstw_get_geo_info(ufsf, geo_buf); +#endif + return 0; +} + +static void ufsf_read_unit_desc(struct ufsf_feature *ufsf, int lun, u8 selector) +{ + u8 unit_buf[UFSF_QUERY_DESC_UNIT_MAX_SIZE]; + int lu_enable, ret = 0; + + ret = ufsf_read_desc(ufsf->hba, QUERY_DESC_IDN_UNIT, lun, selector, + unit_buf, UFSF_QUERY_DESC_UNIT_MAX_SIZE); + if (ret) { + ERR_MSG("read unit desc failed. ret (%d)", ret); + goto out; + } + + lu_enable = unit_buf[UNIT_DESC_PARAM_LU_ENABLE]; + if (!lu_enable) + return; + +#if defined(CONFIG_UFSHPB) + if ((ufshpb_get_state(ufsf) == HPB_NEED_INIT) && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) + ufshpb_get_lu_info(ufsf, lun, unit_buf); +#endif + +#if defined(CONFIG_UFSTW) + if (ufstw_get_state(ufsf) == TW_NEED_INIT) + ufstw_alloc_lu(ufsf, lun, unit_buf); +#endif +out: + return; +} + +int ufsf_device_check(struct ufs_hba *hba) +{ + struct ufsf_feature *ufsf = &hba->ufsf; + int ret, lun; + u32 status; + + ufsf->hba = hba; + + ufshcd_query_attr(ufsf->hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_SUP_VENDOR_OPTIONS, 0, 0, &status); + INFO_MSG("UFS FEATURE SELECTOR Dev %d - D/D %d", status, + UFSFEATURE_SELECTOR); + + ret = ufsf_read_dev_desc(ufsf, UFSFEATURE_SELECTOR); + if (ret) + return ret; + + ret = ufsf_read_geo_desc(ufsf, UFSFEATURE_SELECTOR); + if (ret) + return ret; + + seq_scan_lu(lun) + ufsf_read_unit_desc(ufsf, lun, UFSFEATURE_SELECTOR); + return 0; +} + +static int ufsf_execute_dev_ctx_req(struct ufsf_feature *ufsf, + int lun, unsigned char *cdb, + void *buf, int len) +{ + struct scsi_sense_hdr sshdr; + struct scsi_device *sdev; + int ret = 0; + + sdev = ufsf->sdev_ufs_lu[lun]; + if (!sdev) { + WARN_MSG("cannot find scsi_device"); + return -ENODEV; + } + + ufsf->issue_ioctl = true; + ret = scsi_execute(sdev, cdb, DMA_FROM_DEVICE, buf, len, NULL, &sshdr, + msecs_to_jiffies(30000), 3, 0, 0, NULL); + ufsf->issue_ioctl = false; + + return ret; +} + +static inline void ufsf_set_read_dev_ctx(unsigned char *cdb, int lba, int len) +{ + cdb[0] = READ_10; + cdb[1] = 0x02; + cdb[2] = GET_BYTE_3(lba); + cdb[3] = GET_BYTE_2(lba); + cdb[4] = GET_BYTE_1(lba); + cdb[5] = GET_BYTE_0(lba); + cdb[6] = GET_BYTE_2(len); + cdb[7] = GET_BYTE_1(len); + cdb[8] = GET_BYTE_0(len); +} + +int ufsf_issue_req_dev_ctx(struct ufsf_feature *ufsf, int lun, + unsigned char *buf, int buf_len) +{ + unsigned char cdb[10] = { 0 }; + int cmd_len = buf_len >> OS_PAGE_SHIFT; + int ret = 0; + + ufsf_set_read_dev_ctx(cdb, READ10_DEBUG_LBA, cmd_len); + + ret = ufsf_execute_dev_ctx_req(ufsf, lun, cdb, buf, buf_len); + + if (ret < 0) + ERR_MSG("failed with err %d", ret); + + return ret; +} + +static void ufsf_print_query_buf(unsigned char *field, int size) +{ + unsigned char buf[255]; + int count = 0; + int i; + + count += snprintf(buf, 8, "(0x00):"); + + for (i = 0; i < size; i++) { + count += snprintf(buf + count, 4, " %.2X", field[i]); + + if ((i + 1) % 16 == 0) { + buf[count] = '\n'; + buf[count + 1] = '\0'; + printk(buf); + count = 0; + count += snprintf(buf, 8, "(0x%.2X):", i + 1); + } else if ((i + 1) % 4 == 0) + count += snprintf(buf + count, 3, " :"); + } + buf[count] = '\n'; + buf[count + 1] = '\0'; + printk(buf); +} + +inline int ufsf_check_query(__u32 opcode) +{ + return (opcode & 0xffff0000) >> 16 == UFSFEATURE_QUERY_OPCODE; +} + +static inline void ufsf_set_read10_debug_cmd(unsigned char *cdb, int lba, + int len) +{ + cdb[0] = READ_10; + cdb[1] = 0x02; + cdb[2] = GET_BYTE_3(lba); + cdb[3] = GET_BYTE_2(lba); + cdb[4] = GET_BYTE_1(lba); + cdb[5] = GET_BYTE_0(lba); + cdb[6] = GET_BYTE_2(len); + cdb[7] = GET_BYTE_1(len); + cdb[8] = GET_BYTE_0(len); +} + +int ufsf_query_ioctl(struct ufsf_feature *ufsf, int lun, void __user *buffer, + struct ufs_ioctl_query_data *ioctl_data, u8 selector) +{ + unsigned char *kernel_buf; + int opcode; + int err = 0; + int index = 0; + int length = 0; + int buf_len = 0; + + opcode = ioctl_data->opcode & 0xffff; + + INFO_MSG("op %u idn %u sel %u size %u(0x%X)", opcode, ioctl_data->idn, + selector, ioctl_data->buf_size, ioctl_data->buf_size); + + buf_len = (ioctl_data->idn == QUERY_DESC_IDN_STRING) ? + IOCTL_DEV_CTX_MAX_SIZE : QUERY_DESC_MAX_SIZE; + + kernel_buf = kzalloc(buf_len, GFP_KERNEL); + if (!kernel_buf) { + err = -ENOMEM; + goto out; + } + + switch (opcode) { + case UPIU_QUERY_OPCODE_WRITE_DESC: + err = copy_from_user(kernel_buf, buffer + + sizeof(struct ufs_ioctl_query_data), + ioctl_data->buf_size); + INFO_MSG("buf size %d", ioctl_data->buf_size); + ufsf_print_query_buf(kernel_buf, ioctl_data->buf_size); + if (err) + goto out_release_mem; + break; + + case UPIU_QUERY_OPCODE_READ_DESC: + switch (ioctl_data->idn) { + case QUERY_DESC_IDN_UNIT: + if (!ufs_is_valid_unit_desc_lun(lun)) { + ERR_MSG("No unit descriptor for lun 0x%x", lun); + err = -EINVAL; + goto out_release_mem; + } + index = lun; + INFO_MSG("read lu desc lun: %d", index); + break; + + case QUERY_DESC_IDN_STRING: + if (!ufs_is_valid_unit_desc_lun(lun)) { + ERR_MSG("No unit descriptor for lun 0x%x", lun); + err = -EINVAL; + goto out_release_mem; + } + err = ufsf_issue_req_dev_ctx(ufsf, lun, kernel_buf, + ioctl_data->buf_size); + if (err < 0) + goto out_release_mem; + + goto copy_buffer; + case QUERY_DESC_IDN_DEVICE: + case QUERY_DESC_IDN_GEOMETRY: + case QUERY_DESC_IDN_CONFIGURATION: + break; + + default: + ERR_MSG("invalid idn %d", ioctl_data->idn); + err = -EINVAL; + goto out_release_mem; + } + break; + default: + ERR_MSG("invalid opcode %d", opcode); + err = -EINVAL; + goto out_release_mem; + } + + length = ioctl_data->buf_size; + + err = ufshcd_query_descriptor_retry(ufsf->hba, opcode, ioctl_data->idn, + index, selector, kernel_buf, + &length); + if (err) + goto out_release_mem; + +copy_buffer: + if (opcode == UPIU_QUERY_OPCODE_READ_DESC) { + err = copy_to_user(buffer, ioctl_data, + sizeof(struct ufs_ioctl_query_data)); + if (err) + ERR_MSG("Failed copying back to user."); + + err = copy_to_user(buffer + sizeof(struct ufs_ioctl_query_data), + kernel_buf, ioctl_data->buf_size); + if (err) + ERR_MSG("Fail: copy rsp_buffer to user space."); + } +out_release_mem: + kfree(kernel_buf); +out: + return err; +} + +inline int ufsf_get_scsi_device(struct ufs_hba *hba, struct scsi_device *sdev) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(hba->host->host_lock, flags); + ret = scsi_device_get(sdev); + if (!ret && !scsi_device_online(sdev)) { + spin_unlock_irqrestore(hba->host->host_lock, flags); + scsi_device_put(sdev); + ERR_MSG("scsi_device_get failed.(%d)", ret); + return -ENODEV; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + + return ret; +} + +inline bool ufsf_is_valid_lun(int lun) +{ + return lun < UFS_UPIU_MAX_GENERAL_LUN; +} + +inline void ufsf_slave_configure(struct ufsf_feature *ufsf, + struct scsi_device *sdev) +{ + if (ufsf_is_valid_lun(sdev->lun)) { + ufsf->sdev_ufs_lu[sdev->lun] = sdev; + ufsf->slave_conf_cnt++; + INFO_MSG("lun[%d] sdev(%p) q(%p) slave_conf_cnt(%d/%d)", + (int)sdev->lun, sdev, sdev->request_queue, + ufsf->slave_conf_cnt, ufsf->num_lu); + +#if defined(CONFIG_UFSHPB) + if (ufsf->num_lu == ufsf->slave_conf_cnt && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) { + if (ufshpb_get_state(ufsf) == HPB_NEED_INIT) { + INFO_MSG("wakeup ufshpb_init_handler"); + wake_up(&ufsf->hpb_wait); + } + } +#endif + } +} + +inline void ufsf_prep_fn(struct ufsf_feature *ufsf, + struct ufshcd_lrb *lrbp) +{ +#if defined(CONFIG_UFSHPB) + if (ufshpb_get_state(ufsf) == HPB_PRESENT && + ufsf->issue_ioctl == false && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) + ufshpb_prep_fn(ufsf, lrbp); +#endif + +#if defined(CONFIG_UFSTW) + if (ufstw_get_state(ufsf) == TW_PRESENT) + ufstw_prep_fn(ufsf, lrbp); +#endif +} + +inline void ufsf_reset_lu(struct ufsf_feature *ufsf) +{ +#if defined(CONFIG_UFSTW) + INFO_MSG("run reset_lu.. tw_state(%d) -> TW_RESET", + ufstw_get_state(ufsf)); + ufstw_set_state(ufsf, TW_RESET); + ufstw_reset(ufsf, false); +#endif +} + +inline void ufsf_reset_host(struct ufsf_feature *ufsf) +{ +#if defined(CONFIG_UFSHPB) + if (IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) { + INFO_MSG("run reset_host.. hpb_state(%d) -> HPB_RESET", + ufshpb_get_state(ufsf)); + if (ufshpb_get_state(ufsf) == HPB_PRESENT) + ufshpb_reset_host(ufsf); + } +#endif + +#if defined(CONFIG_UFSTW) + INFO_MSG("run reset_host.. tw_state(%d) -> TW_RESET", + ufstw_get_state(ufsf)); + if (ufstw_get_state(ufsf) == TW_PRESENT) + ufstw_reset_host(ufsf); +#endif +#if defined(CONFIG_UFSHID) + INFO_MSG("run reset_host.. hid_state(%d) -> HID_RESET", + ufshid_get_state(ufsf)); + if (ufshid_get_state(ufsf) == HID_PRESENT) + ufshid_reset_host(ufsf); +#endif +} + +inline void ufsf_init(struct ufsf_feature *ufsf) +{ +#if defined(CONFIG_UFSHPB) + if ((ufshpb_get_state(ufsf) == HPB_NEED_INIT) && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) { + INFO_MSG("init start.. hpb_state (%d)", HPB_NEED_INIT); + schedule_work(&ufsf->hpb_init_work); + } +#endif + +#if defined(CONFIG_UFSTW) + if (ufstw_get_state(ufsf) == TW_NEED_INIT) + ufstw_init(ufsf); +#endif +#if defined(CONFIG_UFSHID) + if (ufshid_get_state(ufsf) == HID_NEED_INIT) + ufshid_init(ufsf); +#endif +} + +inline void ufsf_reset(struct ufsf_feature *ufsf) +{ +#if defined(CONFIG_UFSHPB) + if ((ufshpb_get_state(ufsf) == HPB_RESET) && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) { + INFO_MSG("reset start.. hpb_state %d", HPB_RESET); + ufshpb_reset(ufsf); + } +#endif + +#if defined(CONFIG_UFSTW) + if (ufstw_get_state(ufsf) == TW_RESET && + !ufsf->hba->pm_op_in_progress) { + INFO_MSG("reset start.. tw_state %d", + ufstw_get_state(ufsf)); + ufstw_reset(ufsf, false); + } +#endif +#if defined(CONFIG_UFSHID) + if (ufshid_get_state(ufsf) == HID_RESET) + ufshid_reset(ufsf); +#endif +} + +inline void ufsf_remove(struct ufsf_feature *ufsf) +{ +#if defined(CONFIG_UFSHPB) + if ((ufshpb_get_state(ufsf) == HPB_PRESENT) && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) + ufshpb_remove(ufsf, HPB_NEED_INIT); +#endif + +#if defined(CONFIG_UFSTW) + if (ufstw_get_state(ufsf) == TW_PRESENT) + ufstw_remove(ufsf); +#endif +#if defined(CONFIG_UFSHID) + if (ufshid_get_state(ufsf) == HID_PRESENT) + ufshid_remove(ufsf); +#endif +} + +inline void ufsf_set_init_state(struct ufsf_feature *ufsf) +{ + ufsf->slave_conf_cnt = 0; + ufsf->issue_ioctl = false; +#if defined(CONFIG_UFSHPB) + if (IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) { + INFO_MSG("enable HPB feature, init start!"); + INFO_MSG("ram size is %d", ram_size); + ufshpb_set_state(ufsf, HPB_NEED_INIT); + INIT_WORK(&ufsf->hpb_init_work, ufshpb_init_handler); + init_waitqueue_head(&ufsf->hpb_wait); + } +#endif +#if defined(CONFIG_UFSTW) + ufstw_set_state(ufsf, TW_NEED_INIT); +#endif +#if defined(CONFIG_UFSHID) + ufshid_set_state(ufsf, HID_NEED_INIT); +#endif +} + +inline void ufsf_suspend(struct ufsf_feature *ufsf) +{ +#if defined(CONFIG_UFSHPB) + /* + * if suspend failed, pm could call the suspend function again, + * in this case, ufshpb state already had been changed to SUSPEND state. + * so, we will not call ufshpb_suspend. + */ + if ((ufshpb_get_state(ufsf) == HPB_PRESENT) && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) + ufshpb_suspend(ufsf); +#endif +#if defined(CONFIG_UFSHID) + if (ufshid_get_state(ufsf) == HID_PRESENT) + ufshid_suspend(ufsf); +#endif +} + +inline void ufsf_resume(struct ufsf_feature *ufsf) +{ +#if defined(CONFIG_UFSHPB) + if ((ufshpb_get_state(ufsf) == HPB_SUSPEND || + ufshpb_get_state(ufsf) == HPB_PRESENT) && + IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) { + if (ufshpb_get_state(ufsf) == HPB_PRESENT) + WARN_MSG("warning.. hpb state PRESENT in resuming"); + ufshpb_resume(ufsf); + } +#endif + +#if defined(CONFIG_UFSTW) + if (ufstw_get_state(ufsf) == TW_RESET) + ufstw_reset(ufsf, true); +#endif + +#if defined(CONFIG_UFSHID) + if (ufshid_get_state(ufsf) == HID_SUSPEND) + ufshid_resume(ufsf); +#endif +} + +inline void ufsf_on_idle(struct ufsf_feature *ufsf, bool scsi_req) +{ +#if defined(CONFIG_UFSHID) + if (ufshid_get_state(ufsf) == HID_PRESENT && + !ufsf->hba->outstanding_reqs && scsi_req) + ufshid_on_idle(ufsf); +#endif +} + +inline void ufsf_change_lun(struct ufsf_feature *ufsf, + struct ufshcd_lrb *lrbp) +{ + int ctx_lba = LI_EN_32(lrbp->cmd->cmnd + 2); + + if (unlikely(ufsf->issue_ioctl == true && + ctx_lba == READ10_DEBUG_LBA)) { + lrbp->lun = READ10_DEBUG_LUN; + INFO_MSG("lun 0x%X lba 0x%X", lrbp->lun, ctx_lba); + } +} + +/* + * Wrapper functions for ufshpb. + */ +#if defined(CONFIG_UFSHPB) +inline void ufsf_hpb_noti_rb(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp) +{ + if (ufshpb_get_state(ufsf) == HPB_PRESENT) + ufshpb_rsp_upiu(ufsf, lrbp); +} + +#else +inline void ufsf_hpb_noti_rb(struct ufsf_feature *ufsf, + struct ufshcd_lrb *lrbp) {} +#endif diff --git a/drivers/scsi/ufs/ufsfeature.h b/drivers/scsi/ufs/ufsfeature.h new file mode 100755 index 000000000000..6d1372c5eabc --- /dev/null +++ b/drivers/scsi/ufs/ufsfeature.h @@ -0,0 +1,198 @@ +/* + * Universal Flash Storage Feature Support + * + * Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jinyoung Choi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#ifndef _UFSFEATURE_H_ +#define _UFSFEATURE_H_ + +#include + +#include "ufs.h" + +#include "ufshpb.h" +#include "ufstw.h" +#include "ufshid.h" + +/* Version info */ +#define UFSFEATURE_DD_VER 0x010100 +#define UFSFEATURE_DD_VER_POST "" + +#define UFSFEATURE_QUERY_OPCODE 0x5500 + +/* For read10 debug */ +#define READ10_DEBUG_LUN 0x7F +#define READ10_DEBUG_LBA 0x48504230 + +/* Constant value*/ +#define SECTOR 512 +#define BLOCK 4096 +#define SECTORS_PER_BLOCK (BLOCK / SECTOR) +#define BITS_PER_DWORD 32 +#define sects_per_blk_shift 3 +#define bits_per_dword_shift 5 +#define bits_per_dword_mask 0x1F +#define bits_per_byte_shift 3 + +#define IOCTL_DEV_CTX_MAX_SIZE OS_PAGE_SIZE +#define OS_PAGE_SIZE 4096 +#define OS_PAGE_SHIFT 12 + +#define UFSF_QUERY_REQ_RETRIES 1 + +/* Description */ +#define UFSF_QUERY_DESC_DEVICE_MAX_SIZE 0x5F +#define UFSF_QUERY_DESC_CONFIGURAION_MAX_SIZE 0xE6 +#define UFSF_QUERY_DESC_UNIT_MAX_SIZE 0x2D +#define UFSF_QUERY_DESC_GEOMETRY_MAX_SIZE 0x59 + +#define UFSFEATURE_SELECTOR 0x01 + +/* query_flag */ +#define MASK_QUERY_UPIU_FLAG_LOC 0xFF + +/* For read10 debug */ +#define READ10_DEBUG_LUN 0x7F +#define READ10_DEBUG_LBA 0x48504230 + +/* BIG -> LI */ +#define LI_EN_16(x) be16_to_cpu(*(__be16 *)(x)) +#define LI_EN_32(x) be32_to_cpu(*(__be32 *)(x)) +#define LI_EN_64(x) be64_to_cpu(*(__be64 *)(x)) + +/* LI -> BIG */ +#define GET_BYTE_0(num) (((num) >> 0) & 0xff) +#define GET_BYTE_1(num) (((num) >> 8) & 0xff) +#define GET_BYTE_2(num) (((num) >> 16) & 0xff) +#define GET_BYTE_3(num) (((num) >> 24) & 0xff) +#define GET_BYTE_4(num) (((num) >> 32) & 0xff) +#define GET_BYTE_5(num) (((num) >> 40) & 0xff) +#define GET_BYTE_6(num) (((num) >> 48) & 0xff) +#define GET_BYTE_7(num) (((num) >> 56) & 0xff) + +#define INFO_MSG(msg, args...) pr_info("%s:%d info: " msg "\n", \ + __func__, __LINE__, ##args) +#define ERR_MSG(msg, args...) pr_err("%s:%d err: " msg "\n", \ + __func__, __LINE__, ##args) +#define WARN_MSG(msg, args...) pr_warn("%s:%d warn: " msg "\n", \ + __func__, __LINE__, ##args) + +#define seq_scan_lu(lun) for (lun = 0; lun < UFS_UPIU_MAX_GENERAL_LUN; lun++) + +#define TMSG(ufsf, lun, msg, args...) \ + do { if (ufsf->sdev_ufs_lu[lun] && \ + ufsf->sdev_ufs_lu[lun]->request_queue) \ + blk_add_trace_msg( \ + ufsf->sdev_ufs_lu[lun]->request_queue, \ + msg, ##args); \ + } while (0) \ + +struct ufsf_lu_desc { + /* Common info */ + int lu_enable; /* 03h bLUEnable */ + int lu_queue_depth; /* 06h lu queue depth info*/ + int lu_logblk_size; /* 0Ah bLogicalBlockSize. default 0x0C = 4KB */ + u64 lu_logblk_cnt; /* 0Bh qLogicalBlockCount. */ + +#if defined(CONFIG_UFSHPB) + u16 lu_max_active_hpb_rgns; /* 23h:24h wLUMaxActiveHPBRegions */ + u16 lu_hpb_pinned_rgn_startidx; /* 25h:26h wHPBPinnedRegionStartIdx */ + u16 lu_num_hpb_pinned_rgns; /* 27h:28h wNumHPBPinnedRegions */ + int lu_hpb_pinned_end_offset; +#endif +#if defined(CONFIG_UFSTW) + unsigned int tw_lu_buf_size; +#endif +}; + +struct ufsf_feature { + struct ufs_hba *hba; + int num_lu; + int slave_conf_cnt; + struct scsi_device *sdev_ufs_lu[UFS_UPIU_MAX_GENERAL_LUN]; + bool issue_ioctl; + +#if defined(CONFIG_UFSHPB) + struct ufshpb_dev_info hpb_dev_info; + struct ufshpb_lu *hpb_lup[UFS_UPIU_MAX_GENERAL_LUN]; + struct work_struct hpb_init_work; + struct work_struct hpb_eh_work; + wait_queue_head_t hpb_wait; + atomic_t hpb_state; + struct kref hpb_kref; +#endif +#if defined(CONFIG_UFSTW) + struct ufstw_dev_info tw_dev_info; + struct ufstw_lu *tw_lup[UFS_UPIU_MAX_GENERAL_LUN]; + atomic_t tw_state; +#endif +#if defined(CONFIG_UFSHID) + atomic_t hid_state; + struct ufshid_dev *hid_dev; +#endif +}; + +struct ufs_hba; +struct ufshcd_lrb; +struct ufs_ioctl_query_data; + +int ufsf_device_check(struct ufs_hba *hba); +int ufsf_check_query(__u32 opcode); +int ufsf_query_ioctl(struct ufsf_feature *ufsf, int lun, void __user *buffer, + struct ufs_ioctl_query_data *ioctl_data, + u8 selector); +int ufsf_query_flag_retry(struct ufs_hba *hba, enum query_opcode opcode, + enum flag_idn idn, u8 idx, bool *flag_res); +int ufsf_query_attr_retry(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 idx, u32 *attr_val); +int ufsf_get_scsi_device(struct ufs_hba *hba, struct scsi_device *sdev); +bool ufsf_is_valid_lun(int lun); +void ufsf_slave_configure(struct ufsf_feature *ufsf, struct scsi_device *sdev); +void ufsf_change_lun(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp); + +void ufsf_prep_fn(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp); +void ufsf_reset_lu(struct ufsf_feature *ufsf); +void ufsf_reset_host(struct ufsf_feature *ufsf); +void ufsf_init(struct ufsf_feature *ufsf); +void ufsf_reset(struct ufsf_feature *ufsf); +void ufsf_remove(struct ufsf_feature *ufsf); +void ufsf_set_init_state(struct ufsf_feature *ufsf); +void ufsf_suspend(struct ufsf_feature *ufsf); +void ufsf_resume(struct ufsf_feature *ufsf); +void ufsf_on_idle(struct ufsf_feature *ufsf, bool scsi_req); + +/* for hpb */ +void ufsf_hpb_noti_rb(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp); +#endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index faca66be8fd8..9564d31c8c84 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -55,6 +55,36 @@ #define CREATE_TRACE_POINTS #include +#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_UFSHPB) +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +#include +#define SMEM_SDRAM_INFO 135 +#else +#include +#define SMEM_SDRAM_INFO SMEM_ID_VENDOR1 +#endif + +struct { + unsigned int mr5; + unsigned int mr6; + unsigned int mr7; + unsigned int mr8; + unsigned int ramsize; +} *smem_ddr_info; + +struct storage_info { + char type[16]; /* UFS or eMMC */ + char size[16]; /* size in GB */ + char card_manufacturer[32]; + char product_name[32]; /* model ID */ + char firmware_version[32]; +}; +unsigned int ram_size; +#endif + #define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\ UTP_TASK_REQ_COMPL |\ UFSHCD_ERROR_MASK) @@ -66,6 +96,7 @@ #define UIC_CMD_TIMEOUT 500 #endif +unsigned int storage_mfrid; /* NOP OUT retries waiting for NOP IN response */ #define NOP_OUT_RETRIES 10 /* Timeout after 30 msecs if NOP OUT hangs without response */ @@ -109,6 +140,11 @@ /* Polling time to wait for fDeviceInit */ #define FDEVICEINIT_COMPL_TIMEOUT 5000 /* millisecs */ +#if defined(CONFIG_SCSI_SKHID) +/* for manual gc */ +#define UFSHCD_MANUAL_GC_HOLD_HIBERN8 2000 /* 2 seconds */ +#endif + #define ufshcd_toggle_vreg(_dev, _vreg, _on) \ ({ \ int _ret; \ @@ -200,7 +236,21 @@ struct ufs_pm_lvl_states ufs_pm_lvl_states[] = { {UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE}, {UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE}, }; - +#if defined(CONFIG_SCSI_SKHPB) +static inline int is_support_hpb_100_device(unsigned int mfrid){ + return IS_SKHYNIX_DEVICE(mfrid); +} +#endif +#if defined(CONFIG_UFSFEATURE) +static inline int is_support_hpb_200_device(unsigned int mfrid){ +#if defined(CONFIG_MICRON_HPB) || defined(CONFIG_MICRON_UFSHID) + return (IS_SAMSUNG_DEVICE(mfrid) || IS_MICRON_DEVICE(mfrid)); +#else + return IS_SAMSUNG_DEVICE(mfrid); +#endif + return 0; +} +#endif static inline enum ufs_dev_pwr_mode ufs_get_pm_lvl_to_dev_pwr_mode(enum ufs_pm_level lvl) { @@ -255,10 +305,43 @@ static struct ufs_dev_fix ufs_fixups[] = { UFS_FIX(UFS_VENDOR_SAMSUNG, "KLUDG4UHDB-B2D1", UFS_DEVICE_QUIRK_PA_HIBER8TIME), #endif +#if defined(CONFIG_SCSI_SKHPB) + UFS_FIX(UFS_VENDOR_SKHYNIX, "H28S", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ15ACPMA", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ15AECMA", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ15AECMM", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ15AFAMA", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ15AFAMM", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ15AJAMM", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ21AECMM", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ21AECMZ", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ21AFAMM", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ21AFAMZ", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ21AJAMM", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), + UFS_FIX(UFS_VENDOR_SKHYNIX, "H9HQ21AHDMM", + SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP), +#endif END_FIX }; +#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_UFSHPB) +static int get_dram_info(struct ufs_hba *hba); +static int get_storage_info(struct ufs_hba *hba); +#endif static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); static int ufshcd_reset_and_restore(struct ufs_hba *hba); @@ -340,13 +423,21 @@ static inline void ufshcd_wb_config(struct ufs_hba *hba) ufshcd_wb_toggle_flush(hba, true); } +#if defined(CONFIG_UFSFEATURE) +void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) +#else static void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) +#endif { if (atomic_dec_and_test(&hba->scsi_block_reqs_cnt)) scsi_unblock_requests(hba->host); } +#if defined(CONFIG_UFSFEATURE) +void ufshcd_scsi_block_requests(struct ufs_hba *hba) +#else static void ufshcd_scsi_block_requests(struct ufs_hba *hba) +#endif { if (atomic_inc_return(&hba->scsi_block_reqs_cnt) == 1) scsi_block_requests(hba->host); @@ -1125,8 +1216,12 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba, return false; } +#if defined(CONFIG_UFSFEATURE) +int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us) +#else static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us) +#endif { unsigned long flags; int ret = 0; @@ -1232,7 +1327,7 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up) static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) { - #define DOORBELL_CLR_TOUT_US (1000 * 1000) /* 1 sec */ + #define DOORBELL_CLR_TOUT_US (2000 * 1000) /* 2 sec */ int ret = 0; /* * make sure that there are no outstanding requests when @@ -1620,6 +1715,51 @@ static void ufshcd_clkscaling_init_sysfs(struct ufs_hba *hba) dev_err(hba->dev, "Failed to create sysfs for clkscale_enable\n"); } +#if defined(CONFIG_SCSI_SKHID) +static enum hrtimer_restart ufshcd_mgc_hrtimer_handler(struct hrtimer *timer) +{ + struct ufs_hba *hba = container_of(timer, struct ufs_hba, + manual_gc.hrtimer); + + queue_work(hba->manual_gc.mgc_workq, &hba->manual_gc.hibern8_work); + return HRTIMER_NORESTART; +} + +static void ufshcd_mgc_hibern8_work(struct work_struct *work) +{ + struct ufs_hba *hba = container_of(work, struct ufs_hba, + manual_gc.hibern8_work); + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + /* bkops will be disabled when power down */ +} + +static void ufshcd_init_manual_gc(struct ufs_hba *hba) +{ + struct ufs_manual_gc *mgc = &hba->manual_gc; + char wq_name[sizeof("ufs_mgc_hibern8_work")]; + + mgc->state = MANUAL_GC_ENABLE; + mgc->hagc_support = true; + mgc->delay_ms = UFSHCD_MANUAL_GC_HOLD_HIBERN8; + + hrtimer_init(&mgc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + mgc->hrtimer.function = ufshcd_mgc_hrtimer_handler; + + INIT_WORK(&mgc->hibern8_work, ufshcd_mgc_hibern8_work); + snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_mgc_hibern8_work_%d", + hba->host->host_no); + hba->manual_gc.mgc_workq = create_singlethread_workqueue(wq_name); +} + +static void ufshcd_exit_manual_gc(struct ufs_hba *hba) +{ + hrtimer_cancel(&hba->manual_gc.hrtimer); + cancel_work_sync(&hba->manual_gc.hibern8_work); + destroy_workqueue(hba->manual_gc.mgc_workq); +} +#endif + static void ufshcd_ungate_work(struct work_struct *work) { int ret; @@ -2563,9 +2703,23 @@ static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE; if (likely(lrbp->cmd)) { +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)){ + ufsf_change_lun(&hba->ufsf, lrbp); + ufsf_prep_fn(&hba->ufsf, lrbp); + } +#endif ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, lrbp->cmd->sc_data_direction); ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags); +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid)){ + if (hba->skhpb_state == SKHPB_PRESENT && + hba->issue_ioctl == false) { + skhpb_prep_fn(hba, lrbp); + } + } +#endif } else { ret = -EINVAL; } @@ -2655,6 +2809,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) lrbp->req_abort_skip = false; ufshcd_comp_scsi_upiu(hba, lrbp); +#if defined(CONFIG_UFSFEATURE) && defined(CONFIG_UFSHPB) + if (is_support_hpb_200_device(storage_mfrid)){ + if (cmd->cmnd[0] != 0x28) + BUG_ON(cmd->requeue_cnt); + + if (cmd->requeue_cnt) + err = -EAGAIN; + + if (err) { + ufshcd_release(hba); + lrbp->cmd = NULL; + clear_bit_unlock(tag, &hba->lrb_in_use); + goto out; + } + } +#endif err = ufshcd_map_sg(hba, lrbp); if (err) { @@ -2897,8 +3067,13 @@ static inline void ufshcd_put_dev_cmd_tag(struct ufs_hba *hba, int tag) * NOTE: Since there is only one available tag for device management commands, * it is expected you hold the hba->dev_cmd.lock mutex. */ +#if defined(CONFIG_UFSFEATURE) +int ufshcd_exec_dev_cmd(struct ufs_hba *hba, + enum dev_cmd_type cmd_type, int timeout) +#else static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, enum dev_cmd_type cmd_type, int timeout) +#endif { struct ufshcd_lrb *lrbp; int err; @@ -2967,8 +3142,13 @@ static inline void ufshcd_init_query(struct ufs_hba *hba, (*request)->upiu_req.selector = selector; } +#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_SCSI_SKHID) +int ufshcd_query_flag_retry(struct ufs_hba *hba, + enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res) +#else static int ufshcd_query_flag_retry(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res) +#endif { int ret; int retries; @@ -3137,14 +3317,20 @@ EXPORT_SYMBOL_GPL(ufshcd_query_attr); * * Returns 0 for success, non-zero in case of failure */ +#if defined(CONFIG_SCSI_SKHID) +int ufshcd_query_attr_retry(struct ufs_hba *hba, + enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, + u32 *attr_val) +#else static int ufshcd_query_attr_retry(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) +#endif { int ret = 0; u32 retries; - for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) { + for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) { ret = ufshcd_query_attr(hba, opcode, idn, index, selector, attr_val); if (ret) @@ -4957,13 +5143,23 @@ static int ufshcd_slave_configure(struct scsi_device *sdev) struct request_queue *q = sdev->request_queue; struct ufs_hba *hba = shost_priv(sdev->host); +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)){ + ufsf_slave_configure(&hba->ufsf, sdev); + } +#endif blk_queue_update_dma_pad(q, PRDT_DATA_BYTE_COUNT_PAD - 1); ufshcd_crypto_setup_rq_keyslot_manager(hba, q); if (ufshcd_is_rpm_autosuspend_allowed(hba)) sdev->rpm_autosuspend = 1; - +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid)){ + if (sdev->lun < UFS_UPIU_MAX_GENERAL_LUN) + hba->sdev_ufs_lu[sdev->lun] = sdev; + } +#endif return 0; } @@ -5092,6 +5288,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) if (schedule_work(&hba->eeh_work)) pm_runtime_get_noresume(hba->dev); } + +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)){ + if (scsi_status == SAM_STAT_GOOD) + ufsf_hpb_noti_rb(&hba->ufsf, lrbp); + } +#endif +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid)){ + if (hba->skhpb_state == SKHPB_PRESENT && + scsi_status == SAM_STAT_GOOD) + skhpb_rsp_upiu(hba, lrbp); + } +#endif break; case UPIU_TRANSACTION_REJECT_UPIU: /* TODO: handle Reject UPIU Response */ @@ -5187,12 +5397,22 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, struct scsi_cmnd *cmd; int result; int index; +#if defined(CONFIG_UFSFEATURE) + bool scsi_req = false; +#endif for_each_set_bit(index, &completed_reqs, hba->nutrs) { lrbp = &hba->lrb[index]; cmd = lrbp->cmd; ufshcd_vops_compl_xfer_req(hba, index, (cmd) ? true : false); if (cmd) { +#if defined(CONFIG_UFSFEATURE) && defined(CONFIG_UFSHPB) && defined(CONFIG_HPB_DEBUG) + if (is_support_hpb_200_device(storage_mfrid)) + trace_printk("%llu + %u cmd 0x%X comp tag[%d] out %lX\n", + (unsigned long long) blk_rq_pos(cmd->request), + (unsigned int) blk_rq_sectors(cmd->request), + cmd->cmnd[0], index, hba->outstanding_reqs); +#endif ufshcd_add_command_trace(hba, index, "complete"); result = ufshcd_transfer_rsp_status(hba, lrbp); scsi_dma_unmap(cmd); @@ -5205,6 +5425,10 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, /* Do not touch lrbp after scsi done */ cmd->scsi_done(cmd); __ufshcd_release(hba); +#if defined(CONFIG_UFSFEATURE) + if (IS_SAMSUNG_DEVICE(storage_mfrid)) + scsi_req = true; +#endif } else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE || lrbp->command_type == UTP_CMD_TYPE_UFS_STORAGE) { lrbp->compl_time_stamp = ktime_get(); @@ -5225,6 +5449,10 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, /* we might have free'd some tags above */ wake_up(&hba->dev_cmd.tag_wq); +#if defined(CONFIG_UFSFEATURE) + if (IS_SAMSUNG_DEVICE(storage_mfrid)) + ufsf_on_idle(&hba->ufsf, scsi_req); +#endif } /** @@ -5447,8 +5675,12 @@ static inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status) * to know whether auto bkops is enabled or disabled after this function * returns control to it. */ +#if defined(CONFIG_SCSI_SKHID) +int ufshcd_bkops_ctrl(struct ufs_hba *hba, enum bkops_status status) +#else static int ufshcd_bkops_ctrl(struct ufs_hba *hba, enum bkops_status status) +#endif { int err; u32 curr_status = 0; @@ -6737,6 +6969,18 @@ out: hba->req_abort_count = 0; ufshcd_update_reg_hist(&hba->ufs_stats.dev_reset, (u32)err); if (!err) { +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)) + ufsf_reset_lu(&hba->ufsf); +#endif +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid)){ + if (hba->skhpb_state == SKHPB_PRESENT) + hba->skhpb_state = SKHPB_RESET; + schedule_delayed_work(&hba->skhpb_init_work, + msecs_to_jiffies(10)); + } +#endif err = SUCCESS; } else { dev_err(hba->dev, "%s: failed with err %d\n", __func__, err); @@ -6947,6 +7191,12 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) int err; unsigned long flags; +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)){ + ufsf_reset_host(&hba->ufsf); + } +#endif + /* * Stop the host controller and complete the requests * cleared by h/w @@ -7305,19 +7555,33 @@ static void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf) */ if (!(dev_info->wspecversion >= 0x310 || dev_info->wspecversion == 0x220 || - (hba->dev_quirks & UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES))) + (hba->dev_quirks & UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES))) { + dev_err(hba->dev, "%s: vendor quirks don't support wb, wb_disable\n",__func__); goto wb_disabled; + } - if (hba->desc_size.dev_desc < DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP + 4) + if (hba->desc_size.dev_desc < DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP + 4) { + dev_err(hba->dev, "%s: ufs device don't support ext ufs feature, wb_disable\n", + __func__); goto wb_disabled; + } dev_info->d_ext_ufs_feature_sup = get_unaligned_be32(desc_buf + DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP); - if (!(dev_info->d_ext_ufs_feature_sup & UFS_DEV_WRITE_BOOSTER_SUP)) + if (!(dev_info->d_ext_ufs_feature_sup & UFS_DEV_WRITE_BOOSTER_SUP)) { + dev_err(hba->dev, "%s: ufs device don't support write booster feature, wb_disable\n",__func__); goto wb_disabled; + } +#if defined(CONFIG_UFSTW) + //disable write booster for samsung ufs. turbo write feature is used on samsung v6 or v5 ufs. + if (dev_info->wmanufacturerid == UFS_VENDOR_SAMSUNG) { + dev_err(hba->dev, "%s: it is samsung ufs, just disable wb, will enable TW.wb_disable \n",__func__); + goto wb_disabled; + } +#endif /* * WB may be supported but not configured while provisioning. * The spec says, in dedicated wb buffer mode, @@ -7334,8 +7598,10 @@ static void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf) dev_info->d_wb_alloc_units = get_unaligned_be32(desc_buf + DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS); - if (!dev_info->d_wb_alloc_units) + if (!dev_info->d_wb_alloc_units) { + dev_err(hba->dev, "%s: read wb_share_alloc_units is 0 , wb_disable\n",__func__); goto wb_disabled; + } } else { for (lun = 0; lun < UFS_UPIU_MAX_WB_LUN_ID; lun++) { d_lu_wb_buf_alloc = 0; @@ -7350,8 +7616,10 @@ static void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf) } } - if (!d_lu_wb_buf_alloc) + if (!d_lu_wb_buf_alloc) { + dev_err(hba->dev, "%s: read wb_share_alloc_units is 0 , wb_disable\n",__func__); goto wb_disabled; + } } return; @@ -7804,6 +8072,14 @@ static int ufshcd_add_lus(struct ufs_hba *hba) ufs_bsg_probe(hba); scsi_scan_host(hba->host); +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)){ + if (ufsf_device_check(hba)) + goto out; + ufsf_init(&hba->ufsf); + } +#endif + out: return ret; } @@ -7923,10 +8199,20 @@ reinit: ufshcd_set_active_icc_lvl(hba); ufshcd_wb_config(hba); + +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid)) + schedule_delayed_work(&hba->skhpb_init_work, 0); +#endif /* Enable Auto-Hibernate if configured */ ufshcd_auto_hibern8_enable(hba); out: +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)) + ufsf_reset(&hba->ufsf); +#endif + spin_lock_irqsave(hba->host->host_lock, flags); if (ret) hba->ufshcd_state = UFSHCD_STATE_ERROR; @@ -8788,6 +9074,11 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) req_link_state = UIC_LINK_OFF_STATE; } +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)) + ufsf_suspend(&hba->ufsf); +#endif + ret = ufshcd_crypto_suspend(hba, pm_op); if (ret) goto out; @@ -8798,6 +9089,10 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) */ ufshcd_hold(hba, false); hba->clk_gating.is_suspended = true; +#if defined(CONFIG_SCSI_SKHPB) + if(is_support_hpb_100_device(storage_mfrid)) + skhpb_suspend(hba); +#endif if (hba->clk_scaling.is_allowed) { cancel_work_sync(&hba->clk_scaling.suspend_work); @@ -8922,6 +9217,11 @@ enable_gating: hba->dev_info.b_rpm_dev_flush_capable = false; ufshcd_release(hba); ufshcd_crypto_resume(hba, pm_op); +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)){ + ufsf_resume(&hba->ufsf); + } +#endif out: if (hba->dev_info.b_rpm_dev_flush_capable) { schedule_delayed_work(&hba->rpm_dev_flush_recheck_work, @@ -9064,6 +9364,15 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) cancel_delayed_work(&hba->rpm_dev_flush_recheck_work); } +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)) + ufsf_resume(&hba->ufsf); +#endif +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid)) + skhpb_resume(hba); +#endif + /* Schedule clock gating in case of no access to UFS device yet */ ufshcd_release(hba); @@ -9318,14 +9627,29 @@ EXPORT_SYMBOL(ufshcd_shutdown); */ void ufshcd_remove(struct ufs_hba *hba) { +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)){ + ufsf_remove(&hba->ufsf); + } +#endif ufs_bsg_remove(hba); ufs_sysfs_remove_nodes(hba->dev); + +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid)){ + skhpb_release(hba, SKHPB_NEED_INIT); + } +#endif + scsi_remove_host(hba->host); destroy_workqueue(hba->eh_wq); /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); ufshcd_hba_stop(hba, true); - +#if defined(CONFIG_SCSI_SKHID) + if (IS_SKHYNIX_DEVICE(storage_mfrid)) + ufshcd_exit_manual_gc(hba); +#endif ufshcd_exit_clk_scaling(hba); ufshcd_exit_clk_gating(hba); if (ufshcd_is_clkscaling_supported(hba)) @@ -9510,7 +9834,10 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ufshcd_init_clk_gating(hba); ufshcd_init_clk_scaling(hba); - +#if defined(CONFIG_SCSI_SKHID) + if (IS_SKHYNIX_DEVICE(storage_mfrid)) + ufshcd_init_manual_gc(hba); +#endif /* * In order to avoid any spurious interrupt immediately after * registering UFS controller interrupt handler, clear any pending UFS @@ -9591,6 +9918,22 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) */ ufshcd_set_ufs_dev_active(hba); +#if defined(CONFIG_UFSHPB) || defined(CONFIG_SCSI_SKHPB) + get_storage_info(hba); + get_dram_info(hba); +#endif + +#if defined(CONFIG_UFSFEATURE) + if (is_support_hpb_200_device(storage_mfrid)) { + ufsf_set_init_state(&hba->ufsf); + } +#endif +#if defined(CONFIG_SCSI_SKHPB) + if (is_support_hpb_100_device(storage_mfrid) && IS_RAM_SIZE_GREATER_THAN_4G(ram_size)) { + /* initialize hpb structures */ + ufshcd_init_hpb(hba); + } +#endif async_schedule(ufshcd_async_scan, hba); ufs_sysfs_add_nodes(hba->dev); @@ -9599,6 +9942,10 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) out_remove_scsi_host: scsi_remove_host(hba->host); exit_gating: +#if defined(CONFIG_SCSI_SKHID) + if (IS_SKHYNIX_DEVICE(storage_mfrid)) + ufshcd_exit_manual_gc(hba); +#endif ufshcd_exit_clk_scaling(hba); ufshcd_exit_clk_gating(hba); destroy_workqueue(hba->eh_wq); @@ -9608,6 +9955,85 @@ out_disable: out_error: return err; } +static int __init storage_mfrid_setup(char *str) +{ + storage_mfrid = simple_strtol(str, NULL, 16); + return 1; +} +__setup("storage_mfrid=",storage_mfrid_setup); + +#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_UFSHPB) +static int get_storage_info(struct ufs_hba *hba) +{ + int ret = 0; + struct property *p; + struct device_node *n; + struct storage_info *info; + + n = of_find_node_by_path("/chosen/mmi,storage"); + if (n == NULL) { + ret = 1; + goto err; + } + + info = kzalloc(sizeof(struct storage_info), GFP_KERNEL); + if (!info) { + dev_err(hba->dev,"%s: failed to allocate space for mmi_storage_info\n", + __func__); + ret = 1; + goto err; + } + + for_each_property_of_node(n, p) { + if (!strcmp(p->name, "type") && p->value) + strlcpy(info->type, (char *)p->value, sizeof(info->type)); + if (!strcmp(p->name, "size") && p->value) + strlcpy(info->size, (char *)p->value, sizeof(info->size)); + if (!strcmp(p->name, "manufacturer") && p->value) + strlcpy(info->card_manufacturer, (char *)p->value, sizeof(info->card_manufacturer)); + if (!strcmp(p->name, "product") && p->value) + strlcpy(info->product_name, (char *)p->value, sizeof(info->product_name)); + if (!strcmp(p->name, "firmware") && p->value) + strlcpy(info->firmware_version, (char *)p->value, sizeof(info->firmware_version)); + } + + of_node_put(n); + + dev_info(hba->dev, "manufacturer parsed from choosen is %s\n",info->card_manufacturer); +err: + return ret; +} + +static int get_dram_info(struct ufs_hba *hba) +{ + int ret = -1; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ssize_t ddr_info_size = 0; + smem_ddr_info = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_SDRAM_INFO, + &ddr_info_size); + + if(smem_ddr_info) { + if(ddr_info_size < sizeof(*smem_ddr_info)) { + dev_err(hba->dev,"%s: Invalid SMEM size, cannot get RAM info\n", __func__); + goto err; + } + } +#endif + if (smem_ddr_info == NULL) { + dev_err(hba->dev,"%s: failed to access RAM info in SMEM\n", __func__); + goto err; + } + + ram_size = (smem_ddr_info->ramsize / 1024); + dev_info(hba->dev, "ram_size parsed from SMEM is %d\n",ram_size); + return ram_size; +err: + return ret; +} +#endif + EXPORT_SYMBOL_GPL(ufshcd_init); MODULE_AUTHOR("Santosh Yaragnavi "); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index d9003a0b12a4..aeba90206c3e 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -73,11 +73,26 @@ #include "ufs_quirks.h" #include "ufshci.h" +#if defined(CONFIG_UFSFEATURE) +#include "ufsfeature.h" +#endif + +#if defined(CONFIG_SCSI_SKHPB) +#include "ufshpb_skh.h" +#endif + #define UFSHCD "ufshcd" #define UFSHCD_DRIVER_VERSION "0.2" #define UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8 0x40000 - +extern unsigned int storage_mfrid; +#define IS_SAMSUNG_DEVICE(mfrid) (UFS_VENDOR_SAMSUNG == (mfrid)) +#define IS_SKHYNIX_DEVICE(mfrid) (UFS_VENDOR_SKHYNIX == (mfrid)) +#define IS_MICRON_DEVICE(mfrid) (UFS_VENDOR_MICRON == (mfrid)) +#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_UFSHPB) +extern unsigned int ram_size; +#define IS_RAM_SIZE_GREATER_THAN_4G(ram_size) (ram_size > 4) +#endif struct ufs_hba; enum dev_cmd_type { @@ -469,6 +484,18 @@ struct ufs_clk_gating { struct workqueue_struct *clk_gating_workq; }; +#if defined(CONFIG_SCSI_SKHID) +/* for manual gc */ +struct ufs_manual_gc { + int state; + bool hagc_support; + struct hrtimer hrtimer; + unsigned long delay_ms; + struct work_struct hibern8_work; + struct workqueue_struct *mgc_workq; +}; +#endif + struct ufs_saved_pwr_info { struct ufs_pa_layer_attr info; bool is_valid; @@ -921,6 +948,9 @@ struct ufs_hba { /* Keeps information of the UFS device connected to this host */ struct ufs_dev_info dev_info; bool auto_bkops_enabled; +#if defined(CONFIG_SCSI_SKHID) + struct ufs_manual_gc manual_gc; +#endif struct ufs_vreg_info vreg_info; struct list_head clk_list_head; @@ -1016,6 +1046,24 @@ struct ufs_hba { struct device bsg_dev; struct request_queue *bsg_queue; +#if defined(CONFIG_SCSI_SKHPB) + /* HPB support */ + u32 skhpb_feat; + int skhpb_state; + int skhpb_max_regions; + struct delayed_work skhpb_init_work; + bool issue_ioctl; + struct skhpb_lu *skhpb_lup[UFS_UPIU_MAX_GENERAL_LUN]; + struct work_struct skhpb_eh_work; + u32 skhpb_quirk; + u8 hpb_control_mode; +#define SKHPB_U8_MAX 0xFF + u8 skhpb_quicklist_lu_enable[UFS_UPIU_MAX_GENERAL_LUN]; +#endif + +#if defined(CONFIG_SCSI_SKHPB) + struct scsi_device *sdev_ufs_lu[UFS_UPIU_MAX_GENERAL_LUN]; +#endif #ifdef CONFIG_SCSI_UFS_CRYPTO /* crypto */ union ufs_crypto_capabilities crypto_capabilities; @@ -1028,6 +1076,10 @@ struct ufs_hba { bool wb_buf_flush_enabled; bool wb_enabled; struct delayed_work rpm_dev_flush_recheck_work; + +#if defined(CONFIG_UFSFEATURE) + struct ufsf_feature ufsf; +#endif ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); @@ -1215,6 +1267,12 @@ extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); +#if defined(CONFIG_SCSI_SKHID) +extern int ufshcd_query_attr_retry(struct ufs_hba *hba, + enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, + u32 *attr_val); +extern int ufshcd_bkops_ctrl(struct ufs_hba *hba, enum bkops_status status); +#endif /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 #define DME_PEER 1 @@ -1297,6 +1355,13 @@ void ufshcd_fixup_dev_quirks(struct ufs_hba *hba, struct ufs_dev_fix *fixups); #define SD_ASCII_STD true #define SD_RAW false +#if defined(CONFIG_UFSFEATURE) +void ufshcd_scsi_unblock_requests(struct ufs_hba *hba); +void ufshcd_scsi_block_requests(struct ufs_hba *hba); +int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us); +int ufshcd_exec_dev_cmd(struct ufs_hba *hba, + enum dev_cmd_type cmd_type, int timeout); +#endif int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); @@ -1500,6 +1565,10 @@ static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID; } +#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_SCSI_SKHID) +int ufshcd_query_flag_retry(struct ufs_hba *hba, + enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res); +#endif int ufshcd_dump_regs(struct ufs_hba *hba, size_t offset, size_t len, const char *prefix); diff --git a/drivers/scsi/ufs/ufshid.c b/drivers/scsi/ufs/ufshid.c new file mode 100755 index 000000000000..acb33f5d5013 --- /dev/null +++ b/drivers/scsi/ufs/ufshid.c @@ -0,0 +1,1061 @@ +/* + * Universal Flash Storage Host Initiated Defrag (UFS HID) + * + * Copyright (C) 2019-2019 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jieon Seol + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#include "ufshcd.h" +#include "ufsfeature.h" +#include "ufshid.h" + +static int ufshid_create_sysfs(struct ufshid_dev *hid); + +inline int ufshid_get_state(struct ufsf_feature *ufsf) +{ + return atomic_read(&ufsf->hid_state); +} + +inline void ufshid_set_state(struct ufsf_feature *ufsf, int state) +{ + atomic_set(&ufsf->hid_state, state); +} + +static inline int ufshid_is_not_present(struct ufshid_dev *hid) +{ + enum UFSHID_STATE cur_state = ufshid_get_state(hid->ufsf); + + if (cur_state != HID_PRESENT) { + INFO_MSG("hid_state != HID_PRESENT (%d)", cur_state); + return -ENODEV; + } + return 0; +} + +static int ufshid_read_attr(struct ufshid_dev *hid, u8 idn, u32 *attr_val) +{ + struct ufs_hba *hba = hid->ufsf->hba; + int ret = 0; + + pm_runtime_get_sync(hba->dev); + + ret = ufsf_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR, idn, 0, + attr_val); + if (ret) { + ERR_MSG("read attr [0x%.2X] fail. (%d)", idn, ret); + goto err_out; + } + + HID_DEBUG(hid, "hid_attr read [0x%.2X] %u (0x%X)", idn, *attr_val, + *attr_val); + TMSG(hid->ufsf, 0, "[ufshid] read_attr IDN %s (%d)", + idn == QUERY_ATTR_IDN_HID_OPERATION ? "HID_OP" : + idn == QUERY_ATTR_IDN_HID_FRAG_LEVEL ? "HID_LEV" : "UNKNOWN", idn); +err_out: + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + + return ret; +} + +static int ufshid_write_attr(struct ufshid_dev *hid, u8 idn, u32 val) +{ + struct ufs_hba *hba = hid->ufsf->hba; + int ret = 0; + + pm_runtime_get_sync(hba->dev); + + ret = ufsf_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, idn, 0, + &val); + if (ret) { + ERR_MSG("write attr [0x%.2X] fail. (%d)", idn, ret); + goto err_out; + } + + HID_DEBUG(hid, "hid_attr write [0x%.2X] %u (0x%X)", idn, val, val); + TMSG(hid->ufsf, 0, "[ufshid] write_attr IDN %s (%d)", + idn == QUERY_ATTR_IDN_HID_OPERATION ? "HID_OP" : + idn == QUERY_ATTR_IDN_HID_FRAG_LEVEL ? "HID_LEV" : "UNKNOWN", idn); +err_out: + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + + return ret; +} + +#if defined(CONFIG_MICRON_UFSHID) +static int ufshid_set_flag(struct ufshid_dev *hid, u8 idn) +{ + struct ufs_hba *hba = hid->ufsf->hba; + int ret = 0; + bool flag_result; + pm_runtime_get_sync(hba->dev); + + ret = ufsf_query_flag_retry(hba, UPIU_QUERY_OPCODE_SET_FLAG, idn, 0, + &flag_result); + + if (ret) { + ERR_MSG("set flag [0x%.2X] fail. (%d)", idn, ret); + goto err_out; + } + + HID_DEBUG(hid, "hid_flag set [0x%.2X] result [0x%.2X] ", idn, flag_result); +err_out: + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + + return ret; +} + +static int ufshid_clear_flag(struct ufshid_dev *hid, u8 idn) +{ + struct ufs_hba *hba = hid->ufsf->hba; + int ret = 0; + + pm_runtime_get_sync(hba->dev); + + ret = ufsf_query_flag_retry(hba, UPIU_QUERY_OPCODE_CLEAR_FLAG, idn, 0, + NULL); + if (ret) { + ERR_MSG("clear flag [0x%.2X] fail. (%d)", idn, ret); + goto err_out; + } + + HID_DEBUG(hid, "hid_flag set [0x%.2X] ", idn); +err_out: + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + + return ret; +} +#endif + + +static inline int ufshid_version_check(int spec_version) +{ + INFO_MSG("Support HID Spec : Driver = (%.4x), Device = (%.4x)", + UFSHID_VER, spec_version); + INFO_MSG("HID Driver version (%.6X%s)", + UFSHID_DD_VER, UFSHID_DD_VER_POST); + + if (spec_version != UFSHID_VER) { + ERR_MSG("UFS HID version mismatched"); + return -ENODEV; + } + return 0; +} + +void ufshid_get_dev_info(struct ufsf_feature *ufsf, u8 *desc_buf) +{ + int ret = 0, spec_version; + + ufsf->hid_dev = NULL; + + if(IS_SAMSUNG_DEVICE(storage_mfrid)) { + if (!(LI_EN_32(&desc_buf[DEVICE_DESC_PARAM_EX_FEAT_SUP]) & + UFS_FEATURE_SUPPORT_HID_BIT)) { + INFO_MSG("bUFSExFeaturesSupport: HID not support"); + goto err_out; + } + + INFO_MSG("bUFSExFeaturesSupport: HID support"); + spec_version = + LI_EN_16(&desc_buf[DEVICE_DESC_PARAM_HID_VER]); + ret = ufshid_version_check(spec_version); + if (ret) + goto err_out; + } + +#ifndef CONFIG_MICRON_UFSHID + if(IS_MICRON_DEVICE(storage_mfrid)){ + INFO_MSG("micron: HID not support"); + goto err_out; + } +#endif + + ufsf->hid_dev = kzalloc(sizeof(struct ufshid_dev), GFP_KERNEL); + if (!ufsf->hid_dev) { + ERR_MSG("hid_dev memalloc fail"); + goto err_out; + } + + ufsf->hid_dev->ufsf = ufsf; + return; +err_out: + ufshid_set_state(ufsf, HID_FAILED); +} + +static int ufshid_get_analyze_and_issue_execute(struct ufshid_dev *hid) +{ + u32 attr_val; + int frag_level; + + if(IS_MICRON_DEVICE(storage_mfrid)) { + #if defined(CONFIG_MICRON_UFSHID) + //get micron ufs frag level + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_FRAG_STATUS, &frag_level)) + return -EINVAL; + //get micron ufs hid execution progress + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_PROGRESS, &attr_val)) + return -EINVAL; + HID_DEBUG(hid, "micron frag_level= %d attr_val= %d",frag_level,attr_val); + + if (attr_val != HID_PROG_ONGOING) { + if(frag_level!= HID_LEV_GREEN_MICRON) { + ufshid_set_flag(hid, QUERY_FLAG_IDN_HID_EN); + return HID_REQUIRED; + } else { + return HID_NOT_REQUIRED; + } + } else { + return HID_REQUIRED; + } + #else + return HID_NOT_REQUIRED; + #endif + } else if(IS_SAMSUNG_DEVICE(storage_mfrid)) { + if (ufshid_write_attr(hid, QUERY_ATTR_IDN_HID_OPERATION, + HID_OP_EXECUTE)) + return -EINVAL; + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_FRAG_LEVEL, &attr_val)) + return -EINVAL; + + frag_level = attr_val & HID_FRAG_LEVEL_MASK; + HID_DEBUG(hid, "Frag_lv %d Freg_stat %d HID_need_exec %d", + frag_level, HID_FRAG_UPDATE_STAT(attr_val), + HID_EXECUTE_REQ_STAT(attr_val)); + + if (frag_level == HID_LEV_GRAY) + return -EAGAIN; + + return (HID_EXECUTE_REQ_STAT(attr_val)) ? + HID_REQUIRED : HID_NOT_REQUIRED; + } else { + return -EINVAL; + } +} + +static int ufshid_issue_disable(struct ufshid_dev *hid) +{ + u32 attr_val; + + if(IS_MICRON_DEVICE(storage_mfrid)) { + #if defined(CONFIG_MICRON_UFSHID) + //get micron ufs hid execution progress + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_PROGRESS, &attr_val)) + return -EINVAL; + HID_DEBUG(hid, "micron hid progress = %d",attr_val); + + if(attr_val == HID_PROG_ONGOING) { + if (ufshid_clear_flag(hid, QUERY_FLAG_IDN_HID_EN)) + return -EINVAL; + } + #endif + }else if(IS_SAMSUNG_DEVICE(storage_mfrid)) { + if (ufshid_write_attr(hid, QUERY_ATTR_IDN_HID_OPERATION, + HID_OP_DISABLE)) + return -EINVAL; + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_FRAG_LEVEL, &attr_val)) + return -EINVAL; + + HID_DEBUG(hid, "Frag_lv %d Freg_stat %d HID_need_exec %d", + attr_val & HID_FRAG_LEVEL_MASK, + HID_FRAG_UPDATE_STAT(attr_val), + HID_EXECUTE_REQ_STAT(attr_val)); + } + return 0; +} + +/* + * Lock status: hid_sysfs lock was held when called. + */ +static void ufshid_auto_hibern8_enable(struct ufshid_dev *hid, + unsigned int val) +{ + struct ufs_hba *hba = hid->ufsf->hba; + unsigned long flags; + u32 reg; + + val = !!val; + + /* Update auto hibern8 timer value if supported */ + if (!ufshcd_is_auto_hibern8_supported(hba)) + return; + + pm_runtime_get_sync(hba->dev); + ufshcd_hold(hba, false); + down_write(&hba->clk_scaling_lock); + ufshcd_scsi_block_requests(hba); + /* wait for all the outstanding requests to finish */ + ufshcd_wait_for_doorbell_clr(hba, U64_MAX); + spin_lock_irqsave(hba->host->host_lock, flags); + + reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER); + INFO_MSG("ahit-reg 0x%X", reg); + + if (val ^ (reg == hba->ahit)) { + ufshcd_writel(hba, val ? hba->ahit : HID_AUTO_HIBERN8_DISABLE, + REG_AUTO_HIBERNATE_IDLE_TIMER); + /* Make sure the timer gets applied before further operations */ + mb(); + + INFO_MSG("[Before] is_auto_enabled %d", hid->is_auto_enabled); + hid->is_auto_enabled = val; + + reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER); + INFO_MSG("[After] is_auto_enabled %d ahit-reg 0x%X", + hid->is_auto_enabled, reg); + } else { + INFO_MSG("is_auto_enabled %d. so it does not changed", + hid->is_auto_enabled); + } + + spin_unlock_irqrestore(hba->host->host_lock, flags); + ufshcd_scsi_unblock_requests(hba); + up_write(&hba->clk_scaling_lock); + ufshcd_release(hba); + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); +} + +static void ufshid_block_enter_suspend(struct ufshid_dev *hid) +{ + struct ufs_hba *hba = hid->ufsf->hba; + unsigned long flags; + +#if defined(CONFIG_UFSHID_POC) + if (unlikely(hid->block_suspend)) + return; + + hid->block_suspend = true; +#endif + pm_runtime_get_sync(hba->dev); + ufshcd_hold(hba, false); + + spin_lock_irqsave(hba->host->host_lock, flags); + HID_DEBUG(hid, + "dev->power.usage_count %d hba->clk_gating.active_reqs %d", + atomic_read(&hba->dev->power.usage_count), + hba->clk_gating.active_reqs); + spin_unlock_irqrestore(hba->host->host_lock, flags); +} + +/* + * If the return value is not err, pm_runtime_put_noidle() must be called once. + * IMPORTANT : ufshid_hold_runtime_pm() & ufshid_release_runtime_pm() pair. + */ +static int ufshid_hold_runtime_pm(struct ufshid_dev *hid) +{ + struct ufs_hba *hba = hid->ufsf->hba; + + if (ufshid_get_state(hid->ufsf) == HID_SUSPEND) { + /* Check that device was suspended by System PM */ + pm_runtime_get_sync(hba->dev); + + /* If it success, device was suspended by Runtime PM */ + if (ufshid_get_state(hid->ufsf) == HID_PRESENT && + hba->curr_dev_pwr_mode == UFS_ACTIVE_PWR_MODE && + hba->uic_link_state == UIC_LINK_ACTIVE_STATE) + goto resume_success; + + INFO_MSG("RPM resume failed. Maybe it was SPM suspend"); + INFO_MSG("UFS state (POWER = %d LINK = %d)", + hba->curr_dev_pwr_mode, hba->uic_link_state); + + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + return -ENODEV; + } + + if (ufshid_is_not_present(hid)) + return -ENODEV; + + pm_runtime_get_sync(hba->dev); +resume_success: + return 0; +} + +static inline void ufshid_release_runtime_pm(struct ufshid_dev *hid) +{ + struct ufs_hba *hba = hid->ufsf->hba; + + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); +} + +/* + * Lock status: hid_sysfs lock was held when called. + */ +static int ufshid_trigger_on(struct ufshid_dev *hid) + __must_hold(&hid->sysfs_lock) +{ + int ret; + + if (hid->hid_trigger) + return 0; + + ret = ufshid_hold_runtime_pm(hid); + if (ret) + return ret; + + hid->hid_trigger = true; + HID_DEBUG(hid, "trigger 0 -> 1"); + + ufshid_block_enter_suspend(hid); + + ufshid_auto_hibern8_enable(hid, 0); + + schedule_delayed_work(&hid->hid_trigger_work, 0); + + ufshid_release_runtime_pm(hid); + + return 0; +} + +static void ufshid_allow_enter_suspend(struct ufshid_dev *hid) +{ + struct ufs_hba *hba = hid->ufsf->hba; + unsigned long flags; + +#if defined(CONFIG_UFSHID_POC) + if (unlikely(!hid->block_suspend)) + return; + + hid->block_suspend = false; +#endif + ufshcd_release(hba); + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + + spin_lock_irqsave(hba->host->host_lock, flags); + HID_DEBUG(hid, + "dev->power.usage_count %d hba->clk_gating.active_reqs %d", + atomic_read(&hba->dev->power.usage_count), + hba->clk_gating.active_reqs); + spin_unlock_irqrestore(hba->host->host_lock, flags); +} + +/* + * Lock status: hid_sysfs lock was held when called. + */ +static int ufshid_trigger_off(struct ufshid_dev *hid) + __must_hold(&hid->sysfs_lock) +{ + int ret; + + if (!hid->hid_trigger) + return 0; + + ret = ufshid_hold_runtime_pm(hid); + if (ret) + return ret; + + hid->hid_trigger = false; + HID_DEBUG(hid, "hid_trigger 1 -> 0"); + + ufshid_issue_disable(hid); + + ufshid_auto_hibern8_enable(hid, 1); + + ufshid_allow_enter_suspend(hid); + + ufshid_release_runtime_pm(hid); + + return 0; +} + +static void ufshid_trigger_work_fn(struct work_struct *dwork) +{ + struct ufshid_dev *hid; + int ret; + + hid = container_of(dwork, struct ufshid_dev, hid_trigger_work.work); + + if (ufshid_is_not_present(hid)) + return; + + HID_DEBUG(hid, "start hid_trigger_work_fn"); + + ret = ufshid_get_analyze_and_issue_execute(hid); + + mutex_lock(&hid->sysfs_lock); + if (!hid->hid_trigger) { + HID_DEBUG(hid, "hid_trigger == false, return"); + goto finish_work; + } + + if (ret == HID_NOT_REQUIRED) { + ret = ufshid_trigger_off(hid); + if (likely(!ret)) + goto finish_work; + + WARN_MSG("trigger off fail.. must check it"); + + } else if (ret == HID_REQUIRED) { + HID_DEBUG(hid, "HID_REQUIRED, so sched (%d ms)", + hid->hid_trigger_delay); + + } else { + HID_DEBUG(hid, "issue_HID ERR(%X), so resched for retry", ret); + } + mutex_unlock(&hid->sysfs_lock); + + schedule_delayed_work(&hid->hid_trigger_work, + msecs_to_jiffies(hid->hid_trigger_delay)); + + HID_DEBUG(hid, "end hid_trigger_work_fn"); + return; +finish_work: + mutex_unlock(&hid->sysfs_lock); +} + +void ufshid_init(struct ufsf_feature *ufsf) +{ + struct ufshid_dev *hid; + int ret; + + INFO_MSG("HID_INIT_START"); + + hid = ufsf->hid_dev; + BUG_ON(!hid); + + hid->hid_trigger = false; + hid->hid_trigger_delay = HID_TRIGGER_WORKER_DELAY_MS_DEFAULT; + INIT_DELAYED_WORK(&hid->hid_trigger_work, ufshid_trigger_work_fn); + + hid->hid_debug = false; +#if defined(CONFIG_UFSHID_POC) + hid->hid_debug = true; + hid->block_suspend = false; +#endif + + /* If HCI supports auto hibern8, UFS Driver use it default */ + if (ufshcd_is_auto_hibern8_supported(ufsf->hba)) + hid->is_auto_enabled = true; + else + hid->is_auto_enabled = false; + + ret = ufshid_create_sysfs(hid); + if (ret) { + ERR_MSG("sysfs init fail. so hid driver disabled"); + kfree(hid); + ufshid_set_state(ufsf, HID_FAILED); + return; + } + + INFO_MSG("UFS HID create sysfs finished"); + + ufshid_set_state(ufsf, HID_PRESENT); +} + +void ufshid_reset_host(struct ufsf_feature *ufsf) +{ + struct ufshid_dev *hid = ufsf->hid_dev; + + if (!hid) + return; + + ufshid_set_state(ufsf, HID_RESET); + cancel_delayed_work_sync(&hid->hid_trigger_work); +} + +void ufshid_reset(struct ufsf_feature *ufsf) +{ + struct ufshid_dev *hid = ufsf->hid_dev; + + if (!hid) + return; + + ufshid_set_state(ufsf, HID_PRESENT); + + /* + * hid_trigger will be checked under sysfs_lock in worker. + */ + if (hid->hid_trigger) + schedule_delayed_work(&hid->hid_trigger_work, 0); + + INFO_MSG("reset completed."); +} + +static inline void ufshid_remove_sysfs(struct ufshid_dev *hid) +{ + int ret; + + ret = kobject_uevent(&hid->kobj, KOBJ_REMOVE); + INFO_MSG("kobject removed (%d)", ret); + kobject_del(&hid->kobj); +} + +void ufshid_remove(struct ufsf_feature *ufsf) +{ + struct ufshid_dev *hid = ufsf->hid_dev; + int ret; + + if (!hid) + return; + + INFO_MSG("start HID release"); + + mutex_lock(&hid->sysfs_lock); + + ret = ufshid_trigger_off(hid); + if (unlikely(ret)) + ERR_MSG("trigger off fail ret (%d)", ret); + + ufshid_remove_sysfs(hid); + + ufshid_set_state(ufsf, HID_FAILED); + + mutex_unlock(&hid->sysfs_lock); + + cancel_delayed_work_sync(&hid->hid_trigger_work); + + kfree(hid); + + INFO_MSG("end HID release"); +} + +void ufshid_suspend(struct ufsf_feature *ufsf) +{ + struct ufshid_dev *hid = ufsf->hid_dev; + + if (!hid) + return; + + if (unlikely(hid->hid_trigger)) + ERR_MSG("hid_trigger was set to block the suspend. so weird"); + ufshid_set_state(ufsf, HID_SUSPEND); + + cancel_delayed_work_sync(&hid->hid_trigger_work); +} + +void ufshid_resume(struct ufsf_feature *ufsf) +{ + struct ufshid_dev *hid = ufsf->hid_dev; + + if (!hid) + return; + + if (unlikely(hid->hid_trigger)) + ERR_MSG("hid_trigger need to off"); + ufshid_set_state(ufsf, HID_PRESENT); +} + +/* + * this function is called in irq context. + * so cancel_delayed_work_sync() do not use due to waiting. + */ +void ufshid_on_idle(struct ufsf_feature *ufsf) +{ + struct ufshid_dev *hid = ufsf->hid_dev; + + if (!hid) + return; + /* + * When hid_trigger_work will be scheduled, + * check hid_trigger under sysfs_lock. + */ + if (!hid->hid_trigger) + return; + + if (delayed_work_pending(&hid->hid_trigger_work)) + cancel_delayed_work(&hid->hid_trigger_work); + + schedule_delayed_work(&hid->hid_trigger_work, 0); +} + +/* sysfs function */ +static ssize_t ufshid_sysfs_show_version(struct ufshid_dev *hid, char *buf) +{ + INFO_MSG("HID version (%.4X) D/D version (%.6X%s)", + UFSHID_VER, UFSHID_DD_VER, UFSHID_DD_VER_POST); + + return snprintf(buf, PAGE_SIZE, + "HID version (%.4X) D/D version (%.6X%s)\n", + UFSHID_VER, UFSHID_DD_VER, UFSHID_DD_VER_POST); +} + +static ssize_t ufshid_sysfs_show_trigger(struct ufshid_dev *hid, char *buf) +{ + INFO_MSG("hid_trigger %d", hid->hid_trigger); + + return snprintf(buf, PAGE_SIZE, "%d\n", hid->hid_trigger); +} + +static ssize_t ufshid_sysfs_store_trigger(struct ufshid_dev *hid, + const char *buf, size_t count) +{ + unsigned long val; + ssize_t ret; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val != 0 && val != 1) + return -EINVAL; + + INFO_MSG("HID_trigger %lu", val); + + if (val == hid->hid_trigger) + return count; + + if (val) + ret = ufshid_trigger_on(hid); + else + ret = ufshid_trigger_off(hid); + + if (ret) { + INFO_MSG("Changing trigger val %lu is fail (%ld)", val, ret); + return ret; + } + + return count; +} + +static ssize_t ufshid_sysfs_show_trigger_interval(struct ufshid_dev *hid, + char *buf) +{ + INFO_MSG("hid_trigger_interval %d", hid->hid_trigger_delay); + + return snprintf(buf, PAGE_SIZE, "%d\n", hid->hid_trigger_delay); +} + +static ssize_t ufshid_sysfs_store_trigger_interval(struct ufshid_dev *hid, + const char *buf, + size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < HID_TRIGGER_WORKER_DELAY_MS_MIN || + val > HID_TRIGGER_WORKER_DELAY_MS_MAX) { + INFO_MSG("hid_trigger_interval (min) %4dms ~ (max) %4dms", + HID_TRIGGER_WORKER_DELAY_MS_MIN, + HID_TRIGGER_WORKER_DELAY_MS_MAX); + return -EINVAL; + } + + hid->hid_trigger_delay = (unsigned int)val; + INFO_MSG("hid_trigger_interval %d", hid->hid_trigger_delay); + + return count; +} + +static ssize_t ufshid_sysfs_show_debug(struct ufshid_dev *hid, char *buf) +{ + INFO_MSG("debug %d", hid->hid_debug); + + return snprintf(buf, PAGE_SIZE, "%d\n", hid->hid_debug); +} + +static ssize_t ufshid_sysfs_store_debug(struct ufshid_dev *hid, const char *buf, + size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val != 0 && val != 1) + return -EINVAL; + + hid->hid_debug = val ? true : false; + + INFO_MSG("debug %d", hid->hid_debug); + + return count; +} + +static ssize_t ufshid_sysfs_show_color(struct ufshid_dev *hid, char *buf) +{ + u32 attr_val; + int frag_level; + + if(IS_MICRON_DEVICE(storage_mfrid)){ + #if defined(CONFIG_MICRON_UFSHID) + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_FRAG_STATUS, &attr_val)) + return -EINVAL; + frag_level = attr_val; + /*Micron only has two levels RED & GREEN*/ + return snprintf(buf, PAGE_SIZE, "%s\n", + ((frag_level == HID_LEV_GREEN_MICRON)) ? "GREEN" : + ((frag_level ==HID_LEV_RED_MICRON))?"RED":"UNKNOWN"); + + return snprintf(buf, PAGE_SIZE, "%s\n","Error."); + #endif + } else if(IS_SAMSUNG_DEVICE(storage_mfrid)) { + if (ufshid_write_attr(hid, QUERY_ATTR_IDN_HID_OPERATION, + HID_OP_ANALYZE)) { + ERR_MSG("query HID_OPERATION fail"); + return -EINVAL; + } + + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_FRAG_LEVEL, &attr_val)) { + ERR_MSG("query HID_FRAG_LEVEL fail"); + return -EINVAL; + } + + frag_level = attr_val & HID_FRAG_LEVEL_MASK; + INFO_MSG("Frag_lv %d Freg_stat %d HID_need_exec %d", frag_level, + HID_FRAG_UPDATE_STAT(attr_val), + HID_EXECUTE_REQ_STAT(attr_val)); + + return snprintf(buf, PAGE_SIZE, "%s\n", + frag_level == HID_LEV_RED ? "RED" : + frag_level == HID_LEV_YELLOW ? "YELLOW" : + frag_level == HID_LEV_GREEN ? "GREEN" : + frag_level == HID_LEV_GRAY ? "GRAY" : "UNKNOWN"); + } + return -EINVAL; +} + +#if defined(CONFIG_UFSHID_POC) +static ssize_t ufshid_sysfs_show_debug_op(struct ufshid_dev *hid, char *buf) +{ + u32 attr_val; + + if (ufshid_read_attr(hid, QUERY_ATTR_IDN_HID_OPERATION, &attr_val)) + return -EINVAL; + + INFO_MSG("hid_op %d", attr_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", attr_val); +} + +static ssize_t ufshid_sysfs_store_debug_op(struct ufshid_dev *hid, + const char *buf, size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val >= HID_OP_MAX) + return -EINVAL; + + if (hid->hid_trigger) { + ERR_MSG("debug_op cannot change, current hid_trigger is ON"); + return -EINVAL; + } + + if (ufshid_write_attr(hid, QUERY_ATTR_IDN_HID_OPERATION, val)) + return -EINVAL; + + INFO_MSG("hid_op %ld is set!", val); + return count; +} + +static ssize_t ufshid_sysfs_show_block_suspend(struct ufshid_dev *hid, + char *buf) +{ + INFO_MSG("block suspend %d", hid->block_suspend); + + return snprintf(buf, PAGE_SIZE, "%d\n", hid->block_suspend); +} + +static ssize_t ufshid_sysfs_store_block_suspend(struct ufshid_dev *hid, + const char *buf, size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val != 0 && val != 1) + return -EINVAL; + + INFO_MSG("HID_block_suspend %lu", val); + + if (val == hid->block_suspend) + return count; + + if (val) + ufshid_block_enter_suspend(hid); + else + ufshid_allow_enter_suspend(hid); + + hid->block_suspend = val ? true : false; + + return count; +} + +static ssize_t ufshid_sysfs_show_auto_hibern8_enable(struct ufshid_dev *hid, + char *buf) +{ + INFO_MSG("HCI auto hibern8 %d", hid->is_auto_enabled); + + return snprintf(buf, PAGE_SIZE, "%d\n", hid->is_auto_enabled); +} + +static ssize_t ufshid_sysfs_store_auto_hibern8_enable(struct ufshid_dev *hid, + const char *buf, + size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val != 0 && val != 1) + return -EINVAL; + + ufshid_auto_hibern8_enable(hid, val); + + return count; +} +#endif + +/* SYSFS DEFINE */ +#define define_sysfs_ro(_name) __ATTR(_name, 0444, \ + ufshid_sysfs_show_##_name, NULL) +#define define_sysfs_rw(_name) __ATTR(_name, 0644, \ + ufshid_sysfs_show_##_name, \ + ufshid_sysfs_store_##_name) + +static struct ufshid_sysfs_entry ufshid_sysfs_entries[] = { + define_sysfs_ro(version), + define_sysfs_ro(color), + + define_sysfs_rw(trigger), + define_sysfs_rw(trigger_interval), + + /* debug */ + define_sysfs_rw(debug), +#if defined(CONFIG_UFSHID_POC) + /* Attribute (RAW) */ + define_sysfs_rw(debug_op), + define_sysfs_rw(block_suspend), + define_sysfs_rw(auto_hibern8_enable), +#endif + __ATTR_NULL +}; + +static ssize_t ufshid_attr_show(struct kobject *kobj, struct attribute *attr, + char *page) +{ + struct ufshid_sysfs_entry *entry; + struct ufshid_dev *hid; + ssize_t error; + + entry = container_of(attr, struct ufshid_sysfs_entry, attr); + if (!entry->show) + return -EIO; + + hid = container_of(kobj, struct ufshid_dev, kobj); + error = ufshid_hold_runtime_pm(hid); + if (error) + return error; + + mutex_lock(&hid->sysfs_lock); + error = entry->show(hid, page); + mutex_unlock(&hid->sysfs_lock); + + ufshid_release_runtime_pm(hid); + return error; +} + +static ssize_t ufshid_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t length) +{ + struct ufshid_sysfs_entry *entry; + struct ufshid_dev *hid; + ssize_t error; + + entry = container_of(attr, struct ufshid_sysfs_entry, attr); + if (!entry->store) + return -EIO; + + hid = container_of(kobj, struct ufshid_dev, kobj); + error = ufshid_hold_runtime_pm(hid); + if (error) + return error; + + mutex_lock(&hid->sysfs_lock); + error = entry->store(hid, page, length); + mutex_unlock(&hid->sysfs_lock); + + ufshid_release_runtime_pm(hid); + return error; +} + +static const struct sysfs_ops ufshid_sysfs_ops = { + .show = ufshid_attr_show, + .store = ufshid_attr_store, +}; + +static struct kobj_type ufshid_ktype = { + .sysfs_ops = &ufshid_sysfs_ops, + .release = NULL, +}; + +static int ufshid_create_sysfs(struct ufshid_dev *hid) +{ + struct device *dev = hid->ufsf->hba->dev; + struct ufshid_sysfs_entry *entry; + int err; + + hid->sysfs_entries = ufshid_sysfs_entries; + + kobject_init(&hid->kobj, &ufshid_ktype); + mutex_init(&hid->sysfs_lock); + + INFO_MSG("ufshid creates sysfs ufshid %p dev->kobj %p", + &hid->kobj, &dev->kobj); + + err = kobject_add(&hid->kobj, kobject_get(&dev->kobj), "ufshid"); + if (!err) { + for (entry = hid->sysfs_entries; entry->attr.name != NULL; + entry++) { + INFO_MSG("ufshid sysfs attr creates: %s", + entry->attr.name); + err = sysfs_create_file(&hid->kobj, &entry->attr); + if (err) { + ERR_MSG("create entry(%s) failed", + entry->attr.name); + goto kobj_del; + } + } + kobject_uevent(&hid->kobj, KOBJ_ADD); + } else { + ERR_MSG("kobject_add failed"); + } + + return err; +kobj_del: + err = kobject_uevent(&hid->kobj, KOBJ_REMOVE); + INFO_MSG("kobject removed (%d)", err); + kobject_del(&hid->kobj); + return -EINVAL; +} diff --git a/drivers/scsi/ufs/ufshid.h b/drivers/scsi/ufs/ufshid.h new file mode 100755 index 000000000000..10a2989cd435 --- /dev/null +++ b/drivers/scsi/ufs/ufshid.h @@ -0,0 +1,158 @@ +/* + * Universal Flash Storage Host Initiated Defrag (UFS HID) + * + * Copyright (C) 2019-2019 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jieon Seol + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#ifndef _UFSHID_H_ +#define _UFSHID_H_ + +#include +#include +#include +#include +#include +#include + +#include "../../../block/blk.h" + +#define UFSHID_VER 0x0101 +#define UFSHID_DD_VER 0x010202 +#define UFSHID_DD_VER_POST "" + +#define UFS_FEATURE_SUPPORT_HID_BIT 0x400 + +#define HID_TRIGGER_WORKER_DELAY_MS_DEFAULT 2000 +#define HID_TRIGGER_WORKER_DELAY_MS_MIN 100 +#define HID_TRIGGER_WORKER_DELAY_MS_MAX 10000 + +#define HID_FRAG_LEVEL_MASK 0xF +#define HID_FRAG_UPDATE_STAT_SHIFT 30 +#define HID_EXECUTE_REQ_STAT_SHIFT 31 +#define HID_FRAG_UPDATE_STAT(val) ((val >> HID_FRAG_UPDATE_STAT_SHIFT) & 0x1) +#define HID_EXECUTE_REQ_STAT(val) ((val >> HID_EXECUTE_REQ_STAT_SHIFT) & 0x1) + +#define HID_AUTO_HIBERN8_DISABLE (FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 0) | \ + FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3)) + + +#define HID_DEBUG(hid, msg, args...) \ + do { if (hid->hid_debug) \ + pr_err("%40s:%3d [%01d%02d%02d] " msg "\n", \ + __func__, __LINE__, \ + hid->hid_trigger, \ + atomic_read(&hid->ufsf->hba->dev->power.usage_count),\ + hid->ufsf->hba->clk_gating.active_reqs, ##args); \ + } while (0) + +enum UFSHID_STATE { + HID_NEED_INIT = 0, + HID_PRESENT = 1, + HID_SUSPEND = 2, + HID_FAILED = -2, + HID_RESET = -3, +}; + +enum { + HID_OP_DISABLE = 0, + HID_OP_ANALYZE = 1, + HID_OP_EXECUTE = 2, + HID_OP_MAX +}; + +enum { + HID_NOT_REQUIRED = 0, + HID_REQUIRED = 1 +}; + +enum { + HID_LEV_GRAY = 0, + HID_LEV_GREEN = 1, + HID_LEV_YELLOW = 2, + HID_LEV_RED = 3, +}; + +#if defined(CONFIG_MICRON_UFSHID) +enum { + HID_LEV_GREEN_MICRON = 0, + HID_LEV_RED_MICRON = 1, +}; + +enum { + HID_PROG_IDLE = 0, + HID_PROG_ONGOING = 1, + HID_PROG_STOP = 2, //stopped by Host + HID_PROG_COMPLETE = 3, +}; +#endif + +struct ufshid_dev { + struct ufsf_feature *ufsf; + + unsigned int hid_trigger; /* default value is false */ + struct delayed_work hid_trigger_work; + unsigned int hid_trigger_delay; + + bool is_auto_enabled; + + /* for sysfs */ + struct kobject kobj; + struct mutex sysfs_lock; + struct ufshid_sysfs_entry *sysfs_entries; + + /* for debug */ + bool hid_debug; +#if defined(CONFIG_UFSHID_POC) + bool block_suspend; +#endif +}; + +struct ufshid_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct ufshid_dev *hid, char *buf); + ssize_t (*store)(struct ufshid_dev *hid, const char *buf, size_t count); +}; + +int ufshid_get_state(struct ufsf_feature *ufsf); +void ufshid_set_state(struct ufsf_feature *ufsf, int state); +void ufshid_get_dev_info(struct ufsf_feature *ufsf, u8 *desc_buf); +void ufshid_set_init_state(struct ufsf_feature *ufsf); +void ufshid_init(struct ufsf_feature *ufsf); +void ufshid_reset(struct ufsf_feature *ufsf); +void ufshid_reset_host(struct ufsf_feature *ufsf); +void ufshid_remove(struct ufsf_feature *ufsf); +void ufshid_suspend(struct ufsf_feature *ufsf); +void ufshid_resume(struct ufsf_feature *ufsf); +void ufshid_on_idle(struct ufsf_feature *ufsf); +#endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c new file mode 100755 index 000000000000..1659a648a846 --- /dev/null +++ b/drivers/scsi/ufs/ufshpb.c @@ -0,0 +1,4099 @@ +/* + * Universal Flash Storage Host Performance Booster + * + * Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jinyoung Choi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#include "ufshcd.h" +#include "ufshpb.h" + +#define UFSHCD_REQ_SENSE_SIZE 18 + +static int ufshpb_create_sysfs(struct ufsf_feature *ufsf, + struct ufshpb_lu *hpb); +static void ufshpb_remove_sysfs(struct ufshpb_lu *hpb); + + +static inline void +ufshpb_get_pos_from_lpn(struct ufshpb_lu *hpb, unsigned long lpn, int *rgn_idx, + int *srgn_idx, int *offset) +{ + int rgn_offset; + + *rgn_idx = lpn >> hpb->entries_per_rgn_shift; + rgn_offset = lpn & hpb->entries_per_rgn_mask; + *srgn_idx = rgn_offset >> hpb->entries_per_srgn_shift; + *offset = rgn_offset & hpb->entries_per_srgn_mask; +} + +inline int ufshpb_valid_srgn(struct ufshpb_region *rgn, + struct ufshpb_subregion *srgn) +{ + return rgn->rgn_state != HPB_RGN_INACTIVE && + srgn->srgn_state == HPB_SRGN_CLEAN; +} + +/* Must be held hpb_lock */ +static bool ufshpb_ppn_dirty_check(struct ufshpb_lu *hpb, unsigned long lpn, + int transfer_len) +{ + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + unsigned long cur_lpn = lpn; + int rgn_idx, srgn_idx, srgn_offset, find_size; + int scan_cnt = transfer_len; + + do { + ufshpb_get_pos_from_lpn(hpb, cur_lpn, &rgn_idx, &srgn_idx, + &srgn_offset); + rgn = hpb->rgn_tbl + rgn_idx; + srgn = rgn->srgn_tbl + srgn_idx; + + if (!ufshpb_valid_srgn(rgn, srgn)) + return true; + + if (unlikely(!srgn->mctx || !srgn->mctx->ppn_dirty)) + return true; + + if (hpb->entries_per_srgn < srgn_offset + scan_cnt) { + int cnt = hpb->entries_per_srgn - srgn_offset; + + find_size = hpb->entries_per_srgn; + scan_cnt -= cnt; + cur_lpn += cnt; + } else { + find_size = srgn_offset + scan_cnt; + scan_cnt = 0; + } + + srgn_offset = + find_next_bit((unsigned long *)srgn->mctx->ppn_dirty, + hpb->entries_per_srgn, srgn_offset); + + if (srgn_offset < hpb->entries_per_srgn) + return srgn_offset < find_size; + + } while (scan_cnt); + + return false; +} + +static void ufshpb_set_read16_cmd(struct ufshpb_lu *hpb, struct scsi_cmnd *cmd, + u64 ppn, unsigned int transfer_len, + int hpb_ctx_id) +{ + unsigned char *cdb = cmd->cmnd; + + cdb[0] = READ_16; + cdb[2] = cmd->cmnd[2]; + cdb[3] = cmd->cmnd[3]; + cdb[4] = cmd->cmnd[4]; + cdb[5] = cmd->cmnd[5]; + cdb[6] = GET_BYTE_7(ppn); + cdb[7] = GET_BYTE_6(ppn); + cdb[8] = GET_BYTE_5(ppn); + cdb[9] = GET_BYTE_4(ppn); + cdb[10] = GET_BYTE_3(ppn); + cdb[11] = GET_BYTE_2(ppn); + cdb[12] = GET_BYTE_1(ppn); + cdb[13] = GET_BYTE_0(ppn); + + if (hpb_ctx_id < MAX_HPB_CONTEXT_ID) + cdb[14] = (1 << 7) | hpb_ctx_id; + else + cdb[14] = UFSHPB_GROUP_NUMBER; + + cdb[15] = transfer_len; + + cmd->cmd_len = UFS_CDB_SIZE; +} + +/* called with hpb_lock (irq) */ +static inline void +ufshpb_set_dirty_bits(struct ufshpb_lu *hpb, struct ufshpb_region *rgn, + struct ufshpb_subregion *srgn, int dword, int offset, + unsigned int cnt) +{ + const unsigned long mask = ((1UL << cnt) - 1) & 0xffffffff; + + if (rgn->rgn_state == HPB_RGN_INACTIVE) + return; + + BUG_ON(!srgn->mctx); + srgn->mctx->ppn_dirty[dword] |= (mask << offset); +} + +static inline void ufshpb_get_bit_offset(struct ufshpb_lu *hpb, int srgn_offset, + int *dword, int *offset) +{ + *dword = srgn_offset >> bits_per_dword_shift; + *offset = srgn_offset & bits_per_dword_mask; +} + +static void ufshpb_set_dirty(struct ufshpb_lu *hpb, struct ufshcd_lrb *lrbp, + int rgn_idx, int srgn_idx, int srgn_offset) +{ + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + int cnt, bit_cnt, bit_dword, bit_offset; + + cnt = blk_rq_sectors(lrbp->cmd->request) >> sects_per_blk_shift; + ufshpb_get_bit_offset(hpb, srgn_offset, &bit_dword, &bit_offset); + + do { + bit_cnt = min(cnt, BITS_PER_DWORD - bit_offset); + + rgn = hpb->rgn_tbl + rgn_idx; + srgn = rgn->srgn_tbl + srgn_idx; + + ufshpb_set_dirty_bits(hpb, rgn, srgn, bit_dword, bit_offset, + bit_cnt); + + bit_offset = 0; + bit_dword++; + + if (bit_dword == hpb->dwords_per_srgn) { + bit_dword = 0; + srgn_idx++; + + if (srgn_idx == hpb->srgns_per_rgn) { + srgn_idx = 0; + rgn_idx++; + } + } + cnt -= bit_cnt; + } while (cnt); + + BUG_ON(cnt < 0); +} + +static inline bool ufshpb_is_read_cmd(struct scsi_cmnd *cmd) +{ + return (cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == READ_16); +} + +static inline bool ufshpb_is_write_cmd(struct scsi_cmnd *cmd) +{ + return (cmd->cmnd[0] == WRITE_10 || cmd->cmnd[0] == WRITE_16); +} + +static inline bool ufshpb_is_discard_cmd(struct scsi_cmnd *cmd) +{ + return cmd->cmnd[0] == UNMAP; +} + +static u64 ufshpb_get_ppn(struct ufshpb_map_ctx *mctx, int pos, int *error) +{ + u64 *ppn_table; + struct page *page = NULL; + int index, offset; + + index = pos / HPB_ENTREIS_PER_OS_PAGE; + offset = pos % HPB_ENTREIS_PER_OS_PAGE; + + page = mctx->m_page[index]; + if (unlikely(!page)) { + *error = -ENOMEM; + ERR_MSG("mctx %p cannot get m_page", mctx); + return 0; + } + + ppn_table = page_address(page); + if (unlikely(!ppn_table)) { + *error = -ENOMEM; + ERR_MSG("mctx %p cannot get ppn_table vm", mctx); + return 0; + } + + return ppn_table[offset]; +} + +#if defined(CONFIG_HPB_ERR_INJECTION) +static u64 ufshpb_get_bitflip_ppn(struct ufshpb_lu *hpb, u64 ppn) +{ + return (ppn ^ hpb->err_injection_bitflip); +} + +static u64 ufshpb_get_offset_ppn(struct ufshpb_lu *hpb, u64 ppn) +{ + return (ppn + hpb->err_injection_offset); +} + +static u64 ufshpb_get_random_ppn(struct ufshpb_lu *hpb, u64 ppn) +{ + u64 random_ppn = 0; + + if (!hpb->err_injection_random) + return ppn; + + get_random_bytes(&random_ppn, sizeof(random_ppn)); + + return random_ppn; +} + +static u64 ufshpb_get_err_injection_ppn(struct ufshpb_lu *hpb, u64 ppn) +{ + u64 new_ppn = 0; + + if (hpb->err_injection_select == HPB_ERR_INJECTION_BITFLIP) + new_ppn = ufshpb_get_bitflip_ppn(hpb, ppn); + else if (hpb->err_injection_select == HPB_ERR_INJECTION_OFFSET) + new_ppn = ufshpb_get_offset_ppn(hpb, ppn); + else if (hpb->err_injection_select == HPB_ERR_INJECTION_RANDOM) + new_ppn = ufshpb_get_random_ppn(hpb, ppn); + + return new_ppn; +} +#endif + +inline int ufshpb_get_state(struct ufsf_feature *ufsf) +{ + return atomic_read(&ufsf->hpb_state); +} + +inline void ufshpb_set_state(struct ufsf_feature *ufsf, int state) +{ + atomic_set(&ufsf->hpb_state, state); +} + +static inline int ufshpb_lu_get(struct ufshpb_lu *hpb) +{ + if (!hpb) + return -ENODEV; + + if (ufshpb_get_state(hpb->ufsf) != HPB_PRESENT) + return -EBUSY; + + kref_get(&hpb->ufsf->hpb_kref); + return 0; +} + +static inline void ufshpb_schedule_error_handler(struct kref *kref) +{ + struct ufsf_feature *ufsf; + + ufsf = container_of(kref, struct ufsf_feature, hpb_kref); + schedule_work(&ufsf->hpb_eh_work); +} + +static inline void ufshpb_lu_put(struct ufshpb_lu *hpb) +{ + kref_put(&hpb->ufsf->hpb_kref, ufshpb_schedule_error_handler); +} + +static void ufshpb_failed(struct ufshpb_lu *hpb, const char *f) +{ + ERR_MSG("ufshpb_driver failed. function (%s)", f); + ufshpb_set_state(hpb->ufsf, HPB_FAILED); + ufshpb_lu_put(hpb); +} + +/* + * end_io() is called with held the queue lock. + * so, blk_put_request is not available directly. + */ +static void ufshpb_put_blk_request(struct request *req) +{ + struct request_queue *q = req->q; + + if (q->mq_ops) { + blk_mq_free_request(req); + } else { + lockdep_assert_held(&q->queue_lock); + blk_put_request(req); + } +} + +static inline void ufshpb_put_pre_req(struct ufshpb_lu *hpb, + struct ufshpb_req *pre_req) +{ + pre_req->req = NULL; + pre_req->bio = NULL; + list_add_tail(&pre_req->list_req, &hpb->lh_pre_req_free); + hpb->num_inflight_pre_req--; +} + +static struct ufshpb_req *ufshpb_get_pre_req(struct ufshpb_lu *hpb) +{ + struct ufshpb_req *pre_req; + + if (hpb->num_inflight_pre_req >= hpb->throttle_pre_req) { + HPB_FTRACE(hpb, "inflight %d throttle %d", + hpb->num_inflight_pre_req, hpb->throttle_pre_req); + return NULL; + } + + pre_req = list_first_entry_or_null(&hpb->lh_pre_req_free, + struct ufshpb_req, list_req); + if (unlikely(!pre_req)) { + HPB_DEBUG(hpb, "no pre_req"); + return NULL; + } + + list_del_init(&pre_req->list_req); + hpb->num_inflight_pre_req++; + + return pre_req; +} + +static void ufshpb_pre_req_compl_fn(struct request *req, blk_status_t error) +{ + struct ufshpb_req *pre_req = (struct ufshpb_req *)req->end_io_data; + struct ufshpb_lu *hpb = pre_req->hpb; + unsigned long flags; + struct scsi_sense_hdr sshdr; + + if (error) { + ERR_MSG("block status (%d)", error); + scsi_normalize_sense(pre_req->sense, SCSI_SENSE_BUFFERSIZE, + &sshdr); + ERR_MSG("code %x sense_key %x asc %x ascq %x", + sshdr.response_code, + sshdr.sense_key, sshdr.asc, sshdr.ascq); + ERR_MSG("byte4 %x byte5 %x byte6 %x additional_len %x", + sshdr.byte4, sshdr.byte5, + sshdr.byte6, sshdr.additional_length); + } + + bio_put(pre_req->bio); + ufshpb_put_blk_request(pre_req->req); + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_put_pre_req(pre_req->hpb, pre_req); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + ufshpb_lu_put(pre_req->hpb); +} + +static int ufshpb_prep_entry(struct ufshpb_req *pre_req, struct page *page) +{ + struct ufshpb_lu *hpb = pre_req->hpb; + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + u64 *addr; + u64 entry_ppn = 0; + unsigned long lpn = pre_req->wb.lpn; + int rgn_idx, srgn_idx, srgn_offset; + int i, error = 0; + unsigned long flags; + + addr = page_address(page); + + spin_lock_irqsave(&hpb->hpb_lock, flags); + for (i = 0; i < pre_req->wb.len; i++, lpn++) { + ufshpb_get_pos_from_lpn(hpb, lpn, &rgn_idx, &srgn_idx, + &srgn_offset); + + rgn = hpb->rgn_tbl + rgn_idx; + srgn = rgn->srgn_tbl + srgn_idx; + + if (!ufshpb_valid_srgn(rgn, srgn)) + goto mctx_error; + + BUG_ON(!srgn->mctx); + + entry_ppn = ufshpb_get_ppn(srgn->mctx, srgn_offset, &error); + if (error) + goto mctx_error; + +#if defined(CONFIG_HPB_ERR_INJECTION) + if (hpb->err_injection_select != HPB_ERR_INJECTION_DISABLE) + entry_ppn = + ufshpb_get_err_injection_ppn(hpb, entry_ppn); +#endif + + addr[i] = entry_ppn; + } + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return 0; +mctx_error: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return -ENOMEM; +} + +static int ufshpb_pre_req_add_bio_page(struct request_queue *q, + struct ufshpb_req *pre_req) +{ + struct page *page = pre_req->wb.m_page; + struct bio *bio = pre_req->bio; + int entries_bytes, ret; + + BUG_ON(!page); + + ret = ufshpb_prep_entry(pre_req, page); + if (ret) + return ret; + + entries_bytes = pre_req->wb.len * sizeof(u64); + + ret = bio_add_pc_page(q, bio, page, entries_bytes, 0); + if (ret != entries_bytes) { + ERR_MSG("bio_add_pc_page fail (%d)", ret); + return -ENOMEM; + } + + return 0; +} + +static inline unsigned long ufshpb_get_lpn(struct request *rq) +{ + return blk_rq_pos(rq) / SECTORS_PER_BLOCK; +} + +static inline unsigned int ufshpb_get_len(struct request *rq) +{ + return blk_rq_sectors(rq) / SECTORS_PER_BLOCK; +} + +static inline unsigned int ufshpb_is_unaligned(struct request *rq) +{ + return blk_rq_sectors(rq) % SECTORS_PER_BLOCK; +} + +static inline int ufshpb_issue_ctx_id_ticket(struct ufshpb_lu *hpb) +{ + int hpb_ctx_id; + + hpb->ctx_id_ticket++; + if (hpb->ctx_id_ticket >= MAX_HPB_CONTEXT_ID) + hpb->ctx_id_ticket = 0; + hpb_ctx_id = hpb->ctx_id_ticket; + + return hpb_ctx_id; +} + +static inline void ufshpb_set_prefetch_cmd(unsigned char *cdb, + unsigned long lpn, unsigned int len, + int hpb_ctx_id) +{ + int len_byte = len * HPB_ENTRY_SIZE; + + cdb[0] = UFSHPB_WRITE_BUFFER; + cdb[1] = UFSHPB_WB_ID_PREFETCH; + cdb[2] = GET_BYTE_3(lpn); + cdb[3] = GET_BYTE_2(lpn); + cdb[4] = GET_BYTE_1(lpn); + cdb[5] = GET_BYTE_0(lpn); + cdb[6] = (1 << 7) | hpb_ctx_id; + cdb[7] = GET_BYTE_1(len_byte); + cdb[8] = GET_BYTE_0(len_byte); + cdb[9] = 0x00; /* Control = 0x00 */ +} + +static void ufshpb_hctx_unlock_mimic(struct blk_mq_hw_ctx *hctx, int srcu_idx) +__releases(hctx->srcu) +{ + if (!(hctx->flags & BLK_MQ_F_BLOCKING)) + rcu_read_unlock(); + else + srcu_read_unlock(hctx->srcu, srcu_idx); +} + +static void ufshpb_hctx_lock_mimic(struct blk_mq_hw_ctx *hctx, int *srcu_idx) +__acquires(hctx->srcu) +{ + if (!(hctx->flags & BLK_MQ_F_BLOCKING)) { + /* shut up gcc false positive */ + *srcu_idx = 0; + rcu_read_lock(); + } else + *srcu_idx = srcu_read_lock(hctx->srcu); +} + +static int ufshpb_blk_mq_dispatch_rq_list_mimic(struct blk_mq_hw_ctx *hctx, + struct request *rq) +{ + struct request_queue *q = rq->q; + struct blk_mq_queue_data bd; + blk_status_t ret = 0; + + if (!blk_mq_get_dispatch_budget(hctx)) + return -EAGAIN; + + if (!blk_mq_get_driver_tag(rq)) { + blk_mq_put_dispatch_budget(hctx); + return -EAGAIN; + } + + bd.rq = rq; + + ret = q->mq_ops->queue_rq(hctx, &bd); + if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE) { + /* + * If an I/O scheduler has been configured and we got a + * driver tag for the next request already, free it + * again. + */ + return -EIO; + } + + if (unlikely(ret != BLK_STS_OK)) + return -EIO; + + return (int)ret; +} + +static int ufshpb_blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx, + struct request *rq) +{ + int ret; + + /* RCU or SRCU read lock is needed before checking quiesced flag */ + if (unlikely(blk_mq_hctx_stopped(hctx) || blk_queue_quiesced(rq->q))) + return -EPERM; + + hctx->run++; + + ret = ufshpb_blk_mq_dispatch_rq_list_mimic(hctx, rq); + + return ret; +} + +static int ufshpb_blk_mq_run_hw_queue_mimic(struct blk_mq_hw_ctx *hctx, + struct request *rq) +{ + int srcu_idx; + int ret = 0; + + /* + * We should be running this queue from one of the CPUs that + * are mapped to it. + * + * There are at least two related races now between setting + * hctx->next_cpu from blk_mq_hctx_next_cpu() and running + * __blk_mq_run_hw_queue(): + * + * - hctx->next_cpu is found offline in blk_mq_hctx_next_cpu(), + * but later it becomes online, then this warning is harmless + * at all + * + * - hctx->next_cpu is found online in blk_mq_hctx_next_cpu(), + * but later it becomes offline, then the warning can't be + * triggered, and we depend on blk-mq timeout handler to + * handle dispatched requests to this hctx + */ + if (!cpumask_test_cpu(raw_smp_processor_id(), hctx->cpumask) && + cpu_online(hctx->next_cpu)) { + printk(KERN_WARNING "run queue from wrong CPU %d, hctx %s\n", + raw_smp_processor_id(), + cpumask_empty(hctx->cpumask) ? "inactive" : "active"); + dump_stack(); + } + + /* + * We can't run the queue inline with ints disabled. Ensure that + * we catch bad users of this early. + */ + WARN_ON_ONCE(in_interrupt()); + + if (hctx->flags & BLK_MQ_F_BLOCKING) + return -EPERM; + + ufshpb_hctx_lock_mimic(hctx, &srcu_idx); + ret = ufshpb_blk_mq_sched_dispatch_requests(hctx, rq); + ufshpb_hctx_unlock_mimic(hctx, srcu_idx); + + return ret; +} + +static int ufshpb_insert_pre_req_directly(struct request *rq) +{ + struct blk_mq_hw_ctx *hctx = rq->mq_hctx; + int cpu = get_cpu(); + int ret = 0; + + if (likely(cpumask_test_cpu(cpu, hctx->cpumask))) { + ret = ufshpb_blk_mq_run_hw_queue_mimic(hctx, rq); + put_cpu(); + return ret; + } + put_cpu(); + return -EPERM; +} + +static int ufshpb_blk_execute_rq_nowait_mimic(struct request_queue *q, + struct request *rq, + rq_end_io_fn *done) +{ + int ret = 0; + + WARN_ON(irqs_disabled()); + WARN_ON(!blk_rq_is_passthrough(rq)); + + rq->rq_disk = NULL; + rq->end_io = done; + + ret = ufshpb_insert_pre_req_directly(rq); + return ret; +} + +static int ufshpb_execute_pre_req(struct ufshpb_lu *hpb, struct scsi_cmnd *cmd, + struct ufshpb_req *pre_req, int hpb_ctx_id) +{ + struct scsi_device *sdev = cmd->device; + struct request_queue *q = sdev->request_queue; + struct request *req; + struct scsi_request *rq; + struct bio *bio = pre_req->bio; + int ret = 0; + + pre_req->hpb = hpb; + pre_req->wb.lpn = ufshpb_get_lpn(cmd->request); + pre_req->wb.len = ufshpb_get_len(cmd->request); + ret = ufshpb_pre_req_add_bio_page(q, pre_req); + if (ret) + return ret; + + req = pre_req->req; + + /* 1. request setup */ + blk_rq_append_bio(req, &bio); + req->rq_flags |= RQF_QUIET; + req->timeout = 30 * HZ; + req->end_io_data = (void *)pre_req; + + /* 2. scsi_request setup */ + rq = scsi_req(req); + rq->retries = 1; + ufshpb_set_prefetch_cmd(rq->cmd, pre_req->wb.lpn, pre_req->wb.len, + hpb_ctx_id); + rq->cmd_len = scsi_command_size(rq->cmd); + + ret = ufshpb_blk_execute_rq_nowait_mimic(q, req, + ufshpb_pre_req_compl_fn); + + if (!ret) + atomic64_inc(&hpb->pre_req_cnt); + + return ret; +} + +static int ufshpb_issue_pre_req(struct ufshpb_lu *hpb, struct scsi_cmnd *cmd, + int *hpb_ctx_id) +{ + struct ufshpb_req *pre_req; + struct request *req = NULL; + struct bio *bio = NULL; + unsigned long flags; + int ctx_id; + int ret = 0; + + req = blk_get_request(cmd->device->request_queue, + REQ_OP_SCSI_OUT | REQ_SYNC, + BLK_MQ_REQ_PREEMPT | BLK_MQ_REQ_NOWAIT); + if (IS_ERR(req)) + return -EAGAIN; + + bio = bio_alloc(GFP_ATOMIC, 1); + if (!bio) { + blk_put_request(req); + return -EAGAIN; + } + + spin_lock_irqsave(&hpb->hpb_lock, flags); + pre_req = ufshpb_get_pre_req(hpb); + if (!pre_req) { + ret = -EAGAIN; + goto unlock_out; + } + ctx_id = ufshpb_issue_ctx_id_ticket(hpb); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + pre_req->req = req; + pre_req->bio = bio; + +#if defined(CONFIG_HPB_DEBUG) + { + unsigned int start_lpn, end_lpn; + int s_rgn_idx, s_srgn_idx, s_offset; + int e_rgn_idx, e_srgn_idx, e_offset; + + start_lpn = ufshpb_get_lpn(cmd->request); + end_lpn = start_lpn + ufshpb_get_len(cmd->request) - 1; + + ufshpb_get_pos_from_lpn(hpb, start_lpn, &s_rgn_idx, &s_srgn_idx, + &s_offset); + ufshpb_get_pos_from_lpn(hpb, end_lpn, &e_rgn_idx, &e_srgn_idx, + &e_offset); + HPB_FTRACE(hpb, "R[%05d](%d) - R[%05d](%d)", + s_rgn_idx, hpb->rgn_tbl[s_rgn_idx].rgn_state, + e_rgn_idx, hpb->rgn_tbl[e_rgn_idx].rgn_state); + } +#endif + + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + if (ret == -ENODEV) + ERR_MSG("lu_get failed (%d)", ret); + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + goto free_pre_req; + } + + ret = ufshpb_execute_pre_req(hpb, cmd, pre_req, ctx_id); + if (ret) { + ufshpb_lu_put(hpb); + goto free_pre_req; + } + + *hpb_ctx_id = ctx_id; + + return ret; +free_pre_req: + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_put_pre_req(hpb, pre_req); +unlock_out: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + bio_put(bio); + blk_put_request(req); + return ret; +} + +static inline bool ufshpb_is_support_chunk(int transfer_len) +{ + return transfer_len <= HPB_MULTI_CHUNK_HIGH; +} + +static int ufshpb_fill_passthrough_rq(struct scsi_cmnd *cmd) +{ + struct request *rq = cmd->request; + struct scsi_device *sdp = cmd->device; + struct bio *bio = rq->bio; + sector_t sector = 0; + unsigned int data_len = 0; + u8 *p = NULL; + + /* + * UFS is 4K block device. + */ + if (unlikely(sdp->sector_size != BLOCK)) { + ERR_MSG("this device is not 4K block device"); + return -EACCES; + } + + switch (cmd->cmnd[0]) { + case UNMAP: + /* + * UNMAP command has just a block descriptor(=a phys_segment). + */ + if (unlikely(!bio) || + unlikely(bio->bi_vcnt != 1)) { + ERR_MSG("UNMAP bio info is incorrect"); + return -EINVAL; + } + + p = page_address(bio->bi_io_vec->bv_page); + if (unlikely(!p)) { + ERR_MSG("can't get bio page address"); + return -ENOMEM; + } + + sector = LI_EN_64(p + 8); + data_len = LI_EN_32(p + 16); + break; + case READ_10: + case WRITE_10: + sector = LI_EN_32(cmd->cmnd + 2); + data_len = LI_EN_16(cmd->cmnd + 7); + break; + case READ_16: + case WRITE_16: + sector = LI_EN_64(cmd->cmnd + 2); + data_len = LI_EN_32(cmd->cmnd + 10); + break; + default: + return 0; + }; + + rq->__sector = sector << sects_per_blk_shift; + rq->__data_len = data_len << sects_per_blk_shift << SECTOR_SHIFT; + + return 0; +} + +/* routine : READ10 -> HPB_READ */ +void ufshpb_prep_fn(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp) +{ + struct ufshpb_lu *hpb; + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + struct request *rq; + struct scsi_cmnd *cmd = lrbp->cmd; + u64 ppn = 0; + unsigned long lpn, flags; + int hpb_ctx_id = MAX_HPB_CONTEXT_ID; + int transfer_len = TRANSFER_LEN; + int rgn_idx, srgn_idx, srgn_offset, ret, error = 0; + + /* WKLU could not be HPB-LU */ + if (!lrbp || !ufsf_is_valid_lun(lrbp->lun)) { + cmd->requeue_cnt = 0; + return; + } + + cmd = lrbp->cmd; + + if (!ufshpb_is_read_cmd(cmd)) + cmd->requeue_cnt = 0; + + if (!ufshpb_is_read_cmd(cmd) && !ufshpb_is_write_cmd(cmd) && + !ufshpb_is_discard_cmd(cmd)) + return; + + rq = cmd->request; + if (unlikely(blk_rq_is_passthrough(rq) && + rq->__sector == (sector_t)-1)) { + ret = ufshpb_fill_passthrough_rq(cmd); + if (ret) + return; + } + + hpb = ufsf->hpb_lup[lrbp->lun]; + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + cmd->requeue_cnt = 0; + return; + } + + if (hpb->force_disable) { + if (ufshpb_is_read_cmd(cmd)) + HPB_FTRACE(hpb, "%llu + %u READ_10 (force_disable)", + (unsigned long long) blk_rq_pos(rq), + (unsigned int) blk_rq_sectors(rq)); + + goto put_hpb; + } + + lpn = ufshpb_get_lpn(rq); + ufshpb_get_pos_from_lpn(hpb, lpn, &rgn_idx, &srgn_idx, &srgn_offset); + rgn = hpb->rgn_tbl + rgn_idx; + srgn = rgn->srgn_tbl + srgn_idx; + + /* + * If cmd type is WRITE, bitmap set to dirty. + */ + if (ufshpb_is_write_cmd(cmd) || ufshpb_is_discard_cmd(cmd)) { + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (rgn->rgn_state != HPB_RGN_INACTIVE) { + ufshpb_set_dirty(hpb, lrbp, rgn_idx, srgn_idx, + srgn_offset); + } + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + } + + if (!ufshpb_is_read_cmd(cmd)) + goto put_hpb; + + if (unlikely(ufshpb_is_unaligned(rq))) { + HPB_FTRACE(hpb, "%llu + %u READ_10 R[%05d](%d) (not aligned)", + (unsigned long long) blk_rq_pos(rq), + (unsigned int) blk_rq_sectors(rq), + rgn_idx, rgn->rgn_state); + + goto put_hpb; + } + + transfer_len = ufshpb_get_len(rq); + if (unlikely(!transfer_len)) + goto put_hpb; + + if (!ufshpb_is_support_chunk(transfer_len)) { + HPB_FTRACE(hpb, "%llu + %u READ_10 R[%05d](%d) (not support chunk)", + (unsigned long long) blk_rq_pos(rq), + (unsigned int) blk_rq_sectors(rq), + rgn_idx, rgn->rgn_state); + + goto put_hpb; + } +#if defined(CONFIG_MICRON_HPB) + if (IS_MICRON_DEVICE(storage_mfrid) && transfer_len >= hpb->pre_req_min_tr_len && + transfer_len <= hpb->pre_req_max_tr_len) { + goto put_hpb; + } +#endif + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (ufshpb_ppn_dirty_check(hpb, lpn, transfer_len)) { + atomic64_inc(&hpb->miss); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + HPB_FTRACE(hpb, "%llu + %u READ_10 R[%05d](%d) (hpb map invalid)", + (unsigned long long) blk_rq_pos(rq), + (unsigned int) blk_rq_sectors(rq), + rgn_idx, rgn->rgn_state); + + goto put_hpb; + } + + ppn = ufshpb_get_ppn(srgn->mctx, srgn_offset, &error); +#if defined(CONFIG_HPB_ERR_INJECTION) + if (hpb->err_injection_select != HPB_ERR_INJECTION_DISABLE) + ppn = ufshpb_get_err_injection_ppn(hpb, ppn); +#endif + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + if (unlikely(error)) { + ERR_MSG("ppn failed (%d) R[%05d](%d) S[%d](%d)", + error, rgn_idx, rgn->rgn_state, + srgn_idx, srgn->srgn_state); + ufshpb_lu_put(hpb); + goto wakeup_ee_worker; + } + + /* + * WRITE_BUFFER CMD support 36K (len=9) ~ 512K (len=128) default. + * it is possible to change range of transfer_len through sysfs. + */ + if (transfer_len >= hpb->pre_req_min_tr_len && + transfer_len <= hpb->pre_req_max_tr_len) { + ret = ufshpb_issue_pre_req(hpb, cmd, &hpb_ctx_id); + if (ret) { + if (ret == -EAGAIN) { + if (cmd->requeue_cnt < + hpb->pre_req_requeue_cnt) { + cmd->requeue_cnt++; + goto requeue; + } + } + atomic64_inc(&hpb->miss); + goto put_hpb; + } + } + + ufshpb_set_read16_cmd(hpb, cmd, ppn, transfer_len, hpb_ctx_id); + HPB_FTRACE(hpb, "%llu + %u READ_HPB R[%05d](%d) - context_id %d", + (unsigned long long) blk_rq_pos(rq), + (unsigned int) blk_rq_sectors(rq), + rgn_idx, rgn->rgn_state, hpb_ctx_id); + + atomic64_inc(&hpb->hit); +put_hpb: + cmd->requeue_cnt = 0; +requeue: + ufshpb_lu_put(hpb); + return; +wakeup_ee_worker: + ufshpb_failed(hpb, __func__); +} + +static inline void ufshpb_put_map_req(struct ufshpb_lu *hpb, + struct ufshpb_req *map_req) +{ + map_req->req = NULL; + map_req->bio = NULL; + list_add_tail(&map_req->list_req, &hpb->lh_map_req_free); + hpb->num_inflight_map_req--; +} + +static struct ufshpb_req *ufshpb_get_map_req(struct ufshpb_lu *hpb) +{ + struct ufshpb_req *map_req; + + if (hpb->num_inflight_map_req >= hpb->throttle_map_req) { + HPB_FTRACE(hpb, "inflight %d throttle %d", + hpb->num_inflight_map_req, hpb->throttle_map_req); + return NULL; + } + + map_req = list_first_entry_or_null(&hpb->lh_map_req_free, + struct ufshpb_req, list_req); + if (!map_req) { + HPB_DEBUG(hpb, "no map_req"); + return NULL; + } + + list_del_init(&map_req->list_req); + hpb->num_inflight_map_req++; + + return map_req; +} + +static int ufshpb_clean_dirty_bitmap(struct ufshpb_lu *hpb, + struct ufshpb_subregion *srgn) +{ + struct ufshpb_region *rgn; + + BUG_ON(!srgn->mctx); + + rgn = hpb->rgn_tbl + srgn->rgn_idx; + + if (rgn->rgn_state == HPB_RGN_INACTIVE) { + HPB_DEBUG(hpb, "evicted R[%05d](%d) S[%d](%d)", + srgn->rgn_idx, rgn->rgn_state, + srgn->srgn_idx, srgn->srgn_state); + return -EINVAL; + } + + memset(srgn->mctx->ppn_dirty, 0x00, + hpb->entries_per_srgn >> bits_per_byte_shift); + + return 0; +} + +static void ufshpb_clean_active_subregion(struct ufshpb_lu *hpb, + struct ufshpb_subregion *srgn) +{ + struct ufshpb_region *rgn; + + BUG_ON(!srgn->mctx); + + rgn = hpb->rgn_tbl + srgn->rgn_idx; + + if (rgn->rgn_state == HPB_RGN_INACTIVE) { + HPB_DEBUG(hpb, "evicted R[%05d](%d) S[%d](%d)", + srgn->rgn_idx, rgn->rgn_state, + srgn->srgn_idx, srgn->srgn_state); + return; + } + srgn->srgn_state = HPB_SRGN_CLEAN; +} + +static void ufshpb_error_active_subregion(struct ufshpb_lu *hpb, + struct ufshpb_subregion *srgn) +{ + struct ufshpb_region *rgn; + + BUG_ON(!srgn->mctx); + + rgn = hpb->rgn_tbl + srgn->rgn_idx; + + if (rgn->rgn_state == HPB_RGN_INACTIVE) { + ERR_MSG("evicted R[%05d](%d) S[%d](%d)", + srgn->rgn_idx, rgn->rgn_state, + srgn->srgn_idx, srgn->srgn_state); + return; + } + srgn->srgn_state = HPB_SRGN_DIRTY; +} + +#if defined(CONFIG_HPB_DEBUG) +static void ufshpb_check_ppn(struct ufshpb_lu *hpb, int rgn_idx, int srgn_idx, + struct ufshpb_map_ctx *mctx, const char *str) +{ + int error = 0; + u64 val[2]; + + BUG_ON(!mctx); + + val[0] = ufshpb_get_ppn(mctx, 0, &error); + if (!error) + val[1] = ufshpb_get_ppn(mctx, hpb->entries_per_srgn - 1, + &error); + if (error) + val[0] = val[1] = 0; + + HPB_DEBUG(hpb, "%s RB R[%05d](%d) S[%d](%d) (%llx ~ %llx)", + str, rgn_idx, hpb->rgn_tbl[rgn_idx].rgn_state, + srgn_idx, hpb->rgn_tbl[rgn_idx].srgn_tbl[srgn_idx].srgn_state, + val[0], val[1]); +} +#endif + +static void ufshpb_map_compl_process(struct ufshpb_req *map_req) +{ + struct ufshpb_lu *hpb = map_req->hpb; + struct ufshpb_region *rgn = hpb->rgn_tbl + map_req->rb.rgn_idx; + struct ufshpb_subregion *srgn = rgn->srgn_tbl + map_req->rb.srgn_idx; + unsigned long flags; + + atomic64_inc(&hpb->map_compl_cnt); + +#if defined(CONFIG_HPB_DEBUG) + if (hpb->debug) + ufshpb_check_ppn(hpb, srgn->rgn_idx, srgn->srgn_idx, srgn->mctx, + "COMPL"); +#endif + HPB_FTRACE(hpb, "R[%05d](%d)", + map_req->rb.rgn_idx, rgn->rgn_state); + + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_clean_active_subregion(hpb, srgn); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); +} + +static inline void ufshpb_act_rsp_list_add(struct ufshpb_lu *hpb, + struct ufshpb_subregion *srgn) +{ + atomic64_inc(&hpb->act_rsp_list_cnt); + list_add_tail(&srgn->list_act_srgn, &hpb->lh_act_srgn); + HPB_FTRACE(hpb, "R[%05d](%d) S[%d](%d)", + srgn->rgn_idx, hpb->rgn_tbl[srgn->rgn_idx].rgn_state, + srgn->srgn_idx, srgn->srgn_state); +} + +static inline void ufshpb_act_rsp_list_del(struct ufshpb_lu *hpb, + struct ufshpb_subregion *srgn) +{ + atomic64_dec(&hpb->act_rsp_list_cnt); + list_del_init(&srgn->list_act_srgn); + HPB_FTRACE(hpb, "R[%05d](%d) S[%d](%d)", + srgn->rgn_idx, hpb->rgn_tbl[srgn->rgn_idx].rgn_state, + srgn->srgn_idx, srgn->srgn_state); +} + +static inline void ufshpb_inact_rsp_list_add(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + atomic64_inc(&hpb->inact_rsp_list_cnt); + list_add_tail(&rgn->list_inact_rgn, &hpb->lh_inact_rgn); + HPB_FTRACE(hpb, "R[%05d](%d)", + rgn->rgn_idx, hpb->rgn_tbl[rgn->rgn_idx].rgn_state); +} + +static inline void ufshpb_inact_rsp_list_del(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + atomic64_dec(&hpb->inact_rsp_list_cnt); + list_del_init(&rgn->list_inact_rgn); + HPB_FTRACE(hpb, "R[%05d](%d)", + rgn->rgn_idx, hpb->rgn_tbl[rgn->rgn_idx].rgn_state); +} + +static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx, + int srgn_idx) +{ + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + + rgn = hpb->rgn_tbl + rgn_idx; + srgn = rgn->srgn_tbl + srgn_idx; + + if (!list_empty(&rgn->list_inact_rgn)) + ufshpb_inact_rsp_list_del(hpb, rgn); + + if (list_empty(&srgn->list_act_srgn)) + ufshpb_act_rsp_list_add(hpb, srgn); +} + +static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int rgn_idx) +{ + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + int srgn_idx; + + rgn = hpb->rgn_tbl + rgn_idx; + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + srgn = rgn->srgn_tbl + srgn_idx; + + if (!list_empty(&srgn->list_act_srgn)) + ufshpb_act_rsp_list_del(hpb, srgn); + } + + if (list_empty(&rgn->list_inact_rgn)) + ufshpb_inact_rsp_list_add(hpb, rgn); +} + +static int ufshpb_map_req_error(struct ufshpb_req *map_req) +{ + struct ufshpb_lu *hpb = map_req->hpb; + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + struct scsi_sense_hdr sshdr; + unsigned long flags; + + rgn = hpb->rgn_tbl + map_req->rb.rgn_idx; + srgn = rgn->srgn_tbl + map_req->rb.srgn_idx; + + scsi_normalize_sense(map_req->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); + + ERR_MSG("code %x sense_key %x asc %x ascq %x", + sshdr.response_code, sshdr.sense_key, sshdr.asc, sshdr.ascq); + ERR_MSG("byte4 %x byte5 %x byte6 %x additional_len %x", + sshdr.byte4, sshdr.byte5, sshdr.byte6, sshdr.additional_length); + + if (sshdr.sense_key != ILLEGAL_REQUEST) + return 0; + + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (rgn->rgn_state == HPB_RGN_PINNED) { + if (sshdr.asc == 0x06 && sshdr.ascq == 0x01) { + HPB_DEBUG(hpb, "retry R[%05d] S[%d]", + map_req->rb.rgn_idx, map_req->rb.srgn_idx); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + spin_lock(&hpb->retry_list_lock); + list_add_tail(&map_req->list_req, + &hpb->lh_map_req_retry); + spin_unlock(&hpb->retry_list_lock); + + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_error_active_subregion(hpb, srgn); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + schedule_delayed_work(&hpb->retry_work, + msecs_to_jiffies(RETRY_DELAY_MS)); + return -EAGAIN; + } + HPB_DEBUG(hpb, "dirty R[%05d] S[%d]", + map_req->rb.rgn_idx, map_req->rb.srgn_idx); + ufshpb_error_active_subregion(hpb, srgn); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + } else { + ufshpb_error_active_subregion(hpb, srgn); + + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + ufshpb_update_inactive_info(hpb, map_req->rb.rgn_idx); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + HPB_DEBUG(hpb, "inact R[%05d]", map_req->rb.rgn_idx); + schedule_work(&hpb->task_work); + } + + return 0; +} + +/* routine : map_req compl */ +static void ufshpb_map_req_compl_fn(struct request *req, blk_status_t error) +{ + struct ufshpb_req *map_req = (struct ufshpb_req *) req->end_io_data; + struct ufshpb_lu *hpb = map_req->hpb; + unsigned long flags; + int ret; + + if (ufshpb_get_state(hpb->ufsf) == HPB_FAILED) + goto free_map_req; + + if (error) { + ERR_MSG("ERR %d R[%05d](%d) S[%d](%d)", error, + map_req->rb.rgn_idx, + hpb->rgn_tbl[map_req->rb.rgn_idx].rgn_state, + map_req->rb.srgn_idx, + hpb->rgn_tbl[map_req->rb.rgn_idx].srgn_tbl[map_req->rb.srgn_idx].srgn_state); + ret = ufshpb_map_req_error(map_req); + if (ret) + goto retry_map_req; + } else + ufshpb_map_compl_process(map_req); + +free_map_req: + bio_put(map_req->bio); + ufshpb_put_blk_request(map_req->req); + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_put_map_req(map_req->hpb, map_req); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); +retry_map_req: + scsi_device_put(hpb->ufsf->sdev_ufs_lu[hpb->lun]); + ufshpb_lu_put(hpb); +} + +static inline void ufshpb_set_read_buf_cmd(unsigned char *cdb, int rgn_idx, + int srgn_idx, int srgn_mem_size) +{ + cdb[0] = UFSHPB_READ_BUFFER; + cdb[1] = UFSHPB_RB_ID_READ; + cdb[2] = GET_BYTE_1(rgn_idx); + cdb[3] = GET_BYTE_0(rgn_idx); + cdb[4] = GET_BYTE_1(srgn_idx); + cdb[5] = GET_BYTE_0(srgn_idx); + cdb[6] = GET_BYTE_2(srgn_mem_size); + cdb[7] = GET_BYTE_1(srgn_mem_size); + cdb[8] = GET_BYTE_0(srgn_mem_size); + cdb[9] = 0x00; +} + +static int ufshpb_map_req_add_bio_page(struct ufshpb_lu *hpb, + struct request_queue *q, struct bio *bio, + struct ufshpb_map_ctx *mctx) +{ + struct page *page = NULL; + int i, ret = 0; + + bio_reset(bio); + + for (i = 0; i < hpb->mpages_per_srgn; i++) { + /* virt_to_page(p + (OS_PAGE_SIZE * i)); */ + page = mctx->m_page[i]; + if (!page) + return -ENOMEM; + + ret = bio_add_pc_page(q, bio, page, hpb->mpage_bytes, 0); + + if (ret != hpb->mpage_bytes) { + ERR_MSG("bio_add_pc_page fail (%d)", ret); + return -ENOMEM; + } + } + + return 0; +} + +static int ufshpb_execute_map_req(struct ufshpb_lu *hpb, + struct ufshpb_req *map_req) +{ + struct scsi_device *sdev; + struct request_queue *q; + struct request *req; + struct bio *bio = map_req->bio; + struct scsi_request *rq; + int ret = 0; + + sdev = hpb->ufsf->sdev_ufs_lu[hpb->lun]; + if (!sdev) { + ERR_MSG("cannot find scsi_device"); + return -ENODEV; + } + + ret = ufsf_get_scsi_device(hpb->ufsf->hba, sdev); + if (ret) + return ret; + + q = sdev->request_queue; + + ret = ufshpb_map_req_add_bio_page(hpb, q, bio, map_req->rb.mctx); + if (ret) { + scsi_device_put(sdev); + return ret; + } + + req = map_req->req; + + /* 1. request setup */ + blk_rq_append_bio(req, &bio); /* req->__data_len is setted */ + req->rq_flags |= RQF_QUIET; + req->timeout = 30 * HZ; + req->end_io_data = (void *)map_req; + + /* 2. scsi_request setup */ + rq = scsi_req(req); + rq->retries = 3; + + ufshpb_set_read_buf_cmd(rq->cmd, map_req->rb.rgn_idx, + map_req->rb.srgn_idx, hpb->srgn_mem_size); + rq->cmd_len = scsi_command_size(rq->cmd); + +#if defined(CONFIG_HPB_DEBUG) + if (hpb->debug) + ufshpb_check_ppn(hpb, map_req->rb.rgn_idx, map_req->rb.srgn_idx, + map_req->rb.mctx, "ISSUE"); +#endif + ret = ufshpb_blk_execute_rq_nowait_mimic(q, req, ufshpb_map_req_compl_fn); + if (ret) { + scsi_device_put(sdev); + return ret; + } + + atomic64_inc(&hpb->map_req_cnt); + HPB_FTRACE(hpb, "issue R[%05d](%d) S[%d](%d)", + map_req->rb.rgn_idx, + hpb->rgn_tbl[map_req->rb.rgn_idx].rgn_state, + map_req->rb.srgn_idx, + hpb->rgn_tbl[map_req->rb.rgn_idx].srgn_tbl[map_req->rb.srgn_idx].srgn_state); + + return 0; +} + +static struct ufshpb_map_ctx *ufshpb_get_map_ctx(struct ufshpb_lu *hpb, + int *err) +{ + struct ufshpb_map_ctx *mctx; + + mctx = list_first_entry_or_null(&hpb->lh_map_ctx_free, + struct ufshpb_map_ctx, list_table); + if (mctx) { + list_del_init(&mctx->list_table); + hpb->debug_free_table--; + return mctx; + } + *err = -ENOMEM; + return NULL; +} + +static inline void ufshpb_add_lru_info(struct victim_select_info *lru_info, + struct ufshpb_region *rgn) +{ + rgn->rgn_state = HPB_RGN_ACTIVE; + list_add_tail(&rgn->list_lru_rgn, &lru_info->lh_lru_rgn); + atomic64_inc(&lru_info->active_cnt); +} + +static inline int ufshpb_add_region(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + struct victim_select_info *lru_info; + int srgn_idx; + int err = 0; + + lru_info = &hpb->lru_info; + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + struct ufshpb_subregion *srgn; + + srgn = rgn->srgn_tbl + srgn_idx; + + srgn->mctx = ufshpb_get_map_ctx(hpb, &err); + if (!srgn->mctx) { + ERR_MSG("mctx ERR %d R[%05d](%d) S[%d](%d) free_table %d", + err, srgn->rgn_idx, rgn->rgn_state, + srgn_idx, srgn->srgn_state, + hpb->debug_free_table); + goto out; + } + + srgn->srgn_state = HPB_SRGN_DIRTY; + } + + HPB_FTRACE(hpb, "add R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + + ufshpb_add_lru_info(lru_info, rgn); +out: + return err; +} + +static inline void ufshpb_put_map_ctx(struct ufshpb_lu *hpb, + struct ufshpb_map_ctx *mctx) +{ + list_add(&mctx->list_table, &hpb->lh_map_ctx_free); + hpb->debug_free_table++; +} + +static inline void ufshpb_purge_active_subregion(struct ufshpb_lu *hpb, + struct ufshpb_subregion *srgn, + int state) +{ + if (unlikely(state == HPB_SRGN_UNUSED)) { + ufshpb_put_map_ctx(hpb, srgn->mctx); + srgn->mctx = NULL; + } + + srgn->srgn_state = state; +} + +static inline void ufshpb_cleanup_lru_info(struct victim_select_info *lru_info, + struct ufshpb_region *rgn) +{ + list_del_init(&rgn->list_lru_rgn); + rgn->rgn_state = HPB_RGN_INACTIVE; + atomic64_dec(&lru_info->active_cnt); +} + +static void __ufshpb_evict_region(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + struct victim_select_info *lru_info; + struct ufshpb_subregion *srgn; + int srgn_idx; + + lru_info = &hpb->lru_info; + + HPB_FTRACE(hpb, "evict R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + + ufshpb_cleanup_lru_info(lru_info, rgn); + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + srgn = rgn->srgn_tbl + srgn_idx; + + ufshpb_purge_active_subregion(hpb, srgn, HPB_SRGN_UNUSED); + } +} + +static void ufshpb_hit_lru_info(struct victim_select_info *lru_info, + struct ufshpb_region *rgn) +{ + switch (lru_info->selection_type) { + case LRU: + list_move_tail(&rgn->list_lru_rgn, &lru_info->lh_lru_rgn); + break; + default: + break; + } +} + +/* + * Must be held hpb_lock before call this func. + */ +static int ufshpb_check_issue_state_srgns(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + struct ufshpb_subregion *srgn; + int srgn_idx; + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + srgn = rgn->srgn_tbl + srgn_idx; + + if (srgn->srgn_state == HPB_SRGN_ISSUED) { + HPB_FTRACE(hpb, "R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + + return -EPERM; + } + } + return 0; +} + +static struct ufshpb_region *ufshpb_victim_lru_info(struct ufshpb_lu *hpb) +{ + struct victim_select_info *lru_info = &hpb->lru_info; + struct ufshpb_region *rgn; + struct ufshpb_region *victim_rgn = NULL; + + switch (lru_info->selection_type) { + case LRU: + list_for_each_entry(rgn, &lru_info->lh_lru_rgn, list_lru_rgn) { + if (!rgn) + break; + + if (ufshpb_check_issue_state_srgns(hpb, rgn)) + continue; + + victim_rgn = rgn; + break; + } + break; + default: + break; + } + + return victim_rgn; +} + +static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn) +{ + unsigned long flags; + + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (rgn->rgn_state == HPB_RGN_PINNED) { + /* Pinned region should not drop-out.*/ + ERR_MSG("drop-out ERR R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + goto out; + } + + if (!list_empty(&rgn->list_lru_rgn)) { + if (ufshpb_check_issue_state_srgns(hpb, rgn)) + goto evict_fail; + + __ufshpb_evict_region(hpb, rgn); + } +out: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return 0; +evict_fail: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return -EPERM; +} + +static inline struct +ufshpb_rsp_field *ufshpb_get_hpb_rsp(struct ufshcd_lrb *lrbp) +{ + return (struct ufshpb_rsp_field *)&lrbp->ucd_rsp_ptr->sr.sense_data_len; +} + +static int ufshpb_issue_map_req(struct ufshpb_lu *hpb, + struct ufshpb_subregion *srgn) +{ + struct scsi_device *sdev = hpb->ufsf->sdev_ufs_lu[hpb->lun]; + struct request *req = NULL; + struct bio *bio = NULL; + struct ufshpb_req *map_req = NULL; + unsigned long flags; + int ret = 0; + + req = blk_get_request(sdev->request_queue, REQ_OP_SCSI_IN, + BLK_MQ_REQ_PREEMPT); + if (IS_ERR(req)) + return -EAGAIN; + + bio = bio_alloc(GFP_KERNEL, hpb->mpages_per_srgn); + if (!bio) { + blk_put_request(req); + return -EAGAIN; + } + + spin_lock_irqsave(&hpb->hpb_lock, flags); + + if (srgn->srgn_state == HPB_SRGN_ISSUED) { + ret = -EAGAIN; + goto unlock_out; + } + + ret = ufshpb_clean_dirty_bitmap(hpb, srgn); + if (ret) { + ret = -EAGAIN; + goto unlock_out; + } + + map_req = ufshpb_get_map_req(hpb); + if (!map_req) { + ret = -ENOMEM; + goto unlock_out; + } + + srgn->srgn_state = HPB_SRGN_ISSUED; + + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + map_req->req = req; + map_req->bio = bio; + map_req->hpb = hpb; + map_req->rb.rgn_idx = srgn->rgn_idx; + map_req->rb.srgn_idx = srgn->srgn_idx; + map_req->rb.mctx = srgn->mctx; + map_req->rb.lun = hpb->lun; + + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + if (ret == -ENODEV) + ERR_MSG("lu_get failed (%d)", ret); + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + + goto free_map_req; + } + + ret = ufshpb_execute_map_req(hpb, map_req); + if (ret) + goto map_req_failed; + + return ret; +map_req_failed: + ufshpb_lu_put(hpb); +free_map_req: + spin_lock_irqsave(&hpb->hpb_lock, flags); + srgn->srgn_state = HPB_SRGN_DIRTY; + ufshpb_put_map_req(hpb, map_req); +unlock_out: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + bio_put(bio); + blk_put_request(req); + return ret; +} + +static int ufshpb_load_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn) +{ + struct ufshpb_region *victim_rgn; + struct victim_select_info *lru_info = &hpb->lru_info; + unsigned long flags; + int ret = 0; + + /* + * if already region is added to lru_list, + * just initiate the information of lru. + * because the region already has the map ctx. + * (!list_empty(&rgn->list_region) == region->state=active...) + */ + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (!list_empty(&rgn->list_lru_rgn)) { + ufshpb_hit_lru_info(lru_info, rgn); + goto out; + } + + if (rgn->rgn_state == HPB_RGN_INACTIVE) { + if (atomic64_read(&lru_info->active_cnt) + == lru_info->max_lru_active_cnt) { + victim_rgn = ufshpb_victim_lru_info(hpb); + if (!victim_rgn) { + HPB_DEBUG(hpb, "victim_rgn is NULL"); + ret = -ENOMEM; + goto out; + } + HPB_FTRACE(hpb, "lru_max %lld R[%05d](%d)", + atomic64_read(&lru_info->active_cnt), + victim_rgn->rgn_idx, victim_rgn->rgn_state); + + __ufshpb_evict_region(hpb, victim_rgn); + } + + ret = ufshpb_add_region(hpb, rgn); + if (ret) { + ERR_MSG("add_region failed (%d)", ret); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + goto wakeup_ee_worker; + } + } +out: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return ret; +wakeup_ee_worker: + ufshpb_failed(hpb, __func__); + return ret; +} + +static void ufshpb_rsp_req_region_update(struct ufshpb_lu *hpb, + struct ufshpb_rsp_field *rsp_field) +{ + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + int num, rgn_idx, srgn_idx; + + HPB_FTRACE(hpb, "ACT %d INACT %d", + rsp_field->active_rgn_cnt, rsp_field->inactive_rgn_cnt); + + /* + * If active rgn = inactive rgn, choose inactive rgn. + * So, active process -> inactive process + */ + for (num = 0; num < rsp_field->active_rgn_cnt; num++) { + rgn_idx = be16_to_cpu(rsp_field->hpb_active_field[num].active_rgn); + srgn_idx = be16_to_cpu(rsp_field->hpb_active_field[num].active_srgn); + + if (unlikely(rgn_idx >= hpb->rgns_per_lu)) { + ERR_MSG("R[%05d] is wrong. (Max %d)", + rgn_idx, hpb->rgns_per_lu); + continue; + } + + rgn = hpb->rgn_tbl + rgn_idx; + + if (unlikely(srgn_idx >= rgn->srgn_cnt)) { + ERR_MSG("R[%05d] S[%d] is wrong. (Max %d)", + rgn_idx, srgn_idx, rgn->srgn_cnt); + continue; + } + + srgn = rgn->srgn_tbl + srgn_idx; + + HPB_FTRACE(hpb, "ACT %d R[%05d](%d) S[%d](%d)", + num + 1, rgn_idx, rgn->rgn_state, + srgn_idx, srgn->srgn_idx); + spin_lock(&hpb->rsp_list_lock); + ufshpb_update_active_info(hpb, rgn_idx, srgn_idx); + spin_unlock(&hpb->rsp_list_lock); + + /* It is just blocking HPB_READ */ + spin_lock(&hpb->hpb_lock); + if (srgn->srgn_state == HPB_SRGN_CLEAN) + srgn->srgn_state = HPB_SRGN_DIRTY; + spin_unlock(&hpb->hpb_lock); + atomic64_inc(&hpb->rb_active_cnt); + } + + for (num = 0; num < rsp_field->inactive_rgn_cnt; num++) { + rgn_idx = be16_to_cpu(rsp_field->hpb_inactive_field[num]); + + if (unlikely(rgn_idx >= hpb->rgns_per_lu)) { + ERR_MSG("R[%05d] is wrong. (Max %d)", + rgn_idx, hpb->rgns_per_lu); + continue; + } + + rgn = hpb->rgn_tbl + rgn_idx; + + HPB_FTRACE(hpb, "INACT %d R[%05d](%d)", + num + 1, rgn_idx, rgn->rgn_state); + spin_lock(&hpb->rsp_list_lock); + ufshpb_update_inactive_info(hpb, rgn_idx); + spin_unlock(&hpb->rsp_list_lock); + + /* + * to block HPB_READ + */ + spin_lock(&hpb->hpb_lock); + if (rgn->rgn_state != HPB_RGN_INACTIVE) { + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + srgn = rgn->srgn_tbl + srgn_idx; + if (srgn->srgn_state == HPB_SRGN_CLEAN) + srgn->srgn_state = HPB_SRGN_DIRTY; + } + } + spin_unlock(&hpb->hpb_lock); + + atomic64_inc(&hpb->rb_inactive_cnt); + } + + schedule_work(&hpb->task_work); +} + +static inline int ufshpb_may_field_valid(struct ufshcd_lrb *lrbp, + struct ufshpb_rsp_field *rsp_field) +{ + if (be16_to_cpu(rsp_field->sense_data_len) != DEV_SENSE_SEG_LEN || + rsp_field->desc_type != DEV_DES_TYPE || + rsp_field->additional_len != DEV_ADDITIONAL_LEN || + rsp_field->hpb_type == HPB_RSP_NONE || + rsp_field->active_rgn_cnt > MAX_ACTIVE_NUM || + rsp_field->inactive_rgn_cnt > MAX_INACTIVE_NUM || + (!rsp_field->active_rgn_cnt && !rsp_field->inactive_rgn_cnt)) + return -EINVAL; + + if (!ufsf_is_valid_lun(lrbp->lun)) { + ERR_MSG("LU(%d) is not supported", lrbp->lun); + return -EINVAL; + } + + return 0; +} + +static bool ufshpb_is_empty_rsp_lists(struct ufshpb_lu *hpb) +{ + bool ret = true; + unsigned long flags; + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + if (!list_empty(&hpb->lh_inact_rgn) || !list_empty(&hpb->lh_act_srgn)) + ret = false; + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + + return ret; +} + +/* routine : isr (ufs) */ +void ufshpb_rsp_upiu(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp) +{ + struct ufshpb_lu *hpb; + struct ufshpb_rsp_field *rsp_field; + int data_seg_len, ret; + + data_seg_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) + & MASK_RSP_UPIU_DATA_SEG_LEN; + + if (!data_seg_len) { + bool do_task_work = false; + + if (!ufsf_is_valid_lun(lrbp->lun)) + return; + + hpb = ufsf->hpb_lup[lrbp->lun]; + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + return; + } + + do_task_work = !ufshpb_is_empty_rsp_lists(hpb); + if (do_task_work) + schedule_work(&hpb->task_work); + + goto put_hpb; + } + + rsp_field = ufshpb_get_hpb_rsp(lrbp); + + if (ufshpb_may_field_valid(lrbp, rsp_field)) { + WARN_ON(rsp_field->additional_len != DEV_ADDITIONAL_LEN); + return; + } + + hpb = ufsf->hpb_lup[lrbp->lun]; + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + return; + } + + if (hpb->force_map_req_disable) + goto put_hpb; + + atomic64_inc(&hpb->rb_noti_cnt); + + switch (rsp_field->hpb_type) { + case HPB_RSP_REQ_REGION_UPDATE: + WARN_ON(data_seg_len != DEV_DATA_SEG_LEN); + ufshpb_rsp_req_region_update(hpb, rsp_field); + goto put_hpb; + default: + HPB_DEBUG(hpb, "HPB_TYPE %d is not available", + rsp_field->hpb_type); + goto put_hpb; + } + +put_hpb: + ufshpb_lu_put(hpb); +} + +static int ufshpb_execute_map_req_wait(struct ufshpb_lu *hpb, + unsigned char *cmd, + struct ufshpb_subregion *srgn) +{ + struct ufsf_feature *ufsf = hpb->ufsf; + struct scsi_device *sdev; + struct request_queue *q; + struct request *req; + struct scsi_request *rq; + struct bio *bio; + struct scsi_sense_hdr sshdr; + unsigned long flags; + int ret = 0; + + sdev = ufsf->sdev_ufs_lu[hpb->lun]; + if (!sdev) { + ERR_MSG("cannot find scsi_device"); + return -ENODEV; + } + + q = sdev->request_queue; + + ret = ufsf_get_scsi_device(ufsf->hba, sdev); + if (ret) + return ret; + + req = blk_get_request(q, REQ_OP_SCSI_IN, BLK_MQ_REQ_PREEMPT); + if (IS_ERR(req)) { + ERR_MSG("cannot get request"); + ret = -EIO; + goto sdev_put_out; + } + + bio = bio_alloc(GFP_KERNEL, hpb->mpages_per_srgn); + if (!bio) { + ret = -ENOMEM; + goto req_put_out; + } + + ret = ufshpb_map_req_add_bio_page(hpb, q, bio, srgn->mctx); + if (ret) + goto mem_free_out; + + /* 1. request setup*/ + blk_rq_append_bio(req, &bio); /* req->__data_len */ + req->rq_flags |= RQF_QUIET; + req->timeout = 30 * HZ; + + /* 2. scsi_request setup */ + rq = scsi_req(req); + rq->retries = 3; + rq->cmd_len = scsi_command_size(cmd); + memcpy(rq->cmd, cmd, rq->cmd_len); + + blk_execute_rq(q, NULL, req, 1); + if (rq->result) { + ret = -EIO; + scsi_normalize_sense(rq->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); + ERR_MSG("code %x sense_key %x asc %x ascq %x", + sshdr.response_code, sshdr.sense_key, sshdr.asc, + sshdr.ascq); + ERR_MSG("byte4 %x byte5 %x byte6 %x additional_len %x", + sshdr.byte4, sshdr.byte5, sshdr.byte6, + sshdr.additional_length); + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_error_active_subregion(hpb, srgn); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + } +mem_free_out: + bio_put(bio); +req_put_out: + blk_put_request(req); +sdev_put_out: + scsi_device_put(sdev); + return ret; +} + +static void ufshpb_issue_map_req_from_list(struct ufshpb_lu *hpb) +{ + struct ufshpb_subregion *srgn; + unsigned long flags; + int ret; + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + + while ((srgn = list_first_entry_or_null(&hpb->lh_pinned_srgn, + struct ufshpb_subregion, + list_act_srgn))) { + unsigned char cmd[10] = { 0 }; + + list_del_init(&srgn->list_act_srgn); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + + ufshpb_set_read_buf_cmd(cmd, srgn->rgn_idx, srgn->srgn_idx, + hpb->srgn_mem_size); + +#if defined(CONFIG_HPB_DEBUG) + if (hpb->debug) + ufshpb_check_ppn(hpb, srgn->rgn_idx, srgn->srgn_idx, + srgn->mctx, "ISSUE"); +#endif + HPB_FTRACE(hpb, "issue R[%05d](%d)", + srgn->rgn_idx, hpb->rgn_tbl[srgn->rgn_idx].rgn_state); + + ret = ufshpb_execute_map_req_wait(hpb, cmd, srgn); + if (ret < 0) { + ERR_MSG("issue ERR %d R[%05d](%d) S%0d(%d)", + ret, + srgn->rgn_idx, hpb->rgn_tbl[srgn->rgn_idx].rgn_state, + srgn->srgn_idx, srgn->srgn_state); + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + if (list_empty(&srgn->list_act_srgn)) + list_add(&srgn->list_act_srgn, + &hpb->lh_pinned_srgn); + continue; + } + +#if defined(CONFIG_HPB_DEBUG) + if (hpb->debug) + ufshpb_check_ppn(hpb, srgn->rgn_idx, srgn->srgn_idx, + srgn->mctx, "COMPL"); +#endif + HPB_FTRACE(hpb, "compl R[%05d](%d)", + srgn->rgn_idx, hpb->rgn_tbl[srgn->rgn_idx].rgn_state); + + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_clean_active_subregion(hpb, srgn); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + } + + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); +} + +static void ufshpb_pinned_work_handler(struct work_struct *work) +{ + struct ufshpb_lu *hpb; + int ret; + + hpb = container_of(work, struct ufshpb_lu, pinned_work); + HPB_DEBUG(hpb, "worker start"); + + pm_runtime_get_sync(hpb->ufsf->hba->dev); + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + if (ret == -ENODEV) + ERR_MSG("lu_get failed (%d)", ret); + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + return; + } + + if (!list_empty(&hpb->lh_pinned_srgn)) + ufshpb_issue_map_req_from_list(hpb); + /* + * if its function failed at init time, + * ufshpb-device will request map-req, + * so it is not critical-error, and just finish work-handler + */ + + ufshpb_lu_put(hpb); + pm_runtime_put_sync(hpb->ufsf->hba->dev); + HPB_DEBUG(hpb, "worker end"); +} + +static int ufshpb_check_pm(struct ufshpb_lu *hpb) +{ + struct ufs_hba *hba = hpb->ufsf->hba; + + if (hba->pm_op_in_progress || + hba->curr_dev_pwr_mode != UFS_ACTIVE_PWR_MODE) { + INFO_MSG("hba current power state %d pm_progress %d", + hba->curr_dev_pwr_mode, + hba->pm_op_in_progress); + return -ENODEV; + } + return 0; +} + +static void ufshpb_retry_work_handler(struct work_struct *work) +{ + struct ufshpb_lu *hpb; + struct delayed_work *dwork = to_delayed_work(work); + struct ufshpb_req *map_req, *next; + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + unsigned long flags; + bool do_retry = false; + int ret = 0; + + LIST_HEAD(retry_list); + + hpb = container_of(dwork, struct ufshpb_lu, retry_work); + + if (ufshpb_check_pm(hpb)) + return; + + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + if (ret == -ENODEV) + ERR_MSG("lu_get failed (%d)", ret); + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + return; + } + + HPB_DEBUG(hpb, "worker start"); + + spin_lock_bh(&hpb->retry_list_lock); + list_splice_init(&hpb->lh_map_req_retry, &retry_list); + spin_unlock_bh(&hpb->retry_list_lock); + + list_for_each_entry_safe(map_req, next, &retry_list, list_req) { + if (ufshpb_get_state(hpb->ufsf) == HPB_SUSPEND) { + INFO_MSG("suspend state. Issue READ_BUFFER in the next turn"); + spin_lock_bh(&hpb->retry_list_lock); + list_splice_init(&retry_list, &hpb->lh_map_req_retry); + spin_unlock_bh(&hpb->retry_list_lock); + do_retry = true; + break; + } + + list_del_init(&map_req->list_req); + + rgn = hpb->rgn_tbl + map_req->rb.rgn_idx; + srgn = rgn->srgn_tbl + map_req->rb.srgn_idx; + + bio_put(map_req->bio); + blk_put_request(map_req->req); + spin_lock_irqsave(&hpb->hpb_lock, flags); + ufshpb_put_map_req(hpb, map_req); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + if (ret == -ENODEV) + ERR_MSG("lu_get failed (%d)", ret); + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + continue; + } + + ret = ufshpb_issue_map_req(hpb, srgn); + if (ret) { + ERR_MSG("issue ERR %d R[%05d](%d) S%0d(%d)", + ret, rgn->rgn_idx, rgn->rgn_state, + srgn->srgn_idx, srgn->srgn_state); + spin_lock_bh(&hpb->retry_list_lock); + list_splice_init(&retry_list, &hpb->lh_map_req_retry); + spin_unlock_bh(&hpb->retry_list_lock); + do_retry = true; + break; + } + } + HPB_DEBUG(hpb, "worker end"); + ufshpb_lu_put(hpb); + + if (do_retry) + schedule_delayed_work(&hpb->retry_work, + msecs_to_jiffies(RETRY_DELAY_MS)); +} + +static void ufshpb_add_starved_list(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn, + struct list_head *starved_list) +{ + struct ufshpb_subregion *srgn; + int srgn_idx; + + if (!list_empty(&rgn->list_inact_rgn)) + return; + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + srgn = rgn->srgn_tbl + srgn_idx; + + if (!list_empty(&srgn->list_act_srgn)) + return; + } + /* It will be added to hpb->lh_inact_rgn */ + atomic64_inc(&hpb->inact_rsp_list_cnt); + list_add_tail(&rgn->list_inact_rgn, starved_list); + + HPB_FTRACE(hpb, "R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + +} + +static void ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb) +{ + struct ufshpb_region *rgn; + unsigned long flags; + int ret; + LIST_HEAD(starved_list); + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + while ((rgn = list_first_entry_or_null(&hpb->lh_inact_rgn, + struct ufshpb_region, + list_inact_rgn))) { + if (ufshpb_get_state(hpb->ufsf) == HPB_SUSPEND) { + INFO_MSG("suspend state. Inactivate rgn the next turn"); + break; + } + + HPB_FTRACE(hpb, "inact R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + + ufshpb_inact_rsp_list_del(hpb, rgn); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + + ret = ufshpb_evict_region(hpb, rgn); + if (ret) { + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + ufshpb_add_starved_list(hpb, rgn, &starved_list); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + } + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + } + + list_splice(&starved_list, &hpb->lh_inact_rgn); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); +} + +static void ufshpb_add_active_list(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn, + struct ufshpb_subregion *srgn) +{ + if (!list_empty(&rgn->list_inact_rgn)) + return; + + if (!list_empty(&srgn->list_act_srgn)) { + list_move(&srgn->list_act_srgn, &hpb->lh_act_srgn); + return; + } + + HPB_FTRACE(hpb, "R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + + ufshpb_act_rsp_list_add(hpb, srgn); +} + +static void ufshpb_run_active_subregion_list(struct ufshpb_lu *hpb) +{ + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + while ((srgn = list_first_entry_or_null(&hpb->lh_act_srgn, + struct ufshpb_subregion, + list_act_srgn))) { + if (ufshpb_get_state(hpb->ufsf) == HPB_SUSPEND) { + INFO_MSG("suspend state. Issuing READ_BUFFER the next turn"); + break; + } + + ufshpb_act_rsp_list_del(hpb, srgn); + + if (hpb->force_map_req_disable) { + HPB_DEBUG(hpb, "map_req disabled"); + continue; + } + + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + + rgn = hpb->rgn_tbl + srgn->rgn_idx; + + HPB_FTRACE(hpb, "R[%05d](%d)", + rgn->rgn_idx, rgn->rgn_state); + + ret = ufshpb_load_region(hpb, rgn); + if (ret) + break; + + ret = ufshpb_issue_map_req(hpb, srgn); + if (ret) + break; + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + } + + if (ret) { + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + ufshpb_add_active_list(hpb, rgn, srgn); + } + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); +} + +static void ufshpb_task_work_handler(struct work_struct *work) +{ + struct ufshpb_lu *hpb; + int ret; + + hpb = container_of(work, struct ufshpb_lu, task_work); + ret = ufshpb_lu_get(hpb); + if (unlikely(ret)) { + if (ret == -ENODEV) + ERR_MSG("lu_get failed (%d)", ret); + HPB_DEBUG(hpb, "lu_get failed (%d)", ret); + return; + } + + ufshpb_run_inactive_region_list(hpb); + ufshpb_run_active_subregion_list(hpb); + + ufshpb_lu_put(hpb); +} + +static inline void ufshpb_map_req_mempool_remove(struct ufshpb_lu *hpb) +{ + struct ufshpb_req *map_req; + int i; + + for (i = 0; i < hpb->qd; i++) { + map_req = hpb->map_req + i; + if (map_req->req) + blk_put_request(map_req->req); + if (map_req->bio) + bio_put(map_req->bio); + } + + kfree(hpb->map_req); +} + +static inline void ufshpb_pre_req_mempool_remove(struct ufshpb_lu *hpb) +{ + struct ufshpb_req *pre_req; + int i; + + for (i = 0; i < hpb->qd; i++) { + pre_req = hpb->pre_req + i; + if (pre_req->req) + blk_put_request(pre_req->req); + if (pre_req->bio) + bio_put(pre_req->bio); + __free_page(pre_req->wb.m_page); + } + + kfree(hpb->pre_req); +} + +static void ufshpb_table_mempool_remove(struct ufshpb_lu *hpb) +{ + struct ufshpb_map_ctx *mctx, *next; + int i; + + /* + * the mctx in the lh_map_ctx_free has been allocated completely. + */ + list_for_each_entry_safe(mctx, next, &hpb->lh_map_ctx_free, + list_table) { + for (i = 0; i < hpb->mpages_per_srgn; i++) + __free_page(mctx->m_page[i]); + + vfree(mctx->ppn_dirty); + kfree(mctx->m_page); + kfree(mctx); + hpb->alloc_mctx--; + } +} + +/* + * this function doesn't need to hold lock due to be called in init. + * (hpb_lock, rsp_list_lock, etc..) + */ +static int ufshpb_init_pinned_active_region(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + struct ufshpb_subregion *srgn; + int srgn_idx, j; + int err = 0; + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + srgn = rgn->srgn_tbl + srgn_idx; + + srgn->mctx = ufshpb_get_map_ctx(hpb, &err); + if (err) { + ERR_MSG("mctx ERR %d R[%05d](%d) S[%d](%d) free_table %d", + err, srgn->rgn_idx, rgn->rgn_state, + srgn_idx, srgn->srgn_state, + hpb->debug_free_table); + goto release; + } + + srgn->srgn_state = HPB_SRGN_ISSUED; + /* + * no need to clean ppn_dirty bitmap + * because it is vzalloc-ed @ufshpb_table_mempool_init() + */ + list_add_tail(&srgn->list_act_srgn, &hpb->lh_pinned_srgn); + } + + rgn->rgn_state = HPB_RGN_PINNED; + + return 0; + +release: + for (j = 0; j < srgn_idx; j++) { + srgn = rgn->srgn_tbl + j; + ufshpb_put_map_ctx(hpb, srgn->mctx); + } + + return err; +} + +static inline bool ufshpb_is_pinned_region(struct ufshpb_lu *hpb, int rgn_idx) +{ + if (hpb->lu_pinned_end_offset != PINNED_NOT_SET && + rgn_idx >= hpb->lu_pinned_rgn_startidx && + rgn_idx <= hpb->lu_pinned_end_offset) + return true; + + return false; +} + +static inline void ufshpb_init_jobs(struct ufshpb_lu *hpb) +{ + INIT_WORK(&hpb->pinned_work, ufshpb_pinned_work_handler); + INIT_DELAYED_WORK(&hpb->retry_work, ufshpb_retry_work_handler); + INIT_WORK(&hpb->task_work, ufshpb_task_work_handler); +} + +static inline void ufshpb_cancel_jobs(struct ufshpb_lu *hpb) +{ + cancel_work_sync(&hpb->pinned_work); + cancel_delayed_work_sync(&hpb->retry_work); + cancel_work_sync(&hpb->task_work); +} + +static void ufshpb_init_subregion_tbl(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + int srgn_idx; + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + struct ufshpb_subregion *srgn = rgn->srgn_tbl + srgn_idx; + + INIT_LIST_HEAD(&srgn->list_act_srgn); + + srgn->rgn_idx = rgn->rgn_idx; + srgn->srgn_idx = srgn_idx; + srgn->srgn_state = HPB_SRGN_UNUSED; + } +} + +static inline int ufshpb_alloc_subregion_tbl(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn, + int srgn_cnt) +{ + rgn->srgn_tbl = + kzalloc(sizeof(struct ufshpb_subregion) * srgn_cnt, GFP_KERNEL); + if (!rgn->srgn_tbl) + return -ENOMEM; + + rgn->srgn_cnt = srgn_cnt; + return 0; +} + +static int ufshpb_table_mempool_init(struct ufshpb_lu *hpb) +{ + struct ufshpb_map_ctx *mctx = NULL; + int i, j, k; + + INIT_LIST_HEAD(&hpb->lh_map_ctx_free); + + hpb->alloc_mctx = hpb->lu_max_active_rgns * hpb->srgns_per_rgn; + + for (i = 0; i < hpb->alloc_mctx; i++) { + mctx = kmalloc(sizeof(struct ufshpb_map_ctx), GFP_KERNEL); + if (!mctx) + goto release_mem; + + mctx->m_page = + kzalloc(sizeof(struct page *) * hpb->mpages_per_srgn, + GFP_KERNEL); + if (!mctx->m_page) + goto release_mem; + + mctx->ppn_dirty = vzalloc(hpb->entries_per_srgn >> + bits_per_byte_shift); + if (!mctx->ppn_dirty) + goto release_mem; + + for (j = 0; j < hpb->mpages_per_srgn; j++) { + mctx->m_page[j] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!mctx->m_page[j]) { + for (k = 0; k < j; k++) + __free_page(mctx->m_page[k]); + goto release_mem; + } + } + + INIT_LIST_HEAD(&mctx->list_table); + list_add(&mctx->list_table, &hpb->lh_map_ctx_free); + + hpb->debug_free_table++; + } + + INFO_MSG("The number of mctx = (%d). debug_free_table (%d)", + hpb->alloc_mctx, hpb->debug_free_table); + return 0; +release_mem: + /* + * mctxs already added in lh_map_ctx_free will be removed + * in the caller function. + */ + if (mctx) { + kfree(mctx->m_page); + vfree(mctx->ppn_dirty); + kfree(mctx); + } + return -ENOMEM; +} + +static int ufshpb_map_req_mempool_init(struct ufshpb_lu *hpb) +{ + struct scsi_device *sdev; + struct request_queue *q; + struct ufshpb_req *map_req = NULL; + int qd = hpb->qd; + int i; + + sdev = hpb->ufsf->sdev_ufs_lu[hpb->lun]; + q = sdev->request_queue; + + INIT_LIST_HEAD(&hpb->lh_map_req_free); + INIT_LIST_HEAD(&hpb->lh_map_req_retry); + + hpb->map_req = kzalloc(sizeof(struct ufshpb_req) * qd, GFP_KERNEL); + if (!hpb->map_req) + goto release_mem; + + for (i = 0; i < qd; i++) { + map_req = hpb->map_req + i; + INIT_LIST_HEAD(&map_req->list_req); + map_req->req = NULL; + map_req->bio = NULL; + + list_add_tail(&map_req->list_req, &hpb->lh_map_req_free); + } + + return 0; +release_mem: + kfree(hpb->map_req); + return -ENOMEM; +} + +static int ufshpb_pre_req_mempool_init(struct ufshpb_lu *hpb) +{ + struct scsi_device *sdev; + struct request_queue *q; + struct ufshpb_req *pre_req = NULL; + int qd = hpb->qd; + int i, j; + + INIT_LIST_HEAD(&hpb->lh_pre_req_free); + + sdev = hpb->ufsf->sdev_ufs_lu[hpb->lun]; + q = sdev->request_queue; + + hpb->pre_req = kzalloc(sizeof(struct ufshpb_req) * qd, GFP_KERNEL); + if (!hpb->pre_req) + goto release_mem; + + for (i = 0; i < qd; i++) { + pre_req = hpb->pre_req + i; + INIT_LIST_HEAD(&pre_req->list_req); + pre_req->req = NULL; + pre_req->bio = NULL; + + pre_req->wb.m_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!pre_req->wb.m_page) { + for (j = 0; j < i; j++) + __free_page(hpb->pre_req[j].wb.m_page); + + goto release_mem; + } + list_add_tail(&pre_req->list_req, &hpb->lh_pre_req_free); + } + + return 0; +release_mem: + kfree(hpb->pre_req); + return -ENOMEM; +} + +static void ufshpb_find_lu_qd(struct ufshpb_lu *hpb) +{ + struct scsi_device *sdev; + struct ufs_hba *hba; + + sdev = hpb->ufsf->sdev_ufs_lu[hpb->lun]; + hba = hpb->ufsf->hba; + + /* + * ufshcd_slave_alloc(sdev) -> ufshcd_set_queue_depth(sdev) + * a lu-queue-depth compared with lu_info and hba->nutrs + * is selected in ufshcd_set_queue_depth() + */ + hpb->qd = sdev->queue_depth; + INFO_MSG("lu (%d) queue_depth (%d)", hpb->lun, hpb->qd); + if (!hpb->qd) { + hpb->qd = hba->nutrs; + INFO_MSG("lu_queue_depth is 0. we use device's queue info."); + INFO_MSG("hba->nutrs = (%d)", hba->nutrs); + } + + hpb->throttle_map_req = hpb->qd; + hpb->throttle_pre_req = hpb->qd; + hpb->num_inflight_map_req = 0; + hpb->num_inflight_pre_req = 0; +} + +static void ufshpb_init_lu_constant(struct ufshpb_dev_info *hpb_dev_info, + struct ufshpb_lu *hpb) +{ + unsigned long long rgn_unit_size, rgn_mem_size; + int entries_per_rgn; + + hpb->debug = false; + + ufshpb_find_lu_qd(hpb); + + /* for pre_req */ + hpb->pre_req_min_tr_len = HPB_MULTI_CHUNK_LOW; + hpb->pre_req_max_tr_len = HPB_MULTI_CHUNK_HIGH; + hpb->ctx_id_ticket = 0; + hpb->pre_req_requeue_cnt = DEFAULT_WB_REQUEUE_CNT; + + /* From descriptors */ + rgn_unit_size = (unsigned long long) + SECTOR * (0x01 << hpb_dev_info->rgn_size); + rgn_mem_size = rgn_unit_size / BLOCK * HPB_ENTRY_SIZE; + + hpb->srgn_unit_size = (unsigned long long) + SECTOR * (0x01 << hpb_dev_info->srgn_size); + hpb->srgn_mem_size = hpb->srgn_unit_size / BLOCK * HPB_ENTRY_SIZE; + + /* relation : lu <-> region <-> sub region <-> entry */ + entries_per_rgn = rgn_mem_size / HPB_ENTRY_SIZE; + hpb->entries_per_srgn = hpb->srgn_mem_size / HPB_ENTRY_SIZE; + hpb->srgns_per_rgn = rgn_mem_size / hpb->srgn_mem_size; + + /* + * regions_per_lu = (lu_num_blocks * 4096) / region_unit_size + * = (lu_num_blocks * HPB_ENTRY_SIZE) / region_mem_size + */ + hpb->rgns_per_lu = + ((unsigned long long)hpb->lu_num_blocks + + (rgn_mem_size / HPB_ENTRY_SIZE) - 1) + / (rgn_mem_size / HPB_ENTRY_SIZE); + hpb->srgns_per_lu = + ((unsigned long long)hpb->lu_num_blocks + + (hpb->srgn_mem_size / HPB_ENTRY_SIZE) - 1) + / (hpb->srgn_mem_size / HPB_ENTRY_SIZE); + + /* mempool info */ + hpb->mpage_bytes = OS_PAGE_SIZE; + hpb->mpages_per_srgn = hpb->srgn_mem_size / hpb->mpage_bytes; + + /* Bitmask Info. */ + hpb->dwords_per_srgn = hpb->entries_per_srgn / BITS_PER_DWORD; + hpb->entries_per_rgn_shift = ffs(entries_per_rgn) - 1; + hpb->entries_per_rgn_mask = entries_per_rgn - 1; + hpb->entries_per_srgn_shift = ffs(hpb->entries_per_srgn) - 1; + hpb->entries_per_srgn_mask = hpb->entries_per_srgn - 1; + + INFO_MSG("===== From Device Descriptor! ====="); + INFO_MSG("hpb_region_size = (%d), hpb_subregion_size = (%d)", + hpb_dev_info->rgn_size, hpb_dev_info->srgn_size); + INFO_MSG("===== Constant Values(LU) ====="); + INFO_MSG("region_unit_size = (%lld), region_mem_size (%lld)", + rgn_unit_size, rgn_mem_size); + INFO_MSG("subregion_unit_size = (%lld), subregion_mem_size (%d)", + hpb->srgn_unit_size, hpb->srgn_mem_size); + + INFO_MSG("lu_num_blocks = (%d)", hpb->lu_num_blocks); + INFO_MSG("regions_per_lu = (%d), subregions_per_lu = (%d)", + hpb->rgns_per_lu, hpb->srgns_per_lu); + + INFO_MSG("subregions_per_region = %d", hpb->srgns_per_rgn); + INFO_MSG("entries_per_region (%u) shift (%u) mask (0x%X)", + entries_per_rgn, hpb->entries_per_rgn_shift, + hpb->entries_per_rgn_mask); + INFO_MSG("entries_per_subregion (%u) shift (%u) mask (0x%X)", + hpb->entries_per_srgn, hpb->entries_per_srgn_shift, + hpb->entries_per_srgn_mask); + INFO_MSG("mpages_per_subregion : (%d)", hpb->mpages_per_srgn); + INFO_MSG("==================================="); +} + +static int ufshpb_lu_hpb_init(struct ufsf_feature *ufsf, int lun) +{ + struct ufshpb_lu *hpb = ufsf->hpb_lup[lun]; + struct ufshpb_region *rgn_table, *rgn; + struct ufshpb_subregion *srgn; + int rgn_idx, srgn_idx, total_srgn_cnt, srgn_cnt, i, ret = 0; + + ufshpb_init_lu_constant(&ufsf->hpb_dev_info, hpb); + + rgn_table = kzalloc(sizeof(struct ufshpb_region) * hpb->rgns_per_lu, + GFP_KERNEL); + if (!rgn_table) { + ret = -ENOMEM; + goto out; + } + + INFO_MSG("active_region_table bytes: (%lu)", + (sizeof(struct ufshpb_region) * hpb->rgns_per_lu)); + + hpb->rgn_tbl = rgn_table; + + spin_lock_init(&hpb->hpb_lock); + spin_lock_init(&hpb->retry_list_lock); + spin_lock_init(&hpb->rsp_list_lock); + + /* init lru information */ + INIT_LIST_HEAD(&hpb->lru_info.lh_lru_rgn); + hpb->lru_info.selection_type = LRU; + + INIT_LIST_HEAD(&hpb->lh_pinned_srgn); + INIT_LIST_HEAD(&hpb->lh_act_srgn); + INIT_LIST_HEAD(&hpb->lh_inact_rgn); + + INIT_LIST_HEAD(&hpb->lh_map_ctx_free); + + ufshpb_init_jobs(hpb); + + ret = ufshpb_map_req_mempool_init(hpb); + if (ret) { + ERR_MSG("map_req_mempool init fail!"); + goto release_rgn_table; + } + + ret = ufshpb_pre_req_mempool_init(hpb); + if (ret) { + ERR_MSG("pre_req_mempool init fail!"); + goto release_map_req_mempool; + } + + ret = ufshpb_table_mempool_init(hpb); + if (ret) { + ERR_MSG("ppn table mempool init fail!"); + ufshpb_table_mempool_remove(hpb); + goto release_pre_req_mempool; + } + + total_srgn_cnt = hpb->srgns_per_lu; + INFO_MSG("total_subregion_count: (%d)", total_srgn_cnt); + for (rgn_idx = 0, srgn_cnt = 0; rgn_idx < hpb->rgns_per_lu; + rgn_idx++, total_srgn_cnt -= srgn_cnt) { + rgn = rgn_table + rgn_idx; + rgn->rgn_idx = rgn_idx; + + INIT_LIST_HEAD(&rgn->list_inact_rgn); + + /* init lru region information*/ + INIT_LIST_HEAD(&rgn->list_lru_rgn); + + srgn_cnt = min(total_srgn_cnt, hpb->srgns_per_rgn); + + ret = ufshpb_alloc_subregion_tbl(hpb, rgn, srgn_cnt); + if (ret) + goto release_srgns; + ufshpb_init_subregion_tbl(hpb, rgn); + + if (ufshpb_is_pinned_region(hpb, rgn_idx)) { + ret = ufshpb_init_pinned_active_region(hpb, rgn); + if (ret) + goto release_srgns; + } else { + rgn->rgn_state = HPB_RGN_INACTIVE; + } + } + + if (total_srgn_cnt != 0) { + ERR_MSG("error total_subregion_count: %d", + total_srgn_cnt); + goto release_srgns; + } + + atomic64_set(&hpb->lru_info.active_cnt, 0); + /* + * even if creating sysfs failed, ufshpb could run normally. + * so we don't deal with error handling + */ + ufshpb_create_sysfs(ufsf, hpb); + + return 0; +release_srgns: + for (i = 0; i < rgn_idx; i++) { + rgn = rgn_table + i; + if (rgn->srgn_tbl) { + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; + srgn_idx++) { + srgn = rgn->srgn_tbl + srgn_idx; + if (srgn->mctx) + ufshpb_put_map_ctx(hpb, srgn->mctx); + } + kfree(rgn->srgn_tbl); + } + } + + ufshpb_table_mempool_remove(hpb); +release_pre_req_mempool: + ufshpb_pre_req_mempool_remove(hpb); +release_map_req_mempool: + ufshpb_map_req_mempool_remove(hpb); +release_rgn_table: + kfree(rgn_table); +out: + return ret; +} + +static inline int ufshpb_version_check(struct ufshpb_dev_info *hpb_dev_info, int oem_id) +{ + INFO_MSG("Support HPB Spec : Driver = (%.4X) Device = (%.4X) Vendor = (%.2x)", + UFSHPB_VER, hpb_dev_info->version,oem_id); + + INFO_MSG("HPB Driver Version : (%.6X%s)", + UFSHPB_DD_VER, UFSHPB_DD_VER_POST); + + if (hpb_dev_info->version != UFSHPB_VER && oem_id == 0x1ce) { + ERR_MSG("ERROR: HPB Spec Version mismatch. So HPB disabled."); + return -ENODEV; + } + if (hpb_dev_info->version != 0x200 && oem_id == 0x12c){ + ERR_MSG("ERROR: micron devices HPB Spec Version mismatch. So HPB disabled."); + return -ENODEV; + + } + return 0; +} + +void ufshpb_get_dev_info(struct ufsf_feature *ufsf, u8 *desc_buf) +{ + struct ufshpb_dev_info *hpb_dev_info = &ufsf->hpb_dev_info; + int ret; + int manufacturer = 0; + + if (desc_buf[DEVICE_DESC_PARAM_UFS_FEAT] & UFS_FEATURE_SUPPORT_HPB_BIT) + INFO_MSG("bUFSFeaturesSupport: HPB is set"); + else { + INFO_MSG("bUFSFeaturesSupport: HPB not support"); + ufshpb_set_state(ufsf, HPB_FAILED); + return; + } + + hpb_dev_info->version = LI_EN_16(desc_buf + DEVICE_DESC_PARAM_HPB_VER); + + manufacturer = LI_EN_16(desc_buf + DEVICE_DESC_PARAM_OEM_ID); + + ret = ufshpb_version_check(hpb_dev_info,manufacturer); + if (ret) + ufshpb_set_state(ufsf, HPB_FAILED); +} + +void ufshpb_get_geo_info(struct ufsf_feature *ufsf, u8 *geo_buf) +{ + struct ufshpb_dev_info *hpb_dev_info = &ufsf->hpb_dev_info; + int hpb_device_max_active_rgns = 0; + + hpb_dev_info->num_lu = geo_buf[GEOMETRY_DESC_HPB_NUMBER_LU]; + if (hpb_dev_info->num_lu == 0) { + ERR_MSG("Don't have a lu for hpb."); + ufshpb_set_state(ufsf, HPB_FAILED); + return; + } + + hpb_dev_info->rgn_size = geo_buf[GEOMETRY_DESC_HPB_REGION_SIZE]; + hpb_dev_info->srgn_size = geo_buf[GEOMETRY_DESC_HPB_SUBREGION_SIZE]; + hpb_device_max_active_rgns = + LI_EN_16(geo_buf + GEOMETRY_DESC_HPB_DEVICE_MAX_ACTIVE_REGIONS); + + INFO_MSG("[48] bHPBRegionSize (%u)", hpb_dev_info->rgn_size); + INFO_MSG("[49] bHPBNumberLU (%u)", hpb_dev_info->num_lu); + INFO_MSG("[4A] bHPBSubRegionSize (%u)", hpb_dev_info->srgn_size); + INFO_MSG("[4B:4C] wDeviceMaxActiveHPBRegions (%u)", + hpb_device_max_active_rgns); + + if (hpb_dev_info->rgn_size == 0 || hpb_dev_info->srgn_size == 0 || + hpb_device_max_active_rgns == 0) { + ERR_MSG("HPB NOT normally supported by device"); + ufshpb_set_state(ufsf, HPB_FAILED); + } +} + +void ufshpb_get_lu_info(struct ufsf_feature *ufsf, int lun, u8 *unit_buf) +{ + struct ufsf_lu_desc lu_desc; + struct ufshpb_lu *hpb; + + lu_desc.lu_enable = unit_buf[UNIT_DESC_PARAM_LU_ENABLE]; + lu_desc.lu_queue_depth = unit_buf[UNIT_DESC_PARAM_LU_Q_DEPTH]; + lu_desc.lu_logblk_size = unit_buf[UNIT_DESC_PARAM_LOGICAL_BLK_SIZE]; + lu_desc.lu_logblk_cnt = + LI_EN_64(unit_buf + UNIT_DESC_PARAM_LOGICAL_BLK_COUNT); + lu_desc.lu_max_active_hpb_rgns = + LI_EN_16(unit_buf + UNIT_DESC_HPB_LU_MAX_ACTIVE_REGIONS); + lu_desc.lu_hpb_pinned_rgn_startidx = + LI_EN_16(unit_buf + UNIT_DESC_HPB_LU_PIN_REGION_START_OFFSET); + lu_desc.lu_num_hpb_pinned_rgns = + LI_EN_16(unit_buf + UNIT_DESC_HPB_LU_NUM_PIN_REGIONS); + + if (lu_desc.lu_num_hpb_pinned_rgns > 0) { + lu_desc.lu_hpb_pinned_end_offset = + lu_desc.lu_hpb_pinned_rgn_startidx + + lu_desc.lu_num_hpb_pinned_rgns - 1; + } else + lu_desc.lu_hpb_pinned_end_offset = PINNED_NOT_SET; + + INFO_MSG("LUN(%d) [0A] bLogicalBlockSize (%d)", + lun, lu_desc.lu_logblk_size); + INFO_MSG("LUN(%d) [0B] qLogicalBlockCount (%llu)", + lun, lu_desc.lu_logblk_cnt); + INFO_MSG("LUN(%d) [03] bLuEnable (%d)", lun, lu_desc.lu_enable); + INFO_MSG("LUN(%d) [06] bLuQueueDepth (%d)", lun, + lu_desc.lu_queue_depth); + INFO_MSG("LUN(%d) [23:24] wLUMaxActiveHPBRegions (%d)", + lun, lu_desc.lu_max_active_hpb_rgns); + INFO_MSG("LUN(%d) [25:26] wHPBPinnedRegionStartIdx (%d)", + lun, lu_desc.lu_hpb_pinned_rgn_startidx); + INFO_MSG("LUN(%d) [27:28] wNumHPBPinnedRegions (%d)", + lun, lu_desc.lu_num_hpb_pinned_rgns); + INFO_MSG("LUN(%d) PINNED Start (%d) End (%d)", + lun, lu_desc.lu_hpb_pinned_rgn_startidx, + lu_desc.lu_hpb_pinned_end_offset); + + ufsf->hpb_lup[lun] = NULL; + + if (lu_desc.lu_enable == 0x02 && lu_desc.lu_max_active_hpb_rgns) { + ufsf->hpb_lup[lun] = kzalloc(sizeof(struct ufshpb_lu), + GFP_KERNEL); + if (!ufsf->hpb_lup[lun]) { + ERR_MSG("hpb_lup[%d] alloc failed", lun); + return; + } + + hpb = ufsf->hpb_lup[lun]; + hpb->ufsf = ufsf; + hpb->lun = lun; + hpb->lu_num_blocks = lu_desc.lu_logblk_cnt; + hpb->lu_max_active_rgns = lu_desc.lu_max_active_hpb_rgns; + hpb->lru_info.max_lru_active_cnt = + lu_desc.lu_max_active_hpb_rgns - + lu_desc.lu_num_hpb_pinned_rgns; + hpb->lu_pinned_rgn_startidx = + lu_desc.lu_hpb_pinned_rgn_startidx; + hpb->lu_pinned_end_offset = lu_desc.lu_hpb_pinned_end_offset; + } else { + INFO_MSG("===== LU (%d) is hpb-disabled.", lun); + } +} + +static void ufshpb_error_handler(struct work_struct *work) +{ + struct ufsf_feature *ufsf; + + ufsf = container_of(work, struct ufsf_feature, hpb_eh_work); + + WARN_MSG("driver has failed. but UFSHCD can run without UFSHPB"); + WARN_MSG("UFSHPB will be removed from the kernel"); + + ufshpb_remove(ufsf, HPB_FAILED); +} + +static int ufshpb_init(struct ufsf_feature *ufsf) +{ + int lun, ret; + struct ufshpb_lu *hpb; + int hpb_enabled_lun = 0; + + seq_scan_lu(lun) { + if (!ufsf->hpb_lup[lun]) + continue; + + /* + * HPB need info about request queue in order to issue + * RB-CMD for pinned region. + */ + if (!ufsf->sdev_ufs_lu[lun]) { + WARN_MSG("lun (%d) don't have scsi_device", lun); + continue; + } + + ret = ufshpb_lu_hpb_init(ufsf, lun); + if (ret) { + kfree(ufsf->hpb_lup[lun]); + continue; + } + hpb_enabled_lun++; + } + + if (hpb_enabled_lun == 0) { + ERR_MSG("No UFSHPB LU to init"); + ret = -ENODEV; + goto hpb_failed; + } + + INIT_WORK(&ufsf->hpb_eh_work, ufshpb_error_handler); + + kref_init(&ufsf->hpb_kref); + ufshpb_set_state(ufsf, HPB_PRESENT); + + seq_scan_lu(lun) + if (ufsf->hpb_lup[lun]) { + hpb = ufsf->hpb_lup[lun]; + + INFO_MSG("UFSHPB LU %d working", lun); + if (hpb->lu_pinned_end_offset != PINNED_NOT_SET) + schedule_work(&hpb->pinned_work); + } + + return 0; +hpb_failed: + ufshpb_set_state(ufsf, HPB_FAILED); + return ret; +} + +static void ufshpb_drop_retry_list(struct ufshpb_lu *hpb) +{ + struct ufshpb_req *map_req, *next; + struct request *req; + unsigned long flags; + LIST_HEAD(lh_reqs); + + if (list_empty(&hpb->lh_map_req_retry)) + return; + + spin_lock_irqsave(&hpb->hpb_lock, flags); + list_for_each_entry_safe(map_req, next, &hpb->lh_map_req_retry, + list_req) { + INFO_MSG("drop map_req %p ( %d - %d )", map_req, + map_req->rb.rgn_idx, map_req->rb.srgn_idx); + + list_del_init(&map_req->list_req); + + /* + * blk_put_request() will grap the queue_lock. + * so requests add to a list. + * lock seq : queue_lock -> hpb_lock. + */ + list_add_tail(&map_req->req->queuelist, &lh_reqs); + + bio_put(map_req->bio); + + ufshpb_put_map_req(hpb, map_req); + } + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + list_for_each_entry(req, &lh_reqs, queuelist) + blk_put_request(req); +} + +static void ufshpb_drop_rsp_lists(struct ufshpb_lu *hpb) +{ + struct ufshpb_region *rgn, *next_rgn; + struct ufshpb_subregion *srgn, *next_srgn; + unsigned long flags; + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + list_for_each_entry_safe(rgn, next_rgn, &hpb->lh_inact_rgn, + list_inact_rgn) { + list_del_init(&rgn->list_inact_rgn); + } + + list_for_each_entry_safe(srgn, next_srgn, &hpb->lh_act_srgn, + list_act_srgn) { + list_del_init(&srgn->list_act_srgn); + } + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); +} + +static void ufshpb_destroy_subregion_tbl(struct ufshpb_lu *hpb, + struct ufshpb_region *rgn) +{ + int srgn_idx; + + for (srgn_idx = 0; srgn_idx < rgn->srgn_cnt; srgn_idx++) { + struct ufshpb_subregion *srgn; + + srgn = rgn->srgn_tbl + srgn_idx; + srgn->srgn_state = HPB_SRGN_UNUSED; + + ufshpb_put_map_ctx(hpb, srgn->mctx); + } +} + +static void ufshpb_destroy_region_tbl(struct ufshpb_lu *hpb) +{ + int rgn_idx; + + INFO_MSG("Start"); + + for (rgn_idx = 0; rgn_idx < hpb->rgns_per_lu; rgn_idx++) { + struct ufshpb_region *rgn; + + rgn = hpb->rgn_tbl + rgn_idx; + if (rgn->rgn_state != HPB_RGN_INACTIVE) { + rgn->rgn_state = HPB_RGN_INACTIVE; + + ufshpb_destroy_subregion_tbl(hpb, rgn); + } + + kfree(rgn->srgn_tbl); + } + + ufshpb_table_mempool_remove(hpb); + kfree(hpb->rgn_tbl); + + INFO_MSG("End"); +} + +void ufshpb_remove(struct ufsf_feature *ufsf, int state) +{ + struct ufshpb_lu *hpb; + int lun; + + INFO_MSG("start release"); + ufshpb_set_state(ufsf, HPB_FAILED); + + INFO_MSG("kref count (%d)", + atomic_read(&ufsf->hpb_kref.refcount.refs)); + + seq_scan_lu(lun) { + hpb = ufsf->hpb_lup[lun]; + + INFO_MSG("lun (%d) (%p)", lun, hpb); + + ufsf->hpb_lup[lun] = NULL; + + if (!hpb) + continue; + + ufshpb_cancel_jobs(hpb); + + ufshpb_destroy_region_tbl(hpb); + if (hpb->alloc_mctx != 0) + WARN_MSG("warning: alloc_mctx (%d)", hpb->alloc_mctx); + + ufshpb_map_req_mempool_remove(hpb); + + ufshpb_pre_req_mempool_remove(hpb); + + ufshpb_remove_sysfs(hpb); + + kfree(hpb); + } + + ufshpb_set_state(ufsf, state); + + INFO_MSG("end release"); +} + +void ufshpb_reset(struct ufsf_feature *ufsf) +{ + ufshpb_set_state(ufsf, HPB_PRESENT); +} + +void ufshpb_reset_host(struct ufsf_feature *ufsf) +{ + struct ufshpb_lu *hpb; + int lun; + + ufshpb_set_state(ufsf, HPB_RESET); + seq_scan_lu(lun) { + hpb = ufsf->hpb_lup[lun]; + if (hpb) { + INFO_MSG("UFSHPB lun %d reset", lun); + ufshpb_cancel_jobs(hpb); + ufshpb_drop_retry_list(hpb); + ufshpb_drop_rsp_lists(hpb); + } + } +} + +static inline int ufshpb_probe_lun_done(struct ufsf_feature *ufsf) +{ + return (ufsf->num_lu == ufsf->slave_conf_cnt); +} + +void ufshpb_init_handler(struct work_struct *work) +{ + struct ufsf_feature *ufsf; + int ret = 0; + + ufsf = container_of(work, struct ufsf_feature, hpb_init_work); + + INFO_MSG("probe_done(%d)", ufshpb_probe_lun_done(ufsf)); + + ret = wait_event_timeout(ufsf->hpb_wait, + ufshpb_probe_lun_done(ufsf), + msecs_to_jiffies(10000)); + if (ret == 0) + ERR_MSG("Probing LU is not fully complete."); + + INFO_MSG("probe_done(%d) ret(%d)", ufshpb_probe_lun_done(ufsf), ret); + INFO_MSG("HPB_INIT_START"); + + ret = ufshpb_init(ufsf); + if (ret) + ERR_MSG("UFSHPB driver init failed. (%d)", ret); +} + +void ufshpb_suspend(struct ufsf_feature *ufsf) +{ + struct ufshpb_lu *hpb; + int lun; + + seq_scan_lu(lun) { + hpb = ufsf->hpb_lup[lun]; + if (hpb) { + INFO_MSG("ufshpb_lu %d goto suspend", lun); + INFO_MSG("ufshpb_lu %d changes suspend state", lun); + ufshpb_set_state(ufsf, HPB_SUSPEND); + ufshpb_cancel_jobs(hpb); + } + } +} + +void ufshpb_resume(struct ufsf_feature *ufsf) +{ + struct ufshpb_lu *hpb; + int lun; + + seq_scan_lu(lun) { + hpb = ufsf->hpb_lup[lun]; + if (hpb) { + bool do_task_work = false; + bool do_retry_work = false; + + ufshpb_set_state(ufsf, HPB_PRESENT); + + do_task_work = !ufshpb_is_empty_rsp_lists(hpb); + do_retry_work = + !list_empty_careful(&hpb->lh_map_req_retry); + + INFO_MSG("ufshpb_lu %d resume. do_task_work %d retry %d", + lun, do_task_work, do_retry_work); + + if (do_task_work) + schedule_work(&hpb->task_work); + if (do_retry_work) + schedule_delayed_work(&hpb->retry_work, + msecs_to_jiffies(100)); + } + } +} + +static void ufshpb_stat_reset(struct ufshpb_lu *hpb) +{ + atomic64_set(&hpb->hit, 0); + atomic64_set(&hpb->miss, 0); + atomic64_set(&hpb->rb_noti_cnt, 0); + atomic64_set(&hpb->rb_active_cnt, 0); + atomic64_set(&hpb->rb_inactive_cnt, 0); + atomic64_set(&hpb->map_req_cnt, 0); + atomic64_set(&hpb->map_compl_cnt, 0); + atomic64_set(&hpb->pre_req_cnt, 0); +} + +/* SYSFS functions */ +static ssize_t ufshpb_sysfs_prep_disable_show(struct ufshpb_lu *hpb, char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "force_hpb_read_disable %d\n", + hpb->force_disable); + + INFO_MSG("force_hpb_read_disable %d", hpb->force_disable); + return ret; +} + +static ssize_t ufshpb_sysfs_prep_disable_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (value > 1) + return -EINVAL; + + if (value == 1) + hpb->force_disable = true; + else if (value == 0) + hpb->force_disable = false; + + INFO_MSG("force_hpb_read_disable %d", hpb->force_disable); + + return cnt; +} + +static ssize_t ufshpb_sysfs_map_disable_show(struct ufshpb_lu *hpb, char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "force_map_req_disable %d\n", + hpb->force_map_req_disable); + + INFO_MSG("force_map_req_disable %d", hpb->force_map_req_disable); + + return ret; +} + +static ssize_t ufshpb_sysfs_map_disable_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (value > 1) + return -EINVAL; + + if (value == 1) + hpb->force_map_req_disable = true; + else if (value == 0) + hpb->force_map_req_disable = false; + + INFO_MSG("force_map_req_disable %d", hpb->force_map_req_disable); + + return cnt; +} + +static ssize_t ufshpb_sysfs_throttle_map_req_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + +#if defined(CONFIG_HPB_DEBUG) + ret = snprintf(buf, PAGE_SIZE, "throttle_map_req %d (inflight: %d)\n", + hpb->throttle_map_req, hpb->num_inflight_map_req); +#else + ret = snprintf(buf, PAGE_SIZE, "throttle_map_req %d\n", + hpb->throttle_map_req); +#endif + + INFO_MSG("throttle_map_req %d", hpb->throttle_map_req); + + return ret; +} + +static ssize_t ufshpb_sysfs_throttle_map_req_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long throttle_map_req; + + if (kstrtoul(buf, 0, &throttle_map_req)) + return -EINVAL; + + if (throttle_map_req > hpb->qd) + return -EINVAL; + + if ((int)throttle_map_req < 0 || (int)throttle_map_req > hpb->qd) + return -EINVAL; + + hpb->throttle_map_req = (int)throttle_map_req; + + INFO_MSG("throttle_map_req %d", hpb->throttle_map_req); + + return cnt; +} + +static ssize_t ufshpb_sysfs_throttle_pre_req_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + +#if defined(CONFIG_HPB_DEBUG) + ret = snprintf(buf, PAGE_SIZE, "throttle_pre_req %d (inflight: %d)\n", + hpb->throttle_pre_req, hpb->num_inflight_pre_req); +#else + ret = snprintf(buf, PAGE_SIZE, "throttle_pre_req %d\n", + hpb->throttle_pre_req); +#endif + + INFO_MSG("throttle_pre_req %d", hpb->throttle_pre_req); + + return ret; +} + +static ssize_t ufshpb_sysfs_throttle_pre_req_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long throttle_pre_req; + + if (kstrtoul(buf, 0, &throttle_pre_req)) + return -EINVAL; + + if (throttle_pre_req > hpb->qd) + return -EINVAL; + + if ((int)throttle_pre_req < 0 || (int)throttle_pre_req > hpb->qd) + return -EINVAL; + + hpb->throttle_pre_req = (int)throttle_pre_req; + + INFO_MSG("throttle_pre_req %d", hpb->throttle_pre_req); + + return cnt; +} + +static ssize_t ufshpb_sysfs_pre_req_min_tr_len_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "%d", hpb->pre_req_min_tr_len); + INFO_MSG("pre_req min transfer len %d", hpb->pre_req_min_tr_len); + + return ret; +} + +static ssize_t ufshpb_sysfs_pre_req_min_tr_len_store(struct ufshpb_lu *hpb, + const char *buf, + size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0) + val = 0; + + if (hpb->pre_req_max_tr_len < val || val < HPB_MULTI_CHUNK_LOW) + INFO_MSG("value is wrong. pre_req transfer len %d ~ %d", + HPB_MULTI_CHUNK_LOW, hpb->pre_req_max_tr_len); + else + hpb->pre_req_min_tr_len = val; + + INFO_MSG("pre_req min transfer len %d", hpb->pre_req_min_tr_len); + + return count; +} + +static ssize_t ufshpb_sysfs_pre_req_max_tr_len_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "%d", hpb->pre_req_max_tr_len); + INFO_MSG("pre_req max transfer len %d", hpb->pre_req_max_tr_len); + + return ret; +} + +static ssize_t ufshpb_sysfs_pre_req_max_tr_len_store(struct ufshpb_lu *hpb, + const char *buf, + size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (hpb->pre_req_min_tr_len > val || val > HPB_MULTI_CHUNK_HIGH) { + INFO_MSG("value is wrong. pre_req transfer len %d ~ %d", + hpb->pre_req_min_tr_len, HPB_MULTI_CHUNK_HIGH); + return -EINVAL; + } + + hpb->pre_req_max_tr_len = val; + + INFO_MSG("pre_req max transfer len %d", hpb->pre_req_max_tr_len); + + return count; +} + +static ssize_t ufshpb_sysfs_pre_req_requeue_cnt_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "%d", hpb->pre_req_requeue_cnt); + INFO_MSG("pre_req requeue cnt %d", hpb->pre_req_requeue_cnt); + + return ret; +} + +static ssize_t ufshpb_sysfs_pre_req_requeue_cnt_store(struct ufshpb_lu *hpb, + const char *buf, + size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0) + val = 0; + + hpb->pre_req_requeue_cnt = val; + + INFO_MSG("pre_req requeue cnt %d", hpb->pre_req_requeue_cnt); + + return count; +} + +#if defined(CONFIG_HPB_DEBUG) +static ssize_t ufshpb_sysfs_debug_show(struct ufshpb_lu *hpb, char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "debug %d\n", hpb->debug); + + INFO_MSG("debug %d", hpb->debug); + + return ret; +} + +static ssize_t ufshpb_sysfs_debug_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long debug; + + if (kstrtoul(buf, 0, &debug)) + return -EINVAL; + + if (debug > 1) + return -EINVAL; + + if (debug == 1) + hpb->debug = 1; + else + hpb->debug = 0; + + INFO_MSG("debug %d", hpb->debug); + + return cnt; +} +#endif + +static ssize_t ufshpb_sysfs_rsp_list_cnt_show(struct ufshpb_lu *hpb, char *buf) +{ + long long act_cnt, inact_cnt; + int ret; + + act_cnt = atomic64_read(&hpb->act_rsp_list_cnt); + inact_cnt = atomic64_read(&hpb->inact_rsp_list_cnt); + + ret = snprintf(buf, PAGE_SIZE, + "act_rsp_list_cnt %lld inact_rsp_list_cnt %lld\n", + act_cnt, inact_cnt); + + INFO_MSG("%s", buf); + + return ret; +} + +#if defined(CONFIG_HPB_ERR_INJECTION) +static ssize_t ufshpb_sysfs_err_injection_select_show(struct ufshpb_lu *hpb, + char *buf) +{ + char error_injection[8]; + int ret; + + if (hpb->err_injection_select == HPB_ERR_INJECTION_DISABLE) + strlcpy(error_injection, "disable", 8); + else if (hpb->err_injection_select == HPB_ERR_INJECTION_BITFLIP) + strlcpy(error_injection, "bitflip", 8); + else if (hpb->err_injection_select == HPB_ERR_INJECTION_OFFSET) + strlcpy(error_injection, "offset", 7); + else if (hpb->err_injection_select == HPB_ERR_INJECTION_RANDOM) + strlcpy(error_injection, "random", 7); + + ret = snprintf(buf, PAGE_SIZE, "err_injection_select %s", + error_injection); + + INFO_MSG("err_injection_select %s", error_injection); + + return ret; +} + +static ssize_t ufshpb_sysfs_err_injection_select_store(struct ufshpb_lu *hpb, + const char *buf, + size_t cnt) +{ + char error_injection[8]; + int ret; + + error_injection[7] = '\0'; + + ret = sscanf(buf, "%7s", error_injection); + if (!ret) { + INFO_MSG("input failed..."); + return cnt; + } + + if (!strncmp(error_injection, "disable", 7)) + hpb->err_injection_select = HPB_ERR_INJECTION_DISABLE; + else if (!strncmp(error_injection, "bitflip", 7)) + hpb->err_injection_select = HPB_ERR_INJECTION_BITFLIP; + else if (!strncmp(error_injection, "offset", 6)) + hpb->err_injection_select = HPB_ERR_INJECTION_OFFSET; + else if (!strncmp(error_injection, "random", 6)) + hpb->err_injection_select = HPB_ERR_INJECTION_RANDOM; + else { + hpb->err_injection_select = HPB_ERR_INJECTION_DISABLE; + strlcpy(error_injection, "disable", 8); + } + + INFO_MSG("err_injection_select %s", error_injection); + + return cnt; +} + +static ssize_t ufshpb_sysfs_err_injection_bitflip_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "err_injection_bitflip %llx", + hpb->err_injection_bitflip); + + return ret; +} + +static ssize_t ufshpb_sysfs_err_injection_bitflip_store(struct ufshpb_lu *hpb, + const char *buf, + size_t cnt) +{ + unsigned long long err_injection_bitflip; + + if (kstrtoull(buf, 0, &err_injection_bitflip)) + return -EINVAL; + + hpb->err_injection_bitflip = err_injection_bitflip; + + INFO_MSG("err_injection_bitflip %llx", hpb->err_injection_bitflip); + + return cnt; +} + +static ssize_t ufshpb_sysfs_err_injection_offset_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "err_injection_offset %lld\n", + hpb->err_injection_offset); + + INFO_MSG("err_injection_offset %lld", hpb->err_injection_offset); + + return ret; +} + +static ssize_t ufshpb_sysfs_err_injection_offset_store(struct ufshpb_lu *hpb, + const char *buf, + size_t cnt) +{ + unsigned long long err_injection_offset; + + if (kstrtoull(buf, 0, &err_injection_offset)) + return -EINVAL; + + hpb->err_injection_offset = err_injection_offset; + + INFO_MSG("err_injection_offset %lld", hpb->err_injection_offset); + + return cnt; +} + +static ssize_t ufshpb_sysfs_err_injection_random_show(struct ufshpb_lu *hpb, + char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "err_injection_random %d\n", + hpb->err_injection_random); + + INFO_MSG("err_injection_random %d", hpb->err_injection_random); + + return ret; +} + +static ssize_t ufshpb_sysfs_err_injection_random_store(struct ufshpb_lu *hpb, + const char *buf, + size_t cnt) +{ + unsigned long err_injection_random; + + if (kstrtoul(buf, 0, &err_injection_random)) + return -EINVAL; + + if (err_injection_random > 1) + return -EINVAL; + + hpb->err_injection_random = err_injection_random; + + INFO_MSG("err_injection_random %d", hpb->err_injection_random); + + return cnt; +} +#endif + +static ssize_t ufshpb_sysfs_version_show(struct ufshpb_lu *hpb, char *buf) +{ + int ret; + + ret = snprintf(buf, PAGE_SIZE, "HPB version %.4X D/D version %.6X%s\n", + hpb->ufsf->hpb_dev_info.version, + UFSHPB_DD_VER, UFSHPB_DD_VER_POST); + + INFO_MSG("HPB version %.4X D/D version %.6X%s", + hpb->ufsf->hpb_dev_info.version, + UFSHPB_DD_VER, UFSHPB_DD_VER_POST); + + return ret; +} + +static ssize_t ufshpb_sysfs_hit_show(struct ufshpb_lu *hpb, char *buf) +{ + long long hit_cnt; + int ret; + + hit_cnt = atomic64_read(&hpb->hit); + + ret = snprintf(buf, PAGE_SIZE, "hit_count %lld\n", hit_cnt); + + INFO_MSG("hit_count %lld", hit_cnt); + + return ret; +} + +static ssize_t ufshpb_sysfs_miss_show(struct ufshpb_lu *hpb, char *buf) +{ + long long miss_cnt; + int ret; + + miss_cnt = atomic64_read(&hpb->miss); + + ret = snprintf(buf, PAGE_SIZE, "miss_count %lld\n", miss_cnt); + + INFO_MSG("miss_count %lld", miss_cnt); + + return ret; +} + +static ssize_t ufshpb_sysfs_map_req_show(struct ufshpb_lu *hpb, char *buf) +{ + long long rb_noti_cnt, rb_active_cnt, rb_inactive_cnt; + long long map_req_cnt, map_compl_cnt; + int ret; + + rb_noti_cnt = atomic64_read(&hpb->rb_noti_cnt); + rb_active_cnt = atomic64_read(&hpb->rb_active_cnt); + rb_inactive_cnt = atomic64_read(&hpb->rb_inactive_cnt); + map_req_cnt = atomic64_read(&hpb->map_req_cnt); + map_compl_cnt = atomic64_read(&hpb->map_compl_cnt); + + ret = snprintf(buf, PAGE_SIZE, + "rb_noti %lld ACT %lld INACT %lld map_req_count %lld map_compl_count %lld\n", + rb_noti_cnt, rb_active_cnt, rb_inactive_cnt, + map_req_cnt, map_compl_cnt); + + INFO_MSG("rb_noti %lld ACT %lld INACT %lld map_req_count %lld map_compl_count %lld", + rb_noti_cnt, rb_active_cnt, rb_inactive_cnt, + map_req_cnt, map_compl_cnt); + + return ret; +} + +static ssize_t ufshpb_sysfs_pre_req_show(struct ufshpb_lu *hpb, char *buf) +{ + long long pre_req_cnt; + int ret; + + pre_req_cnt = atomic64_read(&hpb->pre_req_cnt); + + ret = snprintf(buf, PAGE_SIZE, "pre_req_count %lld\n", pre_req_cnt); + + INFO_MSG("pre_req_count %lld", pre_req_cnt); + + return ret; +} + +static ssize_t ufshpb_sysfs_region_stat_show(struct ufshpb_lu *hpb, char *buf) +{ + int ret, pin_cnt = 0, act_cnt = 0, inact_cnt = 0, rgn_idx; + enum HPB_RGN_STATE state; + + for (rgn_idx = 0; rgn_idx < hpb->rgns_per_lu; rgn_idx++) { + state = hpb->rgn_tbl[rgn_idx].rgn_state; + if (state == HPB_RGN_PINNED) + pin_cnt++; + else if (state == HPB_RGN_ACTIVE) + act_cnt++; + else if (state == HPB_RGN_INACTIVE) + inact_cnt++; + } + + ret = snprintf(buf, PAGE_SIZE, + "Total %d pinned %d active %d inactive %d\n", + hpb->rgns_per_lu, pin_cnt, act_cnt, inact_cnt); + + INFO_MSG("Total %d pinned %d active %d inactive %d", + hpb->rgns_per_lu, pin_cnt, act_cnt, inact_cnt); + + return ret; +} + +static ssize_t ufshpb_sysfs_count_reset_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long debug; + + if (kstrtoul(buf, 0, &debug)) + return -EINVAL; + + if (debug != 1) + return -EINVAL; + + INFO_MSG("Stat Init"); + + ufshpb_stat_reset(hpb); + + return cnt; +} + +static ssize_t ufshpb_sysfs_info_lba_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + struct ufshpb_region *rgn; + struct ufshpb_subregion *srgn; + u64 ppn = 0; + unsigned long value, lpn, flags; + int rgn_idx, srgn_idx, srgn_offset, error = 0; + + if (kstrtoul(buf, 0, &value)) { + ERR_MSG("kstrtoul error"); + return -EINVAL; + } + + if (value >= hpb->lu_num_blocks * SECTORS_PER_BLOCK) { + ERR_MSG("value %lu >= lu_num_blocks %d error", + value, hpb->lu_num_blocks * SECTORS_PER_BLOCK); + return -EINVAL; + } + + lpn = value / SECTORS_PER_BLOCK; + + ufshpb_get_pos_from_lpn(hpb, lpn, &rgn_idx, &srgn_idx, &srgn_offset); + + rgn = hpb->rgn_tbl + rgn_idx; + srgn = rgn->srgn_tbl + srgn_idx; + + spin_lock_irqsave(&hpb->hpb_lock, flags); + INFO_MSG("lba %lu lpn %lu region %d state %d subregion %d state %d", + value, lpn, rgn_idx, rgn->rgn_state, srgn_idx, + srgn->srgn_state); + + if (!ufshpb_valid_srgn(rgn, srgn)) { + INFO_MSG("[region %d subregion %d] has not valid hpb info.", + rgn_idx, srgn_idx); + goto out; + } + + if (!srgn->mctx) { + INFO_MSG("mctx is NULL"); + goto out; + } + + ppn = ufshpb_get_ppn(srgn->mctx, srgn_offset, &error); + if (error) { + INFO_MSG("getting ppn is fail from a page."); + goto out; + } + + INFO_MSG("ppn %llx is_dirty %d", ppn, + ufshpb_ppn_dirty_check(hpb, lpn, 1)); +out: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return cnt; +} + +static ssize_t ufshpb_sysfs_info_region_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long rgn_idx; + int srgn_idx; + + if (kstrtoul(buf, 0, &rgn_idx)) + return -EINVAL; + + if (rgn_idx >= hpb->rgns_per_lu) { + ERR_MSG("error region %ld max %d", rgn_idx, hpb->rgns_per_lu); + return -EINVAL; + } + + INFO_MSG("(region state : PINNED=%d ACTIVE=%d INACTIVE=%d)", + HPB_RGN_PINNED, HPB_RGN_ACTIVE, HPB_RGN_INACTIVE); + + INFO_MSG("region %ld state %d", rgn_idx, + hpb->rgn_tbl[rgn_idx].rgn_state); + + for (srgn_idx = 0; srgn_idx < hpb->rgn_tbl[rgn_idx].srgn_cnt; + srgn_idx++) { + INFO_MSG("--- subregion %d state %d", srgn_idx, + hpb->rgn_tbl[rgn_idx].srgn_tbl[srgn_idx].srgn_state); + } + + return cnt; +} + +static ssize_t ufshpb_sysfs_ufshpb_release_store(struct ufshpb_lu *hpb, + const char *buf, size_t cnt) +{ + unsigned long value; + + INFO_MSG("start release function"); + + if (kstrtoul(buf, 0, &value)) { + ERR_MSG("kstrtoul error"); + return -EINVAL; + } + + if (value == 0xab) { + INFO_MSG("magic number %lu release start", value); + goto err_out; + } else { + INFO_MSG("wrong magic number %lu", value); + return -EINVAL; + } + + return cnt; +err_out: + INFO_MSG("ref_cnt %d", + atomic_read(&hpb->ufsf->hpb_kref.refcount.refs)); + ufshpb_failed(hpb, __func__); + + return cnt; +} + +static struct ufshpb_sysfs_entry ufshpb_sysfs_entries[] = { + __ATTR(hpb_read_disable, 0644, + ufshpb_sysfs_prep_disable_show, ufshpb_sysfs_prep_disable_store), + __ATTR(map_cmd_disable, 0644, + ufshpb_sysfs_map_disable_show, ufshpb_sysfs_map_disable_store), + __ATTR(throttle_map_req, 0644, + ufshpb_sysfs_throttle_map_req_show, + ufshpb_sysfs_throttle_map_req_store), + __ATTR(throttle_pre_req, 0644, + ufshpb_sysfs_throttle_pre_req_show, + ufshpb_sysfs_throttle_pre_req_store), + __ATTR(pre_req_min_tr_len, 0644, + ufshpb_sysfs_pre_req_min_tr_len_show, + ufshpb_sysfs_pre_req_min_tr_len_store), + __ATTR(pre_req_max_tr_len, 0644, + ufshpb_sysfs_pre_req_max_tr_len_show, + ufshpb_sysfs_pre_req_max_tr_len_store), + __ATTR(pre_req_requeue_cnt, 0644, + ufshpb_sysfs_pre_req_requeue_cnt_show, + ufshpb_sysfs_pre_req_requeue_cnt_store), +#if defined(CONFIG_HPB_DEBUG) + __ATTR(debug, 0644, + ufshpb_sysfs_debug_show, ufshpb_sysfs_debug_store), +#endif + __ATTR(rsp_list_count, 0444, ufshpb_sysfs_rsp_list_cnt_show, NULL), +#if defined(CONFIG_HPB_ERR_INJECTION) + __ATTR(err_injection_select, 0644, + ufshpb_sysfs_err_injection_select_show, + ufshpb_sysfs_err_injection_select_store), + __ATTR(err_injection_bitflip, 0644, + ufshpb_sysfs_err_injection_bitflip_show, + ufshpb_sysfs_err_injection_bitflip_store), + __ATTR(err_injection_offset, 0644, + ufshpb_sysfs_err_injection_offset_show, + ufshpb_sysfs_err_injection_offset_store), + __ATTR(err_injection_random, 0644, + ufshpb_sysfs_err_injection_random_show, + ufshpb_sysfs_err_injection_random_store), +#endif + __ATTR(hpb_version, 0444, ufshpb_sysfs_version_show, NULL), + __ATTR(hit_count, 0444, ufshpb_sysfs_hit_show, NULL), + __ATTR(miss_count, 0444, ufshpb_sysfs_miss_show, NULL), + __ATTR(map_req_count, 0444, ufshpb_sysfs_map_req_show, NULL), + __ATTR(pre_req_count, 0444, ufshpb_sysfs_pre_req_show, NULL), + __ATTR(region_stat_count, 0444, ufshpb_sysfs_region_stat_show, NULL), + __ATTR(count_reset, 0200, NULL, ufshpb_sysfs_count_reset_store), + __ATTR(get_info_from_lba, 0200, NULL, ufshpb_sysfs_info_lba_store), + __ATTR(get_info_from_region, 0200, NULL, + ufshpb_sysfs_info_region_store), + __ATTR(release, 0200, NULL, ufshpb_sysfs_ufshpb_release_store), + __ATTR_NULL +}; + +static ssize_t ufshpb_attr_show(struct kobject *kobj, struct attribute *attr, + char *page) +{ + struct ufshpb_sysfs_entry *entry; + struct ufshpb_lu *hpb; + ssize_t error; + + entry = container_of(attr, struct ufshpb_sysfs_entry, attr); + hpb = container_of(kobj, struct ufshpb_lu, kobj); + + if (!entry->show) + return -EIO; + + mutex_lock(&hpb->sysfs_lock); + error = entry->show(hpb, page); + mutex_unlock(&hpb->sysfs_lock); + return error; +} + +static ssize_t ufshpb_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t len) +{ + struct ufshpb_sysfs_entry *entry; + struct ufshpb_lu *hpb; + ssize_t error; + + entry = container_of(attr, struct ufshpb_sysfs_entry, attr); + hpb = container_of(kobj, struct ufshpb_lu, kobj); + + if (!entry->store) + return -EIO; + + mutex_lock(&hpb->sysfs_lock); + error = entry->store(hpb, page, len); + mutex_unlock(&hpb->sysfs_lock); + return error; +} + +static const struct sysfs_ops ufshpb_sysfs_ops = { + .show = ufshpb_attr_show, + .store = ufshpb_attr_store, +}; + +static struct kobj_type ufshpb_ktype = { + .sysfs_ops = &ufshpb_sysfs_ops, + .release = NULL, +}; + +static int ufshpb_create_sysfs(struct ufsf_feature *ufsf, struct ufshpb_lu *hpb) +{ + struct device *dev = ufsf->hba->dev; + struct ufshpb_sysfs_entry *entry; + int err; + + hpb->sysfs_entries = ufshpb_sysfs_entries; + + ufshpb_stat_reset(hpb); + atomic64_set(&hpb->act_rsp_list_cnt, 0); + atomic64_set(&hpb->inact_rsp_list_cnt, 0); + + kobject_init(&hpb->kobj, &ufshpb_ktype); + mutex_init(&hpb->sysfs_lock); + + INFO_MSG("ufshpb creates sysfs lu %d %p dev->kobj %p", hpb->lun, + &hpb->kobj, &dev->kobj); + + err = kobject_add(&hpb->kobj, kobject_get(&dev->kobj), + "ufshpb_lu%d", hpb->lun); + if (!err) { + for (entry = hpb->sysfs_entries; entry->attr.name != NULL; + entry++) { + INFO_MSG("ufshpb_lu%d sysfs attr creates: %s", + hpb->lun, entry->attr.name); + if (sysfs_create_file(&hpb->kobj, &entry->attr)) + break; + } + INFO_MSG("ufshpb_lu%d sysfs adds uevent", hpb->lun); + kobject_uevent(&hpb->kobj, KOBJ_ADD); + } + + return err; +} + +static inline void ufshpb_remove_sysfs(struct ufshpb_lu *hpb) +{ + kobject_uevent(&hpb->kobj, KOBJ_REMOVE); + INFO_MSG("ufshpb removes sysfs lu %d %p ", hpb->lun, &hpb->kobj); + kobject_del(&hpb->kobj); +} diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h new file mode 100755 index 000000000000..47271fa6ba33 --- /dev/null +++ b/drivers/scsi/ufs/ufshpb.h @@ -0,0 +1,346 @@ +/* + * Universal Flash Storage Host Performance Booster + * + * Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jinyoung Choi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#ifndef _UFSHPB_H_ +#define _UFSHPB_H_ + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HPB_ERR_INJECTION) +#include +#endif + +#include "../../../block/blk.h" +#include "../../../block/blk-mq.h" +#include "../../../block/blk-mq-sched.h" +#include "../scsi_priv.h" + +/* Version info*/ +#define UFSHPB_VER 0x0201 +#define UFSHPB_DD_VER 0x020800 +#define UFSHPB_DD_VER_POST UFSFEATURE_DD_VER_POST + +/* Constant value*/ +#define MAX_ACTIVE_NUM 2 +#define MAX_INACTIVE_NUM 2 + +#define HPB_ENTRY_SIZE 0x08 +#define HPB_ENTREIS_PER_OS_PAGE (OS_PAGE_SIZE / HPB_ENTRY_SIZE) + +#define RETRY_DELAY_MS 5000 + +/* HPB Support Chunk Size */ +#define HPB_MULTI_CHUNK_LOW 9 +#define HPB_MULTI_CHUNK_HIGH 128 +#define MAX_HPB_CONTEXT_ID 0x7f + +/* Description */ +#define UFS_FEATURE_SUPPORT_HPB_BIT 0x80 + +/* Response UPIU types */ +#define HPB_RSP_NONE 0x00 +#define HPB_RSP_REQ_REGION_UPDATE 0x01 + +/* Vender defined OPCODE */ +#define UFSHPB_READ_BUFFER 0xF9 +#define UFSHPB_WRITE_BUFFER 0xFA + +#define UFSHPB_GROUP_NUMBER 0x11 +#define UFSHPB_RB_ID_READ 0x01 +#define UFSHPB_WB_ID_PREFETCH 0x02 +#define TRANSFER_LEN 0x01 + +#define DEV_DATA_SEG_LEN 0x14 +#define DEV_SENSE_SEG_LEN 0x12 +#define DEV_DES_TYPE 0x80 +#define DEV_ADDITIONAL_LEN 0x10 + +#define PINNED_NOT_SET (-1) +#define DEFAULT_WB_REQUEUE_CNT 3 + +/* + * UFSHPB DEBUG + */ + +#if defined(CONFIG_HPB_DEBUG) +#define HPB_DEBUG(hpb, msg, args...) \ + do { if (hpb && hpb->debug) \ + printk(KERN_ERR "%s:%d " msg "\n", \ + __func__, __LINE__, ##args); \ + } while (0) + +#define HPB_FTRACE(hpb, msg, args...) \ + do { if (hpb && hpb->debug) \ + trace_printk("%04d\t " msg "\n", \ + __LINE__, ##args); \ + } while (0) +#else +#define HPB_DEBUG(hpb, msg, args...) do { } while (0) +#define HPB_FTRACE(hpb, msg, args...) do { } while (0) +#endif + +enum UFSHPB_STATE { + HPB_PRESENT = 1, + HPB_SUSPEND = 2, + HPB_FAILED = -2, + HPB_NEED_INIT = 0, + HPB_RESET = -3, +}; + +enum HPB_RGN_STATE { + HPB_RGN_INACTIVE, + HPB_RGN_ACTIVE, + HPB_RGN_PINNED, +}; + +enum HPB_SRGN_STATE { + HPB_SRGN_UNUSED, + HPB_SRGN_DIRTY, + HPB_SRGN_CLEAN, + HPB_SRGN_ISSUED, +}; + +#if defined(CONFIG_HPB_ERR_INJECTION) +enum HPB_ERR_INJECTION_SELECT { + HPB_ERR_INJECTION_DISABLE, + HPB_ERR_INJECTION_BITFLIP, + HPB_ERR_INJECTION_OFFSET, + HPB_ERR_INJECTION_RANDOM, +}; +#endif + +struct ufshpb_dev_info { + int num_lu; + int version; + int rgn_size; + int srgn_size; +}; + +struct ufshpb_active_field { + __be16 active_rgn; + __be16 active_srgn; +} __packed; + +struct ufshpb_rsp_field { + __be16 sense_data_len; + u8 desc_type; + u8 additional_len; + u8 hpb_type; + u8 reserved; + u8 active_rgn_cnt; + u8 inactive_rgn_cnt; + struct ufshpb_active_field hpb_active_field[2]; + __be16 hpb_inactive_field[2]; +} __packed; + +struct ufshpb_map_ctx { + struct page **m_page; + unsigned int *ppn_dirty; + + struct list_head list_table; +}; + +struct ufshpb_subregion { + struct ufshpb_map_ctx *mctx; + enum HPB_SRGN_STATE srgn_state; + int rgn_idx; + int srgn_idx; + + /* below information is used by rsp_list */ + struct list_head list_act_srgn; +}; + +struct ufshpb_region { + struct ufshpb_subregion *srgn_tbl; + enum HPB_RGN_STATE rgn_state; + int rgn_idx; + int srgn_cnt; + + /* below information is used by rsp_list */ + struct list_head list_inact_rgn; + + /* below information is used by lru */ + struct list_head list_lru_rgn; +}; + +struct ufshpb_req { + struct request *req; + struct bio *bio; + struct ufshpb_lu *hpb; + struct list_head list_req; + void (*end_io)(struct request *rq, int err); + void *end_io_data; + char sense[SCSI_SENSE_BUFFERSIZE]; + + union { + struct { + struct ufshpb_map_ctx *mctx; + unsigned int rgn_idx; + unsigned int srgn_idx; + unsigned int lun; + } rb; + struct { + struct page *m_page; + unsigned int len; + unsigned long lpn; + } wb; + }; +}; + +enum selection_type { + LRU = 1, +}; + +struct victim_select_info { + int selection_type; + struct list_head lh_lru_rgn; + int max_lru_active_cnt; /* supported hpb #region - pinned #region */ + atomic64_t active_cnt; +}; + +struct ufshpb_lu { + struct ufsf_feature *ufsf; + int lun; + int qd; + struct ufshpb_region *rgn_tbl; + + spinlock_t hpb_lock; + + spinlock_t retry_list_lock; + struct ufshpb_req *map_req; + int num_inflight_map_req; + int throttle_map_req; + struct list_head lh_map_req_free; + struct list_head lh_map_req_retry; + struct list_head lh_map_ctx_free; + + spinlock_t rsp_list_lock; + struct list_head lh_pinned_srgn; + struct list_head lh_act_srgn; + struct list_head lh_inact_rgn; + + struct kobject kobj; + struct mutex sysfs_lock; + struct ufshpb_sysfs_entry *sysfs_entries; + + struct ufshpb_req *pre_req; + int num_inflight_pre_req; + int throttle_pre_req; + struct list_head lh_pre_req_free; + int ctx_id_ticket; + int pre_req_min_tr_len; + int pre_req_max_tr_len; + int pre_req_requeue_cnt; + + struct work_struct pinned_work; + struct delayed_work retry_work; + struct work_struct task_work; + + /* for selecting victim */ + struct victim_select_info lru_info; + + int lu_max_active_rgns; + int lu_pinned_rgn_startidx; + int lu_pinned_end_offset; + int srgns_per_lu; + int rgns_per_lu; + int srgns_per_rgn; + int srgn_mem_size; + int entries_per_rgn_shift; + int entries_per_rgn_mask; + int entries_per_srgn; + int entries_per_srgn_shift; + int entries_per_srgn_mask; + int dwords_per_srgn; + unsigned long long srgn_unit_size; + int mpage_bytes; + int mpages_per_srgn; + int lu_num_blocks; + + /* for debug */ + int alloc_mctx; + int debug_free_table; + bool force_disable; + bool force_map_req_disable; + bool debug; + atomic64_t hit; + atomic64_t miss; + atomic64_t rb_noti_cnt; + atomic64_t rb_active_cnt; + atomic64_t rb_inactive_cnt; + atomic64_t map_req_cnt; + atomic64_t map_compl_cnt; + atomic64_t pre_req_cnt; + + atomic64_t act_rsp_list_cnt; + atomic64_t inact_rsp_list_cnt; +#if defined(CONFIG_HPB_ERR_INJECTION) + enum HPB_ERR_INJECTION_SELECT err_injection_select; + u64 err_injection_bitflip; + u64 err_injection_offset; + int err_injection_random; +#endif +}; + +struct ufshpb_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct ufshpb_lu *hpb, char *buf); + ssize_t (*store)(struct ufshpb_lu *hpb, const char *buf, size_t count); +}; + +struct ufs_hba; +struct ufshcd_lrb; + +int ufshpb_get_state(struct ufsf_feature *ufsf); +void ufshpb_set_state(struct ufsf_feature *ufsf, int state); +void ufshpb_get_dev_info(struct ufsf_feature *ufsf, u8 *desc_buf); +void ufshpb_get_geo_info(struct ufsf_feature *ufsf, u8 *geo_buf); +void ufshpb_get_lu_info(struct ufsf_feature *ufsf, int lun, u8 *unit_buf); +void ufshpb_init_handler(struct work_struct *work); +void ufshpb_rsp_upiu(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp); +void ufshpb_resume(struct ufsf_feature *ufsf); +void ufshpb_suspend(struct ufsf_feature *ufsf); +void ufshpb_reset_host(struct ufsf_feature *ufsf); +void ufshpb_reset(struct ufsf_feature *ufsf); +void ufshpb_prep_fn(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp); +void ufshpb_remove(struct ufsf_feature *ufsf, int state); +#endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshpb_skh.c b/drivers/scsi/ufs/ufshpb_skh.c new file mode 100644 index 000000000000..afa7cb0fb866 --- /dev/null +++ b/drivers/scsi/ufs/ufshpb_skh.c @@ -0,0 +1,3275 @@ +/* + * Universal Flash Storage Host Performance Booster + * + * Original work Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * Modified work Copyright (C) 2018, Google, Inc. + * Modified work Copyright (C) 2019 SK hynix + * + * Original work Authors: + * Yongmyung Lee + * Jinyoung Choi + * Modifier + * Kihyun Cho + * Sengman Shin + * Jinsu Lee + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#include +#include +#include +#include +#include + +#include "../../../block/blk.h" + +#include "ufs.h" +#include "ufshcd.h" +#include "ufshpb_skh.h" +#include + +u32 skhpb_debug_mask = SKHPB_LOG_ERR | SKHPB_LOG_INFO; +//u32 skhpb_debug_mask = SKHPB_LOG_ERR | SKHPB_LOG_INFO | SKHPB_LOG_DEBUG | SKHPB_LOG_HEX; +int debug_map_req = SKHPB_MAP_RSP_DISABLE; + +/* + * debug variables + */ +static int skhpb_alloc_mctx; + +/* + * define global constants + */ +static int skhpb_sects_per_blk_shift; +static int skhpb_bits_per_dword_shift; +static int skhpb_bits_per_dword_mask; + +static int skhpb_create_sysfs(struct ufs_hba *hba, struct skhpb_lu *hpb); +static int skhpb_check_lru_evict(struct skhpb_lu *hpb, struct skhpb_region *cb); +static void skhpb_error_handler(struct work_struct *work); +static void skhpb_evict_region(struct skhpb_lu *hpb, + struct skhpb_region *cb); +static void skhpb_purge_active_block(struct skhpb_lu *hpb); +static int skhpb_set_map_req(struct skhpb_lu *hpb, + int region, int subregion, struct skhpb_map_ctx *mctx, + struct skhpb_rsp_info *rsp_info, + enum SKHPB_BUFFER_MODE flag); +static void skhpb_rsp_map_cmd_req(struct skhpb_lu *hpb, + struct skhpb_rsp_info *rsp_info); +static void skhpb_map_loading_trigger(struct skhpb_lu *hpb, + bool only_pinned, bool do_work_handler); + +static inline void skhpb_purge_active_page(struct skhpb_lu *hpb, + struct skhpb_subregion *cp, int state); + +static void skhpb_hit_lru_info(struct skhpb_victim_select_info *lru_info, + struct skhpb_region *cb); + +static inline void skhpb_get_bit_offset( + struct skhpb_lu *hpb, int subregion_offset, + int *dword, int *offset) +{ + *dword = subregion_offset >> skhpb_bits_per_dword_shift; + *offset = subregion_offset & skhpb_bits_per_dword_mask; +} + +/* called with hpb_lock (irq) */ +static bool skhpb_ppn_dirty_check(struct skhpb_lu *hpb, + struct skhpb_subregion *cp, int subregion_offset) +{ + bool is_dirty = false; + unsigned int bit_dword, bit_offset; + + if (!cp->mctx->ppn_dirty) + return true; + + skhpb_get_bit_offset(hpb, subregion_offset, + &bit_dword, &bit_offset); + is_dirty = cp->mctx->ppn_dirty[bit_dword] & (1 << bit_offset) ? true : false; + + return is_dirty; +} + +static void skhpb_ppn_prep(struct skhpb_lu *hpb, + struct ufshcd_lrb *lrbp, skhpb_t ppn, + unsigned int sector_len) +{ + unsigned char cmd[16] = { 0 }; + + if (hpb->hba->skhpb_quirk & SKHPB_QUIRK_USE_READ_16_FOR_ENCRYPTION) + cmd[0] = READ_16; + else + cmd[0] = SKHPB_READ; + cmd[2] = lrbp->cmd->cmnd[2]; + cmd[3] = lrbp->cmd->cmnd[3]; + cmd[4] = lrbp->cmd->cmnd[4]; + cmd[5] = lrbp->cmd->cmnd[5]; + put_unaligned(ppn, (u64 *)&cmd[6]); + cmd[14] = (u8)(sector_len >> skhpb_sects_per_blk_shift); //Transfer length + if (hpb->hba->skhpb_quirk & SKHPB_QUIRK_USE_READ_16_FOR_ENCRYPTION) + cmd[15] = 0x01; //Control + else + cmd[15] = 0x00; //Control + + memcpy(lrbp->cmd->cmnd, cmd, MAX_CDB_SIZE); + memcpy(lrbp->ucd_req_ptr->sc.cdb, cmd, MAX_CDB_SIZE); + + //To verify the values within READ command + /* SKHPB_DRIVER_HEXDUMP("[HPB] HPB READ ", 16, 1, cmd, sizeof(cmd), 1); */ +} + +static inline void skhpb_set_dirty_bits(struct skhpb_lu *hpb, + struct skhpb_region *cb, struct skhpb_subregion *cp, + int dword, int offset, unsigned int count) +{ + const unsigned long mask = ((1UL << count) - 1) & 0xffffffff; + + if (cb->region_state == SKHPB_REGION_INACTIVE) + return; + + BUG_ON(!cp->mctx); + cp->mctx->ppn_dirty[dword] |= (mask << offset); +} + +/* called with hpb_lock (irq) */ +static void skhpb_set_dirty(struct skhpb_lu *hpb, + struct ufshcd_lrb *lrbp, int region, + int subregion, int subregion_offset) +{ + struct skhpb_region *cb; + struct skhpb_subregion *cp; + int count; + int bit_count, bit_dword, bit_offset; + + count = blk_rq_sectors(lrbp->cmd->request) >> skhpb_sects_per_blk_shift; + skhpb_get_bit_offset(hpb, subregion_offset, + &bit_dword, &bit_offset); + + do { + bit_count = min(count, SKHPB_BITS_PER_DWORD - bit_offset); + + cb = hpb->region_tbl + region; + cp = cb->subregion_tbl + subregion; + + skhpb_set_dirty_bits(hpb, cb, cp, + bit_dword, bit_offset, bit_count); + + bit_offset = 0; + bit_dword++; + + if (bit_dword == hpb->dwords_per_subregion) { + bit_dword = 0; + subregion++; + + if (subregion == hpb->subregions_per_region) { + subregion = 0; + region++; + } + } + + count -= bit_count; + } while (count); +} + +static inline bool skhpb_is_encrypted_lrbp(struct ufshcd_lrb *lrbp) +{ + /* To do + * UTRD_CRYPTO_ENABLE is not define at SM8350 + */ + //return (lrbp->utr_descriptor_ptr->header.dword_0 & UTRD_CRYPTO_ENABLE); + return true; +} + +static inline enum SKHPB_CMD skhpb_get_cmd(struct ufshcd_lrb *lrbp) +{ + unsigned char cmd = lrbp->cmd->cmnd[0]; + + if (cmd == READ_10 || cmd == READ_16) + return SKHPB_CMD_READ; + if (cmd == WRITE_10 || cmd == WRITE_16) + return SKHPB_CMD_WRITE; + if (cmd == UNMAP) + return SKHPB_CMD_DISCARD; + return SKHPB_CMD_OTHERS; +} + +static inline void skhpb_get_pos_from_lpn(struct skhpb_lu *hpb, + unsigned int lpn, int *region, int *subregion, int *offset) +{ + int region_offset; + + *region = lpn >> hpb->entries_per_region_shift; + region_offset = lpn & hpb->entries_per_region_mask; + *subregion = region_offset >> hpb->entries_per_subregion_shift; + *offset = region_offset & hpb->entries_per_subregion_mask; +} + +static inline bool skhpb_check_region_subregion_validity(struct skhpb_lu *hpb, + int region, int subregion) +{ + struct skhpb_region *cb; + + if (region >= hpb->regions_per_lu){ + SKHPB_DRIVER_E("[HCM] Out of REGION range - region[%d]:MAX[%d]\n", + region, hpb->regions_per_lu); + return false; + } + cb = hpb->region_tbl + region; + if (subregion >= cb->subregion_count){ + SKHPB_DRIVER_E("[HCM] Out of SUBREGION range - subregion[i]:MAX[%d]\n", + subregion, cb->subregion_count); + return false; + } + return true; +} + + +static inline skhpb_t skhpb_get_ppn(struct skhpb_map_ctx *mctx, int pos) +{ + skhpb_t *ppn_table; + int index, offset; + + index = pos / SKHPB_ENTREIS_PER_OS_PAGE; + offset = pos % SKHPB_ENTREIS_PER_OS_PAGE; + + ppn_table = page_address(mctx->m_page[index]); + return ppn_table[offset]; +} + + +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) +static bool skhpb_subregion_dirty_check(struct skhpb_lu *hpb, struct skhpb_subregion *cp, + int subregion_offset, int reqBlkCnt) +{ + unsigned int bit_dword, bit_offset; + unsigned int tmp; + int checkCnt; + + if (!cp->mctx) + return true; + + if (!cp->mctx->ppn_dirty) + return true; + + skhpb_get_bit_offset(hpb, subregion_offset, + &bit_dword, &bit_offset); + + while (true) { + checkCnt = SKHPB_BITS_PER_DWORD - bit_offset; + if (cp->mctx->ppn_dirty[bit_dword]) { + tmp = cp->mctx->ppn_dirty[bit_dword] << bit_offset; + if (SKHPB_BITS_PER_DWORD - reqBlkCnt > 0) + tmp = tmp >> (SKHPB_BITS_PER_DWORD - reqBlkCnt); + if (tmp) + return true; + } + reqBlkCnt -= checkCnt; + if (reqBlkCnt <= 0) + break; + bit_dword++; + if (bit_dword >= hpb->ppn_dirties_per_subregion) + break; + bit_offset = 0; + } + return false; +} + +static bool skhpb_lc_dirty_check(struct skhpb_lu *hpb, unsigned int lpn, unsigned int rq_sectors) +{ + int reg; + int subReg; + struct skhpb_region *cb; + struct skhpb_subregion *cp; + unsigned long cur_lpn = lpn; + int subRegOffset; + int reqBlkCnt = rq_sectors >> skhpb_sects_per_blk_shift; + + do { + skhpb_get_pos_from_lpn(hpb, cur_lpn, ®, &subReg, &subRegOffset); + if (!skhpb_check_region_subregion_validity(hpb, reg, subReg)) + return true; + cb = hpb->region_tbl + reg; + cp = cb->subregion_tbl + subReg; + + if (cb->region_state == SKHPB_REGION_INACTIVE || + cp->subregion_state != SKHPB_SUBREGION_CLEAN) { + atomic64_inc(&hpb->lc_reg_subreg_miss); + return true; + } + + if (skhpb_subregion_dirty_check(hpb, cp, subRegOffset, reqBlkCnt)) { + //SKHPB_DRIVER_D("[NORMAL READ] DIRTY: Region(%d), SubRegion(%d) \n", reg, subReg); + atomic64_inc(&hpb->lc_entry_dirty_miss); + return true; + } + + if (hpb->entries_per_subregion < subRegOffset + reqBlkCnt) { + reqBlkCnt -= (hpb->entries_per_subregion - subRegOffset); + } else { + reqBlkCnt = 0; + } + + cur_lpn += reqBlkCnt; + } while(reqBlkCnt); + + return false; +} +#endif + +void skhpb_prep_fn(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) +{ + struct skhpb_lu *hpb; + struct skhpb_region *cb; + struct skhpb_subregion *cp; + unsigned int lpn; + skhpb_t ppn = 0; + int region, subregion, subregion_offset; + const struct request *rq; + unsigned long long rq_pos; + unsigned int rq_sectors; + unsigned char cmd; + unsigned long flags; + + /* WKLU could not be HPB-LU */ + if (lrbp->lun >= UFS_UPIU_MAX_GENERAL_LUN) + return; + + hpb = hba->skhpb_lup[lrbp->lun]; + if (!hpb || !hpb->lu_hpb_enable) + return; + + if (hpb->force_hpb_read_disable) + return; + + cmd = skhpb_get_cmd(lrbp); + if (cmd == SKHPB_CMD_OTHERS) + return; + /* + * TODO: check if ICE is not supported or not. + * + * if (cmd==SKHPB_CMD_READ && skhpb_is_encrypted_lrbp(lrbp)) + * return; + */ + rq = lrbp->cmd->request; + rq_pos = blk_rq_pos(rq); + rq_sectors = blk_rq_sectors(rq); + + lpn = rq_pos / SKHPB_SECTORS_PER_BLOCK; + skhpb_get_pos_from_lpn( + hpb, lpn, ®ion, &subregion, &subregion_offset); + if (!skhpb_check_region_subregion_validity(hpb, region, subregion)) + return; + cb = hpb->region_tbl + region; + cp = cb->subregion_tbl + subregion; + + if (cmd == SKHPB_CMD_WRITE || + cmd == SKHPB_CMD_DISCARD) { + if (cb->region_state == SKHPB_REGION_INACTIVE) { + return; + } + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_set_dirty(hpb, lrbp, region, subregion, + subregion_offset); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return; + } + +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + if (((rq_sectors & (SKHPB_SECTORS_PER_BLOCK - 1)) != 0) || + rq_sectors > + (SKHPB_READ_LARGE_CHUNK_MAX_BLOCK_COUNT << skhpb_sects_per_blk_shift)) { +#else + if (rq_sectors != SKHPB_SECTORS_PER_BLOCK) { +#endif + atomic64_inc(&hpb->size_miss); + return; + } + + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (cb->region_state == SKHPB_REGION_INACTIVE) { + atomic64_inc(&hpb->region_miss); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return; + } else if (cp->subregion_state != SKHPB_SUBREGION_CLEAN){ + atomic64_inc(&hpb->subregion_miss); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return; + } + if (rq_sectors <= SKHPB_SECTORS_PER_BLOCK) { + if (skhpb_ppn_dirty_check(hpb, cp, subregion_offset)) { + atomic64_inc(&hpb->entry_dirty_miss); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return; + } + } +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + else { + if (skhpb_lc_dirty_check(hpb, lpn, rq_sectors)) { + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return; + } + atomic64_inc(&hpb->lc_hit); + } +#endif + ppn = skhpb_get_ppn(cp->mctx, subregion_offset); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + //SKHPB_DRIVER_D("XXX ufs ppn %016llx, lba %u\n", (unsigned long long)ppn, lpn); + skhpb_ppn_prep(hpb, lrbp, ppn, rq_sectors); + atomic64_inc(&hpb->hit); + return; +} + +static int skhpb_clean_dirty_bitmap( + struct skhpb_lu *hpb, struct skhpb_subregion *cp) +{ + struct skhpb_region *cb; + + cb = hpb->region_tbl + cp->region; + + /* if mctx is null, active block had been evicted out */ + if (cb->region_state == SKHPB_REGION_INACTIVE || !cp->mctx) { + SKHPB_DRIVER_D("%d - %d error already evicted\n", + cp->region, cp->subregion); + return -EINVAL; + } + + memset(cp->mctx->ppn_dirty, 0x00, + hpb->entries_per_subregion / BITS_PER_BYTE); + return 0; +} + +static void skhpb_clean_active_subregion( + struct skhpb_lu *hpb, struct skhpb_subregion *cp) +{ + struct skhpb_region *cb; + + cb = hpb->region_tbl + cp->region; + + /* if mctx is null, active block had been evicted out */ + if (cb->region_state == SKHPB_REGION_INACTIVE || !cp->mctx) { + SKHPB_DRIVER_D("%d - %d clean already evicted\n", + cp->region, cp->subregion); + return; + } + cp->subregion_state = SKHPB_SUBREGION_CLEAN; +} + +static void skhpb_error_active_subregion( + struct skhpb_lu *hpb, struct skhpb_subregion *cp) +{ + struct skhpb_region *cb; + + cb = hpb->region_tbl + cp->region; + + /* if mctx is null, active block had been evicted out */ + if (cb->region_state == SKHPB_REGION_INACTIVE || !cp->mctx) { + SKHPB_DRIVER_E("%d - %d evicted\n", cp->region, cp->subregion); + return; + } + cp->subregion_state = SKHPB_SUBREGION_DIRTY; +} + +static void skhpb_map_compl_process(struct skhpb_lu *hpb, + struct skhpb_map_req *map_req) +{ + unsigned long flags; + + SKHPB_MAP_REQ_TIME(map_req, map_req->RSP_end, 1); + SKHPB_DRIVER_D("SKHPB RB COMPL BUFFER %d - %d\n", map_req->region, map_req->subregion); + + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_clean_active_subregion(hpb, + hpb->region_tbl[map_req->region].subregion_tbl + + map_req->subregion); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); +} + +static void skhpb_map_inactive_compl_process(struct skhpb_lu *hpb, + struct skhpb_map_req *map_req) +{ + SKHPB_DRIVER_D("SKHPB WB COMPL BUFFER %d\n", map_req->region); + SKHPB_MAP_REQ_TIME(map_req, map_req->RSP_end, 1); +} + +/* + * Must held rsp_list_lock before enter this function + */ +static struct skhpb_rsp_info *skhpb_get_req_info(struct skhpb_lu *hpb) +{ + struct skhpb_rsp_info *rsp_info = + list_first_entry_or_null(&hpb->lh_rsp_info_free, + struct skhpb_rsp_info, + list_rsp_info); + if (!rsp_info) { + SKHPB_DRIVER_E("there is no rsp_info"); + return NULL; + } + list_del(&rsp_info->list_rsp_info); + memset(rsp_info, 0x00, sizeof(struct skhpb_rsp_info)); + + INIT_LIST_HEAD(&rsp_info->list_rsp_info); + + return rsp_info; +} + +static void skhpb_map_req_compl_fn(struct request *req, blk_status_t error) +{ + struct skhpb_map_req *map_req = req->end_io_data; + struct ufs_hba *hba; + struct skhpb_lu *hpb; + struct scsi_sense_hdr sshdr; + struct skhpb_region *cb; + struct scsi_request *scsireq = scsi_req(req); + unsigned long flags; + + /* shut up "bio leak" warning */ + memcpy(map_req->sense, scsireq->sense, SCSI_SENSE_BUFFERSIZE); + req->bio = NULL; + blk_put_request(req); + + hpb = map_req->hpb; + hba = hpb->hba; + cb = hpb->region_tbl + map_req->region; + + if (hba->skhpb_state != SKHPB_PRESENT) + goto free_map_req; + + if (!error) { + skhpb_map_compl_process(hpb, map_req); + goto free_map_req; + } + + SKHPB_DRIVER_E("error number %d ( %d - %d )\n", + error, map_req->region, map_req->subregion); + scsi_normalize_sense(map_req->sense, + SCSI_SENSE_BUFFERSIZE, &sshdr); + SKHPB_DRIVER_E("code %x sense_key %x asc %x ascq %x\n", + sshdr.response_code, + sshdr.sense_key, sshdr.asc, sshdr.ascq); + SKHPB_DRIVER_E("byte4 %x byte5 %x byte6 %x additional_len %x\n", + sshdr.byte4, sshdr.byte5, + sshdr.byte6, sshdr.additional_length); + atomic64_inc(&hpb->rb_fail); + + if (sshdr.sense_key == ILLEGAL_REQUEST) { + if (sshdr.asc == 0x00 && sshdr.ascq == 0x16) { + /* OPERATION IN PROGRESS */ + SKHPB_DRIVER_E("retry rb %d - %d", + map_req->region, map_req->subregion); + + spin_lock(&hpb->map_list_lock); + INIT_LIST_HEAD(&map_req->list_map_req); + list_add_tail(&map_req->list_map_req, &hpb->lh_map_req_retry); + spin_unlock(&hpb->map_list_lock); + + schedule_delayed_work(&hpb->skhpb_map_req_retry_work, + msecs_to_jiffies(5000)); + return; + } + } + // Only change subregion status at here. + // Do not put map_ctx, it will re-use when it is activated again. + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_error_active_subregion(hpb, cb->subregion_tbl + map_req->subregion); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); +free_map_req: + spin_lock(&hpb->map_list_lock); + INIT_LIST_HEAD(&map_req->list_map_req); + list_add_tail(&map_req->list_map_req, &hpb->lh_map_req_free); + spin_unlock(&hpb->map_list_lock); + atomic64_dec(&hpb->alloc_map_req_cnt); +} + +static void skhpb_map_inactive_req_compl_fn( + struct request *req, blk_status_t error) +{ + struct skhpb_map_req *map_req = req->end_io_data; + struct ufs_hba *hba; + struct skhpb_lu *hpb; + struct scsi_sense_hdr sshdr; + struct skhpb_region *cb; + struct scsi_request *scsireq = scsi_req(req); + unsigned long flags; + + /* shut up "bio leak" warning */ + memcpy(map_req->sense, scsireq->sense, SCSI_SENSE_BUFFERSIZE); + req->bio = NULL; + blk_put_request(req); + + hpb = map_req->hpb; + hba = hpb->hba; + cb = hpb->region_tbl + map_req->region; + + if (hba->skhpb_state != SKHPB_PRESENT) + goto free_map_req; + + if (!error) { + skhpb_map_inactive_compl_process(hpb, map_req); + goto free_map_req; + } + + SKHPB_DRIVER_E("error number %d ( %d - %d )", + error, map_req->region, map_req->subregion); + scsi_normalize_sense(map_req->sense, SCSI_SENSE_BUFFERSIZE, + &sshdr); + SKHPB_DRIVER_E("code %x sense_key %x asc %x ascq %x", + sshdr.response_code, + sshdr.sense_key, sshdr.asc, sshdr.ascq); + SKHPB_DRIVER_E("byte4 %x byte5 %x byte6 %x additional_len %x", + sshdr.byte4, sshdr.byte5, + sshdr.byte6, sshdr.additional_length); + atomic64_inc(&hpb->rb_fail); + + if (sshdr.sense_key == ILLEGAL_REQUEST) { + if (cb->is_pinned) { + SKHPB_DRIVER_E("WRITE_BUFFER is not allowed on pinned area: region#%d", + cb->region); + } else { + if (sshdr.asc == 0x00 && sshdr.ascq == 0x16) { + /* OPERATION IN PROGRESS */ + SKHPB_DRIVER_E("retry wb %d", map_req->region); + + spin_lock(&hpb->map_list_lock); + INIT_LIST_HEAD(&map_req->list_map_req); + list_add_tail(&map_req->list_map_req, &hpb->lh_map_req_retry); + spin_unlock(&hpb->map_list_lock); + + schedule_delayed_work(&hpb->skhpb_map_req_retry_work, + msecs_to_jiffies(5000)); + return; + } + } + } + // Only change subregion status at here. + // Do not put map_ctx, it will re-use when it is activated again. + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_error_active_subregion(hpb, cb->subregion_tbl + map_req->subregion); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); +free_map_req: + spin_lock(&hpb->map_list_lock); + INIT_LIST_HEAD(&map_req->list_map_req); + list_add_tail(&map_req->list_map_req, &hpb->lh_map_req_free); + spin_unlock(&hpb->map_list_lock); + atomic64_dec(&hpb->alloc_map_req_cnt); +} + +static int skhpb_execute_req_dev_ctx(struct skhpb_lu *hpb, + unsigned char *cmd, void *buf, int length) +{ + unsigned long flags; + struct scsi_sense_hdr sshdr; + struct scsi_device *sdp; + struct ufs_hba *hba = hpb->hba; + int ret = 0; + + spin_lock_irqsave(hba->host->host_lock, flags); + sdp = hba->sdev_ufs_lu[hpb->lun]; + if (sdp) { + ret = scsi_device_get(sdp); + if (!ret && !scsi_device_online(sdp)) { + ret = -ENODEV; + scsi_device_put(sdp); + } else if (!ret) { + hba->issue_ioctl = true; + } + } else { + ret = -ENODEV; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (ret) + return ret; + + ret = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, + buf, length, &sshdr, + msecs_to_jiffies(30000), 3, NULL); + spin_lock_irqsave(hba->host->host_lock, flags); + hba->issue_ioctl = false; + spin_unlock_irqrestore(hba->host->host_lock, flags); + scsi_device_put(sdp); + return ret; +} + +static inline void skhpb_set_read_dev_ctx_cmd(unsigned char *cmd, int lba, + int length) +{ + cmd[0] = READ_10; + cmd[1] = 0x02; + cmd[2] = SKHPB_GET_BYTE_3(lba); + cmd[3] = SKHPB_GET_BYTE_2(lba); + cmd[4] = SKHPB_GET_BYTE_1(lba); + cmd[5] = SKHPB_GET_BYTE_0(lba); + cmd[6] = SKHPB_GET_BYTE_2(length); + cmd[7] = SKHPB_GET_BYTE_1(length); + cmd[8] = SKHPB_GET_BYTE_0(length); +} + +int skhpb_issue_req_dev_ctx(struct skhpb_lu *hpb, unsigned char *buf, + int buf_length) +{ + unsigned char cmd[10] = { 0 }; + int cmd_len = buf_length >> PAGE_SHIFT; + int ret = 0; + + skhpb_set_read_dev_ctx_cmd(cmd, 0x48504230, cmd_len); + + ret = skhpb_execute_req_dev_ctx(hpb, cmd, buf, buf_length); + if (ret < 0) + SKHPB_DRIVER_E("failed with err %d\n", ret); + return ret; +} + +static inline void skhpb_set_write_buf_cmd(unsigned char *cmd, int region) +{ + cmd[0] = SKHPB_WRITE_BUFFER; + cmd[1] = 0x01; + cmd[2] = SKHPB_GET_BYTE_1(region); + cmd[3] = SKHPB_GET_BYTE_0(region); + cmd[4] = 0x00; + cmd[5] = 0x00; + cmd[6] = 0x00; + cmd[7] = 0x00; + cmd[8] = 0x00; + cmd[9] = 0x00; + + //To verify the values within WRITE_BUFFER command + SKHPB_DRIVER_HEXDUMP("[HPB] WRITE BUFFER ", 16, 1, cmd, 10, 1); +} + +static inline void skhpb_set_read_buf_cmd(unsigned char *cmd, + int region, int subregion, int subregion_mem_size) +{ + cmd[0] = SKHPB_READ_BUFFER; + cmd[1] = 0x01; + cmd[2] = SKHPB_GET_BYTE_1(region); + cmd[3] = SKHPB_GET_BYTE_0(region); + cmd[4] = SKHPB_GET_BYTE_1(subregion); + cmd[5] = SKHPB_GET_BYTE_0(subregion); + cmd[6] = SKHPB_GET_BYTE_2(subregion_mem_size); + cmd[7] = SKHPB_GET_BYTE_1(subregion_mem_size); + cmd[8] = SKHPB_GET_BYTE_0(subregion_mem_size); + cmd[9] = 0x00; + + //To verify the values within READ_BUFFER command + SKHPB_DRIVER_HEXDUMP("[HPB] READ BUFFER ", 16, 1, cmd, 10, 1); +} + +static int skhpb_add_bio_page(struct skhpb_lu *hpb, + struct request_queue *q, struct bio *bio, struct bio_vec *bvec, + struct skhpb_map_ctx *mctx) +{ + struct page *page = NULL; + int i, ret; + + bio_init(bio, bvec, hpb->mpages_per_subregion); + + for (i = 0; i < hpb->mpages_per_subregion; i++) { + page = mctx->m_page[i]; + if (!page) + return -ENOMEM; + + ret = bio_add_pc_page(q, bio, page, hpb->mpage_bytes, 0); + if (ret != hpb->mpage_bytes) { + SKHPB_DRIVER_E("error ret %d\n", ret); + return -EINVAL; + } + } + return 0; +} + +static int skhpb_map_req_issue( + struct skhpb_lu *hpb, struct skhpb_map_req *map_req) +{ + struct request_queue *q = hpb->hba->sdev_ufs_lu[hpb->lun]->request_queue; + struct request *req; + struct scsi_request *scsireq; + unsigned char cmd[10] = { 0 }; + int ret; + + if (map_req->rwbuffer_flag == W_BUFFER) + skhpb_set_write_buf_cmd(cmd, map_req->region); + else + skhpb_set_read_buf_cmd(cmd, map_req->region, map_req->subregion, + map_req->subregion_mem_size); + if (map_req->rwbuffer_flag == W_BUFFER) + req = blk_get_request(q, REQ_OP_SCSI_OUT, BLK_MQ_REQ_PREEMPT); + else + req = blk_get_request(q, REQ_OP_SCSI_IN, BLK_MQ_REQ_PREEMPT); + if (IS_ERR(req)) { + int rv = PTR_ERR(req); + + if (map_req->rwbuffer_flag == W_BUFFER) + SKHPB_DRIVER_E("blk_get_request errno %d, \ + retry #%d, WRITE BUFFER %d\n", + rv, map_req->retry_cnt, map_req->region); + else + SKHPB_DRIVER_E("blk_get_request errno %d, \ + retry #%d, READ BUFFER %d:%d\n", + rv, map_req->retry_cnt, + map_req->region, map_req->subregion); + + if (map_req->retry_cnt == 10) { + /* give up */ + return rv; + } + + spin_lock_bh(&hpb->map_list_lock); + list_add_tail(&map_req->list_map_req, &hpb->lh_map_req_retry); + spin_unlock_bh(&hpb->map_list_lock); + + schedule_delayed_work(&hpb->skhpb_map_req_retry_work, msecs_to_jiffies(10)); + + return 0; + } + scsireq = scsi_req(req); + + scsireq->cmd_len = COMMAND_SIZE(cmd[0]); + BUG_ON(scsireq->cmd_len > sizeof(scsireq->__cmd)); + scsireq->cmd = scsireq->__cmd; + memcpy(scsireq->cmd, cmd, scsireq->cmd_len); + + req->rq_flags |= RQF_QUIET | RQF_PREEMPT; + req->timeout = msecs_to_jiffies(30000); + req->end_io_data = map_req; + + if (map_req->rwbuffer_flag == R_BUFFER) { + ret = skhpb_add_bio_page( + hpb, q, &map_req->bio, map_req->bvec, map_req->mctx); + if (ret) { + SKHPB_DRIVER_E("skhpb_add_bio_page_error %d\n", ret); + goto out_put_request; + } + map_req->pbio = &map_req->bio; + blk_rq_append_bio(req, &map_req->pbio); + } + SKHPB_DRIVER_D("issue map_request: %d - %d\n", + map_req->region, map_req->subregion); + + SKHPB_MAP_REQ_TIME(map_req, map_req->RSP_issue, 0); + if( hpb->hpb_control_mode == HOST_CTRL_MODE) { + if (map_req->rwbuffer_flag == W_BUFFER) + blk_execute_rq_nowait( + q, NULL, req, 0, skhpb_map_inactive_req_compl_fn); + else + blk_execute_rq_nowait(q, NULL, req, 0, skhpb_map_req_compl_fn); + } else + blk_execute_rq_nowait(q, NULL, req, 1, skhpb_map_req_compl_fn); + + if (map_req->rwbuffer_flag == W_BUFFER) + atomic64_inc(&hpb->w_map_req_cnt); + else + atomic64_inc(&hpb->map_req_cnt); + + return 0; + +out_put_request: + blk_put_request(req); + return ret; +} + +static int skhpb_set_map_req(struct skhpb_lu *hpb, + int region, int subregion, struct skhpb_map_ctx *mctx, + struct skhpb_rsp_info *rsp_info, + enum SKHPB_BUFFER_MODE flag) +{ + bool last = hpb->region_tbl[region].subregion_tbl[subregion].last; + struct skhpb_map_req *map_req; + + spin_lock_bh(&hpb->map_list_lock); + map_req = list_first_entry_or_null(&hpb->lh_map_req_free, + struct skhpb_map_req, + list_map_req); + if (!map_req) { + SKHPB_DRIVER_E("There is no map_req\n"); + spin_unlock_bh(&hpb->map_list_lock); + return -ENOMEM; + } + list_del(&map_req->list_map_req); + spin_unlock_bh(&hpb->map_list_lock); + atomic64_inc(&hpb->alloc_map_req_cnt); + + memset(map_req, 0x00, sizeof(struct skhpb_map_req)); + + map_req->hpb = hpb; + map_req->region = region; + map_req->subregion = subregion; + map_req->subregion_mem_size = + last ? hpb->last_subregion_mem_size : hpb->subregion_mem_size; + map_req->mctx = mctx; + map_req->lun = hpb->lun; + map_req->RSP_start = rsp_info->RSP_start; + if (flag == W_BUFFER) { + map_req->rwbuffer_flag = W_BUFFER; + } else + map_req->rwbuffer_flag = R_BUFFER; + + if(skhpb_map_req_issue(hpb, map_req)) { + SKHPB_DRIVER_E("issue Failed!!!\n"); + return -ENOMEM; + } + + return 0; +} + +static struct skhpb_map_ctx *skhpb_get_map_ctx(struct skhpb_lu *hpb) +{ + struct skhpb_map_ctx *mctx; + + mctx = list_first_entry_or_null(&hpb->lh_map_ctx, + struct skhpb_map_ctx, list_table); + if (mctx) { + list_del_init(&mctx->list_table); + hpb->debug_free_table--; + return mctx; + } + return ERR_PTR(-ENOMEM); +} + +static inline void skhpb_add_lru_info(struct skhpb_victim_select_info *lru_info, + struct skhpb_region *cb) +{ + cb->region_state = SKHPB_REGION_ACTIVE; + list_add_tail(&cb->list_region, &lru_info->lru); + atomic64_inc(&lru_info->active_count); +} + +static inline int skhpb_add_region(struct skhpb_lu *hpb, + struct skhpb_region *cb) +{ + struct skhpb_victim_select_info *lru_info; + int subregion; + int err = 0; + + lru_info = &hpb->lru_info; + + //SKHPB_DRIVER_D("E->active region: %d", cb->region); + + for (subregion = 0; subregion < cb->subregion_count; subregion++) { + struct skhpb_subregion *cp; + + cp = cb->subregion_tbl + subregion; + cp->mctx = skhpb_get_map_ctx(hpb); + if (IS_ERR(cp->mctx)) { + err = PTR_ERR(cp->mctx); + goto out; + } + cp->subregion_state = SKHPB_SUBREGION_DIRTY; + } + if (!cb->is_pinned) + skhpb_add_lru_info(lru_info, cb); + + atomic64_inc(&hpb->region_add); +out: + if (err) + SKHPB_DRIVER_E("get mctx failed. err %d subregion %d free_table %d\n", + err, subregion, hpb->debug_free_table); + return err; +} + +static inline void skhpb_put_map_ctx( + struct skhpb_lu *hpb, struct skhpb_map_ctx *mctx) +{ + list_add(&mctx->list_table, &hpb->lh_map_ctx); + hpb->debug_free_table++; +} + +static inline void skhpb_purge_active_page(struct skhpb_lu *hpb, + struct skhpb_subregion *cp, int state) +{ + if (state == SKHPB_SUBREGION_UNUSED) { + skhpb_put_map_ctx(hpb, cp->mctx); + cp->mctx = NULL; + } + cp->subregion_state = state; +} + +static inline void skhpb_cleanup_lru_info( + struct skhpb_victim_select_info *lru_info, + struct skhpb_region *cb) +{ + list_del_init(&cb->list_region); + cb->region_state = SKHPB_REGION_INACTIVE; + cb->hit_count = 0; + atomic64_dec(&lru_info->active_count); +} + +static inline void skhpb_evict_region(struct skhpb_lu *hpb, + struct skhpb_region *cb) +{ + struct skhpb_victim_select_info *lru_info; + struct skhpb_subregion *cp; + int subregion; + + // If the maximum value is exceeded at the time of region addition, + // it may have already been processed. + if (cb->region_state == SKHPB_REGION_INACTIVE) { + SKHPB_DRIVER_D("Region:%d was already inactivated.\n", + cb->region); + return; + } + lru_info = &hpb->lru_info; + + //SKHPB_DRIVER_D("C->EVICT region: %d\n", cb->region); + + skhpb_cleanup_lru_info(lru_info, cb); + atomic64_inc(&hpb->region_evict); + for (subregion = 0; subregion < cb->subregion_count; subregion++) { + cp = cb->subregion_tbl + subregion; + + skhpb_purge_active_page(hpb, cp, SKHPB_SUBREGION_UNUSED); + } +} + +static void skhpb_hit_lru_info(struct skhpb_victim_select_info *lru_info, + struct skhpb_region *cb) +{ + switch (lru_info->selection_type) { + case TYPE_LRU: + list_move_tail(&cb->list_region, &lru_info->lru); + break; + case TYPE_LFU: + if (cb->hit_count != 0xffffffff) + cb->hit_count++; + + list_move_tail(&cb->list_region, &lru_info->lru); + break; + default: + break; + } +} + +static struct skhpb_region *skhpb_victim_lru_info( + struct skhpb_victim_select_info *lru_info) +{ + struct skhpb_region *cb; + struct skhpb_region *victim_cb = NULL; + u32 hit_count = 0xffffffff; + + switch (lru_info->selection_type) { + case TYPE_LRU: + victim_cb = list_first_entry(&lru_info->lru, + struct skhpb_region, list_region); + break; + case TYPE_LFU: + list_for_each_entry(cb, &lru_info->lru, list_region) { + if (hit_count > cb->hit_count) { + hit_count = cb->hit_count; + victim_cb = cb; + } + } + break; + default: + break; + } + return victim_cb; +} + +static int skhpb_check_lru_evict(struct skhpb_lu *hpb, struct skhpb_region *cb) +{ + struct skhpb_victim_select_info *lru_info = &hpb->lru_info; + struct skhpb_region *victim_cb; + unsigned long flags; + if (cb->is_pinned) + return 0; + + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (!list_empty(&cb->list_region)) { + skhpb_hit_lru_info(lru_info, cb); + goto out; + } + + if (cb->region_state != SKHPB_REGION_INACTIVE) + goto out; + + if (atomic64_read(&lru_info->active_count) == lru_info->max_lru_active_count) { + + victim_cb = skhpb_victim_lru_info(lru_info); + + if (!victim_cb) { + SKHPB_DRIVER_E("SKHPB victim_cb is NULL\n"); + goto unlock_error; + } + SKHPB_DRIVER_D("max lru case. victim : %d\n", victim_cb->region); + skhpb_evict_region(hpb, victim_cb); + } + if (skhpb_add_region(hpb, cb)) { + SKHPB_DRIVER_E("SKHPB memory allocation failed\n"); + goto unlock_error; + } + + +out: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return 0; +unlock_error: + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + return -ENOMEM; +} + +static int skhpb_evict_load_region(struct skhpb_lu *hpb, + struct skhpb_rsp_info *rsp_info) +{ + struct skhpb_region *cb; + int region, iter; + unsigned long flags; + + for (iter = 0; iter < rsp_info->inactive_cnt; iter++) { + region = rsp_info->inactive_list.region[iter]; + cb = hpb->region_tbl + region; + + if (cb->is_pinned) { + /* + * Pinned active-block should not drop-out. + * But if so, it would treat error as critical, + * and it will run skhpb_eh_work + */ + SKHPB_DRIVER_E("SKHPB pinned active-block drop-out error\n"); + return -ENOMEM; + } + if (list_empty(&cb->list_region)) + continue; + + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_evict_region(hpb, cb); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + } + + for (iter = 0; iter < rsp_info->active_cnt; iter++) { + region = rsp_info->active_list.region[iter]; + cb = hpb->region_tbl + region; + if(skhpb_check_lru_evict(hpb, cb)) + return -ENOMEM; + } + return 0; +} + +static inline struct skhpb_rsp_field *skhpb_get_hpb_rsp( + struct ufshcd_lrb *lrbp) +{ + return (struct skhpb_rsp_field *)&lrbp->ucd_rsp_ptr->sr.sense_data_len; +} + +static void skhpb_rsp_map_cmd_req(struct skhpb_lu *hpb, + struct skhpb_rsp_info *rsp_info) +{ + struct skhpb_region *cb; + struct skhpb_subregion *cp; + int region, subregion; + int iter; + int ret; + unsigned long flags; + + /* + * Before Issue read buffer CMD for active active block, + * prepare the memory from memory pool. + */ + ret = skhpb_evict_load_region(hpb, rsp_info); + if (ret) { + SKHPB_DRIVER_E("region evict/load failed. ret %d\n", ret); + goto wakeup_ee_worker; + } + for (iter = 0; iter < rsp_info->active_cnt; iter++) { + region = rsp_info->active_list.region[iter]; + subregion = rsp_info->active_list.subregion[iter]; + cb = hpb->region_tbl + region; + + if (region >= hpb->regions_per_lu || + subregion >= cb->subregion_count) { + SKHPB_DRIVER_E("skhpb issue-map %d - %d range error\n", + region, subregion); + goto wakeup_ee_worker; + } + + cp = cb->subregion_tbl + subregion; + + /* + * if subregion_state set SKHPB_SUBREGION_ISSUED, + * active_page has already been added to list, + * so it just ends function. + */ + spin_lock_irqsave(&hpb->hpb_lock, flags); + if (cp->subregion_state == SKHPB_SUBREGION_ISSUED) { + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + continue; + } + + cp->subregion_state = SKHPB_SUBREGION_ISSUED; + + ret = skhpb_clean_dirty_bitmap(hpb, cp); + + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + if (ret) + continue; + + if (!hpb->hba->sdev_ufs_lu[hpb->lun] || + !hpb->hba->sdev_ufs_lu[hpb->lun]->request_queue) + return; + ret = skhpb_set_map_req(hpb, region, subregion, + cp->mctx, rsp_info, R_BUFFER); + SKHPB_DRIVER_D("SEND READ_BUFFER - Region:%d, SubRegion:%d\n", + region, subregion); + if (ret) { + SKHPB_DRIVER_E("skhpb_set_map_req error %d\n", ret); + goto wakeup_ee_worker; + } + } + return; + +wakeup_ee_worker: + hpb->hba->skhpb_state = SKHPB_FAILED; + schedule_work(&hpb->hba->skhpb_eh_work); +} + +/* routine : isr (ufs) */ +void skhpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) +{ + struct skhpb_lu *hpb; + struct skhpb_rsp_field *rsp_field; + struct skhpb_rsp_info *rsp_info; + int data_seg_len, num, blk_idx, update_alert; + + update_alert = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) + & MASK_RSP_UPIU_HPB_UPDATE_ALERT; + data_seg_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) + & MASK_RSP_UPIU_DATA_SEG_LEN; + if (!update_alert || !data_seg_len) { + bool do_tasklet = false; + + if (lrbp->lun >= UFS_UPIU_MAX_GENERAL_LUN) + return; + + hpb = hba->skhpb_lup[lrbp->lun]; + if (!hpb) + return; + + spin_lock(&hpb->rsp_list_lock); + do_tasklet = !list_empty(&hpb->lh_rsp_info); + spin_unlock(&hpb->rsp_list_lock); + + if (do_tasklet) + schedule_work(&hpb->skhpb_rsp_work); + return; + } + rsp_field = skhpb_get_hpb_rsp(lrbp); + if ((get_unaligned_be16(rsp_field->sense_data_len + 0) + != SKHPB_DEV_SENSE_SEG_LEN) || + rsp_field->desc_type != SKHPB_DEV_DES_TYPE || + rsp_field->additional_len != SKHPB_DEV_ADDITIONAL_LEN || + rsp_field->hpb_type == SKHPB_RSP_NONE || + rsp_field->active_region_cnt > SKHPB_MAX_ACTIVE_NUM || + rsp_field->inactive_region_cnt > SKHPB_MAX_INACTIVE_NUM) + return; + + if (rsp_field->lun >= UFS_UPIU_MAX_GENERAL_LUN) { + SKHPB_DRIVER_E("lun is not general = %d", rsp_field->lun); + return; + } + hpb = hba->skhpb_lup[rsp_field->lun]; + if (!hpb) { + SKHPB_DRIVER_E("UFS-LU%d is not SKHPB LU\n", rsp_field->lun); + return; + } + if (hpb->force_map_req_disable) + return; + SKHPB_DRIVER_D("HPB-Info Noti: %d LUN: %d Seg-Len %d, Req_type = %d\n", + rsp_field->hpb_type, rsp_field->lun, + data_seg_len, rsp_field->hpb_type); + if (!hpb->lu_hpb_enable) { + SKHPB_DRIVER_E("LU(%d) not HPB-LU\n", rsp_field->lun); + return; + } + //To verify the values within RESPONSE UPIU. + SKHPB_DRIVER_HEXDUMP("[HPB] RESP UPIU ", 16, 1, + lrbp->ucd_rsp_ptr, sizeof(struct utp_upiu_rsp), 1); + //If wLUMaxActiveHPBRegions == wNumHPBPinnedRegions + if (hpb->lru_info.max_lru_active_count == 0){ + SKHPB_DRIVER_D("max_lru_active_count is 0"); + return; + } + + switch (rsp_field->hpb_type) { + case SKHPB_RSP_REQ_REGION_UPDATE: + atomic64_inc(&hpb->rb_noti_cnt); + WARN_ON(data_seg_len != SKHPB_DEV_DATA_SEG_LEN); + + spin_lock(&hpb->rsp_list_lock); + rsp_info = skhpb_get_req_info(hpb); + spin_unlock(&hpb->rsp_list_lock); + if (!rsp_info) + return; + rsp_info->type = SKHPB_RSP_REQ_REGION_UPDATE; + + for (num = 0; num < rsp_field->active_region_cnt; num++) { + blk_idx = num * SKHPB_PER_ACTIVE_INFO_BYTES; + rsp_info->active_list.region[num] = + get_unaligned_be16(rsp_field->hpb_active_field + blk_idx); + rsp_info->active_list.subregion[num] = + get_unaligned_be16(rsp_field->hpb_active_field + + blk_idx + 2); + SKHPB_DRIVER_D("active num: %d, #block: %d, page#: %d\n", + num + 1, + rsp_info->active_list.region[num], + rsp_info->active_list.subregion[num]); + } + rsp_info->active_cnt = num; + + for (num = 0; num < rsp_field->inactive_region_cnt; num++) { + blk_idx = num * SKHPB_PER_INACTIVE_INFO_BYTES; + rsp_info->inactive_list.region[num] = + get_unaligned_be16(rsp_field->hpb_inactive_field + blk_idx); + SKHPB_DRIVER_D("inactive num: %d, #block: %d\n", + num + 1, rsp_info->inactive_list.region[num]); + } + rsp_info->inactive_cnt = num; + + SKHPB_DRIVER_D("active cnt: %d, inactive cnt: %d\n", + rsp_info->active_cnt, rsp_info->inactive_cnt); + SKHPB_DRIVER_D("add_list %p -> %p\n", + rsp_info, &hpb->lh_rsp_info); + + spin_lock(&hpb->rsp_list_lock); + list_add_tail(&rsp_info->list_rsp_info, &hpb->lh_rsp_info); + spin_unlock(&hpb->rsp_list_lock); + + schedule_work(&hpb->skhpb_rsp_work); + break; + + case SKHPB_RSP_HPB_RESET: + for (num = 0 ; num < UFS_UPIU_MAX_GENERAL_LUN ; num++) + { + hpb = hba->skhpb_lup[num]; + if (!hpb || !hpb->lu_hpb_enable) + continue; + atomic64_inc(&hpb->reset_noti_cnt); + + spin_lock(&hpb->rsp_list_lock); + rsp_info = skhpb_get_req_info(hpb); + spin_unlock(&hpb->rsp_list_lock); + if (!rsp_info) + return; + + rsp_info->type = SKHPB_RSP_HPB_RESET; + + spin_lock(&hpb->rsp_list_lock); + list_add_tail(&rsp_info->list_rsp_info, &hpb->lh_rsp_info); + spin_unlock(&hpb->rsp_list_lock); + + schedule_work(&hpb->skhpb_rsp_work); + } + break; + + default: + SKHPB_DRIVER_E("hpb_type is not available : %d\n", + rsp_field->hpb_type); + break; + } +} + +static int skhpb_read_desc(struct ufs_hba *hba, + u8 desc_id, u8 desc_index, u8 *desc_buf, u32 size) +{ + int err = 0; + + err = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC, + desc_id, desc_index, 0, desc_buf, &size); + if (err) { + SKHPB_DRIVER_E("reading Device Desc failed. err = %d\n", err); + } + return err; +} + +static int skhpb_read_device_desc( + struct ufs_hba *hba, u8 *desc_buf, u32 size) +{ + return skhpb_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, desc_buf, size); +} + +static int skhpb_read_geo_desc(struct ufs_hba *hba, u8 *desc_buf, u32 size) +{ + return skhpb_read_desc(hba, QUERY_DESC_IDN_GEOMETRY, 0, + desc_buf, size); +} + +static int skhpb_read_unit_desc(struct ufs_hba *hba, int lun, + u8 *desc_buf, u32 size) +{ + return skhpb_read_desc(hba, QUERY_DESC_IDN_UNIT, + lun, desc_buf, size); +} + +static inline void skhpb_add_subregion_to_req_list(struct skhpb_lu *hpb, + struct skhpb_subregion *cp) +{ + list_add_tail(&cp->list_subregion, &hpb->lh_subregion_req); + cp->subregion_state = SKHPB_SUBREGION_ISSUED; +} + +static int skhpb_execute_req(struct skhpb_lu *hpb, unsigned char *cmd, + struct skhpb_subregion *cp) +{ + struct ufs_hba *hba = hpb->hba; + struct scsi_device *sdp; + struct request_queue *q; + struct request *req; + struct scsi_request *scsireq; + struct bio bio; + struct bio *pbio = &bio; + struct scsi_sense_hdr sshdr; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(hba->host->host_lock, flags); + sdp = hba->sdev_ufs_lu[hpb->lun]; + if (sdp) { + ret = scsi_device_get(sdp); + if (!ret && !scsi_device_online(sdp)) { + ret = -ENODEV; + scsi_device_put(sdp); + } + } else { + ret = -ENODEV; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (ret) + return ret; + + q = sdp->request_queue; + + req = blk_get_request(q, REQ_OP_SCSI_IN, BLK_MQ_REQ_PREEMPT); + if (IS_ERR(req)) { + ret = PTR_ERR(req); + goto out_put; + } + scsireq = scsi_req(req); + + scsireq->cmd_len = COMMAND_SIZE(cmd[0]); + if (scsireq->cmd_len > sizeof(scsireq->__cmd)) { + scsireq->cmd = kmalloc(scsireq->cmd_len, __GFP_RECLAIM); + if (!scsireq->cmd) { + ret = -ENOMEM; + goto out_put_request; + } + } else { + scsireq->cmd = scsireq->__cmd; + } + memcpy(scsireq->cmd, cmd, scsireq->cmd_len); + + scsireq->retries = 3; + req->timeout = msecs_to_jiffies(10000); + req->rq_flags |= RQF_QUIET | RQF_PREEMPT; + + ret = skhpb_add_bio_page(hpb, q, &bio, hpb->bvec, cp->mctx); + if (ret) + goto out_put_scsi_req; + blk_rq_append_bio(req, &pbio); + + blk_execute_rq(q, NULL, req, 1); + + if (scsireq->result) { + scsi_normalize_sense(scsireq->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); + SKHPB_DRIVER_E("code %x sense_key %x asc %x ascq %x", + sshdr.response_code, sshdr.sense_key, sshdr.asc, + sshdr.ascq); + SKHPB_DRIVER_E("byte4 %x byte5 %x byte6 %x additional_len %x", + sshdr.byte4, sshdr.byte5, sshdr.byte6, + sshdr.additional_length); + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_error_active_subregion(hpb, cp); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + ret = -EIO; + } else { + spin_lock_irqsave(&hpb->hpb_lock, flags); + ret = skhpb_clean_dirty_bitmap(hpb, cp); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + if (ret) { + SKHPB_DRIVER_E("skhpb_clean_dirty_bitmap error %d", ret); + } + ret = 0; + } + +out_put_scsi_req: + scsi_req_free_cmd(scsireq); +out_put_request: + blk_put_request(req); +out_put: + scsi_device_put(sdp); + return ret; +} + +static int skhpb_issue_map_req_from_list(struct skhpb_lu *hpb) +{ + struct skhpb_subregion *cp, *next_cp; + int ret; + unsigned long flags; + + LIST_HEAD(req_list); + + spin_lock_irqsave(&hpb->hpb_lock, flags); + list_splice_init(&hpb->lh_subregion_req, &req_list); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + + list_for_each_entry_safe(cp, next_cp, &req_list, list_subregion) { + int subregion_mem_size = + cp->last ? hpb->last_subregion_mem_size : hpb->subregion_mem_size; + unsigned char cmd[10] = { 0 }; + + skhpb_set_read_buf_cmd(cmd, cp->region, cp->subregion, + subregion_mem_size); + + SKHPB_DRIVER_D("issue map_request: %d - %d/%d\n", + cp->region, cp->subregion, subregion_mem_size); + + ret = skhpb_execute_req(hpb, cmd, cp); + if (ret < 0) { + SKHPB_DRIVER_E("region %d sub %d failed with err %d", + cp->region, cp->subregion, ret); + continue; + } + + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_clean_active_subregion(hpb, cp); + list_del_init(&cp->list_subregion); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + } + + return 0; +} + +static void skhpb_pinned_work_handler(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct skhpb_lu *hpb = + container_of(dwork, struct skhpb_lu, skhpb_pinned_work); + int ret; + + SKHPB_DRIVER_D("worker start\n"); + + if (!list_empty(&hpb->lh_subregion_req)) { + pm_runtime_get_sync(SKHPB_DEV(hpb)); + ret = skhpb_issue_map_req_from_list(hpb); + /* + * if its function failed at init time, + * skhpb-device will request map-req, + * so it is not critical-error, and just finish work-handler + */ + if (ret) + SKHPB_DRIVER_E("failed map-issue. ret %d\n", ret); + pm_runtime_mark_last_busy(SKHPB_DEV(hpb)); + pm_runtime_put_noidle(SKHPB_DEV(hpb)); + } + + SKHPB_DRIVER_D("worker end\n"); +} + +static void skhpb_map_req_retry_work_handler(struct work_struct *work) +{ + struct skhpb_lu *hpb; + struct delayed_work *dwork = to_delayed_work(work); + struct skhpb_map_req *map_req; + int ret = 0; + + LIST_HEAD(retry_list); + + hpb = container_of(dwork, struct skhpb_lu, skhpb_map_req_retry_work); + SKHPB_DRIVER_D("retry worker start"); + + spin_lock_bh(&hpb->map_list_lock); + list_splice_init(&hpb->lh_map_req_retry, &retry_list); + spin_unlock_bh(&hpb->map_list_lock); + + while (1) { + map_req = list_first_entry_or_null(&retry_list, + struct skhpb_map_req, list_map_req); + if (!map_req) { + SKHPB_DRIVER_D("There is no map_req"); + break; + } + list_del(&map_req->list_map_req); + + map_req->retry_cnt++; + + ret = skhpb_map_req_issue(hpb, map_req); + if (ret) { + SKHPB_DRIVER_E("skhpb_map_req_issue error %d", ret); + goto wakeup_ee_worker; + } + } + SKHPB_DRIVER_D("worker end"); + return; + +wakeup_ee_worker: + hpb->hba->skhpb_state = SKHPB_FAILED; + schedule_work(&hpb->hba->skhpb_eh_work); +} + +static void skhpb_delayed_rsp_work_handler(struct work_struct *work) +{ + struct skhpb_lu *hpb = container_of(work, struct skhpb_lu, skhpb_rsp_work); + struct skhpb_rsp_info *rsp_info; + unsigned long flags; + + SKHPB_DRIVER_D("rsp_work enter"); + + while (1) { + if(hpb->hba->clk_gating.is_suspended) { + SKHPB_DRIVER_D("rsp_work break"); + break; + } + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + rsp_info = list_first_entry_or_null(&hpb->lh_rsp_info, + struct skhpb_rsp_info, list_rsp_info); + if (!rsp_info) { + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + break; + } + + SKHPB_RSP_TIME(rsp_info->RSP_start); + + list_del_init(&rsp_info->list_rsp_info); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + + switch (rsp_info->type) { + case SKHPB_RSP_REQ_REGION_UPDATE: + skhpb_rsp_map_cmd_req(hpb, rsp_info); + break; + + case SKHPB_RSP_HPB_RESET: + skhpb_purge_active_block(hpb); + skhpb_map_loading_trigger(hpb, true, false); + break; + + default: + break; + } + + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + list_add_tail(&rsp_info->list_rsp_info, &hpb->lh_rsp_info_free); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + } + SKHPB_DRIVER_D("rsp_work end"); +} + +static void skhpb_init_constant(void) +{ + skhpb_sects_per_blk_shift = ffs(SKHPB_BLOCK) - ffs(SKHPB_SECTOR); + SKHPB_DRIVER_D("skhpb_sects_per_blk_shift: %u %u\n", + skhpb_sects_per_blk_shift, ffs(SKHPB_SECTORS_PER_BLOCK) - 1); + + skhpb_bits_per_dword_shift = ffs(SKHPB_BITS_PER_DWORD) - 1; + skhpb_bits_per_dword_mask = SKHPB_BITS_PER_DWORD - 1; + SKHPB_DRIVER_D("bits_per_dword %u shift %u mask 0x%X\n", + SKHPB_BITS_PER_DWORD, skhpb_bits_per_dword_shift, skhpb_bits_per_dword_mask); +} + +static void skhpb_table_mempool_remove(struct skhpb_lu *hpb) +{ + struct skhpb_map_ctx *mctx, *next; + int i; + + /* + * the mctx in the lh_map_ctx has been allocated completely. + */ + list_for_each_entry_safe(mctx, next, &hpb->lh_map_ctx, list_table) { + list_del(&mctx->list_table); + + for (i = 0; i < hpb->mpages_per_subregion; i++) + __free_page(mctx->m_page[i]); + + kvfree(mctx->ppn_dirty); + kfree(mctx->m_page); + kfree(mctx); + skhpb_alloc_mctx--; + } +} + +static int skhpb_init_pinned_active_block(struct skhpb_lu *hpb, + struct skhpb_region *cb) +{ + struct skhpb_subregion *cp; + int subregion, j; + int err = 0; + unsigned long flags; + + for (subregion = 0 ; subregion < cb->subregion_count ; subregion++) { + cp = cb->subregion_tbl + subregion; + + cp->mctx = skhpb_get_map_ctx(hpb); + if (IS_ERR(cp->mctx)) { + err = PTR_ERR(cp->mctx); + goto release; + } + spin_lock_irqsave(&hpb->hpb_lock, flags); + skhpb_add_subregion_to_req_list(hpb, cp); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + } + return 0; + +release: + for (j = 0 ; j < subregion ; j++) { + cp = cb->subregion_tbl + j; + skhpb_put_map_ctx(hpb, cp->mctx); + } + return err; +} + +static inline bool skhpb_is_pinned( + struct skhpb_lu_desc *lu_desc, int region) +{ + if (lu_desc->lu_hpb_pinned_end_offset != -1 && + region >= lu_desc->hpb_pinned_region_startidx && + region <= lu_desc->lu_hpb_pinned_end_offset) + return true; + + return false; +} + +static inline void skhpb_init_jobs(struct skhpb_lu *hpb) +{ + INIT_DELAYED_WORK(&hpb->skhpb_pinned_work, skhpb_pinned_work_handler); + INIT_DELAYED_WORK(&hpb->skhpb_map_req_retry_work, skhpb_map_req_retry_work_handler); + INIT_WORK(&hpb->skhpb_rsp_work, skhpb_delayed_rsp_work_handler); +} + +static inline void skhpb_cancel_jobs(struct skhpb_lu *hpb) +{ + cancel_delayed_work_sync(&hpb->skhpb_pinned_work); + cancel_work_sync(&hpb->skhpb_rsp_work); + cancel_delayed_work_sync(&hpb->skhpb_map_req_retry_work); +} + +static void skhpb_init_subregion_tbl(struct skhpb_lu *hpb, + struct skhpb_region *cb, bool last_region) +{ + int subregion; + + for (subregion = 0 ; subregion < cb->subregion_count ; subregion++) { + struct skhpb_subregion *cp = cb->subregion_tbl + subregion; + + cp->region = cb->region; + cp->subregion = subregion; + cp->subregion_state = SKHPB_SUBREGION_UNUSED; + cp->last = (last_region && subregion == cb->subregion_count - 1); + } +} + +static inline int skhpb_alloc_subregion_tbl(struct skhpb_lu *hpb, + struct skhpb_region *cb, int subregion_count) +{ + cb->subregion_tbl = kzalloc(sizeof(struct skhpb_subregion) * subregion_count, + GFP_KERNEL); + if (!cb->subregion_tbl) + return -ENOMEM; + + cb->subregion_count = subregion_count; + + return 0; +} + +static int skhpb_table_mempool_init(struct skhpb_lu *hpb, + int num_regions, int subregions_per_region, + int entry_count, int entry_byte) +{ + int i, j; + struct skhpb_map_ctx *mctx = NULL; + + for (i = 0 ; i < num_regions * subregions_per_region ; i++) { + mctx = kzalloc(sizeof(struct skhpb_map_ctx), GFP_KERNEL); + if (!mctx) + goto release_mem; + + mctx->m_page = kzalloc(sizeof(struct page *) * + hpb->mpages_per_subregion, GFP_KERNEL); + if (!mctx->m_page) + goto release_mem; + + mctx->ppn_dirty = kvzalloc(entry_count / BITS_PER_BYTE, GFP_KERNEL); + if (!mctx->ppn_dirty) + goto release_mem; + + for (j = 0; j < hpb->mpages_per_subregion; j++) { + mctx->m_page[j] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!mctx->m_page[j]) + goto release_mem; + } + /* SKSKHPB_DRIVER_D("[%d] mctx->m_page %p get_order %d\n", i, + mctx->m_page, get_order(hpb->mpages_per_subregion)); */ + + INIT_LIST_HEAD(&mctx->list_table); + list_add(&mctx->list_table, &hpb->lh_map_ctx); + + hpb->debug_free_table++; + } + + skhpb_alloc_mctx = num_regions * subregions_per_region; + /* SKSKHPB_DRIVER_D("number of mctx %d %d %d. debug_free_table %d\n", + num_regions * subregions_per_region, num_regions, + subregions_per_region, hpb->debug_free_table); */ + return 0; + +release_mem: + /* + * mctxs already added in lh_map_ctx will be removed + * in the caller function. + */ + if (!mctx) + goto out; + + if (mctx->m_page) { + for (j = 0; j < hpb->mpages_per_subregion; j++) + if (mctx->m_page[j]) + __free_page(mctx->m_page[j]); + kfree(mctx->m_page); + } + kvfree(mctx->ppn_dirty); + kfree(mctx); +out: + return -ENOMEM; +} + +static int skhpb_req_mempool_init(struct ufs_hba *hba, + struct skhpb_lu *hpb, int queue_depth) +{ + struct skhpb_rsp_info *rsp_info = NULL; + struct skhpb_map_req *map_req = NULL; + int i; + + if (!queue_depth) { + queue_depth = hba->nutrs; + SKHPB_DRIVER_E("lu_queue_depth is 0. we use device's queue info.\n"); + SKHPB_DRIVER_E("hba->nutrs = %d\n", hba->nutrs); + } + INIT_LIST_HEAD(&hpb->lh_rsp_info_free); + INIT_LIST_HEAD(&hpb->lh_map_req_free); + INIT_LIST_HEAD(&hpb->lh_map_req_retry); + + hpb->rsp_info = vzalloc(queue_depth * sizeof(struct skhpb_rsp_info)); + if (!hpb->rsp_info) + goto release_mem; + + hpb->map_req = vzalloc(queue_depth * sizeof(struct skhpb_map_req)); + if (!hpb->map_req) + goto release_mem; + + for (i = 0; i < queue_depth; i++) { + rsp_info = hpb->rsp_info + i; + INIT_LIST_HEAD(&rsp_info->list_rsp_info); + list_add_tail(&rsp_info->list_rsp_info, &hpb->lh_rsp_info_free); + } + + for (i = 0; i < queue_depth; i++) { + map_req = hpb->map_req + i; + INIT_LIST_HEAD(&map_req->list_map_req); + list_add_tail(&map_req->list_map_req, &hpb->lh_map_req_free); + } + + return 0; + +release_mem: + return -ENOMEM; +} + +static void skhpb_init_lu_constant(struct skhpb_lu *hpb, + struct skhpb_lu_desc *lu_desc, + struct skhpb_func_desc *func_desc) +{ + unsigned long long region_unit_size, region_mem_size; + unsigned long long last_subregion_unit_size; + int entries_per_region; + + /* From descriptors */ + region_unit_size = SKHPB_SECTOR * (1ULL << func_desc->hpb_region_size); + region_mem_size = region_unit_size / SKHPB_BLOCK * SKHPB_ENTRY_SIZE; + + hpb->subregion_unit_size = SKHPB_SECTOR * (1ULL << func_desc->hpb_subregion_size); + hpb->subregion_mem_size = hpb->subregion_unit_size / + SKHPB_BLOCK * SKHPB_ENTRY_SIZE; + if (func_desc->hpb_region_size == func_desc->hpb_subregion_size) + hpb->identical_size = true; + else + hpb->identical_size = false; + + last_subregion_unit_size = + lu_desc->lu_logblk_cnt * + (1ULL << lu_desc->lu_logblk_size) % hpb->subregion_unit_size; + hpb->last_subregion_mem_size = + last_subregion_unit_size / SKHPB_BLOCK * SKHPB_ENTRY_SIZE; + if (hpb->last_subregion_mem_size == 0) + hpb->last_subregion_mem_size = hpb->subregion_mem_size; + hpb->hpb_ver = func_desc->hpb_ver; + hpb->lu_max_active_regions = lu_desc->lu_max_active_hpb_regions; + hpb->lru_info.max_lru_active_count = + lu_desc->lu_max_active_hpb_regions + - lu_desc->lu_num_hpb_pinned_regions; + + /* relation : lu <-> region <-> sub region <-> entry */ + hpb->lu_num_blocks = lu_desc->lu_logblk_cnt; + entries_per_region = region_mem_size / SKHPB_ENTRY_SIZE; + hpb->entries_per_subregion = hpb->subregion_mem_size / SKHPB_ENTRY_SIZE; + hpb->subregions_per_region = region_mem_size / hpb->subregion_mem_size; + + hpb->hpb_control_mode = func_desc->hpb_control_mode; +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + hpb->ppn_dirties_per_subregion = + hpb->entries_per_subregion/BITS_PER_PPN_DIRTY; + /* SKSKHPB_DRIVER_D("ppn_dirties_per_subregion:%d, %d \n", + hpb->ppn_dirties_per_subregion, BITS_PER_PPN_DIRTY); */ +#endif + /* + * 1. regions_per_lu + * = (lu_num_blocks * 4096) / region_unit_size + * = (lu_num_blocks * SKHPB_ENTRY_SIZE) / region_mem_size + * = lu_num_blocks / (region_mem_size / SKHPB_ENTRY_SIZE) + * + * 2. regions_per_lu = lu_num_blocks / subregion_mem_size (is trik...) + * if SKHPB_ENTRY_SIZE != subregions_per_region, it is error. + */ + hpb->regions_per_lu = (hpb->lu_num_blocks + + (region_mem_size / SKHPB_ENTRY_SIZE) - 1) + / (region_mem_size / SKHPB_ENTRY_SIZE); + hpb->subregions_per_lu = (hpb->lu_num_blocks + + (hpb->subregion_mem_size / SKHPB_ENTRY_SIZE) - 1) + / (hpb->subregion_mem_size / SKHPB_ENTRY_SIZE); + + /* mempool info */ + hpb->mpage_bytes = PAGE_SIZE; + hpb->mpages_per_subregion = hpb->subregion_mem_size / hpb->mpage_bytes; + + /* Bitmask Info. */ + hpb->dwords_per_subregion = hpb->entries_per_subregion / SKHPB_BITS_PER_DWORD; + hpb->entries_per_region_shift = ffs(entries_per_region) - 1; + hpb->entries_per_region_mask = entries_per_region - 1; + hpb->entries_per_subregion_shift = ffs(hpb->entries_per_subregion) - 1; + hpb->entries_per_subregion_mask = hpb->entries_per_subregion - 1; + + SKHPB_DRIVER_I("===== Device Descriptor =====\n"); + SKHPB_DRIVER_I("hpb_region_size = %d, hpb_subregion_size = %d\n", + func_desc->hpb_region_size, + func_desc->hpb_subregion_size); + SKHPB_DRIVER_I("===== Constant Values =====\n"); + SKHPB_DRIVER_I("region_unit_size = %llu, region_mem_size %llu\n", + region_unit_size, region_mem_size); + SKHPB_DRIVER_I("subregion_unit_size = %llu, subregion_mem_size %d\n", + hpb->subregion_unit_size, hpb->subregion_mem_size); + SKHPB_DRIVER_I("last_subregion_mem_size = %d\n", hpb->last_subregion_mem_size); + SKHPB_DRIVER_I("lu_num_blks = %llu, reg_per_lu = %d, subreg_per_lu = %d\n", + hpb->lu_num_blocks, hpb->regions_per_lu, + hpb->subregions_per_lu); + SKHPB_DRIVER_I("subregions_per_region = %d\n", + hpb->subregions_per_region); + SKHPB_DRIVER_I("entries_per_region %u shift %u mask 0x%X\n", + entries_per_region, hpb->entries_per_region_shift, + hpb->entries_per_region_mask); + SKHPB_DRIVER_I("entries_per_subregion %u shift %u mask 0x%X\n", + hpb->entries_per_subregion, + hpb->entries_per_subregion_shift, + hpb->entries_per_subregion_mask); + SKHPB_DRIVER_I("mpages_per_subregion : %d\n", + hpb->mpages_per_subregion); + SKHPB_DRIVER_I("===================================\n"); +} + +static int skhpb_lu_hpb_init(struct ufs_hba *hba, struct skhpb_lu *hpb, + struct skhpb_func_desc *func_desc, + struct skhpb_lu_desc *lu_desc, int lun, + bool *do_work_lun) +{ + struct skhpb_region *cb; + struct skhpb_subregion *cp; + int region, subregion; + int total_subregion_count, subregion_count; + int ret, j; + + *do_work_lun = false; + + spin_lock_init(&hpb->hpb_lock); + spin_lock_init(&hpb->rsp_list_lock); + spin_lock_init(&hpb->map_list_lock); + + /* init lru information */ + INIT_LIST_HEAD(&hpb->lru_info.lru); + hpb->lru_info.selection_type = TYPE_LRU; + + INIT_LIST_HEAD(&hpb->lh_subregion_req); + INIT_LIST_HEAD(&hpb->lh_rsp_info); + INIT_LIST_HEAD(&hpb->lh_map_ctx); + + hpb->lu_hpb_enable = true; + + skhpb_init_lu_constant(hpb, lu_desc, func_desc); + + hpb->region_tbl = vzalloc(sizeof(struct skhpb_region) * hpb->regions_per_lu); + if (!hpb->region_tbl) + return -ENOMEM; + + SKHPB_DRIVER_D("active_block_table bytes: %lu\n", + (sizeof(struct skhpb_region) * hpb->regions_per_lu)); + + ret = skhpb_table_mempool_init(hpb, + lu_desc->lu_max_active_hpb_regions, + hpb->subregions_per_region, + hpb->entries_per_subregion, SKHPB_ENTRY_SIZE); + if (ret) { + SKHPB_DRIVER_E("ppn table mempool init fail!\n"); + goto release_mempool; + } + + ret = skhpb_req_mempool_init(hba, hpb, lu_desc->lu_queue_depth); + if (ret) { + SKHPB_DRIVER_E("rsp_info_mempool init fail!\n"); + goto release_mempool; + } + + total_subregion_count = hpb->subregions_per_lu; + + skhpb_init_jobs(hpb); + + SKHPB_DRIVER_D("total_subregion_count: %d\n", total_subregion_count); + for (region = 0, subregion_count = 0, + total_subregion_count = hpb->subregions_per_lu; + region < hpb->regions_per_lu; + region++, total_subregion_count -= subregion_count) { + cb = hpb->region_tbl + region; + cb->region = region; + + /* init lru region information*/ + INIT_LIST_HEAD(&cb->list_region); + cb->hit_count = 0; + + subregion_count = min(total_subregion_count, + hpb->subregions_per_region); + /* SKSKHPB_DRIVER_D("total: %d subregion_count: %d\n", + total_subregion_count, subregion_count); */ + + ret = skhpb_alloc_subregion_tbl(hpb, cb, subregion_count); + if (ret) + goto release_region_cp; + skhpb_init_subregion_tbl(hpb, cb, region == hpb->regions_per_lu - 1); + + if (skhpb_is_pinned(lu_desc, region)) { + SKHPB_DRIVER_D("region: %d PINNED %d ~ %d\n", + region, lu_desc->hpb_pinned_region_startidx, + lu_desc->lu_hpb_pinned_end_offset); + ret = skhpb_init_pinned_active_block(hpb, cb); + if (ret) + goto release_region_cp; + *do_work_lun = true; + cb->is_pinned = true; + cb->region_state = SKHPB_REGION_ACTIVE; + } else { + /* SKSKHPB_DRIVER_D("region: %d inactive\n", cb->region); */ + cb->is_pinned = false; + cb->region_state = SKHPB_REGION_INACTIVE; + } + } + if (total_subregion_count != 0) { + SKHPB_DRIVER_E("error total_subregion_count: %d\n", + total_subregion_count); + goto release_region_cp; + } + hpb->hba = hba; + hpb->lun = lun; + /* + * even if creating sysfs failed, skhpb could run normally. + * so we don't deal with error handling + */ + skhpb_create_sysfs(hba, hpb); + return 0; + +release_region_cp: + for (j = 0 ; j < region ; j++) { + cb = hpb->region_tbl + j; + if (cb->subregion_tbl) { + for (subregion = 0; subregion < cb->subregion_count; + subregion++) { + cp = cb->subregion_tbl + subregion; + + if (cp->mctx) + skhpb_put_map_ctx(hpb, cp->mctx); + } + kfree(cb->subregion_tbl); + } + } + +release_mempool: + skhpb_table_mempool_remove(hpb); + *do_work_lun = false; + return ret; +} + +static int skhpb_get_hpb_lu_desc(struct ufs_hba *hba, + struct skhpb_lu_desc *lu_desc, int lun) +{ + int ret; + u8 logical_buf[SKHPB_QUERY_DESC_UNIT_MAX_SIZE] = { 0 }; + + ret = skhpb_read_unit_desc(hba, lun, logical_buf, + SKHPB_QUERY_DESC_UNIT_MAX_SIZE); + if (ret) { + SKHPB_DRIVER_E("read unit desc failed. ret %d\n", ret); + return ret; + } + + lu_desc->lu_queue_depth = logical_buf[UNIT_DESC_PARAM_LU_Q_DEPTH]; + + // 2^log, ex) 0x0C = 4KB + lu_desc->lu_logblk_size = logical_buf[UNIT_DESC_PARAM_LOGICAL_BLK_SIZE]; + lu_desc->lu_logblk_cnt = + get_unaligned_be64(&logical_buf[UNIT_DESC_PARAM_LOGICAL_BLK_COUNT]); + + if (logical_buf[UNIT_DESC_PARAM_LU_ENABLE] == LU_HPB_ENABLE) + lu_desc->lu_hpb_enable = true; + else + lu_desc->lu_hpb_enable = false; + + lu_desc->lu_max_active_hpb_regions = + get_unaligned_be16(logical_buf + + UNIT_DESC_HPB_LU_MAX_ACTIVE_REGIONS); + lu_desc->hpb_pinned_region_startidx = + get_unaligned_be16(logical_buf + + UNIT_DESC_HPB_LU_PIN_REGION_START_OFFSET); + lu_desc->lu_num_hpb_pinned_regions = + get_unaligned_be16(logical_buf + + UNIT_DESC_HPB_LU_NUM_PIN_REGIONS); + + if (lu_desc->lu_hpb_enable) { + SKHPB_DRIVER_D("LUN(%d) [0A] bLogicalBlockSize %d\n", + lun, lu_desc->lu_logblk_size); + SKHPB_DRIVER_D("LUN(%d) [0B] qLogicalBlockCount %llu\n", + lun, lu_desc->lu_logblk_cnt); + SKHPB_DRIVER_D("LUN(%d) [03] bLuEnable %d\n", + lun, logical_buf[UNIT_DESC_PARAM_LU_ENABLE]); + SKHPB_DRIVER_D("LUN(%d) [06] bLuQueueDepth %d\n", + lun, lu_desc->lu_queue_depth); + SKHPB_DRIVER_D("LUN(%d) [23:24] wLUMaxActiveHPBRegions %d\n", + lun, lu_desc->lu_max_active_hpb_regions); + SKHPB_DRIVER_D("LUN(%d) [25:26] wHPBPinnedRegionStartIdx %d\n", + lun, lu_desc->hpb_pinned_region_startidx); + SKHPB_DRIVER_D("LUN(%d) [27:28] wNumHPBPinnedRegions %d\n", + lun, lu_desc->lu_num_hpb_pinned_regions); + } + + if (lu_desc->lu_num_hpb_pinned_regions > 0) { + lu_desc->lu_hpb_pinned_end_offset = + lu_desc->hpb_pinned_region_startidx + + lu_desc->lu_num_hpb_pinned_regions - 1; + } else + lu_desc->lu_hpb_pinned_end_offset = -1; + + if (lu_desc->lu_hpb_enable) + SKHPB_DRIVER_I("Enable, LU: %d, MAX_REGION: %d, PIN: %d - %d\n", + lun, + lu_desc->lu_max_active_hpb_regions, + lu_desc->hpb_pinned_region_startidx, + lu_desc->lu_num_hpb_pinned_regions); + return 0; +} + +static void skhpb_quirk_setup(struct ufs_hba *hba, + struct skhpb_func_desc *desc) +{ + if (hba->dev_quirks & SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP) { + hba->skhpb_quirk |= SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP; + SKHPB_DRIVER_I("QUIRK set PURGE_HINT_INFO_WHEN_SLEEP\n"); + } + if (desc->hpb_ver >= 0x0101) { + hba->skhpb_quirk |= SKHPB_QUIRK_USE_READ_16_FOR_ENCRYPTION; + SKHPB_DRIVER_I("QUIRK set USE_READ_16_FOR_ENCRYPTION\n"); + } + if (desc->hpb_ver == 0x0102) { + hba->skhpb_quirk |= SKHPB_QUIRK_ALWAYS_DEVICE_CONTROL_MODE; + SKHPB_DRIVER_I("QUIRK set ALWAYS_DEVICE_CONTROL_MODE\n"); + } +} + +static int skhpb_read_dev_desc_support(struct ufs_hba *hba, + struct skhpb_func_desc *desc) +{ + u8 desc_buf[SKHPB_QUERY_DESC_DEVICE_MAX_SIZE]; + int err; + + err = skhpb_read_device_desc(hba, desc_buf, + SKHPB_QUERY_DESC_DEVICE_MAX_SIZE); + if (err) + return err; + + if (desc_buf[DEVICE_DESC_PARAM_UFS_FEAT] & + SKHPB_UFS_FEATURE_SUPPORT_HPB_BIT) { + hba->skhpb_feat |= SKHPB_UFS_FEATURE_SUPPORT_HPB_BIT; + SKHPB_DRIVER_I("FeaturesSupport= support\n"); + } else { + SKHPB_DRIVER_I("FeaturesSupport= not support\n"); + return -ENODEV; + } + + desc->lu_cnt = desc_buf[DEVICE_DESC_PARAM_NUM_LU]; + SKHPB_DRIVER_D("Dev LU count= %d\n", desc->lu_cnt); + + desc->spec_ver = + (u16)SKHPB_SHIFT_BYTE_1(desc_buf[DEVICE_DESC_PARAM_SPEC_VER]) | + (u16)SKHPB_SHIFT_BYTE_0(desc_buf[DEVICE_DESC_PARAM_SPEC_VER + 1]); + SKHPB_DRIVER_I("Dev Spec Ver= %x.%x\n", + SKHPB_GET_BYTE_1(desc->spec_ver), + SKHPB_GET_BYTE_0(desc->spec_ver) ); + + desc->hpb_ver = + (u16)SKHPB_SHIFT_BYTE_1(desc_buf[DEVICE_DESC_PARAM_HPB_VER]) | + (u16)SKHPB_SHIFT_BYTE_0(desc_buf[DEVICE_DESC_PARAM_HPB_VER + 1]); + SKHPB_DRIVER_I("Dev Ver= %x.%x.%x, DD Ver= %x.%x.%x\n", + (desc->hpb_ver >> 8) & 0xf, + (desc->hpb_ver >> 4) & 0xf, + (desc->hpb_ver >> 0) & 0xf, + SKHPB_GET_BYTE_2(SKHPB_DD_VER), + SKHPB_GET_BYTE_1(SKHPB_DD_VER), + SKHPB_GET_BYTE_0(SKHPB_DD_VER)); + + skhpb_quirk_setup(hba, desc); + + if (hba->skhpb_quirk & SKHPB_QUIRK_ALWAYS_DEVICE_CONTROL_MODE) + desc->hpb_control_mode = DEV_CTRL_MODE; + else + desc->hpb_control_mode = (u8)desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL]; + + + SKHPB_DRIVER_I("HPB Control Mode = %s", + (desc->hpb_control_mode)?"DEV MODE":"HOST MODE"); + if (desc->hpb_control_mode == HOST_CTRL_MODE) { + SKHPB_DRIVER_E("Driver does not support Host Control Mode"); + return -ENODEV; + } + hba->hpb_control_mode = desc->hpb_control_mode; + return 0; +} + +static int skhpb_read_geo_desc_support(struct ufs_hba *hba, + struct skhpb_func_desc *desc) +{ + int err; + u8 geometry_buf[SKHPB_QUERY_DESC_GEOMETRY_MAX_SIZE]; + + err = skhpb_read_geo_desc(hba, geometry_buf, + SKHPB_QUERY_DESC_GEOMETRY_MAX_SIZE); + if (err) + return err; + + desc->hpb_region_size = geometry_buf[GEOMETRY_DESC_HPB_REGION_SIZE]; + desc->hpb_number_lu = geometry_buf[GEOMETRY_DESC_HPB_NUMBER_LU]; + desc->hpb_subregion_size = + geometry_buf[GEOMETRY_DESC_HPB_SUBREGION_SIZE]; + desc->hpb_device_max_active_regions = + get_unaligned_be16(geometry_buf + + GEOMETRY_DESC_HPB_DEVICE_MAX_ACTIVE_REGIONS); + + SKHPB_DRIVER_D("[48] bHPBRegionSize %u\n", desc->hpb_region_size); + SKHPB_DRIVER_D("[49] bHPBNumberLU %u\n", desc->hpb_number_lu); + SKHPB_DRIVER_D("[4A] bHPBSubRegionSize %u\n", desc->hpb_subregion_size); + SKHPB_DRIVER_D("[4B:4C] wDeviceMaxActiveHPBRegions %u\n", + desc->hpb_device_max_active_regions); + + if (desc->hpb_number_lu == 0) { + SKHPB_DRIVER_E("HPB is not supported\n"); + return -ENODEV; + } + /* for activation */ + hba->skhpb_max_regions = desc->hpb_device_max_active_regions; + return 0; +} + +int skhpb_control_validation(struct ufs_hba *hba, + struct skhpb_config_desc *config) +{ + unsigned int num_regions = 0; + int lun; + + if (!(hba->skhpb_feat & SKHPB_UFS_FEATURE_SUPPORT_HPB_BIT)) + return -ENOTSUPP; + + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) { + unsigned char *unit = config->unit[lun]; + + if (unit[SKHPB_CONF_LU_ENABLE] >= LU_SET_MAX) + return -EINVAL; + + /* total should not exceed max_active_regions */ + num_regions += unit[SKHPB_CONF_ACTIVE_REGIONS] << 8; + num_regions += unit[SKHPB_CONF_ACTIVE_REGIONS + 1]; + if (num_regions > hba->skhpb_max_regions) + return -EINVAL; + } + return 0; +} + +static int skhpb_init(struct ufs_hba *hba) +{ + struct skhpb_func_desc func_desc; + int lun, ret, retries; + int hpb_dev = 0; + bool do_work; + + pm_runtime_get_sync(hba->dev); + ret = skhpb_read_dev_desc_support(hba, &func_desc); + if (ret) + goto out_state; + + ret = skhpb_read_geo_desc_support(hba, &func_desc); + if (ret) + goto out_state; + + for (retries = 0; retries < 20; retries++) { + if (!hba->lrb_in_use) { + ret = ufshcd_query_flag(hba, + UPIU_QUERY_OPCODE_SET_FLAG, + QUERY_FLAG_IDN_HPB_RESET, 0, + NULL); + if (!ret) { + SKHPB_DRIVER_I("Query fHPBReset is successfully sent retries = %d\n", retries); + break; + } + } + else + msleep(200); + } + if (ret == 0) { + bool fHPBReset = true; + + ufshcd_query_flag_retry(hba, + UPIU_QUERY_OPCODE_READ_FLAG, + QUERY_FLAG_IDN_HPB_RESET, 0, + &fHPBReset); + if (fHPBReset) + SKHPB_DRIVER_I("fHPBReset still set\n"); + } + + skhpb_init_constant(); + + do_work = false; + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) { + struct skhpb_lu_desc lu_desc; + bool do_work_lun; + + ret = skhpb_get_hpb_lu_desc(hba, &lu_desc, lun); + if (ret) + goto out_state; + + if (lu_desc.lu_hpb_enable == false) + continue; + + hba->skhpb_lup[lun] = kzalloc(sizeof(struct skhpb_lu), + GFP_KERNEL); + if (!hba->skhpb_lup[lun]) { + ret = -ENOMEM; + goto out_free_mem; + } + + ret = skhpb_lu_hpb_init(hba, hba->skhpb_lup[lun], + &func_desc, &lu_desc, lun, &do_work_lun); + if (ret) { + if (ret == -ENODEV) + continue; + else + goto out_free_mem; + } + do_work |= do_work_lun; + hba->skhpb_quicklist_lu_enable[hpb_dev] = lun; + SKHPB_DRIVER_D("skhpb_quicklist_lu_enable[%d] = %d\n", hpb_dev, lun); + hpb_dev++; + } + + if (hpb_dev) + goto done; + + goto out_free_mem; + +done: + INIT_WORK(&hba->skhpb_eh_work, skhpb_error_handler); + hba->skhpb_state = SKHPB_PRESENT; + hba->issue_ioctl = false; + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + + if (do_work) { + for (lun = 0; lun < UFS_UPIU_MAX_GENERAL_LUN; lun++) { + struct skhpb_lu *hpb = hba->skhpb_lup[lun]; + + if (hpb) + schedule_delayed_work(&hpb->skhpb_pinned_work, 0); + } + } + + return 0; + +out_free_mem: + skhpb_release(hba, SKHPB_NOT_SUPPORTED); +out_state: + hba->skhpb_state = SKHPB_NOT_SUPPORTED; + pm_runtime_mark_last_busy(hba->dev); + pm_runtime_put_noidle(hba->dev); + return ret; +} + +static void skhpb_map_loading_trigger(struct skhpb_lu *hpb, + bool only_pinned, bool do_work_handler) +{ + int region, subregion; + unsigned long flags; + + if (do_work_handler) + goto work_out; + flush_delayed_work(&hpb->skhpb_pinned_work); + for (region = 0 ; region < hpb->regions_per_lu ; region++) { + struct skhpb_region *cb; + + cb = hpb->region_tbl + region; + + if (cb->region_state != SKHPB_REGION_ACTIVE && + !cb->is_pinned) + continue; + + if ((only_pinned && cb->is_pinned) || + !only_pinned) { + spin_lock_irqsave(&hpb->hpb_lock, flags); + for (subregion = 0; subregion < cb->subregion_count; + subregion++) + skhpb_add_subregion_to_req_list(hpb, + cb->subregion_tbl + subregion); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + do_work_handler = true; + } + } +work_out: + if (do_work_handler) + schedule_delayed_work(&hpb->skhpb_pinned_work, 0); +} + +static void skhpb_purge_active_block(struct skhpb_lu *hpb) +{ + int region, subregion; + int state; + struct skhpb_region *cb; + struct skhpb_subregion *cp; + unsigned long flags; + + spin_lock_irqsave(&hpb->hpb_lock, flags); + for (region = 0 ; region < hpb->regions_per_lu ; region++) { + cb = hpb->region_tbl + region; + + if (cb->region_state == SKHPB_REGION_INACTIVE) { + continue; + } + + if (cb->is_pinned){ + state = SKHPB_SUBREGION_DIRTY; + } else if (cb->region_state == SKHPB_REGION_ACTIVE) { + state = SKHPB_SUBREGION_UNUSED; + skhpb_cleanup_lru_info(&hpb->lru_info, cb); + } else { + SKHPB_DRIVER_E("Unsupported state of region\n"); + continue; + } + + SKHPB_DRIVER_D("region %d state %d dft %d\n", + region, state, + hpb->debug_free_table); + for (subregion = 0 ; subregion < cb->subregion_count; + subregion++) { + cp = cb->subregion_tbl + subregion; + + skhpb_purge_active_page(hpb, cp, state); + } + SKHPB_DRIVER_D("region %d state %d dft %d\n", + region, state, hpb->debug_free_table); + } + spin_unlock_irqrestore(&hpb->hpb_lock, flags); +} + +static void skhpb_retrieve_rsp_info(struct skhpb_lu *hpb) +{ + struct skhpb_rsp_info *rsp_info; + unsigned long flags; + + while (1) { + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + rsp_info = list_first_entry_or_null(&hpb->lh_rsp_info, + struct skhpb_rsp_info, list_rsp_info); + if (!rsp_info) { + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + break; + } + list_move_tail(&rsp_info->list_rsp_info, + &hpb->lh_rsp_info_free); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + SKHPB_DRIVER_D("add_list %p -> %p", + &hpb->lh_rsp_info_free, rsp_info); + } +} + +static void skhpb_probe(struct ufs_hba *hba) +{ + struct skhpb_lu *hpb; + int lu; + + for (lu = 0 ; lu < UFS_UPIU_MAX_GENERAL_LUN ; lu++) { + hpb = hba->skhpb_lup[lu]; + + if (hpb && hpb->lu_hpb_enable) { + skhpb_cancel_jobs(hpb); + skhpb_retrieve_rsp_info(hpb); + skhpb_purge_active_block(hpb); + SKHPB_DRIVER_I("SKHPB lun %d reset\n", lu); +// tasklet_init(&hpb->hpb_work_handler, +// skhpb_work_handler_fn, (unsigned long)hpb); + } + } + hba->skhpb_state = SKHPB_PRESENT; +} + +static void skhpb_destroy_subregion_tbl(struct skhpb_lu *hpb, + struct skhpb_region *cb) +{ + int subregion; + + for (subregion = 0 ; subregion < cb->subregion_count ; subregion++) { + struct skhpb_subregion *cp; + + cp = cb->subregion_tbl + subregion; + cp->subregion_state = SKHPB_SUBREGION_UNUSED; + skhpb_put_map_ctx(hpb, cp->mctx); + } + + kfree(cb->subregion_tbl); +} + +static void skhpb_destroy_region_tbl(struct skhpb_lu *hpb) +{ + int region; + + if (!hpb->region_tbl) + return; + + for (region = 0 ; region < hpb->regions_per_lu ; region++) { + struct skhpb_region *cb; + + cb = hpb->region_tbl + region; + if (cb->region_state == SKHPB_REGION_ACTIVE) { + cb->region_state = SKHPB_REGION_INACTIVE; + + skhpb_destroy_subregion_tbl(hpb, cb); + } + } + vfree(hpb->region_tbl); +} + +void skhpb_suspend(struct ufs_hba *hba) +{ + int lun; + unsigned long flags; + struct skhpb_lu *hpb; + struct skhpb_rsp_info *rsp_info; + struct skhpb_map_req *map_req; + + if (hba->skhpb_quirk & SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP) { + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) { + hpb = hba->skhpb_lup[lun]; + if (!hpb) + continue; + + while (1) { + spin_lock_irqsave(&hpb->rsp_list_lock, flags); + rsp_info = list_first_entry_or_null(&hpb->lh_rsp_info, + struct skhpb_rsp_info, list_rsp_info); + if (!rsp_info) { + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + break; + } + list_del_init(&rsp_info->list_rsp_info); + list_add_tail(&rsp_info->list_rsp_info, &hpb->lh_rsp_info_free); + atomic64_inc(&hpb->canceled_resp); + spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); + } + while (1) { + spin_lock_bh(&hpb->map_list_lock); + map_req = list_first_entry_or_null(&hpb->lh_map_req_retry, + struct skhpb_map_req, list_map_req); + if (!map_req) { + spin_unlock_bh(&hpb->map_list_lock); + break; + } + list_del_init(&map_req->list_map_req); + list_add_tail(&map_req->list_map_req, &hpb->lh_map_req_free); + atomic64_inc(&hpb->canceled_map_req); + spin_unlock_bh(&hpb->map_list_lock); + } + } + } + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) { + hpb = hba->skhpb_lup[lun]; + if (!hpb) + continue; + skhpb_cancel_jobs(hpb); + } +} + +void skhpb_resume(struct ufs_hba *hba) { + int lun; + struct skhpb_lu *hpb; + + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) { + hpb = hba->skhpb_lup[lun]; + if (!hpb) + continue; + spin_lock_bh(&hpb->map_list_lock); + if (!list_empty(&hpb->lh_map_req_retry)) + schedule_delayed_work(&hpb->skhpb_map_req_retry_work, msecs_to_jiffies(1000)); + spin_unlock_bh(&hpb->map_list_lock); + } +} + +void skhpb_release(struct ufs_hba *hba, int state) +{ + int lun; + + hba->skhpb_state = SKHPB_FAILED; + + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) { + struct skhpb_lu *hpb = hba->skhpb_lup[lun]; + + if (!hpb) + continue; + + hba->skhpb_lup[lun] = NULL; + + if (!hpb->lu_hpb_enable) + continue; + + hpb->lu_hpb_enable = false; + + skhpb_cancel_jobs(hpb); + + skhpb_destroy_region_tbl(hpb); + skhpb_table_mempool_remove(hpb); + + vfree(hpb->rsp_info); + vfree(hpb->map_req); + + kobject_uevent(&hpb->kobj, KOBJ_REMOVE); + kobject_del(&hpb->kobj); // TODO --count & del? + + kfree(hpb); + } + + if (skhpb_alloc_mctx != 0) + SKHPB_DRIVER_E("warning: skhpb_alloc_mctx %d", skhpb_alloc_mctx); + + hba->skhpb_state = state; +} + +void skhpb_init_handler(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct ufs_hba *hba = + container_of(dwork, struct ufs_hba, skhpb_init_work); + struct Scsi_Host *shost = hba->host; + bool async_scan; + + if (hba->skhpb_state == SKHPB_NOT_SUPPORTED) + return; + + spin_lock_irq(shost->host_lock); + async_scan = shost->async_scan; + spin_unlock_irq(shost->host_lock); + if (async_scan) { + schedule_delayed_work(dwork, msecs_to_jiffies(100)); + return; + } + + if (hba->skhpb_state == SKHPB_NEED_INIT) { + int err = skhpb_init(hba); + + if (hba->skhpb_state == SKHPB_NOT_SUPPORTED) + SKHPB_DRIVER_E("Run without HPB - err=%d\n", err); + } else if (hba->skhpb_state == SKHPB_RESET) { + skhpb_probe(hba); + } +} + +void ufshcd_init_hpb(struct ufs_hba *hba) +{ + int lun; + + hba->skhpb_feat = 0; + hba->skhpb_quirk = 0; + hba->skhpb_state = SKHPB_NEED_INIT; + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) { + hba->skhpb_lup[lun] = NULL; + hba->sdev_ufs_lu[lun] = NULL; + hba->skhpb_quicklist_lu_enable[lun] = SKHPB_U8_MAX; + } + + INIT_DELAYED_WORK(&hba->skhpb_init_work, skhpb_init_handler); +} + +static void skhpb_error_handler(struct work_struct *work) +{ + struct ufs_hba *hba; + + hba = container_of(work, struct ufs_hba, skhpb_eh_work); + + SKHPB_DRIVER_E("SKHPB driver runs without SKHPB\n"); + SKHPB_DRIVER_E("SKHPB will be removed from the kernel\n"); + + skhpb_release(hba, SKHPB_FAILED); +} + +static void skhpb_stat_init(struct skhpb_lu *hpb) +{ + atomic64_set(&hpb->hit, 0); + atomic64_set(&hpb->size_miss, 0); + atomic64_set(&hpb->region_miss, 0); + atomic64_set(&hpb->subregion_miss, 0); + atomic64_set(&hpb->entry_dirty_miss, 0); + atomic64_set(&hpb->w_map_req_cnt, 0); +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + atomic64_set(&hpb->lc_entry_dirty_miss, 0); + atomic64_set(&hpb->lc_reg_subreg_miss, 0); + atomic64_set(&hpb->lc_hit, 0); +#endif + atomic64_set(&hpb->rb_noti_cnt, 0); + atomic64_set(&hpb->reset_noti_cnt, 0); + atomic64_set(&hpb->map_req_cnt, 0); + atomic64_set(&hpb->region_evict, 0); + atomic64_set(&hpb->region_add, 0); + atomic64_set(&hpb->rb_fail, 0); + atomic64_set(&hpb->canceled_resp, 0); + atomic64_set(&hpb->canceled_map_req, 0); + atomic64_set(&hpb->alloc_map_req_cnt, 0); +} + + +static ssize_t skhpb_sysfs_info_from_region_store(struct skhpb_lu *hpb, + const char *buf, size_t count) +{ + unsigned long long value; + int region, subregion; + struct skhpb_region *cb; + struct skhpb_subregion *cp; + + if (kstrtoull(buf, 0, &value)) { + SKHPB_DRIVER_E( "kstrtoul error\n"); + return -EINVAL; + } + + if (value >= hpb->regions_per_lu) { + SKHPB_DRIVER_E("value %llu >= regions_per_lu %d error\n", + value, hpb->regions_per_lu); + return -EINVAL; + } + + region = (int)value; + cb = hpb->region_tbl + region; + + SKHPB_DRIVER_I("get_info_from_region[%d]=", region); + SKHPB_DRIVER_I("region %u state %s", region, + ((cb->region_state == SKHPB_REGION_INACTIVE) ? "INACTIVE" : + ((cb->region_state == SKHPB_REGION_ACTIVE) ? "ACTIVE" : "INVALID")) + ); + for (subregion = 0; subregion < cb->subregion_count; subregion++) { + cp = cb->subregion_tbl + subregion; + SKHPB_DRIVER_I("subregion %u state %s", subregion, + ((cp->subregion_state == SKHPB_SUBREGION_UNUSED) ? "UNUSED" : + ((cp->subregion_state == SKHPB_SUBREGION_DIRTY) ? "DIRTY" : + ((cp->subregion_state == SKHPB_SUBREGION_CLEAN) ? "CLEAN" : + (cp->subregion_state == SKHPB_SUBREGION_ISSUED) ? + "ISSUED" : "INVALID"))) + ); + } + return count; +} + +static ssize_t skhpb_sysfs_info_from_lba_store(struct skhpb_lu *hpb, + const char *buf, size_t count) +{ + skhpb_t ppn; + unsigned long long value; + unsigned int lpn; + int region, subregion, subregion_offset; + struct skhpb_region *cb; + struct skhpb_subregion *cp; + unsigned long flags; + int dirty; + + if (kstrtoull(buf, 0, &value)) { + SKHPB_DRIVER_E("kstrtoul error\n"); + return -EINVAL; + } + + if (value > hpb->lu_num_blocks * SKHPB_SECTORS_PER_BLOCK) { + SKHPB_DRIVER_E("value %llu > lu_num_blocks %llu error\n", + value, hpb->lu_num_blocks); + return -EINVAL; + } + lpn = value / SKHPB_SECTORS_PER_BLOCK; + + skhpb_get_pos_from_lpn(hpb, lpn, ®ion, &subregion, + &subregion_offset); + if(!skhpb_check_region_subregion_validity(hpb, region, subregion)) + return -EINVAL; + cb = hpb->region_tbl + region; + cp = cb->subregion_tbl + subregion; + + if (cb->region_state != SKHPB_REGION_INACTIVE) { + ppn = skhpb_get_ppn(cp->mctx, subregion_offset); + spin_lock_irqsave(&hpb->hpb_lock, flags); + dirty = skhpb_ppn_dirty_check(hpb, cp, subregion_offset); + spin_unlock_irqrestore(&hpb->hpb_lock, flags); + } else { + ppn = 0; + dirty = -1; + } + SKHPB_DRIVER_I("get_info_from_lba[%llu]=", value); + SKHPB_DRIVER_I("sector %llu region %d state %s subregion %d state %s", + value, region, + ((cb->region_state == SKHPB_REGION_INACTIVE) ? "INACTIVE" : + ((cb->region_state == SKHPB_REGION_ACTIVE) ? "ACTIVE" : "INVALID")), + subregion, + ((cp->subregion_state == SKHPB_SUBREGION_UNUSED) ? "UNUSED" : + ((cp->subregion_state == SKHPB_SUBREGION_DIRTY) ? "DIRTY" : + ((cp->subregion_state == SKHPB_SUBREGION_CLEAN) ? "CLEAN" : + (cp->subregion_state == SKHPB_SUBREGION_ISSUED) ? + "ISSUED":"INVALID"))) + ); + SKHPB_DRIVER_I("sector %llu lpn %u ppn %llx dirty %d", + value, lpn, ppn, dirty); + return count; +} + +static ssize_t skhpb_sysfs_map_req_show(struct skhpb_lu *hpb, char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "map_req_count[RB_NOTI RESET_NOTI MAP_REQ]= %lld %lld %lld\n", + (long long)atomic64_read(&hpb->rb_noti_cnt), + (long long)atomic64_read(&hpb->reset_noti_cnt), + (long long)atomic64_read(&hpb->map_req_cnt)); +} + +static ssize_t skhpb_sysfs_count_reset_store(struct skhpb_lu *hpb, + const char *buf, size_t count) +{ + unsigned long debug; + + if (kstrtoul(buf, 0, &debug)) + return -EINVAL; + + skhpb_stat_init(hpb); + + return count; +} + +static ssize_t skhpb_sysfs_add_evict_show(struct skhpb_lu *hpb, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "add_evict_count[ADD EVICT]= %lld %lld\n", + (long long)atomic64_read(&hpb->region_add), + (long long)atomic64_read(&hpb->region_evict)); +} + +static ssize_t skhpb_sysfs_statistics_show(struct skhpb_lu *hpb, char *buf) +{ + long long size_miss, region_miss, subregion_miss, entry_dirty_miss, + hit, miss_all, rb_fail, canceled_resp, canceled_map_req; +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + long long lc_dirty_miss = 0, lc_state_miss = 0, lc_hit = 0; +#endif + int count = 0; + + hit = atomic64_read(&hpb->hit); + size_miss = atomic64_read(&hpb->size_miss); + region_miss = atomic64_read(&hpb->region_miss); + subregion_miss = atomic64_read(&hpb->subregion_miss); + entry_dirty_miss = atomic64_read(&hpb->entry_dirty_miss); + rb_fail = atomic64_read(&hpb->rb_fail); + canceled_resp = atomic64_read(&hpb->canceled_resp); + canceled_map_req = atomic64_read(&hpb->canceled_map_req); + +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + lc_dirty_miss = atomic64_read(&hpb->lc_entry_dirty_miss); + lc_state_miss = atomic64_read(&hpb->lc_reg_subreg_miss); + lc_hit = atomic64_read(&hpb->lc_hit); + entry_dirty_miss += lc_dirty_miss; + subregion_miss += lc_state_miss; +#endif + miss_all = size_miss + region_miss + subregion_miss + entry_dirty_miss; + count += snprintf(buf + count, PAGE_SIZE, + "Total: %lld\nHit_Counts: %lld\n", + hit + miss_all, hit); + count += snprintf(buf + count, PAGE_SIZE, + "Miss_Counts[ALL SIZE REG SUBREG DIRTY]= %lld %lld %lld %lld %lld\n", + miss_all, size_miss, region_miss, subregion_miss, entry_dirty_miss); + +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + count += snprintf(buf + count, PAGE_SIZE, + "LARG_CHNK Hit_Count: %lld, miss_count[ALL DIRTY REG_SUBREG]= %lld %lld %lld\n", + lc_hit, lc_dirty_miss + lc_state_miss, lc_dirty_miss, lc_state_miss); +#endif + if (hpb->hba->skhpb_quirk & SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP) + count += snprintf(buf + count, PAGE_SIZE, + "READ_BUFFER miss_count[RB_FAIL CANCEL_RESP CANCEL_MAP]= %lld %lld %lld\n", + rb_fail, canceled_resp, canceled_map_req); + else + count += snprintf(buf + count, PAGE_SIZE, + "READ_BUFFER miss_count[RB_FAIL]= %lld\n", rb_fail); + return count; +} + +static ssize_t skhpb_sysfs_version_show(struct skhpb_lu *hpb, char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "HPBversion[HPB DD]= %x.%x.%x %x.%x.%x\n", + (hpb->hpb_ver >> 8) & 0xf, + (hpb->hpb_ver >> 4) & 0xf, + (hpb->hpb_ver >> 0) & 0xf, + SKHPB_GET_BYTE_2(SKHPB_DD_VER), + SKHPB_GET_BYTE_1(SKHPB_DD_VER), + SKHPB_GET_BYTE_0(SKHPB_DD_VER)); +} + +static ssize_t skhpb_sysfs_active_count_show( + struct skhpb_lu *hpb, char *buf) +{ + struct skhpb_region *cb; + int region; + int pinned_cnt = 0; + + for (region = 0 ; region < hpb->regions_per_lu ; region++) { + cb = hpb->region_tbl + region; + + if (cb->is_pinned && cb->region_state == SKHPB_REGION_ACTIVE) + pinned_cnt++; + } + return snprintf(buf, PAGE_SIZE, + "active_count[ACTIVE_CNT PINNED_CNT]= %ld %d\n", + atomic64_read(&hpb->lru_info.active_count), pinned_cnt); +} + +static ssize_t skhpb_sysfs_hpb_disable_show( + struct skhpb_lu *hpb, char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "[0] BOTH_enabled= %d\ + \n[1] ONLY_HPB_BUFFER_disabled= %d\ + \n[2] ONLY_HPB_READ_disabled= %d\ + \n[3] BOTH_disabled= %d\n", + (hpb->force_map_req_disable == 0 && + hpb->force_hpb_read_disable == 0)?1:0, + hpb->force_map_req_disable, + hpb->force_hpb_read_disable, + (hpb->force_map_req_disable == 1 && + hpb->force_hpb_read_disable == 1)?1:0); +} + +static ssize_t skhpb_sysfs_hpb_disable_store(struct skhpb_lu *hpb, + const char *buf, size_t count) +{ + unsigned long value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (value > 3) { + SKHPB_DRIVER_E("Error, Only [0-3] is valid:\ + \n[0] BOTH_enable\ + \n[1] ONLY_HPB_BUFFER_disable\ + \n[2] ONLY_HPB_READ_disable\ + \n[3] BOTH_disabie\n"); + return -EINVAL; + } + + hpb->force_map_req_disable = value & 0x1; + hpb->force_hpb_read_disable = value & 0x2; + return count; +} + +static ssize_t skhpb_sysfs_hpb_reset_store(struct skhpb_lu *hpb, + const char *buf, size_t count) +{ + unsigned long value; + unsigned int doorbel; + struct skhpb_rsp_info *rsp_info; + unsigned long flags; + int ret, retries, lun; + struct skhpb_lu *hpb_lu; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + if (!value) + return count; + + for (retries = 1; retries <= 20; retries++) { + pm_runtime_get_sync(SKHPB_DEV(hpb)); + ufshcd_hold(hpb->hba, false); + doorbel = ufshcd_readl(hpb->hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + ufshcd_release(hpb->hba); + pm_runtime_mark_last_busy(SKHPB_DEV(hpb)); + pm_runtime_put_noidle(SKHPB_DEV(hpb)); + + if (!doorbel) { + pm_runtime_get_sync(SKHPB_DEV(hpb)); + ret = ufshcd_query_flag_retry(hpb->hba, + UPIU_QUERY_OPCODE_SET_FLAG, + QUERY_FLAG_IDN_HPB_RESET, 0, + NULL); + pm_runtime_mark_last_busy(SKHPB_DEV(hpb)); + pm_runtime_put_noidle(SKHPB_DEV(hpb)); + if(!ret) { + SKHPB_DRIVER_I("Query fHPBReset is successfully sent\n"); + break; + } + } + SKHPB_DRIVER_I("fHPBReset failed and will retry[%d] after some time, DOORBELL: 0x%x\n", + retries, doorbel); + msleep(200); + } + if (ret == 0) { + bool fHPBReset = true; + pm_runtime_get_sync(SKHPB_DEV(hpb)); + ufshcd_query_flag_retry(hpb->hba, + UPIU_QUERY_OPCODE_READ_FLAG, + QUERY_FLAG_IDN_HPB_RESET, 0, + &fHPBReset); + pm_runtime_mark_last_busy(SKHPB_DEV(hpb)); + pm_runtime_put_noidle(SKHPB_DEV(hpb)); + if (fHPBReset) + SKHPB_DRIVER_E("fHPBReset is still in progress at device, but keep going\n"); + } else { + SKHPB_DRIVER_E("Fail to set fHPBReset flag\n"); + goto out; + } + flush_delayed_work(&hpb->skhpb_pinned_work); + for (lun = 0 ; lun < UFS_UPIU_MAX_GENERAL_LUN ; lun++) + { + hpb_lu = hpb->hba->skhpb_lup[lun]; + if (!hpb_lu || !hpb_lu->lu_hpb_enable) + continue; + + skhpb_stat_init(hpb_lu); + + spin_lock_irqsave(&hpb_lu->rsp_list_lock, flags); + rsp_info = skhpb_get_req_info(hpb_lu); + spin_unlock_irqrestore(&hpb_lu->rsp_list_lock, flags); + if (!rsp_info) + goto out; + rsp_info->type = SKHPB_RSP_HPB_RESET; + SKHPB_RSP_TIME(rsp_info->RSP_start); + spin_lock_irqsave(&hpb_lu->rsp_list_lock, flags); + list_add_tail(&rsp_info->list_rsp_info, &hpb_lu->lh_rsp_info); + spin_unlock_irqrestore(&hpb_lu->rsp_list_lock, flags); + SKHPB_DRIVER_I("Host HPB reset start LU%d - fHPBReset\n", lun); + schedule_work(&hpb_lu->skhpb_rsp_work); + } +out: + return count; +} + +static ssize_t skhpb_sysfs_debug_log_show( + struct skhpb_lu *hpb, char *buf) +{ + int value = skhpb_debug_mask; + + if (value == (SKHPB_LOG_OFF)) { + value = SKHPB_LOG_LEVEL_OFF; + } else if (value == (SKHPB_LOG_ERR)) { + value = SKHPB_LOG_LEVEL_ERR; + } else if (value == (SKHPB_LOG_ERR | SKHPB_LOG_INFO)) { + value = SKHPB_LOG_LEVEL_INFO; + } else if (value == (SKHPB_LOG_ERR | SKHPB_LOG_INFO | SKHPB_LOG_DEBUG)) { + value = SKHPB_LOG_LEVEL_DEBUG; + } else if (value == (SKHPB_LOG_ERR | SKHPB_LOG_INFO | SKHPB_LOG_DEBUG | SKHPB_LOG_HEX)) { + value = SKHPB_LOG_LEVEL_HEX; + } + + return snprintf(buf, PAGE_SIZE, "[0] : LOG_LEVEL_OFF\ + \n[1] : LOG_LEVEL_ERR\ + \n[2] : LOG_LEVEL_INFO\ + \n[3] : LOG_LEVEL_DEBUG\ + \n[4] : LOG_LEVEL_HEX\ + \n-----------------------\ + \nLog-Level = %d\n", value); +} + +static ssize_t skhpb_sysfs_debug_log_store(struct skhpb_lu *hpb, + const char *buf, size_t count) +{ + unsigned long value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (value > SKHPB_LOG_LEVEL_HEX) { + SKHPB_DRIVER_E("Error, Only [0-4] is valid:\ + \n[0] : LOG_LEVEL_OFF\ + \n[1] : LOG_LEVEL_ERR\ + \n[2] : LOG_LEVEL_INFO\ + \n[3] : LOG_LEVEL_DEBUG\ + \n[4] : LOG_LEVEL_HEX\n"); + return -EINVAL; + } + + if (value == SKHPB_LOG_LEVEL_OFF) { + skhpb_debug_mask = SKHPB_LOG_OFF; + } else if (value == SKHPB_LOG_LEVEL_ERR) { + skhpb_debug_mask = SKHPB_LOG_ERR; + } else if (value == SKHPB_LOG_LEVEL_INFO) { + skhpb_debug_mask = SKHPB_LOG_ERR | SKHPB_LOG_INFO; + } else if (value == SKHPB_LOG_LEVEL_DEBUG) { + skhpb_debug_mask = SKHPB_LOG_ERR | SKHPB_LOG_INFO | SKHPB_LOG_DEBUG; + } else if (value == SKHPB_LOG_LEVEL_HEX) { + skhpb_debug_mask = SKHPB_LOG_ERR | SKHPB_LOG_INFO | SKHPB_LOG_DEBUG | SKHPB_LOG_HEX; + } + + return count; +} + +static ssize_t skhpb_sysfs_rsp_time_show(struct skhpb_lu *hpb, char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "map_req_time: %s\n", (debug_map_req ? "Enable":"Disable")); +} + +static ssize_t skhpb_sysfs_rsp_time_store(struct skhpb_lu *hpb, + const char *buf, size_t count) +{ + unsigned long value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (value > 1) + return count; + + debug_map_req = value; + return count; +} + +static struct skhpb_sysfs_entry skhpb_sysfs_entries[] = { + __ATTR(hpb_disable, 0644, + skhpb_sysfs_hpb_disable_show, + skhpb_sysfs_hpb_disable_store), + __ATTR(HPBVersion, 0444, skhpb_sysfs_version_show, NULL), + __ATTR(statistics, 0444, skhpb_sysfs_statistics_show, NULL), + __ATTR(active_count, 0444, skhpb_sysfs_active_count_show, NULL), + __ATTR(add_evict_count, 0444, skhpb_sysfs_add_evict_show, NULL), + __ATTR(count_reset, 0200, NULL, skhpb_sysfs_count_reset_store), + __ATTR(map_req_count, 0444, skhpb_sysfs_map_req_show, NULL), + __ATTR(get_info_from_region, 0200, NULL, + skhpb_sysfs_info_from_region_store), + __ATTR(get_info_from_lba, 0200, NULL, skhpb_sysfs_info_from_lba_store), + __ATTR(hpb_reset, 0200, NULL, skhpb_sysfs_hpb_reset_store), + __ATTR(debug_log, 0644, + skhpb_sysfs_debug_log_show, + skhpb_sysfs_debug_log_store), + __ATTR(response_time, 0644, + skhpb_sysfs_rsp_time_show, + skhpb_sysfs_rsp_time_store), + __ATTR_NULL +}; + +static ssize_t skhpb_attr_show(struct kobject *kobj, + struct attribute *attr, char *page) +{ + struct skhpb_sysfs_entry *entry; + struct skhpb_lu *hpb; + ssize_t error; + + entry = container_of(attr, + struct skhpb_sysfs_entry, attr); + hpb = container_of(kobj, struct skhpb_lu, kobj); + + if (!entry->show) + return -EIO; + + mutex_lock(&hpb->sysfs_lock); + error = entry->show(hpb, page); + mutex_unlock(&hpb->sysfs_lock); + return error; +} + +static ssize_t skhpb_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *page, size_t length) +{ + struct skhpb_sysfs_entry *entry; + struct skhpb_lu *hpb; + ssize_t error; + + entry = container_of(attr, struct skhpb_sysfs_entry, attr); + hpb = container_of(kobj, struct skhpb_lu, kobj); + + if (!entry->store) + return -EIO; + + mutex_lock(&hpb->sysfs_lock); + error = entry->store(hpb, page, length); + mutex_unlock(&hpb->sysfs_lock); + return error; +} + +static const struct sysfs_ops skhpb_sysfs_ops = { + .show = skhpb_attr_show, + .store = skhpb_attr_store, +}; + +static struct kobj_type skhpb_ktype = { + .sysfs_ops = &skhpb_sysfs_ops, + .release = NULL, +}; + +static int skhpb_create_sysfs(struct ufs_hba *hba, + struct skhpb_lu *hpb) +{ + struct device *dev = hba->dev; + struct skhpb_sysfs_entry *entry; + int err; + + hpb->sysfs_entries = skhpb_sysfs_entries; + + skhpb_stat_init(hpb); + + kobject_init(&hpb->kobj, &skhpb_ktype); + mutex_init(&hpb->sysfs_lock); + + err = kobject_add(&hpb->kobj, kobject_get(&dev->kobj), + "ufshpb_lu%d", hpb->lun); + if (!err) { + for (entry = hpb->sysfs_entries; + entry->attr.name != NULL ; entry++) { + if (sysfs_create_file(&hpb->kobj, &entry->attr)) + break; + } + kobject_uevent(&hpb->kobj, KOBJ_ADD); + } + return err; +} diff --git a/drivers/scsi/ufs/ufshpb_skh.h b/drivers/scsi/ufs/ufshpb_skh.h new file mode 100644 index 000000000000..5359dda13a52 --- /dev/null +++ b/drivers/scsi/ufs/ufshpb_skh.h @@ -0,0 +1,518 @@ +/* + * Universal Flash Storage Host Performance Booster + * + * Original work Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * Modified work Copyright (C) 2018, Google, Inc. + * Modified work Copyright (C) 2019 SK hynix + * + * Origianl work Authors: + * Yongmyung Lee + * Jinyoung Choi + * Modifier + * Kihyun Cho + * Sengman Shin + * Jinsu Lee + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#ifndef _SKHPB_H_ +#define _SKHPB_H_ + +#include +#include +#include + +/* Version info*/ +#define SKHPB_DD_VER 0x010506 + +/* QUIRKs */ +/* Use READ16 instead of HPB_READ command, + * This is workaround solution to countmeasure QCT ICE issue. */ +#define SKHPB_QUIRK_USE_READ_16_FOR_ENCRYPTION (1 << 0) + +/* This quirk makes HPB driver always works as Devie Control Mode. + * To cover old Configuration descriptor format which interpret + * the bHPBControl field as RESERVED. */ +#define SKHPB_QUIRK_ALWAYS_DEVICE_CONTROL_MODE (1 << 1) + +/* Discard SubRegion activation hint information that has been processed, + * when the host enters RPM/SPM sleep. + * Must not be set the bit in ufs_quirks.h.*/ +#define SKHPB_QUIRK_PURGE_HINT_INFO_WHEN_SLEEP (1 << 20) + +/* Constant value*/ +#define SKHPB_SECTOR 512 +#define SKHPB_BLOCK 4096 +#define SKHPB_SECTORS_PER_BLOCK (SKHPB_BLOCK / SKHPB_SECTOR) +#define SKHPB_BITS_PER_DWORD 32 +#define SKHPB_MAX_ACTIVE_NUM 2 +#define SKHPB_MAX_INACTIVE_NUM 2 + +#define SKHPB_ENTRY_SIZE 0x08 +#define SKHPB_ENTREIS_PER_OS_PAGE (PAGE_SIZE / SKHPB_ENTRY_SIZE) + +/* Description */ +#define SKHPB_UFS_FEATURE_SUPPORT_HPB_BIT 0x80 + +#define SKHPB_QUERY_DESC_DEVICE_MAX_SIZE 0x43 +#define SKHPB_QUERY_DESC_CONFIGURAION_MAX_SIZE 0xE6 +#define SKHPB_QUERY_DESC_UNIT_MAX_SIZE 0x29 +#define SKHPB_QUERY_DESC_GEOMETRY_MAX_SIZE 0x4D + +/* Configuration for HPB */ +#define SKHPB_CONF_LU_ENABLE 0x00 +#define SKHPB_CONF_ACTIVE_REGIONS 0x10 +#define SKHPB_CONF_PINNED_START 0x12 +#define SKHPB_CONF_PINNED_NUM 0x14 + +/* Parameter Macros */ +#define SKHPB_DEV(h) ((h)->hba->dev) +#define SKHPB_MAX_BVEC_SIZE 128 + +/* Use for HPB activate */ +#define SKHPB_CONFIG_LEN 0xd0 + +#define SKHPB_READ_LARGE_CHUNK_SUPPORT +#define SKHPB_READ_LARGE_CHUNK_MAX_BLOCK_COUNT (128) //TRANSFER LENGTH: 8bit + +typedef u64 skhpb_t; + +enum skhpb_lu_set { + LU_DISABLE = 0x00, + LU_ENABLE = 0x01, + LU_HPB_ENABLE = 0x02, + LU_SET_MAX, +}; + +struct skhpb_config_desc { + unsigned char conf_dev_desc[16]; + unsigned char unit[UFS_UPIU_MAX_GENERAL_LUN][24]; +}; + +/* Response UPIU types */ +#define SKHPB_RSP_NONE 0x00 +#define SKHPB_RSP_REQ_REGION_UPDATE 0x01 +#define SKHPB_RSP_HPB_RESET 0x02 +#define SKHPB_PER_ACTIVE_INFO_BYTES 4 +#define SKHPB_PER_INACTIVE_INFO_BYTES 2 + +/* Vender defined OPCODE */ +#define SKHPB_READ 0xF8 +#define SKHPB_READ_BUFFER 0xF9 +#define SKHPB_WRITE_BUFFER 0xFA + +#define SKHPB_DEV_DATA_SEG_LEN 0x14 +#define SKHPB_DEV_SENSE_SEG_LEN 0x12 +#define SKHPB_DEV_DES_TYPE 0x80 +#define SKHPB_DEV_ADDITIONAL_LEN 0x10 + +/* BYTE SHIFT */ +#define SKHPB_ZERO_BYTE_SHIFT 0 +#define SKHPB_ONE_BYTE_SHIFT 8 +#define SKHPB_TWO_BYTE_SHIFT 16 +#define SKHPB_THREE_BYTE_SHIFT 24 + +#define SKHPB_SHIFT_BYTE_0(num) ((num) << SKHPB_ZERO_BYTE_SHIFT) +#define SKHPB_SHIFT_BYTE_1(num) ((num) << SKHPB_ONE_BYTE_SHIFT) + +#define SKHPB_GET_BYTE_0(num) (((num) >> SKHPB_ZERO_BYTE_SHIFT) & 0xff) +#define SKHPB_GET_BYTE_1(num) (((num) >> SKHPB_ONE_BYTE_SHIFT) & 0xff) +#define SKHPB_GET_BYTE_2(num) (((num) >> SKHPB_TWO_BYTE_SHIFT) & 0xff) +#define SKHPB_GET_BYTE_3(num) (((num) >> SKHPB_THREE_BYTE_SHIFT) & 0xff) + +#define REGION_UNIT_SIZE(bit_offset) (0x01 << (bit_offset)) + +enum SKHPB_STATE { + SKHPB_PRESENT = 1, + SKHPB_NOT_SUPPORTED = -1, + SKHPB_FAILED = -2, + SKHPB_NEED_INIT = 0, + SKHPB_RESET = -3, +}; + +enum SKHPB_BUFFER_MODE { + R_BUFFER = 0, + W_BUFFER = 1, +}; + +enum SKHPB_CMD { + SKHPB_CMD_READ = 0, + SKHPB_CMD_WRITE = 1, + SKHPB_CMD_DISCARD = 2, + SKHPB_CMD_OTHERS = 3, +}; + +enum SKHPB_REGION_STATE { + SKHPB_REGION_INACTIVE, + SKHPB_REGION_ACTIVE, +}; + +enum SKHPB_SUBREGION_STATE { + SKHPB_SUBREGION_UNUSED, + SKHPB_SUBREGION_DIRTY, + SKHPB_SUBREGION_CLEAN, + SKHPB_SUBREGION_ISSUED, +}; + +enum SKHPB_CONTROL_MODE { + HOST_CTRL_MODE = 0, + DEV_CTRL_MODE = 1, +}; + +enum SKHPB_RST_TIME { + SKHPB_MAP_RSP_DISABLE = 0, + SKHPB_MAP_RSP_ENABLE = 1, +}; + +struct skhpb_func_desc { + /*** Device Descriptor ***/ + /* 06h bNumberLU */ + int lu_cnt; + /* 10h wSpecVersion */ + u16 spec_ver; + /* 40h HPB Version */ + u16 hpb_ver; + /* 42h HPB control mode */ + u8 hpb_control_mode; + + /*** Geometry Descriptor ***/ + /* 48h bHPBRegionSize (UNIT: 512KB) */ + u8 hpb_region_size; + /* 49h bHPBNumberLU */ + u8 hpb_number_lu; + /* 4Ah bHPBSubRegionSize */ + u8 hpb_subregion_size; + /* 4B:4Ch wDeviceMaxActiveHPBRegions */ + u16 hpb_device_max_active_regions; +}; + +struct skhpb_lu_desc { + /*** Unit Descriptor ****/ + /* 03h bLUEnable */ + int lu_enable; + /* 06h lu queue depth info*/ + int lu_queue_depth; + /* 0Ah bLogicalBlockSize. default 0x0C = 4KB */ + int lu_logblk_size; + /* 0Bh qLogicalBlockCount. same as the read_capacity ret val. */ + u64 lu_logblk_cnt; + + /* 23h:24h wLUMaxActiveHPBRegions */ + u16 lu_max_active_hpb_regions; + /* 25h:26h wHPBPinnedRegionStartIdx */ + u16 hpb_pinned_region_startidx; + /* 27h:28h wNumHPBPinnedRegions */ + u16 lu_num_hpb_pinned_regions; + + + /* if 03h value is 02h, hpb_enable is set. */ + bool lu_hpb_enable; + + int lu_hpb_pinned_end_offset; +}; + +struct skhpb_rsp_active_list { + u16 region[SKHPB_MAX_ACTIVE_NUM]; + u16 subregion[SKHPB_MAX_ACTIVE_NUM]; +}; + +struct skhpb_rsp_inactive_list { + u16 region[SKHPB_MAX_INACTIVE_NUM]; +}; + +struct skhpb_rsp_update_entry { + unsigned int lpn; + skhpb_t ppn; +}; + +struct skhpb_rsp_info { + int type; + int active_cnt; + int inactive_cnt; + struct skhpb_rsp_active_list active_list; + struct skhpb_rsp_inactive_list inactive_list; + + __u64 RSP_start; + __u64 RSP_tasklet_enter; + + struct list_head list_rsp_info; +}; + +struct skhpb_rsp_field { + u8 sense_data_len[2]; + u8 desc_type; + u8 additional_len; + u8 hpb_type; + u8 lun; + u8 active_region_cnt; + u8 inactive_region_cnt; + u8 hpb_active_field[8]; + u8 hpb_inactive_field[4]; +}; + +struct skhpb_map_ctx { + struct page **m_page; + unsigned int *ppn_dirty; + + struct list_head list_table; +}; + +struct skhpb_subregion { + struct skhpb_map_ctx *mctx; + enum SKHPB_SUBREGION_STATE subregion_state; + int region; + int subregion; + bool last; + struct list_head list_subregion; +}; + +struct skhpb_region { + struct skhpb_subregion *subregion_tbl; + enum SKHPB_REGION_STATE region_state; + bool is_pinned; + int region; + int subregion_count; + /*below information is used by lru*/ + struct list_head list_region; + int hit_count; +}; + +struct skhpb_map_req { + struct skhpb_lu *hpb; + struct skhpb_map_ctx *mctx; + struct bio bio; + struct bio *pbio; + struct bio_vec bvec[SKHPB_MAX_BVEC_SIZE]; + void (*end_io)(struct request *rq, int err); + void *end_io_data; + int region; + int subregion; + int subregion_mem_size; + int lun; + int retry_cnt; + + /* for debug : RSP Profiling */ + __u64 RSP_start; // get the request from device + __u64 RSP_issue; // issue scsi cmd + __u64 RSP_end; // complete the request + + char sense[SCSI_SENSE_BUFFERSIZE]; + + struct list_head list_map_req; + int rwbuffer_flag; +}; + +enum SKHPB_SELECTION_TYPE { + TYPE_LRU = 1, + TYPE_LFU = 2, +}; + +struct skhpb_victim_select_info { + int selection_type; + struct list_head lru; + int max_lru_active_count; // supported hpb #region - pinned #region + atomic64_t active_count; +}; + +struct skhpb_lu { + struct skhpb_region *region_tbl; + struct skhpb_rsp_info *rsp_info; + struct skhpb_map_req *map_req; + + struct list_head lh_map_ctx; + struct list_head lh_subregion_req; + struct list_head lh_rsp_info; + + struct list_head lh_rsp_info_free; + struct list_head lh_map_req_free; + struct list_head lh_map_req_retry; + int debug_free_table; + + bool lu_hpb_enable; + + struct delayed_work skhpb_pinned_work; + struct delayed_work skhpb_map_req_retry_work; + struct work_struct skhpb_rsp_work; + struct bio_vec bvec[SKHPB_MAX_BVEC_SIZE]; + + int subregions_per_lu; + int regions_per_lu; + int subregion_mem_size; + int last_subregion_mem_size; + + /* for selecting victim */ + struct skhpb_victim_select_info lru_info; + + int hpb_ver; + int lu_max_active_regions; + + int entries_per_subregion; + int entries_per_subregion_shift; + int entries_per_subregion_mask; + + int entries_per_region_shift; + int entries_per_region_mask; + int subregions_per_region; + + int dwords_per_subregion; + unsigned long long subregion_unit_size; + bool identical_size; + +#define BITS_PER_PPN_DIRTY (BITS_PER_BYTE * sizeof(unsigned int)) + int ppn_dirties_per_subregion; + + int mpage_bytes; + int mpages_per_subregion; + + /* for debug constant variables */ + unsigned long long lu_num_blocks; + + int lun; + + struct ufs_hba *hba; + + spinlock_t hpb_lock; + spinlock_t rsp_list_lock; + spinlock_t map_list_lock; + + struct kobject kobj; + struct mutex sysfs_lock; + struct skhpb_sysfs_entry *sysfs_entries; + + bool hpb_control_mode; + + /* for debug */ + bool force_hpb_read_disable; + bool force_map_req_disable; + bool read_buf_debug; + atomic64_t hit; + atomic64_t size_miss; + atomic64_t region_miss; + atomic64_t subregion_miss; + atomic64_t entry_dirty_miss; + atomic64_t rb_noti_cnt; + atomic64_t rb_fail; + atomic64_t reset_noti_cnt; + atomic64_t w_map_req_cnt; +#if defined(SKHPB_READ_LARGE_CHUNK_SUPPORT) + atomic64_t lc_entry_dirty_miss; + atomic64_t lc_reg_subreg_miss; + atomic64_t lc_hit; +#endif + atomic64_t map_req_cnt; + atomic64_t region_add; + atomic64_t region_evict; + atomic64_t canceled_resp; + atomic64_t canceled_map_req; + atomic64_t alloc_map_req_cnt; +}; + +struct skhpb_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct skhpb_lu *hpb, char *buf); + ssize_t (*store)(struct skhpb_lu *hpb, const char *, size_t); +}; + +struct ufshcd_lrb; + +void ufshcd_init_hpb(struct ufs_hba *hba); +void skhpb_init_handler(struct work_struct *work); +void skhpb_prep_fn(struct ufs_hba *hba, struct ufshcd_lrb *lrbp); +void skhpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp); +void skhpb_suspend(struct ufs_hba *hba); +void skhpb_resume(struct ufs_hba *hba); +void skhpb_release(struct ufs_hba *hba, int state); +int skhpb_issue_req_dev_ctx(struct skhpb_lu *hpb, unsigned char *buf, + int buf_length); +int skhpb_control_validation(struct ufs_hba *hba, + struct skhpb_config_desc *config); + +extern u32 skhpb_debug_mask; +extern int debug_map_req; +enum SKHPB_LOG_LEVEL { + SKHPB_LOG_LEVEL_OFF = 0, + SKHPB_LOG_LEVEL_ERR = 1, + SKHPB_LOG_LEVEL_INFO = 2, + SKHPB_LOG_LEVEL_DEBUG = 3, + SKHPB_LOG_LEVEL_HEX = 4, +}; +enum SKHPB_LOG_MASK { + SKHPB_LOG_OFF = SKHPB_LOG_LEVEL_OFF, /* 0 */ + SKHPB_LOG_ERR = (1U << SKHPB_LOG_LEVEL_ERR), /* 2 */ + SKHPB_LOG_INFO = (1U << SKHPB_LOG_LEVEL_INFO), /* 4 */ + SKHPB_LOG_DEBUG = (1U << SKHPB_LOG_LEVEL_DEBUG), /* 8 */ + SKHPB_LOG_HEX = (1U << SKHPB_LOG_LEVEL_HEX), /* 16 */ +}; +#define SKHPB_DRIVER_E(fmt, args...) \ + do { \ + if(likely(skhpb_debug_mask & SKHPB_LOG_ERR)) \ + pr_err("[HPB E][%s:%d] " fmt, __func__, __LINE__, ##args); \ + } while (0) + +#define SKHPB_DRIVER_I(fmt, args...) \ + do { \ + if(unlikely(skhpb_debug_mask & SKHPB_LOG_INFO)) \ + pr_err("[HPB][%s:%d] " fmt, __func__, __LINE__, ##args); \ + } while (0) + +#define SKHPB_DRIVER_D(fmt, args...) \ + do { \ + if(unlikely(skhpb_debug_mask & SKHPB_LOG_DEBUG)) \ + printk(KERN_DEBUG "[HPB][%s:%d] " fmt, __func__, __LINE__, ##args); \ + } while (0) + +#define SKHPB_DRIVER_HEXDUMP(fmt, args...) \ + do { \ + if(unlikely(skhpb_debug_mask & SKHPB_LOG_HEX)) { \ + print_hex_dump(KERN_DEBUG, fmt, DUMP_PREFIX_ADDRESS, ##args); \ + } \ + } while (0) + +#define SKHPB_MAP_REQ_TIME(map_req, val, print) \ + do { \ + if(unlikely(debug_map_req)) { \ + val = ktime_to_us(ktime_get()); \ + if(print) { \ + SKHPB_DRIVER_I("SKHPB COMPL BUFFER %d - %d\n", \ + map_req->region, map_req->subregion); \ + SKHPB_DRIVER_I("start~issue = %lluus, issue~end = %lluus\n", \ + map_req->RSP_issue - map_req->RSP_start, \ + map_req->RSP_end - map_req->RSP_issue); \ + } \ + } \ + } while (0) + +#define SKHPB_RSP_TIME(val) \ + do { \ + if(unlikely(debug_map_req)) { \ + val = ktime_to_us(ktime_get()); \ + } \ + } while(0) + +#endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufstw.c b/drivers/scsi/ufs/ufstw.c new file mode 100755 index 000000000000..b9853b118ae1 --- /dev/null +++ b/drivers/scsi/ufs/ufstw.c @@ -0,0 +1,900 @@ +/* + * Universal Flash Storage Turbo Write + * + * Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jinyoung Choi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#include "ufshcd.h" +#include "ufstw.h" + +static int ufstw_create_sysfs(struct ufsf_feature *ufsf, struct ufstw_lu *tw); + +inline int ufstw_get_state(struct ufsf_feature *ufsf) +{ + return atomic_read(&ufsf->tw_state); +} + +inline void ufstw_set_state(struct ufsf_feature *ufsf, int state) +{ + atomic_set(&ufsf->tw_state, state); +} + +static int ufstw_is_not_present(struct ufsf_feature *ufsf) +{ + enum UFSTW_STATE cur_state = ufstw_get_state(ufsf); + + if (cur_state != TW_PRESENT) { + INFO_MSG("tw_state != TW_PRESENT (%d)", cur_state); + return -ENODEV; + } + return 0; +} + +#define FLAG_IDN_NAME(idn) \ + (idn == QUERY_FLAG_IDN_TW_EN ? "tw_enable" : \ + idn == QUERY_FLAG_IDN_TW_BUF_FLUSH_EN ? "flush_enable" : \ + idn == QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN ? \ + "flush_hibern" : "unknown") + +#define ATTR_IDN_NAME(idn) \ + (idn == QUERY_ATTR_IDN_TW_FLUSH_STATUS ? "flush_status" : \ + idn == QUERY_ATTR_IDN_TW_AVAIL_BUF_SIZE ? "avail_buffer_size" :\ + idn == QUERY_ATTR_IDN_TW_BUF_LIFETIME_EST ? "lifetime_est" : \ + idn == QUERY_ATTR_IDN_TW_CURR_BUF_SIZE ? "current_buf_size" : \ + "unknown") + +static int ufstw_read_lu_attr(struct ufstw_lu *tw, u8 idn, u32 *attr_val) +{ + struct ufs_hba *hba = tw->ufsf->hba; + int err = 0, lun; + u32 val; + + lun = (tw->lun == TW_LU_SHARED) ? 0 : tw->lun; + err = ufsf_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR, idn, + (u8)lun, &val); + if (err) { + ERR_MSG("read attr [0x%.2X](%s) failed. (%d)", idn, + ATTR_IDN_NAME(idn), err); + goto out; + } + + *attr_val = val; + + INFO_MSG("read attr LUN(%d) [0x%.2X](%s) success (%u)", + lun, idn, ATTR_IDN_NAME(idn), *attr_val); +out: + return err; +} + +static int ufstw_set_lu_flag(struct ufstw_lu *tw, u8 idn, bool *flag_res) +{ + struct ufs_hba *hba = tw->ufsf->hba; + int err = 0, lun; + + lun = (tw->lun == TW_LU_SHARED) ? 0 : tw->lun; + err = ufsf_query_flag_retry(hba, UPIU_QUERY_OPCODE_SET_FLAG, idn, + (u8)lun, NULL); + if (err) { + ERR_MSG("set flag [0x%.2X](%s) failed. (%d)", idn, + FLAG_IDN_NAME(idn), err); + goto out; + } + + *flag_res = true; + + INFO_MSG("set flag LUN(%d) [0x%.2X](%s) success. (%u)", + lun, idn, FLAG_IDN_NAME(idn), *flag_res); +out: + return err; +} + +static int ufstw_clear_lu_flag(struct ufstw_lu *tw, u8 idn, bool *flag_res) +{ + struct ufs_hba *hba = tw->ufsf->hba; + int err = 0, lun; + + lun = (tw->lun == TW_LU_SHARED) ? 0 : tw->lun; + err = ufsf_query_flag_retry(hba, UPIU_QUERY_OPCODE_CLEAR_FLAG, idn, + (u8)lun, NULL); + if (err) { + ERR_MSG("clear flag [0x%.2X](%s) failed. (%d)", idn, + FLAG_IDN_NAME(idn), err); + goto out; + } + + *flag_res = false; + + INFO_MSG("clear flag LUN(%d) [0x%.2X](%s) success. (%u)", + lun, idn, FLAG_IDN_NAME(idn), *flag_res); +out: + return err; +} + +static int ufstw_read_lu_flag(struct ufstw_lu *tw, u8 idn, bool *flag_res) +{ + struct ufs_hba *hba = tw->ufsf->hba; + int err = 0, lun; + bool val; + + lun = (tw->lun == TW_LU_SHARED) ? 0 : tw->lun; + err = ufsf_query_flag_retry(hba, UPIU_QUERY_OPCODE_READ_FLAG, idn, + (u8)lun, &val); + if (err) { + ERR_MSG("read flag [0x%.2X](%s) failed. (%d)", idn, + FLAG_IDN_NAME(idn), err); + goto out; + } + + *flag_res = val; + + INFO_MSG("read flag LUN(%d) [0x%.2X](%s) success. (%u)", + lun, idn, FLAG_IDN_NAME(idn), *flag_res); +out: + return err; +} + +static inline bool ufstw_is_write_lrbp(struct ufshcd_lrb *lrbp) +{ + if (lrbp->cmd->cmnd[0] == WRITE_10 || lrbp->cmd->cmnd[0] == WRITE_16) + return true; + + return false; +} + +static void ufstw_switch_disable_state(struct ufstw_lu *tw) +{ + int err = 0; + + WARN_MSG("dTurboWriteBUfferLifeTImeEst (0x%.2X)", tw->lifetime_est); + WARN_MSG("tw-mode will change to disable-mode"); + + mutex_lock(&tw->sysfs_lock); + ufstw_set_state(tw->ufsf, TW_FAILED); + mutex_unlock(&tw->sysfs_lock); + + if (tw->tw_enable) { + pm_runtime_get_sync(tw->ufsf->hba->dev); + err = ufstw_clear_lu_flag(tw, QUERY_FLAG_IDN_TW_EN, + &tw->tw_enable); + pm_runtime_put_sync(tw->ufsf->hba->dev); + if (err) + WARN_MSG("tw_enable flag clear failed"); + } +} + +static int ufstw_check_lifetime_not_guarantee(struct ufstw_lu *tw) +{ + bool disable_flag = false; + + if (tw->lifetime_est & MASK_UFSTW_LIFETIME_NOT_GUARANTEE) { + if (tw->lun == TW_LU_SHARED) + WARN_MSG("lun-shared lifetime_est[31] (1)"); + else + WARN_MSG("lun %d lifetime_est[31] (1)", + tw->lun); + + WARN_MSG("Device not guarantee the lifetime of TW Buffer"); +#if defined(CONFIG_UFSTW_IGNORE_GUARANTEE_BIT) + WARN_MSG("but we will ignore them for PoC"); +#else + disable_flag = true; +#endif + } + + if (disable_flag || + (tw->lifetime_est & ~MASK_UFSTW_LIFETIME_NOT_GUARANTEE) >= + UFSTW_MAX_LIFETIME_VALUE) { + ufstw_switch_disable_state(tw); + return -ENODEV; + } + + return 0; +} + +static void ufstw_lifetime_work_fn(struct work_struct *work) +{ + struct ufstw_lu *tw; + int ret; + + tw = container_of(work, struct ufstw_lu, tw_lifetime_work); + + if (ufstw_is_not_present(tw->ufsf)) + return; + + pm_runtime_get_sync(tw->ufsf->hba->dev); + ret = ufstw_read_lu_attr(tw, QUERY_ATTR_IDN_TW_BUF_LIFETIME_EST, + &tw->lifetime_est); + pm_runtime_put_sync(tw->ufsf->hba->dev); + if (ret) + return; + + ufstw_check_lifetime_not_guarantee(tw); +} + +void ufstw_prep_fn(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp) +{ + struct ufstw_lu *tw; + + if (!lrbp || !ufsf_is_valid_lun(lrbp->lun)) + return; + + if (!ufstw_is_write_lrbp(lrbp)) + return; + + tw = ufsf->tw_lup[lrbp->lun]; + if (!tw) + return; + + if (!tw->tw_enable) + return; + + spin_lock_bh(&tw->lifetime_lock); + tw->stat_write_sec += blk_rq_sectors(lrbp->cmd->request); + + if (tw->stat_write_sec > UFSTW_LIFETIME_SECT) { + tw->stat_write_sec = 0; + spin_unlock_bh(&tw->lifetime_lock); + schedule_work(&tw->tw_lifetime_work); + return; + } + spin_unlock_bh(&tw->lifetime_lock); + + TMSG(tw->ufsf, lrbp->lun, "%s:%d tw_lifetime_work %u", + __func__, __LINE__, tw->stat_write_sec); +} + +static inline void ufstw_init_lu_jobs(struct ufstw_lu *tw) +{ + INIT_WORK(&tw->tw_lifetime_work, ufstw_lifetime_work_fn); +} + +static inline void ufstw_cancel_lu_jobs(struct ufstw_lu *tw) +{ + int ret; + + ret = cancel_work_sync(&tw->tw_lifetime_work); + INFO_MSG("cancel_work_sync(tw_lifetime_work) ufstw_lu[%d] (%d)", + tw->lun, ret); +} + +static inline int ufstw_version_check(struct ufstw_dev_info *tw_dev_info) +{ + INFO_MSG("Support TW Spec : Driver = %.4X, Device = %.4X", + UFSTW_VER, tw_dev_info->tw_ver); + + INFO_MSG("TW Driver Version : %.6X%s", UFSTW_DD_VER, + UFSTW_DD_VER_POST); + + if (tw_dev_info->tw_ver != UFSTW_VER) + return -ENODEV; + + return 0; +} + +void ufstw_get_dev_info(struct ufsf_feature *ufsf, u8 *desc_buf) +{ + struct ufstw_dev_info *tw_dev_info = &ufsf->tw_dev_info; + + if (LI_EN_32(&desc_buf[DEVICE_DESC_PARAM_EX_FEAT_SUP]) & + UFS_FEATURE_SUPPORT_TW_BIT) { + INFO_MSG("bUFSExFeaturesSupport: TW is set"); + } else { + ERR_MSG("bUFSExFeaturesSupport: TW not support"); + ufstw_set_state(ufsf, TW_FAILED); + return; + } + tw_dev_info->tw_buf_no_reduct = + desc_buf[DEVICE_DESC_PARAM_TW_RETURN_TO_USER]; + tw_dev_info->tw_buf_type = desc_buf[DEVICE_DESC_PARAM_TW_BUF_TYPE]; + tw_dev_info->tw_shared_buf_alloc_units = + LI_EN_32(&desc_buf[DEVICE_DESC_PARAM_TW_SHARED_BUF_ALLOC_UNITS]); + tw_dev_info->tw_ver = LI_EN_16(&desc_buf[DEVICE_DESC_PARAM_TW_VER]); + + if (ufstw_version_check(tw_dev_info)) { + ERR_MSG("TW Spec Version mismatch. TW disabled"); + ufstw_set_state(ufsf, TW_FAILED); + return; + } + + INFO_MSG("tw_dev [53] bTurboWriteBufferNoUserSpaceReductionEn (%u)", + tw_dev_info->tw_buf_no_reduct); + INFO_MSG("tw_dev [54] bTurboWriteBufferType (%u)", + tw_dev_info->tw_buf_type); + INFO_MSG("tw_dev [55] dNumSharedTUrboWriteBufferAllocUnits (%u)", + tw_dev_info->tw_shared_buf_alloc_units); + + if (tw_dev_info->tw_buf_type == TW_BUF_TYPE_SHARED && + tw_dev_info->tw_shared_buf_alloc_units == 0) { + ERR_MSG("TW use shared buffer. But alloc unit is (0)"); + ufstw_set_state(ufsf, TW_FAILED); + return; + } +} + +void ufstw_get_geo_info(struct ufsf_feature *ufsf, u8 *geo_buf) +{ + struct ufstw_dev_info *tw_dev_info = &ufsf->tw_dev_info; + + tw_dev_info->tw_number_lu = geo_buf[GEOMETRY_DESC_TW_NUMBER_LU]; + if (tw_dev_info->tw_number_lu == 0) { + ERR_MSG("Turbo Write is not supported"); + ufstw_set_state(ufsf, TW_FAILED); + return; + } + + INFO_MSG("tw_geo [4F:52] dTurboWriteBufferMaxNAllocUnits (%u)", + LI_EN_32(&geo_buf[GEOMETRY_DESC_TW_MAX_SIZE])); + INFO_MSG("tw_geo [53] bDeviceMaxTurboWriteLUs (%u)", + tw_dev_info->tw_number_lu); + INFO_MSG("tw_geo [54] bTurboWriteBufferCapAdjFac (%u)", + geo_buf[GEOMETRY_DESC_TW_CAP_ADJ_FAC]); + INFO_MSG("tw_geo [55] bSupportedTWBufferUserSpaceReductionTypes (%u)", + geo_buf[GEOMETRY_DESC_TW_SUPPORT_USER_REDUCTION_TYPES]); + INFO_MSG("tw_geo [56] bSupportedTurboWriteBufferTypes (%u)", + geo_buf[GEOMETRY_DESC_TW_SUPPORT_BUF_TYPE]); +} + +static void ufstw_alloc_shared_lu(struct ufsf_feature *ufsf) +{ + struct ufstw_lu *tw; + + tw = kzalloc(sizeof(struct ufstw_lu), GFP_KERNEL); + if (!tw) { + ERR_MSG("ufstw_lu[shared] memory alloc failed"); + return; + } + + tw->lun = TW_LU_SHARED; + tw->ufsf = ufsf; + ufsf->tw_lup[0] = tw; + + INFO_MSG("ufstw_lu[shared] is TurboWrite-Enabled"); +} + +static void ufstw_get_lu_info(struct ufsf_feature *ufsf, int lun, u8 *lu_buf) +{ + struct ufsf_lu_desc lu_desc; + struct ufstw_lu *tw; + + lu_desc.tw_lu_buf_size = + LI_EN_32(&lu_buf[UNIT_DESC_TW_LU_WRITE_BUFFER_ALLOC_UNIT]); + + ufsf->tw_lup[lun] = NULL; + + if (lu_desc.tw_lu_buf_size) { + ufsf->tw_lup[lun] = + kzalloc(sizeof(struct ufstw_lu), GFP_KERNEL); + if (!ufsf->tw_lup[lun]) { + ERR_MSG("ufstw_lu[%d] memory alloc faild", lun); + return; + } + + tw = ufsf->tw_lup[lun]; + tw->ufsf = ufsf; + tw->lun = lun; + INFO_MSG("ufstw_lu[%d] [29:2C] dLUNumTWBufferAllocUnits (%u)", + lun, lu_desc.tw_lu_buf_size); + INFO_MSG("ufstw_lu[%d] is TurboWrite-Enabled.", lun); + } else { + INFO_MSG("ufstw_lu[%d] [29:2C] dLUNumTWBufferAllocUnits (%u)", + lun, lu_desc.tw_lu_buf_size); + INFO_MSG("ufstw_lu[%d] is TurboWrite-disabled", lun); + } +} + +inline void ufstw_alloc_lu(struct ufsf_feature *ufsf, + int lun, u8 *lu_buf) +{ + if (ufsf->tw_dev_info.tw_buf_type == TW_BUF_TYPE_SHARED && + !ufsf->tw_lup[0]) + ufstw_alloc_shared_lu(ufsf); + else if (ufsf->tw_dev_info.tw_buf_type == TW_BUF_TYPE_LU) + ufstw_get_lu_info(ufsf, lun, lu_buf); +} + +static inline void ufstw_print_lu_flag_attr(struct ufstw_lu *tw) +{ + char lun_str[20] = { 0 }; + + if (tw->lun == TW_LU_SHARED) + snprintf(lun_str, 7, "shared"); + else + snprintf(lun_str, 2, "%d", tw->lun); + + INFO_MSG("tw_flag ufstw_lu[%s] IDN (0x%.2X) tw_enable (%d)", + lun_str, QUERY_FLAG_IDN_TW_EN, tw->tw_enable); + INFO_MSG("tw_flag ufstw_lu[%s] IDN (0x%.2X) flush_enable (%d)", + lun_str, QUERY_FLAG_IDN_TW_BUF_FLUSH_EN, + tw->flush_enable); + INFO_MSG("tw_flag ufstw_lu[%s] IDN (0x%.2X) flush_hibern (%d)", + lun_str, QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN, + tw->flush_during_hibern_enter); + + INFO_MSG("tw_attr ufstw_lu[%s] IDN (0x%.2X) flush_status (%u)", + lun_str, QUERY_ATTR_IDN_TW_FLUSH_STATUS, tw->flush_status); + INFO_MSG("tw_attr ufstw_lu[%s] IDN (0x%.2X) buffer_size (%u)", + lun_str, QUERY_ATTR_IDN_TW_AVAIL_BUF_SIZE, + tw->available_buffer_size); + INFO_MSG("tw_attr ufstw_lu[%s] IDN (0x%.2X) buffer_lifetime (0x%.2X)", + lun_str, QUERY_ATTR_IDN_TW_BUF_LIFETIME_EST, + tw->lifetime_est); +} + +static inline void ufstw_lu_update(struct ufstw_lu *tw) +{ + /* Flag */ + pm_runtime_get_sync(tw->ufsf->hba->dev); + if (ufstw_read_lu_flag(tw, QUERY_FLAG_IDN_TW_EN, &tw->tw_enable)) + goto error_put; + + if (ufstw_read_lu_flag(tw, QUERY_FLAG_IDN_TW_BUF_FLUSH_EN, + &tw->flush_enable)) + goto error_put; + + if (ufstw_read_lu_flag(tw, QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN, + &tw->flush_during_hibern_enter)) + goto error_put; + + /* Attribute */ + if (ufstw_read_lu_attr(tw, QUERY_ATTR_IDN_TW_FLUSH_STATUS, + &tw->flush_status)) + goto error_put; + + if (ufstw_read_lu_attr(tw, QUERY_ATTR_IDN_TW_AVAIL_BUF_SIZE, + &tw->available_buffer_size)) + goto error_put; + + ufstw_read_lu_attr(tw, QUERY_ATTR_IDN_TW_BUF_LIFETIME_EST, + &tw->lifetime_est); +error_put: + pm_runtime_put_sync(tw->ufsf->hba->dev); +} + +static int ufstw_lu_init(struct ufsf_feature *ufsf, int lun) +{ + struct ufstw_lu *tw; + int ret = 0; + + if (lun == TW_LU_SHARED) + tw = ufsf->tw_lup[0]; + else + tw = ufsf->tw_lup[lun]; + + tw->ufsf = ufsf; + spin_lock_init(&tw->lifetime_lock); + + ufstw_lu_update(tw); + + ret = ufstw_check_lifetime_not_guarantee(tw); + if (ret) + goto err_out; + + ufstw_print_lu_flag_attr(tw); + + tw->stat_write_sec = 0; + + ufstw_init_lu_jobs(tw); + +#if defined(CONFIG_UFSTW_BOOT_ENABLED) + pm_runtime_get_sync(ufsf->hba->dev); + ufstw_set_lu_flag(tw, QUERY_FLAG_IDN_TW_EN, &tw->tw_enable); + ufstw_set_lu_flag(tw, QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN, + &tw->flush_during_hibern_enter); + pm_runtime_put_sync(ufsf->hba->dev); +#endif + ret = ufstw_create_sysfs(ufsf, tw); + if (ret) + ERR_MSG("create sysfs failed"); +err_out: + return ret; +} + +void ufstw_init(struct ufsf_feature *ufsf) +{ + int lun, ret = 0; + unsigned int tw_enabled_lun = 0; + + INFO_MSG("init start.. tw_state (%d)", ufstw_get_state(ufsf)); + + if (ufsf->tw_dev_info.tw_buf_type == TW_BUF_TYPE_SHARED) { + if (!ufsf->tw_lup[0]) { + ERR_MSG("tw_lup memory allocation failed"); + goto out; + } + BUG_ON(ufsf->tw_lup[0]->lun != TW_LU_SHARED); + + ret = ufstw_lu_init(ufsf, TW_LU_SHARED); + if (ret) + goto out_free_mem; + + INFO_MSG("ufstw_lu[shared] working"); + tw_enabled_lun++; + } else { + seq_scan_lu(lun) { + if (!ufsf->tw_lup[lun]) + continue; + + ret = ufstw_lu_init(ufsf, lun); + if (ret) + goto out_free_mem; + + INFO_MSG("ufstw_lu[%d] working", lun); + tw_enabled_lun++; + } + + if (tw_enabled_lun > ufsf->tw_dev_info.tw_number_lu) { + ERR_MSG("lu count mismatched"); + goto out_free_mem; + } + } + + if (tw_enabled_lun == 0) { + ERR_MSG("tw_enabled_lun count zero"); + goto out_free_mem; + } + + ufstw_set_state(ufsf, TW_PRESENT); + return; +out_free_mem: + seq_scan_lu(lun) { + kfree(ufsf->tw_lup[lun]); + ufsf->tw_lup[lun] = NULL; + } +out: + ERR_MSG("Turbo write initialization failed"); + + ufstw_set_state(ufsf, TW_FAILED); +} + +static inline void ufstw_remove_sysfs(struct ufstw_lu *tw) +{ + int ret; + + ret = kobject_uevent(&tw->kobj, KOBJ_REMOVE); + INFO_MSG("kobject removed (%d)", ret); + kobject_del(&tw->kobj); +} + +void ufstw_remove(struct ufsf_feature *ufsf) +{ + struct ufstw_lu *tw; + int lun; + + dump_stack(); + INFO_MSG("start release"); + + ufstw_set_state(ufsf, TW_FAILED); + + if (ufsf->tw_dev_info.tw_buf_type == TW_BUF_TYPE_SHARED) { + tw = ufsf->tw_lup[0]; + INFO_MSG("ufstw_lu[shared] %p", tw); + ufsf->tw_lup[0] = NULL; + ufstw_cancel_lu_jobs(tw); + ufstw_remove_sysfs(tw); + kfree(tw); + } else { + seq_scan_lu(lun) { + tw = ufsf->tw_lup[lun]; + INFO_MSG("ufstw_lu[%d] %p", lun, tw); + + if (!tw) + continue; + ufsf->tw_lup[lun] = NULL; + ufstw_cancel_lu_jobs(tw); + ufstw_remove_sysfs(tw); + kfree(tw); + } + } + + INFO_MSG("end release"); +} + +static void ufstw_reset_query_handling(struct ufstw_lu *tw) +{ + int ret; + + pm_runtime_get_sync(tw->ufsf->hba->dev); + if (tw->tw_enable) { + ret = ufstw_set_lu_flag(tw, QUERY_FLAG_IDN_TW_EN, + &tw->tw_enable); + if (ret) + tw->tw_enable = false; + } + + if (tw->flush_enable) { + ret = ufstw_set_lu_flag(tw, QUERY_FLAG_IDN_TW_BUF_FLUSH_EN, + &tw->flush_enable); + if (ret) + tw->flush_enable = false; + } + + if (tw->flush_during_hibern_enter) { + ret = ufstw_set_lu_flag(tw, + QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN, + &tw->flush_during_hibern_enter); + if (ret) + tw->flush_during_hibern_enter = false; + } + + pm_runtime_put_sync(tw->ufsf->hba->dev); +} + +void ufstw_reset_host(struct ufsf_feature *ufsf) +{ + struct ufstw_lu *tw; + int lun; + + if (ufstw_is_not_present(ufsf)) + return; + + ufstw_set_state(ufsf, TW_RESET); + if (ufsf->tw_dev_info.tw_buf_type == TW_BUF_TYPE_SHARED) { + tw = ufsf->tw_lup[0]; + + INFO_MSG("ufstw_lu[shared] cancel jobs"); + ufstw_cancel_lu_jobs(tw); + } else { + seq_scan_lu(lun) { + tw = ufsf->tw_lup[lun]; + if (!tw) + continue; + + INFO_MSG("ufstw_lu[%d] cancel jobs", lun); + ufstw_cancel_lu_jobs(tw); + } + } +} + +void ufstw_reset(struct ufsf_feature *ufsf, bool resume) +{ + struct ufstw_lu *tw; + int lun; + + INFO_MSG("ufstw reset start. reason: %s", + resume ? "resume" : "reset"); + if (ufstw_get_state(ufsf) != TW_RESET) { + ERR_MSG("tw_state error (%d)", ufstw_get_state(ufsf)); + return; + } + + ufstw_set_state(ufsf, TW_PRESENT); + + if (ufsf->tw_dev_info.tw_buf_type == TW_BUF_TYPE_SHARED) { + tw = ufsf->tw_lup[0]; + + INFO_MSG("ufstw_lu[shared] reset"); + ufstw_reset_query_handling(tw); + } else { + seq_scan_lu(lun) { + tw = ufsf->tw_lup[lun]; + if (!tw) + continue; + + INFO_MSG("ufstw_lu[%d] reset", lun); + ufstw_reset_query_handling(tw); + } + } + + INFO_MSG("ufstw reset finish"); +} + +#define ufstw_sysfs_attr_show_func(_query, _name, _IDN, hex) \ +static ssize_t ufstw_sysfs_show_##_name(struct ufstw_lu *tw, char *buf) \ +{ \ + int ret; \ + \ + if (ufstw_is_not_present(tw->ufsf)) \ + return -ENODEV; \ + \ + pm_runtime_get_sync(tw->ufsf->hba->dev); \ + ret = ufstw_read_lu_##_query(tw, _IDN, &tw->_name); \ + pm_runtime_put_sync(tw->ufsf->hba->dev); \ + if (ret) \ + return -ENODEV; \ + \ + INFO_MSG("read "#_query" "#_name" %u (0x%X)", \ + tw->_name, tw->_name); \ + if (hex) \ + return snprintf(buf, PAGE_SIZE, "0x%.2X\n", tw->_name); \ + return snprintf(buf, PAGE_SIZE, "%u\n", tw->_name); \ +} + +#define ufstw_sysfs_attr_store_func(_name, _IDN) \ +static ssize_t ufstw_sysfs_store_##_name(struct ufstw_lu *tw, \ + const char *buf, \ + size_t count) \ +{ \ + unsigned long val; \ + ssize_t ret = count; \ + \ + if (kstrtoul(buf, 0, &val)) \ + return -EINVAL; \ + \ + if (!(val == 0 || val == 1)) \ + return -EINVAL; \ + \ + INFO_MSG("val %lu", val); \ + if (ufstw_is_not_present(tw->ufsf)) \ + return -ENODEV; \ + \ + pm_runtime_get_sync(tw->ufsf->hba->dev); \ + if (val) { \ + if (ufstw_set_lu_flag(tw, _IDN, &tw->_name)) \ + ret = -ENODEV; \ + } else { \ + if (ufstw_clear_lu_flag(tw, _IDN, &tw->_name)) \ + ret = -ENODEV; \ + } \ + pm_runtime_put_sync(tw->ufsf->hba->dev); \ + \ + INFO_MSG(#_name " query success"); \ + return ret; \ +} + +ufstw_sysfs_attr_show_func(flag, tw_enable, QUERY_FLAG_IDN_TW_EN, 0); +ufstw_sysfs_attr_store_func(tw_enable, QUERY_FLAG_IDN_TW_EN); +ufstw_sysfs_attr_show_func(flag, flush_enable, + QUERY_FLAG_IDN_TW_BUF_FLUSH_EN, 0); +ufstw_sysfs_attr_store_func(flush_enable, QUERY_FLAG_IDN_TW_BUF_FLUSH_EN); +ufstw_sysfs_attr_show_func(flag, flush_during_hibern_enter, + QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN, 0); +ufstw_sysfs_attr_store_func(flush_during_hibern_enter, + QUERY_FLAG_IDN_TW_FLUSH_DURING_HIBERN); + +ufstw_sysfs_attr_show_func(attr, flush_status, + QUERY_ATTR_IDN_TW_FLUSH_STATUS, 0); +ufstw_sysfs_attr_show_func(attr, available_buffer_size, + QUERY_ATTR_IDN_TW_AVAIL_BUF_SIZE, 0); +ufstw_sysfs_attr_show_func(attr, lifetime_est, + QUERY_ATTR_IDN_TW_BUF_LIFETIME_EST, 1); +ufstw_sysfs_attr_show_func(attr, curr_buffer_size, + QUERY_ATTR_IDN_TW_CURR_BUF_SIZE, 0); + +#define ufstw_sysfs_attr_ro(_name) __ATTR(_name, 0444,\ + ufstw_sysfs_show_##_name, NULL) +#define ufstw_sysfs_attr_rw(_name) __ATTR(_name, 0644,\ + ufstw_sysfs_show_##_name, \ + ufstw_sysfs_store_##_name) + +static struct ufstw_sysfs_entry ufstw_sysfs_entries[] = { + /* Flag */ + ufstw_sysfs_attr_rw(tw_enable), + ufstw_sysfs_attr_rw(flush_enable), + ufstw_sysfs_attr_rw(flush_during_hibern_enter), + /* Attribute */ + ufstw_sysfs_attr_ro(flush_status), + ufstw_sysfs_attr_ro(available_buffer_size), + ufstw_sysfs_attr_ro(lifetime_est), + ufstw_sysfs_attr_ro(curr_buffer_size), + __ATTR_NULL +}; + +static ssize_t ufstw_attr_show(struct kobject *kobj, struct attribute *attr, + char *page) +{ + struct ufstw_sysfs_entry *entry; + struct ufstw_lu *tw; + ssize_t error; + + entry = container_of(attr, struct ufstw_sysfs_entry, attr); + if (!entry->show) + return -EIO; + + tw = container_of(kobj, struct ufstw_lu, kobj); + + mutex_lock(&tw->sysfs_lock); + error = entry->show(tw, page); + mutex_unlock(&tw->sysfs_lock); + return error; +} + +static ssize_t ufstw_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t length) +{ + struct ufstw_sysfs_entry *entry; + struct ufstw_lu *tw; + ssize_t error; + + entry = container_of(attr, struct ufstw_sysfs_entry, attr); + if (!entry->store) + return -EIO; + + tw = container_of(kobj, struct ufstw_lu, kobj); + + mutex_lock(&tw->sysfs_lock); + error = entry->store(tw, page, length); + mutex_unlock(&tw->sysfs_lock); + return error; +} + +static const struct sysfs_ops ufstw_sysfs_ops = { + .show = ufstw_attr_show, + .store = ufstw_attr_store, +}; + +static struct kobj_type ufstw_ktype = { + .sysfs_ops = &ufstw_sysfs_ops, + .release = NULL, +}; + +static int ufstw_create_sysfs(struct ufsf_feature *ufsf, struct ufstw_lu *tw) +{ + struct device *dev = ufsf->hba->dev; + struct ufstw_sysfs_entry *entry; + int err; + char lun_str[20] = { 0 }; + + tw->sysfs_entries = ufstw_sysfs_entries; + + kobject_init(&tw->kobj, &ufstw_ktype); + mutex_init(&tw->sysfs_lock); + + if (tw->lun == TW_LU_SHARED) { + snprintf(lun_str, 6, "ufstw"); + INFO_MSG("ufstw creates sysfs ufstw-shared"); + } else { + snprintf(lun_str, 10, "ufstw_lu%d", tw->lun); + INFO_MSG("ufstw creates sysfs ufstw_lu%d", tw->lun); + } + + err = kobject_add(&tw->kobj, kobject_get(&dev->kobj), lun_str); + if (!err) { + for (entry = tw->sysfs_entries; entry->attr.name != NULL; + entry++) { + if (tw->lun == TW_LU_SHARED) + INFO_MSG("ufstw-shared sysfs attr creates: %s", + entry->attr.name); + else + INFO_MSG("ufstw_lu(%d) sysfs attr creates: %s", + tw->lun, entry->attr.name); + + err = sysfs_create_file(&tw->kobj, &entry->attr); + if (err) { + ERR_MSG("create entry(%s) failed", + entry->attr.name); + goto kobj_del; + } + } + kobject_uevent(&tw->kobj, KOBJ_ADD); + } else { + ERR_MSG("kobject_add failed"); + } + + return err; +kobj_del: + err = kobject_uevent(&tw->kobj, KOBJ_REMOVE); + INFO_MSG("kobject removed (%d)", err); + kobject_del(&tw->kobj); + return -EINVAL; +} diff --git a/drivers/scsi/ufs/ufstw.h b/drivers/scsi/ufs/ufstw.h new file mode 100755 index 000000000000..2d3bfc8c7d4a --- /dev/null +++ b/drivers/scsi/ufs/ufstw.h @@ -0,0 +1,126 @@ +/* + * Universal Flash Storage tw Write + * + * Copyright (C) 2017-2018 Samsung Electronics Co., Ltd. + * + * Authors: + * Yongmyung Lee + * Jinyoung Choi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * See the COPYING file in the top-level directory or visit + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This program is provided "AS IS" and "WITH ALL FAULTS" and + * without warranty of any kind. You are solely responsible for + * determining the appropriateness of using and distributing + * the program and assume all risks associated with your exercise + * of rights with respect to the program, including but not limited + * to infringement of third party rights, the risks and costs of + * program errors, damage to or loss of data, programs or equipment, + * and unavailability or interruption of operations. Under no + * circumstances will the contributor of this Program be liable for + * any damages of any kind arising from your use or distribution of + * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. + */ + +#ifndef _UFSTW_H_ +#define _UFSTW_H_ + +#include +#include +#include +#include +#include + +#include "../../../block/blk.h" + +#define UFSTW_VER 0x0110 +#define UFSTW_DD_VER 0x010300 +#define UFSTW_DD_VER_POST UFSFEATURE_DD_VER_POST + +#define UFSTW_LIFETIME_SECT 2097152 /* 1GB */ +#define UFSTW_MAX_LIFETIME_VALUE 0x0B +#define MASK_UFSTW_LIFETIME_NOT_GUARANTEE 0x80 +#define UFS_FEATURE_SUPPORT_TW_BIT 0x100 + +#define TW_LU_SHARED -1 + +enum UFSTW_STATE { + TW_NEED_INIT = 0, + TW_PRESENT = 1, + TW_RESET = -3, + TW_FAILED = -4 +}; + +enum { + TW_BUF_TYPE_LU = 0, + TW_BUF_TYPE_SHARED, +}; + +struct ufstw_dev_info { + /* from Device Descriptor */ + u16 tw_ver; + u32 tw_shared_buf_alloc_units; + u8 tw_buf_no_reduct; + u8 tw_buf_type; + + /* from Geometry Descriptor */ + u8 tw_number_lu; +}; + +struct ufstw_lu { + struct ufsf_feature *ufsf; + + int lun; + + bool tw_enable; + bool flush_enable; + bool flush_during_hibern_enter; + + unsigned int flush_status; + unsigned int available_buffer_size; + unsigned int curr_buffer_size; + + unsigned int lifetime_est; + spinlock_t lifetime_lock; + u32 stat_write_sec; + struct work_struct tw_lifetime_work; + + /* for sysfs */ + struct kobject kobj; + struct mutex sysfs_lock; + struct ufstw_sysfs_entry *sysfs_entries; +}; + +struct ufstw_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct ufstw_lu *tw, char *buf); + ssize_t (*store)(struct ufstw_lu *tw, const char *buf, size_t count); +}; + +struct ufshcd_lrb; + +int ufstw_get_state(struct ufsf_feature *ufsf); +void ufstw_set_state(struct ufsf_feature *ufsf, int state); +void ufstw_get_dev_info(struct ufsf_feature *ufsf, u8 *desc_buf); +void ufstw_get_geo_info(struct ufsf_feature *ufsf, u8 *geo_buf); +void ufstw_alloc_lu(struct ufsf_feature *ufsf, int lun, u8 *lu_buf); + +void ufstw_prep_fn(struct ufsf_feature *ufsf, struct ufshcd_lrb *lrbp); +void ufstw_init(struct ufsf_feature *ufsf); +void ufstw_remove(struct ufsf_feature *ufsf); +void ufstw_reset_host(struct ufsf_feature *ufsf); +void ufstw_reset(struct ufsf_feature *ufsf, bool resume); +#endif /* End of Header */ diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 33674ebaf5da..e53c92605219 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -4,6 +4,176 @@ # menu "Qualcomm SoC drivers" +config MMI_DEVICE_DTBS + depends on ARCH_QCOM + default n + bool "Motorola Mobility Device DTBs" + help + Only include Motorola device specific DTBs to limit the number that + get built during kernel compilation. + +config DENVER_DTB + depends on ARCH_QCOM + default n + bool "DENVER DTBs" + help + Only include DENVER specific DTBs to limit the number that + get built during kernel compilation. + +config IBIZA_DTB + depends on ARCH_QCOM + default n + bool "IBIZA DTBs" + help + Only include IBIZA specific DTBs to limit the number that + get built during kernel compilation. + +config CORFUP_DTB + depends on ARCH_QCOM + default n + bool "CORFUP DTBs" + help + Only include CORFUP specific DTBs to limit the number that + get built during kernel compilation. + +config CORFUQ_DTB + depends on ARCH_QCOM + default n + bool "CORFUQ DTBs" + help + Only include CORFUQ specific DTBs to limit the number that + get built during kernel compilation. + +config CORFUR_DTB + depends on ARCH_QCOM + default n + bool "CORFUR DTBs" + help + Only include CORFUR specific DTBs to limit the number that + get built during kernel compilation. + +config CYPFG_DTB + depends on ARCH_QCOM + default n + bool "CYPFG DTBs" + help + Only include CYPFG specific DTBs to limit the number that + get built during kernel compilation. + +config CYPFQ_DTB + depends on ARCH_QCOM + default n + bool "CYPFQ DTBs" + help + Only include CYPFQ specific DTBs to limit the number that + get built during kernel compilation. + + +config RHODEC_DTB + depends on ARCH_QCOM + default n + bool "RHODEC DTBs" + help + Only include RHODEC specific DTBs to limit the number that + get built during kernel compilation. + + +config CYPFR_DTB + depends on ARCH_QCOM + default n + bool "CYPFR DTBs" + help + Only include CYPFR specific DTBs to limit the number that + get built during kernel compilation. + +config MILANF_DTB + depends on ARCH_QCOM + default n + bool "MILANF DTBs" + help + Only include MILANF specific DTBs to limit the number that + get built during kernel compilation. + +config PENANG_DTB + depends on ARCH_QCOM + default n + bool "PENANG DTBs" + help + Only include PENANG specific DTBs to limit the number that + get built during kernel compilation. + +config PNANGN_DTB + depends on ARCH_QCOM + default n + bool "PNANGN DTBs" + help + Only include PNANGN specific DTBs to limit the number that + get built during kernel compilation. + +config BANGKK_DTB + depends on ARCH_QCOM + default n + bool "BANGKK DTBs" + help + Only include PENANG specific DTBs to limit the number that + get built during kernel compilation. + +config FOGOR_DTB + depends on ARCH_QCOM + default n + bool "FOGOR DTBs" + help + Only include FOGOR specific DTBs to limit the number that + get built during kernel compilation. + +config FOGOS_DTB + depends on ARCH_QCOM + default n + bool "FOGOS DTBs" + help + Only include FOGOS specific DTBs to limit the number that + get built during kernel compilation. + +config FOGO_DTB + depends on ARCH_QCOM + default n + bool "FOGO DTBs" + help + Only include FOGO specific DTBs to limit the number that + get built during kernel compilation. + +config PENANGB_DTB + depends on ARCH_QCOM + default n + bool "PENANGB DTBs" + help + Only include PENANG specific DTBs to limit the number that + get built during kernel compilation. + +config RHODEP_DTB + depends on ARCH_QCOM + default n + bool "RHODEP DTBs" + help + Only include RHODEP specific DTBs to limit the number that + get built during kernel compilation. + +config MIAMI_DTB + depends on ARCH_QCOM + default n + bool "MIAMI DTBs" + help + Only include MIAMI specific DTBs to limit the number that + get built during kernel compilation. + +config RHODEI_DTB + depends on ARCH_QCOM + default n + bool "RHODEI DTBs" + help + Only include RHODEI specific DTBs to limit the number that + get built during kernel compilation. + config QCOM_AOSS_QMP tristate "Qualcomm AOSS Driver" depends on ARCH_QCOM || COMPILE_TEST @@ -1323,6 +1493,15 @@ config MSM_SLATECOM_INTERFACE from hardware. Hardware will provide access to read registers and read/write AHB memory in the device. +config MSM_SLATECOM_RPMSG + tristate "Provide support for slate GLINK channel" + depends on MSM_SLATECOM_INTERFACE + help + MSM_SLATECOM_RPMSG informs SLATECOM_INTERFACE driver if GLINK channel + has been opened by remote processor. It doesn't maintain state machine + and is probed when Slate opens channel and removed when the channel is + closed by remote processor. + config QCOM_AOP_SET_DDR tristate "Sysfs to communicate with AOP using QMP protocol" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 6bd8d4a71359..7faed000a678 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_RENAME_BLOCK_DEVICE) += rename_block_device.o obj-$(CONFIG_QCOM_ADSP_MANUAL_VOTE) += adsp_vote_qmi.o adsp_lpm_voting_v01.o obj-$(CONFIG_MSM_SLATECOM) += slatecom_spi.o obj-$(CONFIG_MSM_SLATECOM_INTERFACE) += slatecom_interface.o +obj-$(CONFIG_MSM_SLATECOM_RPMSG) += slatecom_rpmsg.o obj-$(CONFIG_QCOM_AOP_SET_DDR) += aop-set-ddr.o obj-$(CONFIG_CPU_V7) += idle-v7.o obj-$(CONFIG_DUMP_XBL_LOG) += dump_boot_log.o diff --git a/drivers/soc/qcom/fsa4480-i2c.c b/drivers/soc/qcom/fsa4480-i2c.c index e263d671dc6c..61b0f25d4d68 100644 --- a/drivers/soc/qcom/fsa4480-i2c.c +++ b/drivers/soc/qcom/fsa4480-i2c.c @@ -67,6 +67,8 @@ static const struct fsa4480_reg_val fsa_reg_i2c_defaults[] = { {FSA4480_SWITCH_SETTINGS, 0x98}, }; +static bool fsa_reg_notifier; + static void fsa4480_usbc_update_settings(struct fsa4480_priv *fsa_priv, u32 switch_control, u32 switch_enable) { @@ -103,7 +105,7 @@ static int fsa4480_usbc_event_changed_psupply(struct fsa4480_priv *fsa_priv, dev = fsa_priv->dev; if (!dev) return -EINVAL; - dev_dbg(dev, "%s: queueing usbc_analog_work\n", + dev_info(dev, "%s: queueing usbc_analog_work\n", __func__); pm_stay_awake(fsa_priv->dev); queue_work(system_freezable_wq, &fsa_priv->usbc_analog_work); @@ -190,9 +192,14 @@ static int fsa4480_usbc_analog_setup_switches_psupply( goto done; } - dev_dbg(dev, "%s: setting GPIOs active = %d rcvd intval 0x%X\n", + dev_info(dev, "%s: setting GPIOs active = %d rcvd intval 0x%X\n", __func__, mode.intval != TYPEC_ACCESSORY_NONE, mode.intval); - atomic_set(&(fsa_priv->usbc_mode), mode.intval); + if ((atomic_read(&(fsa_priv->usbc_mode)) != mode.intval) || fsa_reg_notifier) { + atomic_set(&(fsa_priv->usbc_mode), mode.intval); + fsa_reg_notifier = false; + } else { + goto done; + } switch (mode.intval) { /* add all modes FSA should notify for in here */ @@ -308,8 +315,9 @@ int fsa4480_reg_notifier(struct notifier_block *nb, * as part of the init sequence check if there is a connected * USB C analog adapter */ - dev_dbg(fsa_priv->dev, "%s: verify if USB adapter is already inserted\n", + dev_info(fsa_priv->dev, "%s: verify if USB adapter is already inserted\n", __func__); + fsa_reg_notifier = true; rc = fsa4480_usbc_analog_setup_switches(fsa_priv); regmap_update_bits(fsa_priv->regmap, FSA4480_SWITCH_CONTROL, 0x07, fsa_priv->switch_control); @@ -494,10 +502,10 @@ static int fsa4480_probe(struct i2c_client *i2c, goto err_data; } - fsa_priv->iio_ch = iio_channel_get(fsa_priv->dev, "typec_mode"); + fsa_priv->iio_ch = iio_channel_get(fsa_priv->dev, "typec_accessory_mode"); if (!fsa_priv->iio_ch) { dev_err(fsa_priv->dev, - "%s: iio_channel_get failed for typec_mode\n", + "%s: iio_channel_get failed for typec_accessory_mode\n", __func__); goto err_supply; } diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 7f336477b24f..197e07fe90d3 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -25,7 +25,7 @@ #define QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT 2000 #define QMI_SERVREG_LOC_SERVER_TIMEOUT 2000 #define INITIAL_TIMEOUT 100000 -#define LOCATOR_SERVICE_TIMEOUT 300000 +#define LOCATOR_SERVICE_TIMEOUT 900000 #define LOCATOR_NOT_PRESENT 0 #define LOCATOR_PRESENT 1 diff --git a/drivers/soc/qcom/slatecom_interface.c b/drivers/soc/qcom/slatecom_interface.c index 36bf60355d8a..0001b31dbe21 100644 --- a/drivers/soc/qcom/slatecom_interface.c +++ b/drivers/soc/qcom/slatecom_interface.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #define pr_fmt(msg) "slatecom_dev:" msg @@ -31,7 +32,7 @@ #include "peripheral-loader.h" #include "../../misc/qseecom_kernel.h" -//#include "pil_slate_intf.h" +#include "slatecom_rpmsg.h" #define SLATECOM "slate_com_dev" @@ -44,6 +45,7 @@ #define MPPS_DOWN_EVENT_TO_SLATE_TIMEOUT 3000 #define ADSP_DOWN_EVENT_TO_SLATE_TIMEOUT 3000 #define MAX_APP_NAME_SIZE 100 +#define SCOM_GLINK_INTENT_SIZE 308 /*pil_slate_intf.h*/ #define RESULT_SUCCESS 0 @@ -95,7 +97,14 @@ struct slatedaemon_priv { unsigned long attrs; u32 cmd_status; struct device *platform_dev; + bool slatecom_rpmsg; + bool slate_resp_cmplt; + void *lhndl; + wait_queue_head_t link_state_wait; + char rx_buf[SCOM_GLINK_INTENT_SIZE]; + struct mutex glink_mutex; }; +static void *slatecom_intf_drv; struct slate_event { enum slate_event_type e_type; @@ -182,6 +191,108 @@ static int send_uevent(struct slate_event *pce) return kobject_uevent_env(&dev_ret->kobj, KOBJ_CHANGE, envp); } +void slatecom_intf_notify_glink_channel_state(bool state) +{ + struct slatedaemon_priv *dev = + container_of(slatecom_intf_drv, struct slatedaemon_priv, lhndl); + + pr_debug("%s: slate_ctrl channel state: %d\n", __func__, state); + dev->slatecom_rpmsg = state; +} +EXPORT_SYMBOL(slatecom_intf_notify_glink_channel_state); + +void slatecom_rx_msg(void *data, int len) +{ + struct slatedaemon_priv *dev = + container_of(slatecom_intf_drv, struct slatedaemon_priv, lhndl); + + if (len > SCOM_GLINK_INTENT_SIZE) { + pr_err("Invalid slatecom_intf glink intent size\n"); + return; + } + dev->slate_resp_cmplt = true; + wake_up(&dev->link_state_wait); + memcpy(dev->rx_buf, data, len); +} +EXPORT_SYMBOL(slatecom_rx_msg); + +static int slatecom_tx_msg(struct slatedaemon_priv *dev, void *msg, size_t len) +{ + int rc = 0; + uint8_t resp = 0; + + mutex_lock(&dev->glink_mutex); + if (!dev->slatecom_rpmsg) { + pr_err("slatecom-rpmsg is not probed yet, waiting for it to be probed\n"); + goto err_ret; + } + rc = slatecom_rpmsg_tx_msg(msg, len); + + /* wait for sending command to SLATE */ + rc = wait_event_timeout(dev->link_state_wait, + (rc == 0), msecs_to_jiffies(TIMEOUT_MS)); + if (rc == 0) { + pr_err("failed to send command to SLATE %d\n", rc); + goto err_ret; + } + + /* wait for getting response from SLATE */ + rc = wait_event_timeout(dev->link_state_wait, + dev->slate_resp_cmplt, + msecs_to_jiffies(TIMEOUT_MS)); + if (rc == 0) { + pr_err("failed to get SLATE response %d\n", rc); + goto err_ret; + } + dev->slate_resp_cmplt = false; + /* check SLATE response */ + resp = *(uint8_t *)dev->rx_buf; + if (!(resp == 0x01)) { + pr_err("Bad SLATE response\n"); + rc = -EINVAL; + goto err_ret; + } + rc = 0; + +err_ret: + mutex_unlock(&dev->glink_mutex); + return rc; +} + +/** + * send_state_change_cmd send state transition event to Slate + * and wait for the response. + * The response is returned to the caller. + */ +static int send_state_change_cmd(struct slate_ui_data *ui_obj_msg) +{ + int ret = 0; + struct wear_header wear_header_t = {0, 0}; + struct slatedaemon_priv *dev = container_of(slatecom_intf_drv, + struct slatedaemon_priv, + lhndl); + uint32_t state = ui_obj_msg->cmd; + + switch (state) { + case STATE_TWM_ENTER: + wear_header_t.opcode = GMI_WEAR_MGR_ENTER_TWM; + break; + case STATE_DS_ENTER: + wear_header_t.opcode = GMI_WEAR_MGR_ENTER_TRACKER_DS; + break; + case STATE_DS_EXIT: + wear_header_t.opcode = GMI_WEAR_MGR_EXIT_TRACKER_DS; + break; + default: + pr_err("Invalid MSM State transtion cmd\n"); + break; + } + ret = slatecom_tx_msg(dev, &wear_header_t.opcode, sizeof(wear_header_t.opcode)); + if (ret < 0) + pr_err("MSM State transtion event cmd failed\n"); + return ret; +} + static int slatecom_char_open(struct inode *inode, struct file *file) { int ret; @@ -382,6 +493,16 @@ static long slate_com_ioctl(struct file *filp, } ret = 0; break; + case DEVICE_STATE_TRANSITION: + if (copy_from_user(&ui_obj_msg, (void __user *)arg, + sizeof(ui_obj_msg))) { + pr_err("The copy from user failed for state transition cmd\n"); + ret = -EFAULT; + } + ret = send_state_change_cmd(&ui_obj_msg); + if (ret < 0) + pr_err("device state transition cmd failed\n"); + break; default: ret = -ENOIOCTLCMD; break; diff --git a/drivers/soc/qcom/slatecom_interface.h b/drivers/soc/qcom/slatecom_interface.h index a64777a3b12f..9fa010771415 100644 --- a/drivers/soc/qcom/slatecom_interface.h +++ b/drivers/soc/qcom/slatecom_interface.h @@ -36,6 +36,80 @@ void set_slate_dsp_state(bool status); * Set slate bt state */ void set_slate_bt_state(bool status); +void slatecom_intf_notify_glink_channel_state(bool state); +void slatecom_rx_msg(void *data, int len); +/* + * WM header type - generic header structure + */ +struct wear_header { + uint32_t opcode; + uint32_t payload_size; +}; + +/** + * Opcodes to be received on slate-control channel. + */ +enum WMSlateCtrlChnlOpcode { + /* + * Command to slate to enter TWM mode + */ + GMI_WEAR_MGR_ENTER_TWM = 1, + + /* + * Notification to slate about Modem Processor Sub System + * is down due to a subsystem reset. + */ + GMI_WEAR_MGR_SSR_MPSS_DOWN_NOTIFICATION = 2, + + /* + * Notification to slate about Modem Processor Sub System + * being brought up after a subsystem reset. + */ + GMI_WEAR_MGR_SSR_MPSS_UP_NOTIFICATION = 3, + + /* + * Notification to slate about ADSP Sub System + * is down due to a subsystem reset. + */ + GMI_WEAR_MGR_SSR_ADSP_DOWN_INDICATION = 8, + + /* + * Notification to slate about Modem Processor + * Sub System being brought up after a subsystem reset. + */ + GMI_WEAR_MGR_SSR_ADSP_UP_INDICATION = 9, + + /* + * Notification to MSM for generic wakeup in tracker mode + */ + GMI_WEAR_MGR_WAKE_UP_NO_REASON = 10, + + /* + * Notification to Slate About Entry to Tracker-DS + */ + GMI_WEAR_MGR_ENTER_TRACKER_DS = 11, + + /* + * Notification to Slate About Entry to Tracker-DS + */ + GMI_WEAR_MGR_EXIT_TRACKER_DS = 12, + + /* + * Notification to Slate About Time-sync update + */ + GMI_WEAR_MGR_TIME_SYNC_UPDATE = 13, /* payload struct: wear_time_sync_t*/ + + /* + * Notification to Slate About Timeval UTC + */ + GMI_WEAR_MGR_TIMEVAL_UTC = 14, /* payload struct: wear_timeval_utc_t*/ + + /* + * Notification to Slate About Daylight saving time + */ + GMI_WEAR_MGR_DST = 15, /* payload struct: wear_dst_t*/ + +}; #endif /* SLATECOM_INTERFACE_H */ diff --git a/drivers/soc/qcom/slatecom_rpmsg.c b/drivers/soc/qcom/slatecom_rpmsg.c new file mode 100644 index 000000000000..919be0338bde --- /dev/null +++ b/drivers/soc/qcom/slatecom_rpmsg.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ +#include +#include +#include +#include +#include "slatecom_rpmsg.h" + +static struct slatecom_rpmsg_dev *pdev; + +int slatecom_rpmsg_tx_msg(void *msg, size_t len) +{ + int ret = 0; + + if (pdev == NULL || !pdev->chnl_state) + pr_err("pmsg_device is null, channel is closed\n"); + + pdev->message = msg; + pdev->message_length = len; + if (pdev->message) { + ret = rpmsg_send(pdev->channel, pdev->message, pdev->message_length); + if (ret) + pr_err("rpmsg_send failed: %d\n", ret); + + } + return ret; +} +EXPORT_SYMBOL(slatecom_rpmsg_tx_msg); + +static int slatecom_rpmsg_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + void *msg = NULL; + + pdev = devm_kzalloc(&rpdev->dev, sizeof(*pdev), GFP_KERNEL); + if (!pdev) + return -ENOMEM; + + pdev->channel = rpdev->ept; + pdev->dev = &rpdev->dev; + if (pdev->channel == NULL) + return -ENOMEM; + + pdev->chnl_state = true; + dev_set_drvdata(&rpdev->dev, pdev); + + /* send a callback to slatecom-interface driver*/ + slatecom_intf_notify_glink_channel_state(true); + if (pdev->message == NULL) + ret = slatecom_rpmsg_tx_msg(msg, 0); + return 0; +} + +static void slatecom_rpmsg_remove(struct rpmsg_device *rpdev) +{ + pdev->chnl_state = false; + pdev->message = NULL; + dev_dbg(&rpdev->dev, "rpmsg client driver is removed\n"); + slatecom_intf_notify_glink_channel_state(false); + dev_set_drvdata(&rpdev->dev, NULL); +} + +static int slatecom_rpmsg_cb(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 src) +{ + struct slatecom_rpmsg_dev *dev = + dev_get_drvdata(&rpdev->dev); + + if (!dev) + return -ENODEV; + slatecom_rx_msg(data, len); + return 0; +} + +static const struct rpmsg_device_id rpmsg_driver_slatecom_id_table[] = { + { "SLATECOM_CTRL" }, + {}, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_slatecom_id_table); + +static const struct of_device_id rpmsg_driver_slatecom_of_match[] = { + { .compatible = "qcom,slatecom-rpmsg" }, + {}, +}; + +static struct rpmsg_driver rpmsg_slatecom_client = { + .id_table = rpmsg_driver_slatecom_id_table, + .probe = slatecom_rpmsg_probe, + .callback = slatecom_rpmsg_cb, + .remove = slatecom_rpmsg_remove, + .drv = { + .name = "qcom,slatecom_rpmsg", + .of_match_table = rpmsg_driver_slatecom_of_match, + }, +}; +module_rpmsg_driver(rpmsg_slatecom_client); + +MODULE_DESCRIPTION("Interface Driver for SLATECOM and RPMSG"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/slatecom_rpmsg.h b/drivers/soc/qcom/slatecom_rpmsg.h new file mode 100644 index 000000000000..1d80bb0511dc --- /dev/null +++ b/drivers/soc/qcom/slatecom_rpmsg.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ +#ifndef SLATECOMRPMSG_H +#define SLATECOMRPMSG_H + +#include +#include "slatecom_interface.h" + +#define TIMEOUT_MS 2000 + +struct slatecom_rpmsg_dev { + struct rpmsg_endpoint *channel; + struct device *dev; + bool chnl_state; + void *message; + size_t message_length; +}; + + +#if IS_ENABLED(CONFIG_MSM_SLATECOM_RPMSG) +int slatecom_rpmsg_tx_msg(void *msg, size_t len); +#else +static inline int slatecom_rpmsg_tx_msg(void *msg, size_t len) +{ + return -EIO; +} +#endif + +#endif diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index ddd30f512b0d..f256c812e1f5 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -46,6 +46,10 @@ #define desc_to_data(d) container_of(d, struct pil_tz_data, desc) #define subsys_to_data(d) container_of(d, struct pil_tz_data, subsys_desc) +static char pil_ssr_reason[MAX_SSR_REASON_LEN]; +static char *ssr_reason = pil_ssr_reason; +module_param(ssr_reason, charp, S_IRUGO); + /** * struct reg_info - regulator info * @reg: regulator handle @@ -761,7 +765,7 @@ static struct pil_reset_ops pil_ops_trusted = { static void log_failure_reason(const struct pil_tz_data *d) { size_t size; - char *smem_reason, reason[MAX_SSR_REASON_LEN]; + char *smem_reason; const char *name = d->subsys_desc.name; if (d->smem_id == -1) @@ -778,8 +782,8 @@ static void log_failure_reason(const struct pil_tz_data *d) return; } - strlcpy(reason, smem_reason, min(size, (size_t)MAX_SSR_REASON_LEN)); - pr_err("%s subsystem failure reason: %s.\n", name, reason); + strlcpy(pil_ssr_reason, smem_reason, min((size_t)size, (size_t)sizeof(pil_ssr_reason))); + pr_err("%s subsystem failure reason: %s.\n", name, pil_ssr_reason); } static int subsys_shutdown(const struct subsys_desc *subsys, bool force_stop) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index be33e013fd70..db1d276b567c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -961,3 +961,8 @@ config SPI_DYNAMIC def_bool ACPI || OF_DYNAMIC || SPI_SLAVE endif # SPI + +config IS_XFER_IN_PROGRESS + bool "Check if xfer is in progress" + help + Force suspend if spi xfers were completed. diff --git a/drivers/spi/spi-msm-geni.c b/drivers/spi/spi-msm-geni.c index 200955a3a6db..db735e1c5bae 100644 --- a/drivers/spi/spi-msm-geni.c +++ b/drivers/spi/spi-msm-geni.c @@ -164,6 +164,9 @@ struct spi_geni_master { int num_rx_eot; int num_xfers; atomic_t is_irq_enable; +#ifdef CONFIG_IS_XFER_IN_PROGRESS + bool is_xfer_in_progress; +#endif void *ipc; bool gsi_mode; /* GSI Mode */ bool shared_ee; /* Dual EE use case */ @@ -1006,13 +1009,14 @@ static int spi_geni_prepare_message(struct spi_master *spi, if (mas->shared_ee) { if (mas->setup) { +#ifdef CONFIG_IS_XFER_IN_PROGRESS /* Client to respect system suspend */ if (!pm_runtime_enabled(mas->dev)) { GENI_SE_ERR(mas->ipc, false, NULL, "%s: System suspended\n", __func__); return -EACCES; } - +#endif ret = pm_runtime_get_sync(mas->dev); if (ret < 0) { dev_err(mas->dev, @@ -1020,8 +1024,10 @@ static int spi_geni_prepare_message(struct spi_master *spi, __func__, ret); WARN_ON_ONCE(1); pm_runtime_put_noidle(mas->dev); +#ifdef CONFIG_IS_XFER_IN_PROGRESS /* Set device in suspended since resume failed */ pm_runtime_set_suspended(mas->dev); +#endif goto exit_prepare_message; } ret = 0; @@ -1335,6 +1341,10 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) /* Adjust the IB based on the max speed of the slave in kHz.*/ rsc->ib = (max_speed * DEFAULT_BUS_WIDTH) / 1000; +#ifdef CONFIG_IS_XFER_IN_PROGRESS + mas->is_xfer_in_progress = true; +#endif + /* * Not required for LE as below intializations are specific * to usecases. For LE, client takes care of get_sync. @@ -1342,12 +1352,15 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) if (mas->is_le_vm) return 0; +#ifdef CONFIG_IS_XFER_IN_PROGRESS /* Client to respect system suspend */ if (!pm_runtime_enabled(mas->dev)) { GENI_SE_ERR(mas->ipc, false, NULL, "%s: System suspended\n", __func__); + mas->is_xfer_in_progress = false; return -EACCES; } +#endif if (mas->gsi_mode && !mas->shared_ee) { struct se_geni_rsc *rsc; @@ -1366,6 +1379,12 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) "%s: Error %d pinctrl_select_state\n", __func__, ret); } + if (mas->dev->power.disable_depth > 0) { + dev_err(mas->dev, "%s:disable_depth not zero %d\n", + __func__, mas->dev->power.disable_depth); + pm_runtime_enable(mas->dev); + } + if (!mas->setup || !mas->shared_ee) { ret = pm_runtime_get_sync(mas->dev); if (ret < 0) { @@ -1374,8 +1393,11 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) __func__, ret); WARN_ON_ONCE(1); pm_runtime_put_noidle(mas->dev); +#ifdef CONFIG_IS_XFER_IN_PROGRESS /* Set device in suspended since resume failed */ pm_runtime_set_suspended(mas->dev); + mas->is_xfer_in_progress = false; +#endif return ret; } @@ -1384,6 +1406,9 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) if (ret) { GENI_SE_ERR(mas->ipc, true, NULL, "%s mas_setup failed: %d\n", __func__, ret); +#ifdef CONFIG_IS_XFER_IN_PROGRESS + mas->is_xfer_in_progress = false; +#endif return ret; } } @@ -1405,8 +1430,12 @@ static int spi_geni_unprepare_transfer_hardware(struct spi_master *spi) struct spi_geni_master *mas = spi_master_get_devdata(spi); int count = 0; - if (mas->shared_ee || mas->is_le_vm) + if (mas->shared_ee || mas->is_le_vm) { +#ifdef CONFIG_IS_XFER_IN_PROGRESS + mas->is_xfer_in_progress = false; +#endif return 0; + } if (mas->gsi_mode) { struct se_geni_rsc *rsc; @@ -1435,6 +1464,9 @@ static int spi_geni_unprepare_transfer_hardware(struct spi_master *spi) pm_runtime_mark_last_busy(mas->dev); pm_runtime_put_autosuspend(mas->dev); } +#ifdef CONFIG_IS_XFER_IN_PROGRESS + mas->is_xfer_in_progress = false; +#endif return 0; } @@ -1661,6 +1693,7 @@ static int spi_geni_transfer_one(struct spi_master *spi, GENI_SE_DBG(mas->ipc, false, mas->dev, "current xfer_timeout:%lu ms.\n", xfer_timeout); +#ifdef CONFIG_IS_XFER_IN_PROGRESS /* Double check PM status, client might have not taken wakelock and * continue to queue more transfers. Post auto-suspend, system suspend * can keep driver to forced suspend, hence it's client's responsibility @@ -1671,6 +1704,7 @@ static int spi_geni_transfer_one(struct spi_master *spi, "%s: device is PM suspended\n", __func__); return -EACCES; } +#endif if (mas->cur_xfer_mode != GSI_DMA) { reinit_completion(&mas->xfer_done); @@ -2206,6 +2240,9 @@ static int spi_geni_probe(struct platform_device *pdev) "M - DRIVER GENI_SPI_%d Ready", spi->bus_num); place_marker(boot_marker); dev_info(&pdev->dev, "%s: completed\n", __func__); +#ifdef CONFIG_IS_XFER_IN_PROGRESS + geni_mas->is_xfer_in_progress = false; +#endif return ret; spi_geni_probe_unmap: devm_iounmap(&pdev->dev, geni_mas->base); @@ -2314,11 +2351,23 @@ static int spi_geni_suspend(struct device *dev) int ret = 0; struct spi_master *spi = get_spi_master(dev); struct spi_geni_master *geni_mas = spi_master_get_devdata(spi); - +#ifdef CONFIG_IS_XFER_IN_PROGRESS + if (geni_mas->is_xfer_in_progress) { + ret = -EBUSY; + if (!pm_runtime_status_suspended(dev)) { + GENI_SE_ERR(geni_mas->ipc, true, dev, + ":%s: runtime PM is active\n", __func__); + return ret; + } + GENI_SE_ERR(geni_mas->ipc, true, dev, + "%s System suspend not allowed while xfer in progress\n", + __func__); +#else if (!pm_runtime_status_suspended(dev)) { GENI_SE_ERR(geni_mas->ipc, true, dev, ":%s: runtime PM is active\n", __func__); ret = -EBUSY; +#endif return ret; } diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 61d2a8cbf894..ce974bdfc7d0 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -481,6 +481,56 @@ static int ion_init_sysfs(void) return 0; } +#ifdef CONFIG_DEBUG_FS +static int ion_debug_allbufs_show(struct seq_file *s, void *unused) +{ + struct ion_device *dev = s->private; + struct rb_node *n; + int i; + unsigned long total_len = 0; + + seq_printf(s, "%16.s %16.s %12.s %12.s %20.s %s\n", "heap", + "buffer", "size", "ref cnt", "allocator", "references"); + + down_read(&dev->lock); + mutex_lock(&dev->buffer_lock); + for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { + struct ion_buffer *buf = rb_entry(n, struct ion_buffer, node); + int buf_refcount = buf->ref_cnt; + total_len += buf->size; + seq_printf(s, "%16.s %16pK %12.x %12.d %20.d %s", + buf->heap->name, buf, (int)buf->size, + buf_refcount, buf->pid, ""); + + for(i = 0; i < buf->ref_cnt && i < MAX_CLIENTS_NUM; i++) + seq_printf(s, "%u, ", buf->client_pids[i]); + + seq_puts(s, "\n"); + } + + if (s->file && s->file->f_path.dentry + && !strcmp(s->file->f_path.dentry->d_iname, "check_all_bufs_total")) + seq_printf(s, "%16.s %s is: %lld(%lld KB)\n", + "heap", "total size", total_len, total_len/1024); + + mutex_unlock(&dev->buffer_lock); + up_read(&dev->lock); + return 0; +} + +static int ion_debug_allbufs_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_allbufs_show, inode->i_private); +} + +static const struct file_operations debug_allbufs_fops = { + .open = ion_debug_allbufs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + static int ion_device_create(void) { struct ion_device *idev; @@ -506,10 +556,22 @@ static int ion_device_create(void) goto err_sysfs; } + #ifdef CONFIG_DEBUG_FS + idev->buffers = RB_ROOT; + mutex_init(&idev->buffer_lock); + #endif idev->debug_root = debugfs_create_dir("ion", NULL); init_rwsem(&idev->lock); plist_head_init(&idev->heaps); internal_dev = idev; + + #ifdef CONFIG_DEBUG_FS + debugfs_create_file("check_all_bufs", 0664, idev->debug_root, idev, + &debug_allbufs_fops); + debugfs_create_file("check_all_bufs_total", 0664, idev->debug_root, idev, + &debug_allbufs_fops); + #endif + return 0; err_sysfs: diff --git a/drivers/staging/android/ion/ion_buffer.c b/drivers/staging/android/ion/ion_buffer.c index eb0e4d285a57..acfff735ff4e 100644 --- a/drivers/staging/android/ion/ion_buffer.c +++ b/drivers/staging/android/ion/ion_buffer.c @@ -31,6 +31,33 @@ static void track_buffer_destroyed(struct ion_buffer *buffer) trace_ion_stat(buffer->sg_table, -buffer->size, total); } +#ifdef CONFIG_DEBUG_FS +/* this function should only be called while dev->buffer_lock is held */ +static void ion_buffer_add(struct ion_device *dev, struct ion_buffer *buffer) +{ + struct rb_node **p = &dev->buffers.rb_node; + struct rb_node *parent = NULL; + struct ion_buffer *entry; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_buffer, node); + + if (buffer < entry) { + p = &(*p)->rb_left; + } else if (buffer > entry) { + p = &(*p)->rb_right; + } else { + pr_err("%s: buffer already found.", __func__); + BUG(); + } + } + + rb_link_node(&buffer->node, parent, p); + rb_insert_color(&buffer->node, &dev->buffers); +} +#endif + /* this function should only be called while dev->lock is held */ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, struct ion_device *dev, @@ -84,6 +111,16 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, INIT_LIST_HEAD(&buffer->attachments); mutex_init(&buffer->lock); track_buffer_created(buffer); + + #ifdef CONFIG_DEBUG_FS + mutex_lock(&dev->buffer_lock); + ion_buffer_add(dev, buffer); + mutex_unlock(&dev->buffer_lock); + + buffer->pid = task_pid_nr(current->group_leader); + buffer->client_pids[buffer->ref_cnt++] = buffer->pid; + #endif + return buffer; err1: @@ -240,6 +277,12 @@ int ion_buffer_destroy(struct ion_device *dev, struct ion_buffer *buffer) return -EINVAL; } + #ifdef CONFIG_DEBUG_FS + mutex_lock(&dev->buffer_lock); + rb_erase(&buffer->node, &dev->buffers); + mutex_unlock(&dev->buffer_lock); + #endif + heap = buffer->heap; track_buffer_destroyed(buffer); diff --git a/drivers/staging/android/ion/ion_dma_buf.c b/drivers/staging/android/ion/ion_dma_buf.c index b7295fee31c7..ece61efe4583 100644 --- a/drivers/staging/android/ion/ion_dma_buf.c +++ b/drivers/staging/android/ion/ion_dma_buf.c @@ -259,6 +259,30 @@ static void *ion_dma_buf_map(struct dma_buf *dmabuf, unsigned long offset) return ion_buffer_kmap_get(buffer) + offset * PAGE_SIZE; } +#ifdef CONFIG_DEBUG_FS +static int ion_dma_buf_import_buf_add_by_moto(struct dma_buf *dmabuf) +{ + struct ion_buffer *buffer = dmabuf->priv; + int i; + int found_pid = 0; + pid_t task_pid = task_pid_nr(current->group_leader); + + mutex_lock(&buffer->lock); + for (i = 0; i < buffer->ref_cnt && i < MAX_CLIENTS_NUM; i++) { + if (buffer->client_pids[i] == task_pid) { + found_pid = 1; + break; + } + } + if (!found_pid && buffer->ref_cnt < MAX_CLIENTS_NUM) + buffer->client_pids[buffer->ref_cnt++] = task_pid; + + mutex_unlock(&buffer->lock); + + return 0; +} +#endif + static int ion_dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) { struct ion_buffer *buffer = dmabuf->priv; @@ -278,6 +302,10 @@ static int ion_dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) mutex_unlock(&buffer->lock); } + #ifdef CONFIG_DEBUG_FS + ion_dma_buf_import_buf_add_by_moto(dmabuf); + #endif + if (ret) pr_err("%s: failure mapping buffer to userspace\n", __func__); @@ -353,6 +381,9 @@ static const struct dma_buf_ops dma_buf_ops = { .vmap = ion_dma_buf_vmap, .vunmap = ion_dma_buf_vunmap, .get_flags = ion_dma_buf_get_flags, + #ifdef CONFIG_DEBUG_FS + .import_buf_add_by_moto = ion_dma_buf_import_buf_add_by_moto, + #endif }; struct dma_buf *ion_dmabuf_alloc(struct ion_device *dev, size_t len, diff --git a/drivers/staging/android/ion/ion_private.h b/drivers/staging/android/ion/ion_private.h index db4e90683f4c..9c2aa833f3ee 100644 --- a/drivers/staging/android/ion/ion_private.h +++ b/drivers/staging/android/ion/ion_private.h @@ -20,6 +20,8 @@ /** * struct ion_device - the metadata of the ion device node + * @buffers: an rb tree of all the existing buffers + * @buffer_lock: lock protecting the tree of buffers * @dev: the actual misc device * @lock: rwsem protecting the tree of heaps, heap_bitmap and * clients @@ -32,6 +34,10 @@ struct ion_device { struct plist_head heaps; struct dentry *debug_root; int heap_cnt; + #ifdef CONFIG_DEBUG_FS + struct rb_root buffers; + struct mutex buffer_lock; + #endif }; /* ion_buffer manipulators */ diff --git a/drivers/thermal/qcom/bcl_soc.c b/drivers/thermal/qcom/bcl_soc.c index 71e04377fe78..3ba3b2be47b0 100644 --- a/drivers/thermal/qcom/bcl_soc.c +++ b/drivers/thermal/qcom/bcl_soc.c @@ -15,7 +15,7 @@ #include #include #include - +#include #include "../thermal_core.h" #define BCL_DRIVER_NAME "bcl_soc_peripheral" @@ -30,10 +30,55 @@ struct bcl_device { bool irq_enabled; struct thermal_zone_device *tz_dev; struct thermal_zone_of_device_ops ops; + int bcl_not_mitigate_icl; + struct iio_channel **ext_iio_chans; }; static struct bcl_device *bcl_perph; +enum bcl_mmi_ext_iio_channels { + SMB5_HW_CURRENT_MAX = 0, +}; + +static const char * const bcl_mmi_ext_iio_chan_name[] = { + [SMB5_HW_CURRENT_MAX] = "usb_hw_current_max", +}; + +static bool is_chan_valid(enum bcl_mmi_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(bcl_perph->ext_iio_chans[chan])) + return false; + + if (!bcl_perph->ext_iio_chans[chan]) { + bcl_perph->ext_iio_chans[chan] = iio_channel_get(bcl_perph->dev, + bcl_mmi_ext_iio_chan_name[chan]); + if (IS_ERR(bcl_perph->ext_iio_chans[chan])) { + rc = PTR_ERR(bcl_perph->ext_iio_chans[chan]); + bcl_perph->ext_iio_chans[chan] = NULL; + pr_err("Failed to get IIO channel %s, rc=%d\n", + bcl_mmi_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int bcl_mmi_read_iio_chan(enum bcl_mmi_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chan)) { + rc = iio_read_channel_processed( + bcl_perph->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + static int bcl_set_soc(void *data, int low, int high) { if (high == bcl_perph->trip_temp) @@ -61,6 +106,18 @@ static int bcl_read_soc(void *data, int *val) int err = 0; *val = 0; + + if (bcl_perph->bcl_not_mitigate_icl) { + err = bcl_mmi_read_iio_chan(SMB5_HW_CURRENT_MAX, &ret.intval); + if (err) + pr_err("HW current max read error:%d\n", err); + else if ((ret.intval /1000) >= bcl_perph->bcl_not_mitigate_icl) { + pr_debug("charger current more than bcl_not_mitigate_icl %dmA\n", + bcl_perph->bcl_not_mitigate_icl); + return err; + } + } + if (!batt_psy) batt_psy = power_supply_get_by_name("battery"); if (batt_psy) { @@ -127,6 +184,18 @@ static int bcl_soc_remove(struct platform_device *pdev) return 0; } +static int bcl_mmi_init_iio_psy(struct platform_device *pdev) +{ + bcl_perph->ext_iio_chans = devm_kcalloc(bcl_perph->dev, + ARRAY_SIZE(bcl_mmi_ext_iio_chan_name), + sizeof(*bcl_perph->ext_iio_chans), + GFP_KERNEL); + if (!bcl_perph->ext_iio_chans) + return -ENOMEM; + + return 0; +} + static int bcl_soc_probe(struct platform_device *pdev) { int ret = 0; @@ -157,6 +226,16 @@ static int bcl_soc_probe(struct platform_device *pdev) bcl_perph->tz_dev = NULL; goto bcl_soc_probe_exit; } + + bcl_mmi_init_iio_psy(pdev); + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,bcl-not-mitigate-icl", + &bcl_perph->bcl_not_mitigate_icl); + if (ret < 0) { + bcl_perph->bcl_not_mitigate_icl = 0; + } + thermal_zone_device_update(bcl_perph->tz_dev, THERMAL_DEVICE_UP); schedule_work(&bcl_perph->soc_eval_work); diff --git a/drivers/thermal/qcom/qmi_cooling.c b/drivers/thermal/qcom/qmi_cooling.c index 22a0ee9ac2c5..d4077ae421ae 100644 --- a/drivers/thermal/qcom/qmi_cooling.c +++ b/drivers/thermal/qcom/qmi_cooling.c @@ -173,7 +173,7 @@ static int qmi_tmd_send_state_request(struct qmi_cooling_device *qmi_cdev, goto qmi_send_exit; } ret = 0; - pr_debug("Requested qmi state:%d for %s\n", state, qmi_cdev->cdev_name); + pr_warn("Requested qmi state:%d for %s\n", state, qmi_cdev->cdev_name); qmi_send_exit: mutex_unlock(&tmd->mutex); diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 8978951c03b4..0f73f04a0409 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -188,8 +188,11 @@ static void update_passive_instance(struct thermal_zone_device *tz, * If value is +1, activate a passive instance. * If value is -1, deactivate a passive instance. */ - if (type == THERMAL_TRIP_PASSIVE || type == THERMAL_TRIPS_NONE) + if (type == THERMAL_TRIP_PASSIVE || type == THERMAL_TRIPS_NONE) { tz->passive += value; + dev_warn(&tz->device, "[%s] temp=%d, passive=%d\n", + tz->type, tz->temperature, tz->passive); + } } #ifdef CONFIG_QTI_THERMAL diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index 065278bba5e2..5711de4c84d9 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -234,6 +234,7 @@ struct msm_geni_serial_port { bool pm_auto_suspend_disable; atomic_t is_clock_off; enum uart_error_code uart_error; + unsigned long ser_clk_cfg; }; static const struct uart_ops msm_geni_serial_pops; @@ -303,6 +304,28 @@ static void msm_geni_update_uart_error_code(struct msm_geni_serial_port *port, "%s: uart_error_code:%d\n", __func__, port->uart_error); } } + +/* + * The below API is used to enable and disable serial clock divider. + */ +static void msm_geni_update_se_clk_cfg(struct uart_port *uport, bool enable) +{ + struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); + + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: enable:%d ser_clk_cfg:0x%x\n", + __func__, enable, msm_port->ser_clk_cfg); + + if (enable) { + geni_write_reg_nolog(msm_port->ser_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG); + geni_write_reg_nolog(msm_port->ser_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG); + geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG); + } else { + geni_write_reg_nolog(0x1, uport->membase, GENI_SER_M_CLK_CFG); + geni_write_reg_nolog(0x1, uport->membase, GENI_SER_S_CLK_CFG); + geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG); + } +} + /* * We are enabling the interrupts once the polling operations * is completed. @@ -1581,7 +1604,6 @@ static int stop_rx_sequencer(struct uart_port *uport) * for interrupt latency or system delay cases. */ udelay(STALE_DELAY); - dma_rx_status = geni_read_reg_nolog(uport->membase, SE_DMA_RX_IRQ_STAT); /* The transfer is completed at HW level and the completion @@ -2745,7 +2767,11 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, clk_freq_diff, baud, clk_rate, clk_div); uport->uartclk = clk_rate; - clk_set_rate(port->serial_rsc.se_clk, clk_rate); + ret = clk_set_rate(port->serial_rsc.se_clk, clk_rate); + if (ret) { + dev_err(uport->dev, "Error setting clock rate\n"); + IPC_LOG_MSG(port->ipc_log_misc, "%s: SE clock set_rate error:%d\n", __func__, ret); + } ser_clk_cfg |= SER_CLK_EN; ser_clk_cfg |= (clk_div << CLK_DIV_SHFT); @@ -2754,10 +2780,12 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, geni_write_reg_nolog(ser_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG); geni_write_reg_nolog(ser_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG); + port->ser_clk_cfg = ser_clk_cfg; geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG); msm_geni_serial_termios_cfg(uport, termios); - IPC_LOG_MSG(port->ipc_log_misc, "%s: baud %d\n", __func__, baud); + IPC_LOG_MSG(port->ipc_log_misc, "%s: baud %d ser_clk_cfg:0x%x\n", + __func__, baud, port->ser_clk_cfg); exit_set_termios: msm_geni_serial_start_rx(uport); if (!uart_console(uport)) @@ -3017,10 +3045,12 @@ 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_geni_update_se_clk_cfg(uport, true); 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); + msm_geni_update_se_clk_cfg(uport, false); se_geni_resources_off(&msm_port->serial_rsc); } } @@ -3044,11 +3074,14 @@ static void msm_geni_serial_hs_pm(struct uart_port *uport, if (old_state == UART_PM_STATE_UNDEFINED) old_state = UART_PM_STATE_OFF; - if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) { se_geni_resources_on(&msm_port->serial_rsc); - else if (new_state == UART_PM_STATE_OFF && - old_state == UART_PM_STATE_ON) + msm_geni_update_se_clk_cfg(uport, true); + } else if (new_state == UART_PM_STATE_OFF && + old_state == UART_PM_STATE_ON) { + msm_geni_update_se_clk_cfg(uport, false); se_geni_resources_off(&msm_port->serial_rsc); + } } static const struct uart_ops msm_geni_console_pops = { @@ -3106,8 +3139,10 @@ static int msm_geni_serial_get_ver_info(struct uart_port *uport) struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); /* clks_on/off only for HSUART, as console remains actve */ - if (!msm_port->is_console) + if (!msm_port->is_console) { se_geni_clks_on(&msm_port->serial_rsc); + msm_geni_update_se_clk_cfg(uport, true); + } /* Basic HW and FW info */ if (unlikely(get_se_proto(uport->membase) != UART)) { dev_err(uport->dev, "%s: Invalid FW %d loaded.\n", @@ -3137,8 +3172,10 @@ static int msm_geni_serial_get_ver_info(struct uart_port *uport) msm_geni_serial_enable_interrupts(uport); exit_ver_info: - if (!msm_port->is_console) + if (!msm_port->is_console) { + msm_geni_update_se_clk_cfg(uport, false); se_geni_clks_off(&msm_port->serial_rsc); + } return ret; } @@ -3603,6 +3640,7 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) if (port->wakeup_byte && port->wakeup_irq) msm_geni_serial_allow_rx(port); + msm_geni_update_se_clk_cfg(&port->uport, false); ret = se_geni_resources_off(&port->serial_rsc); if (ret) { dev_err(dev, "%s: Error ret %d\n", __func__, ret); @@ -3647,6 +3685,7 @@ static int msm_geni_serial_runtime_resume(struct device *dev) __pm_relax(port->geni_wake); goto exit_runtime_resume; } + msm_geni_update_se_clk_cfg(&port->uport, true); start_rx_sequencer(&port->uport); /* Ensure that the Rx is running before enabling interrupts */ mb(); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bef2a5001aed..fc0ee8de5602 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4577,6 +4577,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) * device says it supports the new USB 2.0 Link PM errata by setting the BESL * support bit in the BOS descriptor. */ +#if 0 static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); @@ -4594,6 +4595,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) usb_enable_usb2_hardware_lpm(udev); } } +#endif static int hub_enable_device(struct usb_device *udev) { @@ -4955,7 +4957,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, /* notify HCD that we have a device connected and addressed */ if (hcd->driver->update_device) hcd->driver->update_device(hcd, udev); - hub_set_initial_usb2_lpm_policy(udev); fail: if (retval) { hub_port_disable(hub, port1, 0); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index a368d8ddcfba..c8c6e501d789 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -65,6 +65,11 @@ /* AHB2PHY read/write waite value */ #define ONE_READ_WRITE_WAIT 0x11 +#undef dev_dbg +#undef pr_debug +#define dev_dbg dev_err +#define pr_debug pr_err + /* XHCI registers */ #define USB3_HCSPARAMS1 (0x4) #define USB3_PORTSC (0x420) @@ -5146,6 +5151,12 @@ static int dwc3_msm_probe(struct platform_device *pdev) device_create_file(&pdev->dev, &dev_attr_bus_vote); device_create_file(&pdev->dev, &dev_attr_usb_data_enabled); + if (of_property_read_bool(mdwc->dev->of_node, + "qcom,force-adb-enable")) { + dev_err(mdwc->dev, "%s: force usb start device mode\n", __func__); + dwc3_start_stop_device(mdwc, true); + } + return 0; put_dwc3: diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 69d6fd45aea8..119414c7328d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -4591,6 +4591,8 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) dwc3_disconnect_gadget(dwc); __dwc3_gadget_stop(dwc); + synchronize_irq(dwc->irq_gadget); + return 0; } diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 13f953240fca..d35772f8fd76 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1756,7 +1756,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->desc.bcdUSB = cpu_to_le16(0x0320); cdev->desc.bMaxPacketSize0 = 9; } else { - cdev->desc.bcdUSB = cpu_to_le16(0x0210); + cdev->desc.bcdUSB = cpu_to_le16(0x0200); } } else { if (gadget->lpm_capable) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index f4f8e0c32c03..c232aa6703e8 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -96,6 +96,7 @@ struct gadget_info { struct work_struct work; struct device *dev; #endif + bool secure; }; static inline struct gadget_info *to_gadget_info(struct config_item *item) @@ -294,11 +295,19 @@ static int unregister_gadget(struct gadget_info *gi) if (!gi->composite.gadget_driver.udc_name) return -ENODEV; + if (gi->secure) { + gi->unbinding = false; + kfree(gi->composite.gadget_driver.udc_name); + gi->composite.gadget_driver.udc_name = NULL; + return 0; + } + gi->unbinding = true; ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); - if (ret) + if (ret) { + pr_err("unregister gadget failed, ret = %d\n", ret); return ret; - + } gi->unbinding = false; kfree(gi->composite.gadget_driver.udc_name); gi->composite.gadget_driver.udc_name = NULL; @@ -321,6 +330,8 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, if (name[len - 1] == '\n') name[len - 1] = '\0'; + pr_debug("UDC store = %s\n", name); + mutex_lock(&gi->lock); if (!strlen(name) || strcmp(name, "none") == 0) { @@ -334,10 +345,12 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, goto err; } gi->composite.gadget_driver.udc_name = name; - ret = usb_gadget_probe_driver(&gi->composite.gadget_driver); - if (ret) { - gi->composite.gadget_driver.udc_name = NULL; - goto err; + if (!gi->secure) { + ret = usb_gadget_probe_driver(&gi->composite.gadget_driver); + if (ret) { + gi->composite.gadget_driver.udc_name = NULL; + goto err; + } } #ifdef CONFIG_USB_CONFIGFS_UEVENT schedule_work(&gi->work); @@ -1439,7 +1452,8 @@ static int configfs_composite_bind(struct usb_gadget *gadget, cdev->use_os_string = true; cdev->b_vendor_code = gi->b_vendor_code; memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); - } + } else + cdev->use_os_string = false; if (gadget_is_otg(gadget) && !otg_desc[0]) { struct usb_descriptor_header *usb_desc; @@ -1789,8 +1803,71 @@ out: static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); +static ssize_t secure_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct gadget_info *gi = dev_get_drvdata(pdev); + + if (!gi) + return -ENODEV; + + return scnprintf(buf, PAGE_SIZE, "%d\n", gi->secure); +} + +static ssize_t secure_store(struct device *pdev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gadget_info *gi = dev_get_drvdata(pdev); + unsigned long mode, r; + int ret; + + if (!gi) + return -ENODEV; + + r = kstrtoul(buf, 0, &mode); + if (r) { + dev_err(pdev, "Invalid value = %lu\n", mode); + return -EINVAL; + } + + mode = !!mode; + if (mode == gi->secure) + return count; + + mutex_lock(&gi->lock); + + gi->secure = mode; + + if (!gi->composite.gadget_driver.udc_name) { + mutex_unlock(&gi->lock); + return count; + } + pr_debug("Secure Store , UDC = %s, secure = %d\n", + gi->composite.gadget_driver.udc_name, + gi->secure); + + if (gi->secure) { + ret = usb_gadget_unregister_driver( + &gi->composite.gadget_driver); + if (ret) + pr_err("Failed detaching UDC from gadget %d\n", ret); + } else { + ret = usb_gadget_probe_driver(&gi->composite.gadget_driver); + if (ret) { + gi->composite.gadget_driver.udc_name = NULL; + pr_err("Failed attaching UDC to gadget %d\n", ret); + } + } + mutex_unlock(&gi->lock); + + return count; +} + +static DEVICE_ATTR(secure, S_IRUGO | S_IWUSR, secure_show, secure_store); + static struct device_attribute *android_usb_attributes[] = { &dev_attr_state, + &dev_attr_secure, NULL }; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 794780e67059..c12a7317dac3 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1772,6 +1772,7 @@ static int ffs_fs_get_tree(struct fs_context *fc) return invalf(fc, "No source specified"); ffs = ffs_data_new(fc->source); + if (IS_ERR_OR_NULL(ffs)) { if (!ffs) return -ENOMEM; @@ -1779,6 +1780,9 @@ static int ffs_fs_get_tree(struct fs_context *fc) return PTR_ERR(ffs); } + if (IS_ERR(ffs)) + return PTR_ERR(ffs); + ffs->file_perms = ctx->perms; ffs->no_disconnect = ctx->no_disconnect; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 7e23c1a240df..49d5529fd935 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -209,6 +209,7 @@ #include #include #include +#include #include #include #include @@ -231,6 +232,13 @@ #define FSG_DRIVER_DESC "Mass Storage Function" #define FSG_DRIVER_VERSION "2009/09/11" +enum fsg_restart_type { + FSG_RESTART_NONE, + FSG_REBOOT, + FSG_REBOOT_BL, +}; + + static const char fsg_string_interface[] = "Mass Storage"; #include "storage_common.h" @@ -314,6 +322,9 @@ struct fsg_common { void *private_data; char inquiry_string[INQUIRY_STRING_LEN]; + + enum fsg_restart_type restart_type; + struct delayed_work restart_work; }; struct fsg_dev { @@ -1197,12 +1208,105 @@ static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) return 8; } +struct toc_header { + u8 data_len_msb; + u8 data_len_lsb; + u8 first_track_number; + u8 last_track_number; +}; + +struct toc_descriptor { + u8 ctrl; + u8 adr; + u8 tno; + u8 point; + u8 min; + u8 sec; + u8 frame; + u8 zero; + u8 pmin; + u8 psec; + u8 pframe; +}; + +static int build_toc_response_buf(u8 *dest) +{ + struct toc_header *pheader = (struct toc_header *)dest; + struct toc_descriptor *pdesc; + + /* build header */ + pheader->data_len_msb = 0x00; + pheader->data_len_lsb = 0x2E; /* TOC data length */ + pheader->first_track_number = 0x01; + pheader->last_track_number = 0x01; + + /* toc descriptor 1 */ + pdesc = (struct toc_descriptor *)&dest[4]; + pdesc->ctrl = 0x01; + pdesc->adr = 0x16; + pdesc->tno = 0x00; + pdesc->point = 0xA0; + pdesc->min = 0x00; + pdesc->sec = 0x00; + pdesc->frame = 0x00; + pdesc->zero = 0x00; + pdesc->pmin = 0x01; /* first track number */ + pdesc->psec = 0x00; + pdesc->pframe = 0x00; + + /* toc descriptor 2 */ + pdesc = pdesc + 1; + pdesc->ctrl = 0x01; + pdesc->adr = 0x16; + pdesc->tno = 0x00; + pdesc->point = 0xA1; + pdesc->min = 0x00; + pdesc->sec = 0x00; + pdesc->frame = 0x00; + pdesc->zero = 0x00; + pdesc->pmin = 0x01; /* last track number */ + pdesc->psec = 0x00; + pdesc->pframe = 0x00; + + /* toc descriptor 3 */ + pdesc = pdesc + 1; + pdesc->ctrl = 0x01; + pdesc->adr = 0x16; + pdesc->tno = 0x00; + pdesc->point = 0xA2; + pdesc->min = 0x00; + pdesc->sec = 0x00; + pdesc->frame = 0x00; + pdesc->zero = 0x00; + pdesc->pmin = 0x4F; /* pmin, psec, pframe represents */ + pdesc->psec = 0x21; /* start position of lead-out */ + pdesc->pframe = 0x029; + + /* toc descriptor 4 */ + pdesc = pdesc + 1; + pdesc->ctrl = 0x01; + pdesc->adr = 0x14; + pdesc->tno = 0x00; + pdesc->point = 0x01; + pdesc->min = 0x00; + pdesc->sec = 0x00; + pdesc->frame = 0x00; + pdesc->zero = 0x00; + pdesc->pmin = 0x00; /* pmin, psec, pframe represents */ + pdesc->psec = 0x02; /* start position of track */ + pdesc->pframe = 0x00; + + /* return total packet length */ + return (sizeof(struct toc_descriptor)*4) + sizeof(struct toc_header); +} + + static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) { struct fsg_lun *curlun = common->curlun; - int msf = common->cmnd[1] & 0x02; int start_track = common->cmnd[6]; u8 *buf = (u8 *)bh->buf; + int toc_buf_len = 0; if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ start_track > 1) { @@ -1210,18 +1314,8 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) return -EINVAL; } - memset(buf, 0, 20); - buf[1] = (20-2); /* TOC data length */ - buf[2] = 1; /* First track number */ - buf[3] = 1; /* Last track number */ - buf[5] = 0x16; /* Data track, copying allowed */ - buf[6] = 0x01; /* Only track is number 1 */ - store_cdrom_address(&buf[8], msf, 0); - - buf[13] = 0x16; /* Lead-out track is data */ - buf[14] = 0xAA; /* Lead-out track number */ - store_cdrom_address(&buf[16], msf, curlun->num_sectors); - return 20; + toc_buf_len = build_toc_response_buf(buf); + return toc_buf_len; } static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) @@ -1947,8 +2041,12 @@ static int do_scsi_command(struct fsg_common *common) goto unknown_cmnd; common->data_size_from_cmnd = get_unaligned_be16(&common->cmnd[7]); + /* Set bit 9 to 1 in the mask because Mac Sends a value in byte + * 9 of the READ_TOC . Windows does not set it, but changing + * the mask covers both host envs. + */ reply = check_command(common, 10, DATA_DIR_TO_HOST, - (7<<6) | (1<<1), 1, + (0xf<<6) | (1<<1), 1, "READ TOC"); if (reply == 0) reply = do_read_toc(common, bh); @@ -2044,6 +2142,35 @@ static int do_scsi_command(struct fsg_common *common) reply = do_write(common); break; + case SC_REBOOT: + common->data_size_from_cmnd = 0; + reply = check_command(common, common->cmnd_size, + DATA_DIR_NONE, 0, 0, "REBOOT BL"); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } else { + pr_err("Triggered Reboot from SCSI Command\n"); + common->restart_type = FSG_REBOOT; + schedule_delayed_work(&common->restart_work, + msecs_to_jiffies(1000)); + } + break; + case SC_REBOOT_2: + common->data_size_from_cmnd = 0; + reply = check_command(common, common->cmnd_size, + DATA_DIR_NONE, 0, 0, "REBOOT"); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } else { + pr_err("Triggered Reboot BL from SCSI Command\n"); + common->restart_type = FSG_REBOOT_BL; + schedule_delayed_work(&common->restart_work, + msecs_to_jiffies(1000)); + } + break; + /* * Some mandatory commands that we recognize but don't implement. * They don't mean much in this setting. It's left as an exercise @@ -2590,6 +2717,32 @@ static void fsg_lun_release(struct device *dev) /* Nothing needs to be done */ } +static int disable_restarts; +module_param(disable_restarts, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_restarts, "Disable SCSI Initiated Reboots"); +static void fsg_restart_work(struct work_struct *work) +{ + struct fsg_common *common = container_of(work, + struct fsg_common, + restart_work.work); + + if (disable_restarts) { + pr_err("SCSI Reboots Disabled\n"); + return; + } + + switch (common->restart_type) { + case FSG_REBOOT: + kernel_restart(""); + break; + case FSG_REBOOT_BL: + kernel_restart("bootloader"); + break; + default: + break; + } +} + static struct fsg_common *fsg_common_setup(struct fsg_common *common) { if (!common) { @@ -2605,6 +2758,7 @@ static struct fsg_common *fsg_common_setup(struct fsg_common *common) init_completion(&common->thread_notifier); init_waitqueue_head(&common->io_wait); init_waitqueue_head(&common->fsg_wait); + INIT_DELAYED_WORK(&common->restart_work, fsg_restart_work); common->state = FSG_STATE_TERMINATED; memset(common->luns, 0, sizeof(common->luns)); diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 03851c9c770a..e389d4098796 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1470,7 +1470,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) */ if (!ncm_opts->bound) { mutex_lock(&ncm_opts->lock); - ncm_opts->net = gether_setup_default(); + /* MOTODESK-3077 Use ncm interface name. suzh1 2021-04-03 */ + ncm_opts->net = gether_setup_name_default("ncm"); if (IS_ERR(ncm_opts->net)) { status = PTR_ERR(ncm_opts->net); mutex_unlock(&ncm_opts->lock); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 2839abb1d86c..eb84b4e2a1ae 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -891,6 +891,32 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) pd->requested_current = curr; pd->requested_pdo = pdo_pos; + usbpd_warn(&pd->dev, "select_pdo: PDO:%d, %d uV, %d uA\n", + pdo_pos, uv, ua); + + return 0; +} + +static int pd_get_pdo(struct usbpd *pd, int pdo_pos, int *uv_max, int *uv_min, int *ua) +{ + u8 type; + u32 pdo = pd->received_pdos[pdo_pos - 1]; + + if (0 == pdo) + return -ENOTSUPP; + + type = PD_SRC_PDO_TYPE(pdo); + if (type == PD_SRC_PDO_TYPE_FIXED) { + *ua = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; + *uv_max = *uv_min = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + } else if (type == PD_SRC_PDO_TYPE_AUGMENTED) { + *uv_max = PD_APDO_MAX_VOLT(pdo) * 100000; + *uv_min = PD_APDO_MIN_VOLT(pdo) * 100000; + *ua = PD_APDO_MAX_CURR(pdo) * 50000; + } else { + usbpd_err(&pd->dev, "Only Fixed or Programmable PDOs supported\n"); + return -ENOTSUPP; + } return 0; } @@ -2624,6 +2650,8 @@ static void enter_state_snk_startup(struct usbpd *pd) memset(&pd->partner_identity, 0, sizeof(pd->partner_identity)); pd->partner_desc.usb_pd = false; pd->partner_desc.accessory = TYPEC_ACCESSORY_NONE; + if (pd->typec_mode == QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) + pd->partner_desc.accessory = TYPEC_ACCESSORY_AUDIO; pd->partner = typec_register_partner(pd->typec_port, &pd->partner_desc); } @@ -3709,6 +3737,7 @@ static int usbpd_process_typec_mode(struct usbpd *pd, break; case QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n"); + pd->current_pr = PR_SINK; break; default: usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", @@ -4228,9 +4257,232 @@ static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr, return -EINVAL; } +int usbpd_select_pdo_match(struct usbpd *pd) +{ + int uv_diff = 0, max_uv_diff = 0, pdo = 0; + int pdo_max_uv = 0, pdo_min_uv = 0, pdo_ua = 0; + int uv_in, i; + int pdo_pos; + u8 type = -1; + int ret; + union power_supply_propval val; + + if (!pd) + return -EINVAL; + + mutex_lock(&pd->swap_lock); + + if (pd->current_pr == PR_SRC) { + usbpd_err(&pd->dev, "select_pdo: not support in source mode\n"); + ret = -ENOTSUPP; + goto out; + } + + /* Only allowed if we are already in explicit sink contract */ + if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { + usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n"); + ret = -EBUSY; + goto out; + } + + /* Check the Max Input Voltage from USBPSY */ + power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_MAX, &val); + uv_in = val.intval; + /* start with 5 V */ + max_uv_diff = 5000000; + for (i = 1; i < 8; i++) { + pdo_pos = pd->received_pdos[i - 1]; + if (pdo_pos != 0) + type = PD_SRC_PDO_TYPE(pdo_pos); + if (type == PD_SRC_PDO_TYPE_FIXED && + !pd_get_pdo(pd, i, &pdo_max_uv, &pdo_min_uv, &pdo_ua)) { + if (pdo_max_uv <= uv_in) { + uv_diff = uv_in - pdo_max_uv; + if (uv_diff < max_uv_diff) { + max_uv_diff = uv_diff; + pdo = i; + } + } + } + } + + if (pdo < 1 || pdo > 7) { + usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo); + ret = pd->current_voltage; + goto out; + } + + pd_get_pdo(pd, pdo, &pdo_max_uv, &pdo_min_uv, &pdo_ua); + ret = pd_select_pdo(pd, pdo, pdo_max_uv, pdo_ua); + if (ret) + goto out; + else + ret = pdo_max_uv; + + reinit_completion(&pd->is_ready); + pd->send_request = true; + kick_sm(pd, 0); + + /* wait for operation to complete */ + if (!wait_for_completion_timeout(&pd->is_ready, + msecs_to_jiffies(1000))) { + usbpd_err(&pd->dev, "select_pdo: request timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* determine if request was accepted/rejected */ + if (pd->selected_pdo != pd->requested_pdo || + pd->current_voltage != pd->requested_voltage) { + usbpd_err(&pd->dev, "select_pdo: request rejected\n"); + ret = -EINVAL; + } + +out: + pd->send_request = false; + mutex_unlock(&pd->swap_lock); + return ret; +} +EXPORT_SYMBOL(usbpd_select_pdo_match); + +int usbpd_get_pdo_info(struct usbpd *pd, struct usbpd_pdo_info *pdo_info, int length) +{ + int pdo = 0; + int i; + int ret = 0; + + if (!pd || !pdo_info) + return -EINVAL; + + if (length > PD_MAX_PDO_NUM) { + usbpd_err(&pd->dev, + "the members of the struct pd_info[] array must be less than %d, " + "err length %d\n", PD_MAX_PDO_NUM, length); + return -EINVAL; + } + + mutex_lock(&pd->swap_lock); + + if (pd->current_pr == PR_SRC) { + usbpd_err(&pd->dev, "select_pdo:not support in source mode\n"); + ret = -ENOTSUPP; + goto out; + } + usbpd_err(&pd->dev, "select pdo info ,pd->current_state %d\n", pd->current_state); + + if (!is_sink_tx_ok(pd)) + usbpd_err(&pd->dev, "select pdo info , !is_sink_tx_ok\n"); + + /* Only allowed if we are already in explicit sink contract */ + if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { + usbpd_err(&pd->dev, "select_pdo_info: Cannot select new PDO yet\n"); + ret = -EBUSY; + goto out; + } + + for (i = 0; i < length; i++) { + pdo = pd->received_pdos[i]; + if (pdo == 0) + break; + + pdo_info[i].pdo_pos = i + 1; + pdo_info[i].type = PD_SRC_PDO_TYPE(pdo); + if (pdo_info[i].type == PD_SRC_PDO_TYPE_FIXED) { + pdo_info[i].ua = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; + pdo_info[i].uv_max = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + pdo_info[i].uv_min = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + + printk(KERN_INFO "pdo %d, Fixed supply\n" + "Voltage:%d (uV)\n" + "Max Current:%d (uA)\n", + pdo_info[i].pdo_pos, + pdo_info[i].uv_max, + pdo_info[i].ua); + + } else if (pdo_info[i].type == PD_SRC_PDO_TYPE_AUGMENTED) { + pdo_info[i].uv_max = PD_APDO_MAX_VOLT(pdo) * 100000; + pdo_info[i].uv_min = PD_APDO_MIN_VOLT(pdo) * 100000; + pdo_info[i].ua = PD_APDO_MAX_CURR(pdo) * 50000; + + printk(KERN_INFO "pdo %d, Programmable Power supply\n" + "Voltage:%d (uV)\n" + "Max Current:%d (uA)\n", + pdo_info[i].pdo_pos, + pdo_info[i].uv_max, + pdo_info[i].ua); + + } else + usbpd_err(&pd->dev, "Only get Fixed or Programmable PDOs supported\n"); + } +out: + mutex_unlock(&pd->swap_lock); + return ret; +} +EXPORT_SYMBOL(usbpd_get_pdo_info); + +int usbpd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) +{ + int ret; + + if (!pd) + return -EINVAL; + + mutex_lock(&pd->swap_lock); + + if (pd->current_pr == PR_SRC) { + usbpd_err(&pd->dev, "select_pdo:not support in source mode\n"); + ret = -ENOTSUPP; + goto out; + } + + /* Only allowed if we are already in explicit sink contract */ + if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { + usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n"); + ret = -EBUSY; + goto out; + } + + if (pdo_pos < 1 || pdo_pos > 7) { + usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo_pos); + ret = -EINVAL; + goto out; + } + + ret = pd_select_pdo(pd, pdo_pos, uv, ua); + if (ret) + goto out; + + reinit_completion(&pd->is_ready); + pd->send_request = true; + kick_sm(pd, 0); + + /* wait for operation to complete */ + if (!wait_for_completion_timeout(&pd->is_ready, + msecs_to_jiffies(1000))) { + usbpd_err(&pd->dev, "select_pdo: request timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* determine if request was accepted/rejected */ + if (pd->selected_pdo != pd->requested_pdo || + pd->current_voltage != pd->requested_voltage) { + usbpd_err(&pd->dev, "select_pdo: request rejected\n"); + ret = -EINVAL; + } + +out: + pd->send_request = false; + mutex_unlock(&pd->swap_lock); + return ret; +} +EXPORT_SYMBOL(usbpd_select_pdo); + static ssize_t select_pdo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { +#ifdef QCOM_BASE struct usbpd *pd = dev_get_drvdata(dev); int src_cap_id; int pdo, uv = 0, ua = 0; @@ -4292,6 +4544,9 @@ out: pd->send_request = false; mutex_unlock(&pd->swap_lock); return ret ? ret : size; +#else + return size; +#endif } static ssize_t select_pdo_show(struct device *dev, diff --git a/drivers/usb/phy/phy-msm-snps-hs.c b/drivers/usb/phy/phy-msm-snps-hs.c index a7c8d043b56a..8dfbdc702770 100644 --- a/drivers/usb/phy/phy-msm-snps-hs.c +++ b/drivers/usb/phy/phy-msm-snps-hs.c @@ -114,6 +114,9 @@ struct msm_hsphy { int *param_override_seq; int param_override_seq_cnt; + int *host_param_override_seq; + int host_param_override_seq_cnt; + void __iomem *phy_rcal_reg; u32 rcal_mask; @@ -397,9 +400,14 @@ static int msm_hsphy_init(struct usb_phy *uphy) VBUSVLDEXT0, VBUSVLDEXT0); /* set parameter ovrride if needed */ - if (phy->param_override_seq) + if ((phy->phy.flags & PHY_HOST_MODE) && + phy->host_param_override_seq) { + hsusb_phy_write_seq(phy->base, phy->host_param_override_seq, + phy->host_param_override_seq_cnt, 0); + } else if (phy->param_override_seq) { hsusb_phy_write_seq(phy->base, phy->param_override_seq, phy->param_override_seq_cnt, 0); + } if (phy->pre_emphasis) { u8 val = TXPREEMPAMPTUNE0(phy->pre_emphasis) & @@ -823,6 +831,34 @@ static int msm_hsphy_probe(struct platform_device *pdev) if (IS_ERR(phy->phy_reset)) return PTR_ERR(phy->phy_reset); + phy->host_param_override_seq_cnt = of_property_count_elems_of_size( + dev->of_node, + "qcom,host-param-override-seq", + sizeof(*phy->host_param_override_seq)); + if (phy->host_param_override_seq_cnt > 0) { + phy->host_param_override_seq = devm_kcalloc(dev, + phy->host_param_override_seq_cnt, + sizeof(*phy->host_param_override_seq), + GFP_KERNEL); + if (!phy->host_param_override_seq) + return -ENOMEM; + + if (phy->host_param_override_seq_cnt % 2) { + dev_err(dev, "invalid host_param_override_seq_len\n"); + return -EINVAL; + } + + ret = of_property_read_u32_array(dev->of_node, + "qcom,host-param-override-seq", + phy->host_param_override_seq, + phy->host_param_override_seq_cnt); + if (ret) { + dev_err(dev, "qcom,host_param-override-seq read failed %d\n", + ret); + return ret; + } + } + phy->param_override_seq_cnt = of_property_count_elems_of_size( dev->of_node, "qcom,param-override-seq", diff --git a/drivers/video/backlight/qcom-spmi-wled.c b/drivers/video/backlight/qcom-spmi-wled.c index 3ac367e1ec32..b58d2e4fc4a8 100644 --- a/drivers/video/backlight/qcom-spmi-wled.c +++ b/drivers/video/backlight/qcom-spmi-wled.c @@ -243,6 +243,22 @@ struct wled_flash_config { int safety_timer; }; +struct bl_step_seq { + u32 level; + u32 sleep_ms; +}; + +struct bl_step_config { + struct bl_step_seq *sequence; + u32 count; +}; + +struct low_bl_config { + int low_bl_threshold; + int low_bl_remap_percent; + int low_bl_delay_ms; +}; + struct wled { const char *name; struct platform_device *pdev; @@ -283,6 +299,11 @@ struct wled { u8 num_strings; u32 leds_per_string; u32 exp_map[EXP_DIMMING_TABLE_SIZE]; + bool low_bl_force_cabc_disable; + bool only_low_bl_force_cabc_disable; + struct low_bl_config low_bl_cfg; + bool bl_step_up_enable; + struct bl_step_config bl_step_cfg; }; enum wled5_mod_sel { @@ -320,6 +341,8 @@ static const char *const wled_iio_prop_names[] = { static int wled_flash_setup(struct wled *wled); +static int wled_set_brightness_step(struct wled *wled, u16 brightness); + static inline bool is_wled4(struct wled *wled) { if (*wled->version == WLED_PMI8998 || *wled->version == WLED_PM660L) @@ -545,12 +568,64 @@ static int wled_update_status(struct backlight_device *bl) mutex_lock(&wled->lock); if (brightness) { + if (wled->low_bl_force_cabc_disable) { + if (brightness <= wled->low_bl_cfg.low_bl_threshold) { + brightness = brightness * wled->low_bl_cfg.low_bl_remap_percent/100; + if (!wled->cabc_disabled) { + wled->cabc_config(wled, false); + wled->cabc_disabled = true; + pr_info("under low brightness(%d), will disable cabc\n", brightness); + } + + } + else if ((wled->brightness < wled->low_bl_cfg.low_bl_threshold) && (wled->cabc_disabled) && wled->brightness) { + wled->cabc_disabled = false; + wled->cabc_config(wled, true); + pr_info("exit low brightness(%d), will enable cabc\n", brightness); + } + } + + if (wled->only_low_bl_force_cabc_disable) { + if (brightness < wled->low_bl_cfg.low_bl_threshold) { + brightness = brightness * wled->low_bl_cfg.low_bl_remap_percent/100; + if (!wled->cabc_disabled) { + wled->cabc_config(wled, false); + wled->cabc_disabled = true; + pr_info("under low brightness(%d), will disable cabc\n", brightness); + } + + } + else if ((wled->brightness < wled->low_bl_cfg.low_bl_threshold) && (wled->cabc_disabled)){ + wled->cabc_disabled = false; + wled->cabc_config(wled, true); + pr_info("exit low brightness(%d), will enable cabc\n", brightness); + } + } + + if (wled->bl_step_up_enable) { + rc = wled_set_brightness_step(wled, brightness); + if (rc < 0) { + pr_err("wled failed to set brightness step rc:%d\n", rc); + goto unlock_mutex; + } + } rc = wled_set_brightness(wled, brightness); if (rc < 0) { pr_err("wled failed to set brightness rc:%d\n", rc); goto unlock_mutex; } + if (wled->cabc_disabled && wled->low_bl_force_cabc_disable && !wled->brightness) { + if (brightness <= wled->low_bl_cfg.low_bl_threshold) + pr_info("keep cabc disabled\n"); + else { + msleep(wled->low_bl_cfg.low_bl_delay_ms); + wled->cabc_disabled = false; + wled->cabc_config(wled, true); + pr_info("enable wled cabc\n"); + } + } + if (is_wled5(wled)) { rc = wled5_sample_hold_control(wled, brightness, false); if (rc < 0) { @@ -598,6 +673,16 @@ static int wled_update_status(struct backlight_device *bl) } } + if (!wled->cabc_disabled && wled->low_bl_force_cabc_disable) { + wled->cabc_config(wled, false); + wled->cabc_disabled = true; + pr_info("disable wled cabc\n"); + } + if (wled->only_low_bl_force_cabc_disable && wled->cabc_disabled ) { + wled->cabc_disabled = false; + wled->cabc_config(wled, true); + pr_info("screen off, enable wled cabc\n"); + } rc = wled_module_enable(wled, brightness); if (rc < 0) { pr_err("wled disable failed rc:%d\n", rc); @@ -1248,6 +1333,35 @@ exp_dimm_fail: return rc; } +static int parse_low_bl_config(struct wled *wled) +{ + struct device *dev = &wled->pdev->dev; + u32 val = 0; + + if (of_property_read_bool(dev->of_node, "mmi,low-bl-force-cabc-disable")) + wled->low_bl_force_cabc_disable = true; + if (of_property_read_bool(dev->of_node, "mmi,only-low-bl-force-cabc-disable")) + wled->only_low_bl_force_cabc_disable = true; + + if (wled->low_bl_force_cabc_disable || wled->only_low_bl_force_cabc_disable) { + of_property_read_u32(dev->of_node, "mmi,low-bl-threshold", &val); + wled->low_bl_cfg.low_bl_threshold = val; + + of_property_read_u32(dev->of_node, "mmi,low-bl-remap-percent", &val); + wled->low_bl_cfg.low_bl_remap_percent = val; + + val = 0; + of_property_read_u32(dev->of_node, "mmi,low-bl-delay-ms", &val); + wled->low_bl_cfg.low_bl_delay_ms = val; + + pr_info(" low-bl-force-cabc-disbale enabled, low-bl-threshold %d low-bl-remap_percent %d low_bl_delay_ms %d\n", + wled->low_bl_cfg.low_bl_threshold, wled->low_bl_cfg.low_bl_remap_percent, wled->low_bl_cfg.low_bl_delay_ms); + + wled->cabc_disabled = false; + } + return 0; +} + static int wled5_setup(struct wled *wled) { int rc, temp, i; @@ -1310,6 +1424,10 @@ static int wled5_setup(struct wled *wled) if (rc < 0) return rc; + wled->low_bl_force_cabc_disable = false; + wled->only_low_bl_force_cabc_disable = false; + if (wled->cfg.cabc_sel) parse_low_bl_config(wled); + /* Enable one of the modulators A or B based on mod_sel */ addr = wled->sink_addr + WLED5_SINK_MOD_A_EN_REG; val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_MOD_EN : 0; @@ -2541,6 +2659,152 @@ static int wled_configure(struct wled *wled, struct device *dev) return 0; } +static int wled_parse_bl_step_sequence(struct wled *wled) +{ + int rc = 0; + int i; + u32 length = 0; + u32 count = 0; + u32 size = 0; + u32 *arr_32 = NULL; + const u32 *arr; + struct bl_step_seq *seq; + struct device *dev = &wled->pdev->dev; + + wled->bl_step_up_enable = false; + wled->bl_step_up_enable = of_property_read_bool(dev->of_node, + "qcom,wled-bl-step-up"); + + if (!wled->bl_step_up_enable) goto error; + + arr = of_get_property(dev->of_node, + "qcom,wled-bl-step-sequence", &length); + + if (!arr) { + pr_err(" wled-bl-step-sequence not found\n"); + rc = -EINVAL; + goto error; + } + if (length & 0x1) { + pr_err("syntax error for wled-bl-step-sequence\n"); + rc = -EINVAL; + goto error; + } + + pr_debug("BL STEP SEQ LENGTH = %d\n", length); + length = length / sizeof(u32); + + size = length * sizeof(u32); + + arr_32 = kzalloc(size, GFP_KERNEL); + if (!arr_32) { + rc = -ENOMEM; + goto error; + } + + rc = of_property_read_u32_array(dev->of_node, "qcom,wled-bl-step-sequence", + arr_32, length); + if (rc) { + pr_err("cannot read wled-bl-step-sequence\n"); + goto error_free_arr_32; + } + + count = length / 2; + size = count * sizeof(*seq); + seq = kzalloc(size, GFP_KERNEL); + if (!seq) { + rc = -ENOMEM; + goto error_free_arr_32; + } + + wled->bl_step_cfg.sequence = seq; + wled->bl_step_cfg.count = count; + + for (i = 0; i < length; i += 2) { + seq->level = arr_32[i]; + seq->sleep_ms = arr_32[i + 1]; + pr_debug(" %d: level %d sleep_ms %d\n", i, seq->level, seq->sleep_ms); + seq++; + } + + +error_free_arr_32: + kfree(arr_32); + +error: + return rc; +} + + +/* +* This is workaround for pmic 6150/7150 wled boost current issue, QC HW team confirm that +* PMIC WLED low brightness(<255) is pfm mode, high brightness(>255) is pwm mode, when +* brightness from pfm mode to pwm mode transfer fastly, this mode switch will caused +* a higher boost current, in Liberty, it may reach 1.7A, this is a risk for hardware, +* even pmic damage +*/ + +static int wled_set_brightness_step(struct wled *wled, u16 brightness) +{ + int rc = 0; + int i, start_index, end_index; + struct bl_step_seq *seq = wled->bl_step_cfg.sequence; + u32 count = wled->bl_step_cfg.count; + + if (!seq || !count) { + rc = -EINVAL; + goto exit; + } + + if ((wled->brightness >= seq[count-1].level) || + (brightness <= seq[0].level) || (wled->brightness >= brightness)) + goto exit; + + start_index = 0; + end_index = 0; + for (i = 0; i < count; i++) { + if ( wled->brightness < seq[i].level) { + start_index = i; + break; + } + } + + for (i = count-1; i >= 0; i--) { + if ( brightness > seq[i].level) { + end_index = i; + break; + } + } + + if ( end_index < start_index) goto exit; + + pr_info(" set_brightness_step : %d -> [%d, %d]-> %d\n", + wled->brightness, seq[start_index].level, seq[end_index].level, brightness); + + for (i = start_index; i <= end_index; i++) + { + rc = wled_set_brightness(wled, seq[i].level); + if (rc < 0) { + pr_err("wled failed to set brightness high_threshold:%d\n", rc); + goto exit; + } + mdelay(seq[i].sleep_ms); + + pr_debug("set brightness %d, sleep %d ms\n", seq[i].level, seq[i].sleep_ms); + + if (!!brightness != wled->prev_state) { + rc = wled_module_enable(wled, !!brightness); + if (rc < 0) { + pr_err("wled enable failed rc:%d\n", rc); + goto exit; + } + wled->prev_state = !!brightness; + } + } +exit: + return rc; +} + static const struct backlight_ops wled_ops = { .update_status = wled_update_status, .get_brightness = wled_get_brightness, @@ -2594,6 +2858,11 @@ static int wled_probe(struct platform_device *pdev) return rc; } + rc = wled_parse_bl_step_sequence(wled); + if (rc < 0) { + dev_err(&pdev->dev, "wled parse bl_step_sequence rc:%d\n", rc); + } + mutex_init(&wled->lock); val = WLED_DEFAULT_BRIGHTNESS; diff --git a/fs/Kconfig b/fs/Kconfig index f9b97a8b4e80..be0622d91cb2 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -15,6 +15,20 @@ config VALIDATE_FS_PARSER Enable this to perform validation of the parameter description for a filesystem when it is registered. +config FS_EPOLL_WAKEUP_DEBUG + bool "debug eventpoll wakeup source" + default n + help + This option enables printing thread ID and name of eventpoll wakeup source + while waking up system for debug purpose. Keep it off if not sure. + +config FELICA_MOUNT_BLOCK + bool "Block mount sdcard to system path" + default n + help + Mounts on "/system", "/system_ext", "/product", "/vendor" should be blocked + in the root environment. + if BLOCK config FS_IOMAP diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 752497e773b0..21f290bac813 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1461,7 +1461,14 @@ static int ep_create_wakeup_source(struct epitem *epi) struct wakeup_source *ws; if (!epi->ep->ws) { +#ifdef CONFIG_FS_EPOLL_WAKEUP_DEBUG + char full_ep_name[64]; + snprintf(full_ep_name, 64, "eventpoll-%s-%d", + current->comm, current->pid); + epi->ep->ws = wakeup_source_register(NULL, full_ep_name); +#else epi->ep->ws = wakeup_source_register(NULL, "eventpoll"); +#endif if (!epi->ep->ws) return -ENOMEM; } diff --git a/fs/namespace.c b/fs/namespace.c index 7955b65334e8..74c623e6ccda 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "pnode.h" #include "internal.h" @@ -3102,6 +3103,68 @@ char *copy_mount_string(const void __user *data) return data ? strndup_user(data, PATH_MAX) : NULL; } +#ifdef CONFIG_FELICA_MOUNT_BLOCK +/* + * Felica requirement: + * Mounts on "/system", "/system_ext", "/product", "/vendor" should be blocked + * e.g. + * adb root + * adb shell mount -r -w sdcard /system + * adb shell mount -r -w sdcard /system_ext + * adb shell mount -r -w sdcard /product + * adb shell mount -r -w sdcard /vendor + * adb shell mount -r -w /dev/block/vold/public:179,1 /system + * adb shell mount -r -w /dev/block/vold/public:179,1 /system_ext + * adb shell mount -r -w /dev/block/vold/public:179,1 /product + * adb shell mount -r -w /dev/block/vold/public:179,1 /vendor +*/ +static bool mount_block_check(unsigned long flags, struct path *path) +{ + int i; + char *buf, *pathname; + u32 secid, su_secid, init_secid; + const char *su_secctx = "u:r:su:s0"; + const char *init_secctx = "u:r:init:s0"; + const char *blocklist[] = {"/system", "/system_ext", "/product", "/vendor", "/odm", "/oem"}; + int len = ARRAY_SIZE(blocklist); + bool ret = false; + + /* "adb remount" is allowed */ + if (flags & MS_REMOUNT) + return ret; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return ret; + + pathname = d_path(path, buf, PAGE_SIZE); + if (IS_ERR(pathname)) + goto out_putname; + + /* Check mount point */ + for (i = 0; i < len; i++) { + if (!strncmp(pathname, blocklist[i], strlen(blocklist[i]))) + break; + } + if (i == len) + goto out_putname; + + security_secctx_to_secid(su_secctx, strlen(su_secctx), &su_secid); + security_secctx_to_secid(init_secctx, strlen(init_secctx), &init_secid); + security_task_getsecid(current, &secid); + + /* "su" should be blocked, the secid of su equals init at init first stage*/ + if ((secid != init_secid) && (secid == su_secid)) { + pr_warn("Mount on %s is not allowed with %d\n", pathname, secid); + ret = true; + } + +out_putname: + free_page((unsigned long)buf); + return ret; +} +#endif + /* * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to * be given to the mount() call (ie: read-only, no-dev, no-suid etc). @@ -3145,6 +3208,10 @@ long do_mount(const char *dev_name, const char __user *dir_name, retval = -EPERM; if (!retval && (flags & SB_MANDLOCK) && !may_mandlock()) retval = -EPERM; +#ifdef CONFIG_FELICA_MOUNT_BLOCK + if (mount_block_check(flags, &path)) + retval = -EPERM; +#endif if (retval) goto dput_out; diff --git a/fs/timerfd.c b/fs/timerfd.c index 48305ba41e3c..c2d289795686 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -238,6 +238,12 @@ static __poll_t timerfd_poll(struct file *file, poll_table *wait) spin_lock_irqsave(&ctx->wqh.lock, flags); if (ctx->ticks) events |= EPOLLIN; + + if (ctx->expired && isalarm(ctx)) + pr_info("%s: comm:%s pid:%d exp:%llu\n", __func__, + current->comm, current->pid, + ktime_to_ms(ctx->t.alarm.node.expires)); + spin_unlock_irqrestore(&ctx->wqh.lock, flags); return events; diff --git a/gen_headers_arm.bp b/gen_headers_arm.bp index 05661be1f7a8..525d83fa016a 100644 --- a/gen_headers_arm.bp +++ b/gen_headers_arm.bp @@ -1064,22 +1064,6 @@ genrule { ":qti_generate_gen_headers_arm", ], exclude_srcs: gen_headers_exclude_srcs_arm, - cmd: "python3 $(location kernel_headers.py) " + - kernel_headers_verbose + - "--header_arch arm " + - "--gen_dir $(genDir) " + - "--arch_asm_kbuild $(location arch/arm/include/uapi/asm/Kbuild) " + - "--arch_include_uapi $(locations arch/arm/include/uapi/**/*.h) " + - "--techpack_include_uapi $(locations techpack/*/include/uapi/*/**/*.h) " + - "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild) " + - "headers " + - "--old_gen_headers_bp $(location gen_headers_arm.bp) " + - "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm) " + - "--version_makefile $(location Makefile) " + - "--arch_syscall_tool $(location arch/arm/tools/syscallhdr.sh) " + - "--arch_syscall_tbl $(location arch/arm/tools/syscall.tbl) " + - "--headers_install $(location headers_install.sh) " + - "--unifdef $(location unifdef) " + - "--include_uapi $(locations include/uapi/**/*.h)", out: ["linux/version.h"] + gen_headers_out_arm, + defaults: ["mmi_kernel_headers_cmdrules_arm"], } diff --git a/gen_headers_arm64.bp b/gen_headers_arm64.bp index bee9c6226a0e..b2130898935f 100644 --- a/gen_headers_arm64.bp +++ b/gen_headers_arm64.bp @@ -1058,20 +1058,6 @@ genrule { ":qti_generate_gen_headers_arm64", ], exclude_srcs: gen_headers_exclude_srcs_arm64, - cmd: "python3 $(location kernel_headers.py) " + - kernel_headers_verbose + - "--header_arch arm64 " + - "--gen_dir $(genDir) " + - "--arch_asm_kbuild $(location arch/arm64/include/uapi/asm/Kbuild) " + - "--arch_include_uapi $(locations arch/arm64/include/uapi/**/*.h) " + - "--techpack_include_uapi $(locations techpack/*/include/uapi/*/**/*.h) " + - "--asm_generic_kbuild $(location include/uapi/asm-generic/Kbuild) " + - "headers " + - "--old_gen_headers_bp $(location gen_headers_arm64.bp) " + - "--new_gen_headers_bp $(location :qti_generate_gen_headers_arm64) " + - "--version_makefile $(location Makefile) " + - "--headers_install $(location headers_install.sh) " + - "--unifdef $(location unifdef) " + - "--include_uapi $(locations include/uapi/**/*.h)", out: ["linux/version.h"] + gen_headers_out_arm64, + defaults: ["mmi_kernel_headers_cmdrules_arm64"], } diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index f307f014440b..2159df2ae857 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -22,6 +22,8 @@ struct mipi_dsi_device; #define MIPI_DSI_MSG_UNICAST BIT(2) /* Stack all commands until lastcommand bit and trigger all in one go */ #define MIPI_DSI_MSG_LASTCOMMAND BIT(3) +/* transmit message is a DSI read message */ +#define MIPI_DSI_MSG_READ BIT(4) /** * struct mipi_dsi_msg - read/write DSI buffer diff --git a/include/dt-bindings/iio/qti_power_supply_iio.h b/include/dt-bindings/iio/qti_power_supply_iio.h index 84611eb3bcc6..bd20db8144cd 100644 --- a/include/dt-bindings/iio/qti_power_supply_iio.h +++ b/include/dt-bindings/iio/qti_power_supply_iio.h @@ -154,4 +154,35 @@ /* BMS */ #define PSY_IIO_CLEAR_SOH 0x7c + +/*MMI add PSY_IIO properties*/ +#define PSY_IIO_MMI_CP_INPUT_CURRENT_NOW 0x100 +#define PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW 0x101 +#define PSY_IIO_MMI_CP_STATUS1 0x102 +#define PSY_IIO_MMI_CP_CLEAR_ERROR 0x103 +#define PSY_IIO_MMI_QC3P_POWER 0x104 + +/*MMI add PSY_IIO properties for discrete charging*/ +#define PSY_IIO_MMI_OTG_ENABLE 0x105 +#define PSY_IIO_INPUT_VOLTAGE_LIMIT 0x106 +#define PSY_IIO_MANUFACTURER 0x107 +#define PSY_IIO_MODEL_NAME 0x108 + +#define PSY_IIO_CP_CLEAR_ERROR 0x109 +#define PSY_IIO_INPUT_CURRENT_SETTLED 0x10a +#define PSY_IIO_QC3P_REAL_TYPE 0x10b +#define PSY_IIO_QC3P_POWER 0x10c +#define PSY_IIO_BATTERY_DP_DM 0x10d +#define PSY_IIO_START_DETECTION 0x10e +#define PSY_IIO_USB_CHARGING_ENABLED 0x10f +#define PSY_IIO_DETECTION_READY 0x110 +#define PSY_IIO_QC3P_START_POLICY 0x111 +#define PSY_IIO_QC3P_FIRMWARE_NUM 0x112 +#define PSY_IIO_USB_TERMINATION_ENABLED 0x113 +#define PSY_IIO_START_BC12_DETECTION 0x114 +#define PSY_IIO_DETECTION_BC12_READY 0x115 +#define PSY_IIO_READ_USBIN_VOLTAGE 0x116 + +#define PSY_IIO_MMI_PD_VDM_VERIFY 0x117 +#define PSY_IIO_BC12_CHG_TYPE 0x118 #endif /* __QTI_POWER_SUPPLY_IIO_H__ */ diff --git a/include/dt-bindings/moto/moto-mem-reserve.h b/include/dt-bindings/moto/moto-mem-reserve.h new file mode 100644 index 000000000000..98093729d3f3 --- /dev/null +++ b/include/dt-bindings/moto/moto-mem-reserve.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Motorola Mobility LLC + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MOTO_MEM_RESERVE_H +#define __MOTO_MEM_RESERVE_H + +#define RAMOOPS_CONSOLE_SIZE 0x40000 +#define RAMOOPS_PMSG_SIZE 0x40000 +#define RAMOOPS_RECORD_SIZE 0x3f800 +#define RAMOOPS_SIZE (RAMOOPS_CONSOLE_SIZE + RAMOOPS_PMSG_SIZE + RAMOOPS_RECORD_SIZE) +#define RAMOOPS_BASE_ADDR 0xaf000000 + +#define WDOG_CPUCTX_SIZE_PERCPU 0x5c00 /* sizeof(struct msm_wdog_cpuctx) */ +#define WDOG_CPUCTX_SIZE_8CPUS (WDOG_CPUCTX_SIZE_PERCPU * 8) +#define WDOG_CPUCTX_SIZE_4CPUS (WDOG_CPUCTX_SIZE_PERCPU * 4) + +#define WDOG_CPUCTX_END_ADDR RAMOOPS_BASE_ADDR + +#define WDOG_CPUCTX_SIZE WDOG_CPUCTX_SIZE_8CPUS +#define WDOG_CPUCTX_BASE (WDOG_CPUCTX_END_ADDR - WDOG_CPUCTX_SIZE_8CPUS) + +#define TZLOG_BCK_SIZE 0x30000 +#define TZLOG_BCK_BASE (WDOG_CPUCTX_BASE - TZLOG_BCK_SIZE) + +#define MMI_ANNOTATE_SIZE 0x800 +#define MMI_ANNOTATE_BASE (TZLOG_BCK_BASE - MMI_ANNOTATE_SIZE) + +#endif /* __MOTO_MEM_RESERVE_H */ diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index accce2b534e7..dee33939822c 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -348,6 +348,18 @@ struct dma_buf_ops { * will be populated with the buffer's flags. */ int (*get_flags)(struct dma_buf *dmabuf, unsigned long *flags); + + /** + * @import_buf_add_by_moto: + * + * This is called by gr_ion_alloc added by Motorola + * + * Returns: + * always return 0 + */ + #ifdef CONFIG_DEBUG_FS + int (*import_buf_add_by_moto)(struct dma_buf *); + #endif }; /** diff --git a/include/linux/input/qpnp-power-on.h b/include/linux/input/qpnp-power-on.h index 5e4f39ac9298..92d81b30849f 100644 --- a/include/linux/input/qpnp-power-on.h +++ b/include/linux/input/qpnp-power-on.h @@ -55,8 +55,17 @@ enum pon_restart_reason { PON_RESTART_REASON_DMVERITY_CORRUPTED = 0x04, PON_RESTART_REASON_DMVERITY_ENFORCE = 0x05, PON_RESTART_REASON_KEYS_CLEAR = 0x06, + PON_RESTART_REASON_HW_WARMRESET = 0x18, }; +#define RESET_EXTRA_POST_HWWARM_RESET_REASON BIT(6) +#define RESET_EXTRA_POST_PANIC_REASON (BIT(4) | BIT(5)) +#define RESET_EXTRA_POST_PMICWDT_REASON BIT(5) +#define RESET_EXTRA_POST_WDT_REASON BIT(4) +#define RESET_EXTRA_POST_REBOOT_MASK (BIT(4) | BIT(5) | BIT(6)) +#define RESET_EXTRA_PANIC_REASON BIT(3) +#define RESET_EXTRA_REBOOT_BL_REASON BIT(2) + #if IS_ENABLED(CONFIG_INPUT_QPNP_POWER_ON) int qpnp_pon_system_pwr_off(enum pon_power_off_type type); int qpnp_pon_is_warm_reset(void); @@ -65,6 +74,7 @@ int qpnp_pon_wd_config(bool enable); int qpnp_pon_set_restart_reason(enum pon_restart_reason reason); bool qpnp_pon_check_hard_reset_stored(void); int qpnp_pon_modem_pwr_off(enum pon_power_off_type type); +int qpnp_pon_store_extra_reset_info(u16 mask, u16 val); #else @@ -104,6 +114,10 @@ static inline int qpnp_pon_modem_pwr_off(enum pon_power_off_type type) return -ENODEV; } +static inline int qpnp_pon_store_extra_reset_info(u16 mask, u16 val) +{ + return -ENODEV; +} #endif #endif diff --git a/include/linux/ion.h b/include/linux/ion.h index 80c6fdeb4822..8767a3ae0e38 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -20,8 +20,13 @@ #include #include +#ifdef CONFIG_DEBUG_FS +#define MAX_CLIENTS_NUM 16 +#endif + /** * struct ion_buffer - metadata for a particular buffer + * @node: node in the ion_device buffers tree * @list: element in list of deferred freeable buffers * @heap: back pointer to the heap the buffer came from * @flags: buffer specific flags @@ -36,7 +41,14 @@ * @attachments: list of devices attached to this buffer */ struct ion_buffer { + #ifdef CONFIG_DEBUG_FS + union { + struct rb_node node; + struct list_head list; + }; + #else struct list_head list; + #endif struct ion_heap *heap; unsigned long flags; unsigned long private_flags; @@ -47,6 +59,11 @@ struct ion_buffer { void *vaddr; struct sg_table *sg_table; struct list_head attachments; + #ifdef CONFIG_DEBUG_FS + pid_t pid; + pid_t client_pids[MAX_CLIENTS_NUM]; + int ref_cnt; + #endif }; /** diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index a1b04d916526..528380117ab8 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -333,7 +333,9 @@ struct mem_cgroup { #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_GKI_OPT_FEATURES) struct deferred_split deferred_split_queue; #endif - +#ifdef CONFIG_QGKI + ANDROID_OEM_DATA(1); +#endif struct mem_cgroup_per_node *nodeinfo[0]; /* WARNING: nodeinfo must be the last member here */ }; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index af6bc6f7bfd4..39dc9c9f4b94 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -483,6 +483,8 @@ struct mmc_host { #if defined(CONFIG_SDC_QTI) #define MMC_CAP2_CLK_SCALE (1 << 28) /* Allow dynamic clk scaling */ #endif +/* use max discard ignoring max_busy_timeout parameter */ +#define MMC_CAP2_MAX_DISCARD_SIZE (1 << 31) int fixed_drv_type; /* fixed driver type for non-removable media */ diff --git a/include/linux/mmi-pmic-voter.h b/include/linux/mmi-pmic-voter.h new file mode 100644 index 000000000000..2879839600cc --- /dev/null +++ b/include/linux/mmi-pmic-voter.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2017, 2019-2020, The Linux Foundation. All rights reserved. + */ + + #ifndef __MMI_PMIC_VOTER_H +#define __MMI_PMIC_VOTER_H + +#include + +int mmi_get_effective_result(struct votable *votable); +int mmi_vote(struct votable *votable, const char *client_str, bool enabled, int val); +int mmi_rerun_election(struct votable *votable); +struct votable *mmi_find_votable(const char *name); +int mmi_pmic_vote_force_val_set(struct votable *votable, u32 val); +int mmi_pmic_vote_force_active_get(struct votable *votable, u64 *val); +int mmi_pmic_vote_force_active_set(struct votable *votable, u64 val); + +#endif /* __MMI_PMIC_VOTER_H */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 6f2a15015435..3bae2136989a 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -758,7 +758,9 @@ typedef struct pglist_data { enum zone_type kswapd_classzone_idx; int kswapd_failures; /* Number of 'reclaimed == 0' runs */ - +#ifdef CONFIG_QGKI + ANDROID_OEM_DATA(1); +#endif #ifdef CONFIG_COMPACTION int kcompactd_max_order; enum zone_type kcompactd_classzone_idx; diff --git a/include/linux/pmic-voter.h b/include/linux/pmic-voter.h index d1202928ddd8..78239ca19927 100644 --- a/include/linux/pmic-voter.h +++ b/include/linux/pmic-voter.h @@ -44,4 +44,8 @@ void destroy_votable(struct votable *votable); void lock_votable(struct votable *votable); void unlock_votable(struct votable *votable); +int pmic_vote_force_val_set(struct votable *votable, u32 val); +int pmic_vote_force_active_get(struct votable *votable, u64 *val); +int pmic_vote_force_active_set(struct votable *votable, u64 val); + #endif /* __PMIC_VOTER_H */ diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 5b292531dd2f..04a2741fbf67 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -209,6 +209,7 @@ extern int qcom_scm_invoke_smc_legacy(phys_addr_t in_buf, size_t in_buf_size, extern int qcom_scm_invoke_smc(phys_addr_t in_buf, size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size, int32_t *result, u64 *response_type, unsigned int *data); +extern int trustonic_smc_fastcall(void *fc_generic, size_t size); extern int qcom_scm_invoke_callback_response(phys_addr_t out_buf, size_t out_buf_size, int32_t *result, u64 *response_type, unsigned int *data); diff --git a/include/linux/qti_power_supply.h b/include/linux/qti_power_supply.h index 8b5386389c3d..83c573e570c4 100644 --- a/include/linux/qti_power_supply.h +++ b/include/linux/qti_power_supply.h @@ -113,4 +113,10 @@ enum power_supply_typec_power_role { QTI_POWER_SUPPLY_TYPEC_PR_SOURCE, }; +enum mmi_qc3p_power { + QTI_POWER_SUPPLY_QC3P_NONE, + QTI_POWER_SUPPLY_QC3P_18W, + QTI_POWER_SUPPLY_QC3P_27W, + QTI_POWER_SUPPLY_QC3P_45W, +}; #endif /* __QTI_POWER_SUPPLY_H_ */ diff --git a/include/linux/statfs.h b/include/linux/statfs.h index 9bc69edb8f18..20f695b90aab 100644 --- a/include/linux/statfs.h +++ b/include/linux/statfs.h @@ -40,8 +40,14 @@ struct kstatfs { #define ST_NOATIME 0x0400 /* do not update access times */ #define ST_NODIRATIME 0x0800 /* do not update directory access times */ #define ST_RELATIME 0x1000 /* update atime relative to mtime/ctime */ +#define ST_NOSYMFOLLOW 0x2000 /* do not follow symlinks */ struct dentry; extern int vfs_get_fsid(struct dentry *dentry, __kernel_fsid_t *fsid); +static inline __kernel_fsid_t u64_to_fsid(u64 v) +{ + return (__kernel_fsid_t){.val = {(u32)v, (u32)(v>>32)}}; +} + #endif diff --git a/include/linux/swap.h b/include/linux/swap.h index b407d3963649..bb50a6d0eee4 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -397,6 +397,7 @@ extern void end_swap_bio_write(struct bio *bio); extern int __swap_writepage(struct page *page, struct writeback_control *wbc, bio_end_io_t end_write_func); extern int swap_set_page_dirty(struct page *page); +extern bool swap_slot_has_sync_io(swp_entry_t entry); int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, unsigned long nr_pages, sector_t start_block); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 4e2b9feb2c62..e4c934ca7c95 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -875,7 +875,7 @@ asmlinkage long sys_munlockall(void); asmlinkage long sys_mincore(unsigned long start, size_t len, unsigned char __user * vec); asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior); -asmlinkage long sys_process_madvise(int which, pid_t upid, +asmlinkage long sys_process_madvise(int pidfd, const struct iovec __user *vec, unsigned long vlen, int behavior, unsigned long flags); asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, diff --git a/include/linux/usb/usbpd.h b/include/linux/usb/usbpd.h index 037abb0d25d0..b57ec920e3d6 100644 --- a/include/linux/usb/usbpd.h +++ b/include/linux/usb/usbpd.h @@ -36,6 +36,8 @@ enum usbpd_svdm_cmd_type { #define USBPD_SVDM_EXIT_MODE 0x5 #define USBPD_SVDM_ATTENTION 0x6 +#define PD_MAX_PDO_NUM 7 + /* * Implemented by client */ @@ -65,6 +67,14 @@ struct usbpd_svid_handler { bool discovered; }; +struct usbpd_pdo_info { + int pdo_pos; + int uv_max; + int uv_min; + int ua; + int type; +}; + enum plug_orientation { ORIENTATION_NONE, ORIENTATION_CC1, @@ -99,6 +109,21 @@ int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, int obj_pos, const u32 *vdos, int num_vdos); +/* + * Look for best match for PDO selection in Source Mode. + */ +int usbpd_select_pdo_match(struct usbpd *pd); + +/* +* support that directly set pdo which is need in Source Mode. +*/ +int usbpd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua); + +/* +* Transmit all effective pdo info. +*/ +int usbpd_get_pdo_info(struct usbpd *pd, struct usbpd_pdo_info *pdo_info, int length); + /* * Get current status of CC pin orientation. * diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index cf01d5c720f3..f79e26064655 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -147,6 +147,9 @@ struct scsi_cmnd { ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); ANDROID_KABI_RESERVE(4); +#if defined(CONFIG_UFSFEATURE) && defined(CONFIG_UFSHPB) + u8 requeue_cnt; +#endif }; /* Variant of blk_mq_rq_from_pdu() that verifies the type of its argument. */ diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index c36860111932..fca5a614cd45 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -169,6 +169,8 @@ /* Vendor specific CDBs start here */ #define VENDOR_SPECIFIC_CDB 0xc0 +#define SC_REBOOT 0xd7 +#define SC_REBOOT_2 0xd8 /* * SCSI command lengths diff --git a/include/sound/initval.h b/include/sound/initval.h index 3ddae2b4177e..46c35c069d56 100644 --- a/include/sound/initval.h +++ b/include/sound/initval.h @@ -22,6 +22,7 @@ #define SNDRV_DEFAULT_PTR1 SNDRV_DEFAULT_STR1 #define SNDRV_DEFAULT_IDX { [0 ... (SNDRV_CARDS-1)] = -1 } +#define SNDRV_DEFAULT_IDX_N { 1, [1 ... (SNDRV_CARDS-1)] = -1 } #define SNDRV_DEFAULT_STR { [0 ... (SNDRV_CARDS-1)] = NULL } #define SNDRV_DEFAULT_ENABLE { 1, [1 ... (SNDRV_CARDS-1)] = 0 } #define SNDRV_DEFAULT_ENABLE_PNP { [0 ... (SNDRV_CARDS-1)] = 1 } diff --git a/include/trace/hooks/mm.h b/include/trace/hooks/mm.h new file mode 100644 index 000000000000..24834016718f --- /dev/null +++ b/include/trace/hooks/mm.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifdef PROTECT_TRACE_INCLUDE_PATH +#undef PROTECT_TRACE_INCLUDE_PATH + +#include +#include +#include + +#else /* PROTECT_TRACE_INCLUDE_PATH */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mm + +#define TRACE_INCLUDE_PATH trace/hooks + +#if !defined(_TRACE_HOOK_MM_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HOOK_MM_H + +#include + +#include +#include +#include +#include +#include + +#ifdef __GENKSYMS__ +struct slabinfo; +struct cgroup_subsys_state; +struct device; +struct mem_cgroup; +struct readahead_control; +#else +/* struct slabinfo */ +#include <../mm/slab.h> +/* struct cgroup_subsys_state */ +#include +/* struct device */ +#include +/* struct mem_cgroup */ +#include +/* struct readahead_control */ +#include +#endif /* __GENKSYMS__ */ +struct cma; +struct swap_slots_cache; +struct page_vma_mapped_walk; + +DECLARE_HOOK(android_vh_rmqueue, + TP_PROTO(struct zone *preferred_zone, struct zone *zone, + unsigned int order, gfp_t gfp_flags, + unsigned int alloc_flags, int migratetype), + TP_ARGS(preferred_zone, zone, order, + gfp_flags, alloc_flags, migratetype)); + +DECLARE_HOOK(android_vh_mem_cgroup_alloc, + TP_PROTO(struct mem_cgroup *memcg), + TP_ARGS(memcg)); +DECLARE_HOOK(android_vh_mem_cgroup_free, + TP_PROTO(struct mem_cgroup *memcg), + TP_ARGS(memcg)); +DECLARE_HOOK(android_vh_mem_cgroup_id_remove, + TP_PROTO(struct mem_cgroup *memcg), + TP_ARGS(memcg)); +DECLARE_HOOK(android_vh_mem_cgroup_css_online, + TP_PROTO(struct cgroup_subsys_state *css, struct mem_cgroup *memcg), + TP_ARGS(css, memcg)); +DECLARE_HOOK(android_vh_mem_cgroup_css_offline, + TP_PROTO(struct cgroup_subsys_state *css, struct mem_cgroup *memcg), + TP_ARGS(css, memcg)); +/* macro versions of hooks are no longer required */ + +#endif /* _TRACE_HOOK_MM_H */ + +/* This part must be outside protection */ +#include + +#endif /* PROTECT_TRACE_INCLUDE_PATH */ diff --git a/include/trace/hooks/vmscan.h b/include/trace/hooks/vmscan.h new file mode 100644 index 000000000000..0f156f3aad7d --- /dev/null +++ b/include/trace/hooks/vmscan.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM vmscan + +#define TRACE_INCLUDE_PATH trace/hooks + +#if !defined(_TRACE_HOOK_VMSCAN_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HOOK_VMSCAN_H + +#include +#include + +DECLARE_HOOK(android_vh_tune_scan_type, + TP_PROTO(char *scan_type), + TP_ARGS(scan_type)); +DECLARE_HOOK(android_vh_tune_swappiness, + TP_PROTO(int *swappiness), + TP_ARGS(swappiness)); +DECLARE_HOOK(android_vh_tune_inactive_ratio, + TP_PROTO(unsigned long *inactive_ratio, int file), + TP_ARGS(inactive_ratio, file)) +#endif /* _TRACE_HOOK_VMSCAN_H */ +/* This part must be outside protection */ +#include diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h index f76d11725c6c..83e118dd84f3 100644 --- a/include/uapi/linux/dma-buf.h +++ b/include/uapi/linux/dma-buf.h @@ -39,6 +39,7 @@ struct dma_buf_sync { #define DMA_BUF_BASE 'b' #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) +#define DMA_BUF_IOCTL_IMPORT_BUF_ADD_BY_MOTO _IO(DMA_BUF_BASE, 0xCC) /* 32/64bitness of this uapi was botched in android, there's no difference * between them in actual uapi, they're just different numbers. diff --git a/include/uapi/linux/slatecom_interface.h b/include/uapi/linux/slatecom_interface.h index 6f58ef8057e6..d41af0e47178 100644 --- a/include/uapi/linux/slatecom_interface.h +++ b/include/uapi/linux/slatecom_interface.h @@ -20,6 +20,7 @@ #define SLATECOM_ADSP_DOWN2_SLATE 10 #define SLATECOM_SLATE_WEAR_LOAD 11 #define SLATECOM_SLATE_WEAR_UNLOAD 12 +#define SLATECOM_DEVICE_STATE_TRANSITION 13 #define EXCHANGE_CODE 'V' struct slate_ui_data { @@ -47,6 +48,15 @@ enum slate_event_type { SLATE_BT_READY, }; +enum device_state_transition { + STATE_TWM_ENTER = 1, + STATE_TWM_EXIT, + STATE_DS_ENTER, + STATE_DS_EXIT, + STATE_S2D_ENTER, + STATE_S2D_EXIT, +}; + #define REG_READ \ _IOWR(EXCHANGE_CODE, SLATECOM_REG_READ, \ struct slate_ui_data) @@ -86,6 +96,9 @@ enum slate_event_type { #define SLATE_ADSP_DOWN2_SLATE_DONE \ _IOWR(EXCHANGE_CODE, SLATECOM_ADSP_DOWN2_SLATE, \ struct slate_ui_data) +#define DEVICE_STATE_TRANSITION \ + _IOWR(EXCHANGE_CODE, SLATECOM_DEVICE_STATE_TRANSITION, \ + struct slate_ui_data) #endif /* LINUX_SLATECOM_INTERFACE_H */ diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 674236afec31..81b7da409d02 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -171,6 +171,8 @@ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK = 8, SNDRV_COMPRESS_ADJUST_SESSION_CLOCK = 9, SNDRV_COMPRESS_LATENCY_MODE = 10, + SNDRV_COMPRESS_MIN_BLK_SIZE = 11, + SNDRV_COMPRESS_MAX_BLK_SIZE = 12, }; #define SNDRV_COMPRESS_PATH_DELAY SNDRV_COMPRESS_PATH_DELAY @@ -183,6 +185,9 @@ enum sndrv_compress_encoder { #define SNDRV_COMPRESS_ADJUST_SESSION_CLOCK SNDRV_COMPRESS_ADJUST_SESSION_CLOCK #define SNDRV_COMPRESS_LATENCY_MODE SNDRV_COMPRESS_LATENCY_MODE +#define SNDRV_COMPRESS_MIN_BLK_SIZE SNDRV_COMPRESS_MIN_BLK_SIZE +#define SNDRV_COMPRESS_MAX_BLK_SIZE SNDRV_COMPRESS_MAX_BLK_SIZE + /** * struct snd_compr_metadata - compressed stream metadata * @key: key id diff --git a/init/Kconfig b/init/Kconfig index 9c56d0ce45bb..3d16587c5e90 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -639,6 +639,15 @@ config IKHEADERS or similar programs. If you build the headers as a module, a module called kheaders.ko is built which can be loaded on-demand to get access to headers. +config DUMP_GPIO_ON_SUSPEND + bool "dump gpio on suspend" + +config DUMP_CLK_ON_SUSPEND + bool "dump clk on suspend" + +config DUMP_REGULATOR_ON_SUSPEND + bool "dump regulator on suspend" + config LOG_BUF_SHIFT int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" range 12 25 if !H8300 diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index e91d87d69f5f..0f36ad391d4e 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -4315,6 +4315,7 @@ int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) return cgroup_add_cftypes(ss, cfts); } +EXPORT_SYMBOL_GPL(cgroup_add_legacy_cftypes); /** * cgroup_file_notify - generate a file modified event for a cgroup_file * @cfile: target cgroup_file diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 4424982820d0..c19225cc1f98 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -27,10 +27,12 @@ #include #include #include +#include #include #include #include #include +#include #include "power.h" @@ -62,6 +64,46 @@ static DECLARE_SWAIT_QUEUE_HEAD(s2idle_wait_head); enum s2idle_states __read_mostly s2idle_state; static DEFINE_RAW_SPINLOCK(s2idle_lock); +const char *suspend_state_name[] = { +"ON", +"TO IDLE", +"STANDBY", +"MEM", +"MIN", +"MAX", +}; + +void print_time(bool running) +{ + static ktime_t kt_boot0 = 0; + ktime_t kt_boot; + u32 rem; + unsigned long delta; + struct timespec ts; + struct rtc_time tm; + char sign = '-'; + int tz_min = sys_tz.tz_minuteswest; + if (tz_min < 0) { + sign = '+'; + tz_min = -tz_min; + } + + kt_boot = ktime_get_boottime(); + delta = div_u64(kt_boot - kt_boot0, NSEC_PER_SEC); + + kt_boot0 = kt_boot; + kt_boot = div_u64_rem(kt_boot, NSEC_PER_SEC, &rem); + + getnstimeofday(&ts); + ts.tv_sec -= sys_tz.tz_minuteswest * 60; + rtc_time_to_tm(ts.tv_sec, &tm); + pr_err("realtime: %u-%02u-%02u %02u:%02u:%02u.%06u UTC%c%02d:%02d boottime: %5lu.%06u %s: %lu\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(ts.tv_nsec / 1000), + sign, tz_min / 60, tz_min % 60, + (long)kt_boot, (int)rem / 1000, running ? "running" : "sleep", delta); +} + /** * pm_suspend_default_s2idle - Check if suspend-to-idle is the default suspend. * @@ -120,7 +162,7 @@ static void s2idle_enter(void) static void s2idle_loop(void) { - pm_pr_dbg("suspend-to-idle\n"); + pr_err("suspend-to-idle\n"); /* * Suspend-to-idle equals: @@ -143,7 +185,7 @@ static void s2idle_loop(void) s2idle_enter(); } - pm_pr_dbg("resume from suspend-to-idle\n"); + pr_err("resume from suspend-to-idle\n"); } void s2idle_wake(void) @@ -325,7 +367,7 @@ static int suspend_test(int level) { #ifdef CONFIG_PM_DEBUG if (pm_test_level == level) { - pr_info("suspend debug: Waiting for %d second(s).\n", + pr_err("suspend debug: Waiting for %d second(s).\n", pm_test_delay); mdelay(pm_test_delay * 1000); return 1; @@ -609,6 +651,18 @@ static int enter_state(suspend_state_t state) return error; } +static void pm_suspend_marker(char *annotation) +{ + struct timespec ts; + struct rtc_time tm; + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", + annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); +} + /** * pm_suspend - Externally visible function for suspending the system. * @state: System sleep state to enter. @@ -623,7 +677,10 @@ int pm_suspend(suspend_state_t state) if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) return -EINVAL; - pr_info("suspend entry (%s)\n", mem_sleep_labels[state]); + pm_suspend_marker("entry"); + pr_err("suspend entry (%s) state=%d (%s)\n", mem_sleep_labels[state], state, + state < ARRAY_SIZE(suspend_state_name) ? suspend_state_name[state] : "unknow"); + print_time(true); error = enter_state(state); if (error) { suspend_stats.fail++; @@ -631,7 +688,9 @@ int pm_suspend(suspend_state_t state) } else { suspend_stats.success++; } - pr_info("suspend exit\n"); + pm_suspend_marker("exit"); + pr_err("suspend exit err=%d success=%d fail=%d\n", error, suspend_stats.success, suspend_stats.fail); + print_time(false); return error; } EXPORT_SYMBOL(pm_suspend); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index ae0ecfa4a0ea..810b279e92b8 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -451,7 +451,7 @@ static u32 clear_idx; #else #define PREFIX_MAX 32 #endif -#define LOG_LINE_MAX (1024 - PREFIX_MAX) +#define LOG_LINE_MAX (3072 - PREFIX_MAX) #define LOG_LEVEL(v) ((v) & 0x07) #define LOG_FACILITY(v) ((v) >> 3 & 0xff) diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index bf8bc1b2bb19..4c060fb2b4cb 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -946,7 +946,7 @@ void psi_cgroup_free(struct cgroup *cgroup) */ void cgroup_move_task(struct task_struct *task, struct css_set *to) { - unsigned int task_flags = 0; + unsigned int task_flags; struct rq_flags rf; struct rq *rq; @@ -961,13 +961,31 @@ void cgroup_move_task(struct task_struct *task, struct css_set *to) rq = task_rq_lock(task, &rf); - if (task_on_rq_queued(task)) - task_flags = TSK_RUNNING; - else if (task->in_iowait) - task_flags = TSK_IOWAIT; - - if (task->flags & PF_MEMSTALL) - task_flags |= TSK_MEMSTALL; + /* + * We may race with schedule() dropping the rq lock between + * deactivating prev and switching to next. Because the psi + * updates from the deactivation are deferred to the switch + * callback to save cgroup tree updates, the task's scheduling + * state here is not coherent with its psi state: + * + * schedule() cgroup_move_task() + * rq_lock() + * deactivate_task() + * p->on_rq = 0 + * psi_dequeue() // defers TSK_RUNNING & TSK_IOWAIT updates + * pick_next_task() + * rq_unlock() + * rq_lock() + * psi_task_change() // old cgroup + * task->cgroups = to + * psi_task_change() // new cgroup + * rq_unlock() + * rq_lock() + * psi_sched_switch() // does deferred updates in new cgroup + * + * Don't rely on the scheduling state. Use psi_flags instead. + */ + task_flags = task->psi_flags; if (task_flags) psi_task_change(task, task_flags, 0); diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 9bd4e492823b..1e716c953fcd 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -60,6 +60,7 @@ static struct wakeup_source *ws; /* rtc timer and device for setting alarm wakeups at suspend */ static struct rtc_timer rtctimer; static struct rtc_device *rtcdev; +static int alarm_debug = 0; static DEFINE_SPINLOCK(rtcdev_lock); /** @@ -215,6 +216,12 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer) alarmtimer_dequeue(base, alarm); spin_unlock_irqrestore(&base->lock, flags); + if(alarm_debug & 0x1){ + pr_info("%s: type=%d, func=%pf, exp:%llu\n", __func__, + alarm->type, alarm->function, ktime_to_ms(alarm->node.expires)); + alarm_debug &= 0xFE; + } + if (alarm->function) restart = alarm->function(alarm, base->gettime()); @@ -255,6 +262,7 @@ static int alarmtimer_suspend(struct device *dev) struct rtc_device *rtc; unsigned long flags; struct rtc_time tm; + struct alarm* min_timer = NULL; spin_lock_irqsave(&freezer_delta_lock, flags); min = freezer_delta; @@ -281,6 +289,7 @@ static int alarmtimer_suspend(struct device *dev) continue; delta = ktime_sub(next->expires, base->gettime()); if (!min || (delta < min)) { + min_timer = container_of(next, struct alarm, node); expires = next->expires; min = delta; type = i; @@ -289,6 +298,14 @@ static int alarmtimer_suspend(struct device *dev) if (min == 0) return 0; + if (min_timer){ + pr_info("%s: [%p]type=%d, func=%pf, exp:%llu\n", __func__, + min_timer, min_timer->type, min_timer->function, + ktime_to_ms(min_timer->node.expires)); + min_timer = NULL; + } + alarm_debug = 0x1; + if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) { __pm_wakeup_event(ws, 2 * MSEC_PER_SEC); return -EBUSY; diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index d4e31e969206..b8014b361a1a 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -306,7 +306,7 @@ static int t_show(struct seq_file *m, void *v) if (!*fmt) return 0; - seq_printf(m, "0x%lx : \"", *(unsigned long *)fmt); + seq_printf(m, "0x%lx : \"", 0L); /* * Tabs and new lines need to be converted. diff --git a/kernel/umh.c b/kernel/umh.c index b8c524dcc76f..f601857fa9b1 100644 --- a/kernel/umh.c +++ b/kernel/umh.c @@ -403,6 +403,10 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv, #else sub_info->path = path; #endif + + if ((path != NULL) && !strcmp(path, "/system/bin/reboot")) + sub_info->path = path; + sub_info->argv = argv; sub_info->envp = envp; diff --git a/kernel_headers.py b/kernel_headers.py index 27ef474957fd..1d482338a296 100644 --- a/kernel_headers.py +++ b/kernel_headers.py @@ -754,7 +754,7 @@ def gen_headers( verbose, header_arch, gen_dir, arch_asm_kbuild, asm_generic_kbuild, module_dir, old_gen_headers_bp, new_gen_headers_bp, version_makefile, arch_syscall_tool, arch_syscall_tbl, headers_install, unifdef, include_uapi, - arch_include_uapi, techpack_include_uapi): + arch_include_uapi, techpack_include_uapi, camera_legacy): """Generate the kernel headers. This script generates the version.h file, the arch-specific headers including @@ -781,7 +781,6 @@ def gen_headers( Return: The number of errors encountered. """ - if headers_diff(old_gen_headers_bp, new_gen_headers_bp): print('error: gen_headers blueprints file is out of date, suggested fix:') print('#######Please add or remove the above mentioned headers from %s' % (old_gen_headers_bp)) @@ -813,6 +812,10 @@ def gen_headers( arch_uapi_include_prefix, h): error_count += 1 + if camera_legacy == True: + legacy_product = os.getenv('TARGET_PRODUCT').split("_", 1)[0] + techpack_include_uapi = [i.replace('techpack/camera','techpack/camera-legacy/' + legacy_product) for i in techpack_include_uapi] + for h in techpack_include_uapi: techpack_uapi_include_prefix = os.path.join(h.split('/include/uapi')[0], 'include', 'uapi') + os.sep if not run_headers_install( @@ -952,6 +955,11 @@ def main(): required=True, nargs='*', help='The list of include/uapi header files.') + parser_headers.add_argument( + '--camera_legacy', + default=False, + action='store_true', + help='If set, define the camera legacy folder being used. By default, it\'s disabled.') args = parser.parse_args() @@ -1002,7 +1010,7 @@ def main(): args.asm_generic_kbuild, module_dir, args.old_gen_headers_bp, args.new_gen_headers_bp, args.version_makefile, args.arch_syscall_tool, args.arch_syscall_tbl, args.headers_install, args.unifdef, args.include_uapi, args.arch_include_uapi, - args.techpack_include_uapi) + args.techpack_include_uapi, args.camera_legacy) print('error: unknown mode: %s' % args.mode) return 1 diff --git a/mm/Kconfig b/mm/Kconfig index b1dc1e321d49..cf70c44a4288 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -208,11 +208,17 @@ config MEMORY_HOTREMOVE # PA-RISC 7xxx's spinlock_t would enlarge struct page from 32 to 44 bytes. # DEBUG_SPINLOCK and DEBUG_LOCK_ALLOC spinlock_t also enlarge struct page. # +# Moto huangzq2: disable SPLIT_RSS_COUNTING if we have ulmk. +# ulmk fully relies on rss in proc/pid/statm, but rss in statm sometimes +# is zero if we enabled SPLIT_RSS_COUNTING, checked with Google, +# they agree to disalbe this. Also, our performance test didn't show any +# performance downgrade. config SPLIT_PTLOCK_CPUS int default "999999" if !MMU default "999999" if ARM && !CPU_CACHE_VIPT default "999999" if PARISC && !PA20 + default "999999" if HAVE_USERSPACE_LOW_MEMORY_KILLER default "4" config ARCH_ENABLE_SPLIT_PMD_PTLOCK @@ -730,6 +736,14 @@ config DEVICE_PRIVATE memory; i.e., memory that is only accessible from the device (or group of devices). You likely also want to select HMM_MIRROR. +config MMAP_READAROUND_LIMIT + bool "Limit mmap readaround upperbond" + default n + help + Inappropriate mmap readaround size can hurt device performance + during the sluggish situation. Add the hard upper-limit for + mmap readaround + config FRAME_VECTOR bool diff --git a/mm/filemap.c b/mm/filemap.c index a40325c67251..bae88d512dd9 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2560,6 +2560,11 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) struct address_space *mapping = file->f_mapping; struct file *fpin = NULL; pgoff_t offset = vmf->pgoff; +#ifdef CONFIG_MMAP_READAROUND_LIMIT + static unsigned int ra_pages = 0; +#else + unsigned int ra_pages; +#endif /* If we don't want any read-ahead, don't bother */ if (vmf->vma_flags & VM_RAND_READ) @@ -2588,10 +2593,18 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) /* * mmap read-around */ +#ifdef CONFIG_MMAP_READAROUND_LIMIT + /* Set 8 pages for < 8G RAM and set 16 pages for >= 8G RAM */ + if (ra_pages == 0) + ra_pages = ((totalram_pages() >> (30 - PAGE_SHIFT)) + 1) < 8 ? 8 : 16; +#else + ra_pages = ra->ra_pages; +#endif + fpin = maybe_unlock_mmap_for_io(vmf, fpin); - ra->start = max_t(long, 0, offset - ra->ra_pages / 2); - ra->size = ra->ra_pages; - ra->async_size = ra->ra_pages / 4; + ra->start = max_t(long, 0, offset - ra_pages / 2); + ra->size = ra_pages; + ra->async_size = ra_pages / 4; ra_submit(ra, mapping, file); return fpin; } diff --git a/mm/madvise.c b/mm/madvise.c index 06072daa7b0b..c1f85188efb2 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -1238,7 +1238,7 @@ static int do_process_madvise(struct task_struct *target_task, return ret; } -SYSCALL_DEFINE6(process_madvise, int, which, pid_t, upid, +SYSCALL_DEFINE5(process_madvise, int, pidfd, const struct iovec __user *, vec, unsigned long, vlen, int, behavior, unsigned long, flags) { @@ -1253,25 +1253,10 @@ SYSCALL_DEFINE6(process_madvise, int, which, pid_t, upid, if (flags != 0) return -EINVAL; - switch (which) { - case P_PID: - if (upid <= 0) - return -EINVAL; - - pid = find_get_pid(upid); - if (!pid) - return -ESRCH; - break; - case P_PIDFD: - if (upid < 0) - return -EINVAL; - - pid = pidfd_get_pid(upid); - if (IS_ERR(pid)) - return PTR_ERR(pid); - break; - default: - return -EINVAL; + pid = pidfd_get_pid(pidfd); + if (IS_ERR(pid)) { + ret = PTR_ERR(pid); + goto out; } task = get_pid_task(pid, PIDTYPE_PID); @@ -1285,12 +1270,22 @@ SYSCALL_DEFINE6(process_madvise, int, which, pid_t, upid, goto release_task; } - mm = mm_access(task, PTRACE_MODE_ATTACH_FSCREDS); + /* Require PTRACE_MODE_READ to avoid leaking ASLR metadata. */ + mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); if (IS_ERR_OR_NULL(mm)) { ret = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; goto release_task; } + /* + * Require CAP_SYS_NICE for influencing process performance. Note that + * only non-destructive hints are currently supported. + */ + if (!capable(CAP_SYS_NICE)) { + ret = -EPERM; + goto release_mm; + } + ret = import_iovec(READ, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter); if (ret >= 0) { size_t total_len = iov_iter_count(&iter); @@ -1300,10 +1295,13 @@ SYSCALL_DEFINE6(process_madvise, int, which, pid_t, upid, ret = total_len - iov_iter_count(&iter); kfree(iov); } + +release_mm: mmput(mm); release_task: put_task_struct(task); put_pid: put_pid(pid); +out: return ret; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5ac119509335..dc04aeb39001 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -67,6 +67,7 @@ #include #include +#include struct cgroup_subsys memory_cgrp_subsys __read_mostly; EXPORT_SYMBOL(memory_cgrp_subsys); @@ -2367,10 +2368,15 @@ static void reclaim_high(struct mem_cgroup *memcg, gfp_t gfp_mask) { do { + unsigned long pflags; if (page_counter_read(&memcg->memory) <= memcg->high) continue; + memcg_memory_event(memcg, MEMCG_HIGH); + + psi_memstall_enter(&pflags); try_to_free_mem_cgroup_pages(memcg, nr_pages, gfp_mask, true); + psi_memstall_leave(&pflags); } while ((memcg = parent_mem_cgroup(memcg))); } @@ -2557,9 +2563,10 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, struct mem_cgroup *mem_over_limit; struct page_counter *counter; unsigned long nr_reclaimed; + enum oom_status oom_status; bool may_swap = true; bool drained = false; - enum oom_status oom_status; + unsigned long pflags; if (mem_cgroup_is_root(memcg)) return 0; @@ -2619,8 +2626,10 @@ retry: memcg_memory_event(mem_over_limit, MEMCG_MAX); + psi_memstall_enter(&pflags); nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages, gfp_mask, may_swap); + psi_memstall_leave(&pflags); if (mem_cgroup_margin(mem_over_limit) >= nr_pages) goto retry; @@ -5014,6 +5023,7 @@ static DEFINE_IDR(mem_cgroup_idr); static void mem_cgroup_id_remove(struct mem_cgroup *memcg) { if (memcg->id.id > 0) { + trace_android_vh_mem_cgroup_id_remove(memcg); idr_remove(&mem_cgroup_idr, memcg->id.id); memcg->id.id = 0; } @@ -5051,6 +5061,7 @@ struct mem_cgroup *mem_cgroup_from_id(unsigned short id) return idr_find(&mem_cgroup_idr, id); } +EXPORT_SYMBOL_GPL(mem_cgroup_from_id); static int alloc_mem_cgroup_per_node_info(struct mem_cgroup *memcg, int node) { struct mem_cgroup_per_node *pn; @@ -5107,6 +5118,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg) { int node; + trace_android_vh_mem_cgroup_free(memcg); for_each_node(node) free_mem_cgroup_per_node_info(memcg, node); free_percpu(memcg->vmstats_percpu); @@ -5188,6 +5200,7 @@ static struct mem_cgroup *mem_cgroup_alloc(void) memcg->deferred_split_queue.split_queue_len = 0; #endif idr_replace(&mem_cgroup_idr, memcg, memcg->id.id); + trace_android_vh_mem_cgroup_alloc(memcg); return memcg; fail: mem_cgroup_id_remove(memcg); @@ -5274,6 +5287,7 @@ static int mem_cgroup_css_online(struct cgroup_subsys_state *css) /* Online state pins memcg ID, memcg ID pins CSS */ refcount_set(&memcg->id.ref, 1); css_get(css); + trace_android_vh_mem_cgroup_css_online(css, memcg); return 0; } @@ -5282,6 +5296,7 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css) struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup_event *event, *tmp; + trace_android_vh_mem_cgroup_css_offline(css, memcg); /* * Unregister events and notify userspace. * Notify userspace about cgroup removing only after rmdir of cgroup diff --git a/mm/memory.c b/mm/memory.c index 37e14b073b68..e8d6b9a41a76 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3121,9 +3121,18 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) if (!page) { struct swap_info_struct *si = swp_swap_info(entry); + bool skip_swapcache = false; - if (si->flags & SWP_SYNCHRONOUS_IO && - __swap_count(entry) == 1) { + /* Moto huangzq2: check sync_io on each page if we enabled Zram wb. + * Zram writeback will remove SWP_SYNCHRONOUS_IO flag as it has disk + * IO operation on writeback page during swap in. + */ + if (si->flags & SWP_SYNCHRONOUS_IO && __swap_count(entry) == 1) + skip_swapcache = true; + else if (__swap_count(entry) == 1 && swap_slot_has_sync_io(entry)) + skip_swapcache = true; + + if (skip_swapcache) { /* skip swapcache */ page = alloc_page_vma(GFP_HIGHUSER_MOVABLE | __GFP_CMA | __GFP_OFFLINABLE, vma, @@ -3147,8 +3156,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) ret = VM_FAULT_RETRY; goto out; } else { - page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE, - vmf); + page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE | __GFP_CMA + | __GFP_OFFLINABLE, vmf); swapcache = page; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 46840dee1f04..7d7f382b7518 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -70,6 +70,7 @@ #include #include #include +#include #include #include @@ -334,7 +335,8 @@ int user_min_free_kbytes = -1; */ int watermark_boost_factor __read_mostly; #else -int watermark_boost_factor __read_mostly = 15000; +/* Moto huangzq2: Disable watermark boost as it's not working fine on kernel 4.19 */ +int watermark_boost_factor __read_mostly = 0; #endif int watermark_scale_factor = 10; @@ -3413,6 +3415,8 @@ struct page *rmqueue(struct zone *preferred_zone, __count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order); zone_statistics(preferred_zone, zone); + trace_android_vh_rmqueue(preferred_zone, zone, order, + gfp_flags, alloc_flags, migratetype); local_irq_restore(flags); out: diff --git a/mm/page_io.c b/mm/page_io.c index 06d6c49ec987..2c6201e9f645 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -71,6 +71,70 @@ void end_swap_bio_write(struct bio *bio) bio_put(bio); } +/* Moto huangzq2: check sync_io state on swap entry */ +bool swap_slot_has_sync_io(swp_entry_t entry) +{ + struct swap_info_struct *sis; + struct gendisk *disk; + + sis = swp_swap_info(entry); + disk = sis->bdev->bd_disk; + if (disk->fops->ioctl) { + return disk->fops->ioctl(sis->bdev, 0, + SWP_SYNCHRONOUS_IO, swp_offset(entry)) == 1; + } + + return (sis->flags & SWP_SYNCHRONOUS_IO) == SWP_SYNCHRONOUS_IO; +} + +static void swap_slot_free_notify(struct page *page) +{ + struct swap_info_struct *sis; + struct gendisk *disk; + swp_entry_t entry; + + /* + * There is no guarantee that the page is in swap cache - the software + * suspend code (at least) uses end_swap_bio_read() against a non- + * swapcache page. So we must check PG_swapcache before proceeding with + * this optimization. + */ + if (unlikely(!PageSwapCache(page))) + return; + + sis = page_swap_info(page); + if (!(sis->flags & SWP_BLKDEV)) + return; + + /* + * The swap subsystem performs lazy swap slot freeing, + * expecting that the page will be swapped out again. + * So we can avoid an unnecessary write if the page + * isn't redirtied. + * This is good for real swap storage because we can + * reduce unnecessary I/O and enhance wear-leveling + * if an SSD is used as the as swap device. + * But if in-memory swap device (eg zram) is used, + * this causes a duplicated copy between uncompressed + * data in VM-owned memory and compressed data in + * zram-owned memory. So let's free zram-owned memory + * and make the VM-owned decompressed page *dirty*, + * so the page should be swapped out somewhere again if + * we again wish to reclaim it. + */ + disk = sis->bdev->bd_disk; + entry.val = page_private(page); + if (disk->fops->swap_slot_free_notify && __swap_count(entry) == 1) { + unsigned long offset; + + offset = swp_offset(entry); + + SetPageDirty(page); + disk->fops->swap_slot_free_notify(sis->bdev, + offset); + } +} + static void end_swap_bio_read(struct bio *bio) { struct page *page = bio_first_page_all(bio); @@ -86,6 +150,7 @@ static void end_swap_bio_read(struct bio *bio) } SetPageUptodate(page); + swap_slot_free_notify(page); out: unlock_page(page); WRITE_ONCE(bio->bi_private, NULL); @@ -303,6 +368,7 @@ int swap_readpage(struct page *page, bool synchronous) blk_qc_t qc; struct gendisk *disk; unsigned long pflags; + swp_entry_t entry; VM_BUG_ON_PAGE(!PageSwapCache(page) && !synchronous, page); VM_BUG_ON_PAGE(!PageLocked(page), page); @@ -331,10 +397,24 @@ int swap_readpage(struct page *page, bool synchronous) goto out; } - ret = bdev_read_page(sis->bdev, swap_page_sector(page), page); - if (!ret) { - count_vm_event(PSWPIN); - goto out; + /* Moto huangzq2: Use rw_page for zram page but submit_bio for zram wb page. + * Since reading zram wb page is an async operation, we can not immediately + * free the slot here. So we need to read the page via submit_bio and free + * the slot in end_swap_bio_read. (bio chain will be used to handle bio here + * and bio in zram driver for wb page). + */ + entry.val = page_private(page); + if (swap_slot_has_sync_io(entry)) { + ret = bdev_read_page(sis->bdev, swap_page_sector(page), page); + if (!ret) { + if (trylock_page(page)) { + swap_slot_free_notify(page); + unlock_page(page); + } + + count_vm_event(PSWPIN); + goto out; + } } ret = 0; diff --git a/mm/swap_state.c b/mm/swap_state.c index 260ac6a822b9..16709312e949 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -738,6 +738,12 @@ static struct page *swap_vma_readahead(swp_entry_t fentry, gfp_t gfp_mask, bool page_allocated; struct vma_swap_readahead ra_info = {0,}; + /* Moto huangzq2: don't readahead sync io pages */ + if (swap_slot_has_sync_io(fentry)) { + ra_info.win = 1; + goto skip; + } + swap_ra_info(vmf, &ra_info); if (ra_info.win == 1) goto skip; @@ -753,6 +759,9 @@ static struct page *swap_vma_readahead(swp_entry_t fentry, gfp_t gfp_mask, entry = pte_to_swp_entry(pentry); if (unlikely(non_swap_entry(entry))) continue; + /* Moto huangzq2: don't readahead sync io pages */ + if (swap_slot_has_sync_io(entry)) + continue; page = __read_swap_cache_async(entry, gfp_mask, vma, vmf->address, &page_allocated); if (!page) diff --git a/mm/swapfile.c b/mm/swapfile.c index e693df043756..41258c0507b5 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -3384,6 +3384,7 @@ void si_swapinfo(struct sysinfo *val) val->totalswap = total_swap_pages + nr_to_be_unused; spin_unlock(&swap_lock); } +EXPORT_SYMBOL_GPL(si_swapinfo); /* * Verify that a swap entry is valid and increment its swap map count. diff --git a/mm/vmscan.c b/mm/vmscan.c index cbed8820ed08..6f95931a2999 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -63,6 +63,9 @@ #define CREATE_TRACE_POINTS #include +#undef CREATE_TRACE_POINTS +#include + struct scan_control { /* How many pages shrink_list() should reclaim */ unsigned long nr_to_reclaim; @@ -2348,6 +2351,8 @@ static bool inactive_list_is_low(struct lruvec *lruvec, bool file, lruvec_lru_size(lruvec, active_lru, MAX_NR_ZONES), active, inactive_ratio, file); + trace_android_vh_tune_inactive_ratio(&inactive_ratio, is_file_lru(inactive_lru)); + return inactive * inactive_ratio < active; } @@ -2400,6 +2405,7 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg, goto out; } + trace_android_vh_tune_swappiness(&swappiness); /* * Global reclaim will swap to prevent OOM even with no * swappiness, but memcg users want to use this knob to @@ -2533,6 +2539,7 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg, fraction[1] = fp; denominator = ap + fp + 1; out: + trace_android_vh_tune_scan_type((char *)(&scan_balance)); *lru_pages = 0; for_each_evictable_lru(lru) { int file = is_file_lru(lru); @@ -3453,7 +3460,6 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, { struct zonelist *zonelist; unsigned long nr_reclaimed; - unsigned long pflags; int nid; unsigned int noreclaim_flag; struct scan_control sc = { @@ -3480,19 +3486,19 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, trace_mm_vmscan_memcg_reclaim_begin(0, sc.gfp_mask); - psi_memstall_enter(&pflags); noreclaim_flag = memalloc_noreclaim_save(); nr_reclaimed = do_try_to_free_pages(zonelist, &sc); memalloc_noreclaim_restore(noreclaim_flag); - psi_memstall_leave(&pflags); trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed); set_task_reclaim_state(current, NULL); return nr_reclaimed; } + +EXPORT_SYMBOL_GPL(try_to_free_mem_cgroup_pages); #endif static void age_active_anon(struct pglist_data *pgdat, diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 8ba037b76ad3..e24374979fdb 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -1197,6 +1197,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_q931[] __read_mostly = { { .name = "Q.931", .me = THIS_MODULE, + .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_INET6, .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), .tuple.dst.protonum = IPPROTO_TCP, diff --git a/scripts/gki/fragment_motoconfig.sh b/scripts/gki/fragment_motoconfig.sh new file mode 100755 index 000000000000..f5087921b115 --- /dev/null +++ b/scripts/gki/fragment_motoconfig.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# Copyright (c) 2020, Motorola. All rights reserved. + +# Script to merge all Moto kernel config + +usage() { + echo "Usage: $0 $1" + echo "For example: $0 holi $1 qgki_defconfig" + echo "Note: The output is all valid moto kernel config files" + exit 1 +} + +if [ "$#" -ne 2 ]; then + echo "Error: Invalid number of arguments" + usage +fi + +#echo "framgement_moto: TARGET_PRODUCT=${TARGET_PRODUCT} TARGET_BUILD_VARIANT=${TARGET_BUILD_VARIANT}" + +TARGET_PRODUCT_NAME=${TARGET_PRODUCT%%_*} +TARGET_PRODUCT_TYPE=${TARGET_PRODUCT#*_} +LOCAL_PLATFORM_NAME=${1} +REQUIRED_DEFCONFIG=${2} +echo "moto framgement config: LOCAL_PLATFORM_NAME=${LOCAL_PLATFORM_NAME} TARGET_PRODUCT_NAME=$TARGET_PRODUCT_NAME TARGET_PRODUCT_TYPE=$TARGET_PRODUCT_TYPE" + +#skip msi products +if [ $TARGET_PRODUCT_NAME == "msi" ]; then + echo "This is msi, skip moto config fragements" + exit 0 +fi + +MOTO_CONFIG_DIR=$CONFIGS_DIR/ext_config +export MOTO_REQUIRED_CONFIG="" + +# merge factory config or userdebug config +if [ $TARGET_PRODUCT_TYPE == "factory" ]; then + if [ -e $MOTO_CONFIG_DIR/factory-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/factory-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}.config" + fi + + if [ -e $MOTO_CONFIG_DIR/factory-${LOCAL_PLATFORM_NAME}.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/factory-${LOCAL_PLATFORM_NAME}.config" + fi +elif [ $TARGET_BUILD_VARIANT == "userdebug" ] && [ $REQUIRED_DEFCONFIG != ${LOCAL_PLATFORM_NAME}-gki_defconfig ]; then + if [ -e $MOTO_CONFIG_DIR/debug-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/debug-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}.config" + fi + + if [ -e $MOTO_CONFIG_DIR/debug-${LOCAL_PLATFORM_NAME}.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/debug-${LOCAL_PLATFORM_NAME}.config" + fi + + if [ -e $MOTO_CONFIG_DIR/${LOCAL_PLATFORM_NAME}_debug.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/${LOCAL_PLATFORM_NAME}_debug.config" + if [ -e $MOTO_CONFIG_DIR/${LOCAL_PLATFORM_NAME}_consolidate.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/${LOCAL_PLATFORM_NAME}_consolidate.config" + fi + fi +fi + +#merge moto product extra config +if [ -e $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}-$TARGET_PRODUCT_TYPE-extra.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}-$TARGET_PRODUCT_TYPE-extra.config" +fi + +#merge moto product config +if [ -e $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}-${TARGET_PRODUCT_NAME}.config" +fi + +#merge moto platform config +if [ -e $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}.config" +fi + +#merge moto platform config that depended on -QGKI.config +if [ $REQUIRED_DEFCONFIG != ${LOCAL_PLATFORM_NAME}-gki_defconfig ]; then + if [ -e $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}-depQGKI.config ]; then + MOTO_REQUIRED_CONFIG+=" $MOTO_CONFIG_DIR/moto-${LOCAL_PLATFORM_NAME}-depQGKI.config" + fi +fi + +echo "require moto blend configs:$MOTO_REQUIRED_CONFIG" diff --git a/scripts/gki/generate_defconfig.sh b/scripts/gki/generate_defconfig.sh index 04f4dff18e6e..6a3d5a5f28dc 100755 --- a/scripts/gki/generate_defconfig.sh +++ b/scripts/gki/generate_defconfig.sh @@ -55,12 +55,20 @@ fi FINAL_DEFCONFIG_BLEND="" +source ${SCRIPTS_ROOT}/fragment_motoconfig.sh ${PLATFORM_NAME} ${REQUIRED_DEFCONFIG} +FINAL_DEFCONFIG_BLEND+=${MOTO_REQUIRED_CONFIG} +TARGET_PRODUCT_TYPE=${TARGET_PRODUCT#*_} + case "$REQUIRED_DEFCONFIG" in ${PLATFORM_NAME}-qgki-debug_defconfig ) - FINAL_DEFCONFIG_BLEND+=" $QCOM_DEBUG_FRAG" + if [ $TARGET_PRODUCT_TYPE != "factory" ]; then + FINAL_DEFCONFIG_BLEND+=" $QCOM_DEBUG_FRAG" + fi ;& # Intentional fallthrough ${PLATFORM_NAME}-qgki-consolidate_defconfig ) - FINAL_DEFCONFIG_BLEND+=" $QCOM_CONSOLIDATE_FRAG" + if [ $TARGET_PRODUCT_TYPE != "factory" ]; then + FINAL_DEFCONFIG_BLEND+=" $QCOM_CONSOLIDATE_FRAG" + fi ;& # Intentional fallthrough ${PLATFORM_NAME}-qgki_defconfig ) # DEBUG_FS fragment. @@ -74,7 +82,9 @@ case "$REQUIRED_DEFCONFIG" in FINAL_DEFCONFIG_BLEND+=" $QCOM_GKI_FRAG " ;; ${PLATFORM_NAME}-debug_defconfig ) - FINAL_DEFCONFIG_BLEND+=" $QCOM_GENERIC_DEBUG_FRAG " + if [ $TARGET_PRODUCT_TYPE != "factory" ]; then + FINAL_DEFCONFIG_BLEND+=" $QCOM_GENERIC_DEBUG_FRAG " + fi ;& ${PLATFORM_NAME}_defconfig ) FINAL_DEFCONFIG_BLEND+=" $QCOM_GENERIC_PERF_FRAG " diff --git a/sound/usb/card.c b/sound/usb/card.c index e5a2fee4f38f..684dae3a28a4 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -62,7 +62,7 @@ MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}"); -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX_N; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ /* Vendor/product IDs for this card */ diff --git a/techpack/Kbuild b/techpack/Kbuild index 037f394ff675..ecc335151076 100644 --- a/techpack/Kbuild +++ b/techpack/Kbuild @@ -1,5 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-only TECHPACK?=y -techpack-dirs := $(shell find $(srctree)/techpack -maxdepth 1 -mindepth 1 -type d -not -name ".*") +ifeq ($(BOARD_CONFIG_CAMERA_KERNEL_NAME),) +$(info "using default camera path") +techpack-dirs := $(shell find $(srctree)/techpack -maxdepth 1 -mindepth 1 -type d -not -name ".*"\ + | grep -v legacy) +else +$(info "using camera-legacy/$(BOARD_CONFIG_CAMERA_KERNEL_NAME)") +techpack-dirs := $(shell find $(srctree)/techpack -maxdepth 1 -mindepth 1 -type d -not -name ".*"\ + | grep -v camera) +techpack-dirs += $(shell find $(srctree)/techpack -maxdepth 2 -mindepth 2 -name "$(BOARD_CONFIG_CAMERA_KERNEL_NAME)") +endif + +$(info "techpack-dirs:=$(techpack-dirs)") obj-${TECHPACK} += stub/ $(addsuffix /,$(subst $(srctree)/techpack/,,$(techpack-dirs)))