diff --git a/arch/arm/configs/vendor/sdm429-bg-perf_defconfig b/arch/arm/configs/vendor/sdm429-bg-perf_defconfig index 72b1a9356d89..ca033da861da 100644 --- a/arch/arm/configs/vendor/sdm429-bg-perf_defconfig +++ b/arch/arm/configs/vendor/sdm429-bg-perf_defconfig @@ -260,7 +260,6 @@ CONFIG_QPNP_MISC=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y -CONFIG_DM_DEFAULT_KEY=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y CONFIG_DM_VERITY_FEC=y diff --git a/arch/arm/configs/vendor/sdm429-bg_defconfig b/arch/arm/configs/vendor/sdm429-bg_defconfig index 903a827ae43f..8bc9db3d82a1 100644 --- a/arch/arm/configs/vendor/sdm429-bg_defconfig +++ b/arch/arm/configs/vendor/sdm429-bg_defconfig @@ -267,7 +267,6 @@ CONFIG_QPNP_MISC=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y -CONFIG_DM_DEFAULT_KEY=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y CONFIG_DM_VERITY_FEC=y diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 765fcd19cf9e..b4ebe399cfb3 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1117,6 +1117,16 @@ config ARM64_TAGGED_ADDR_ABI to system calls as pointer arguments. For details, see Documentation/arm64/tagged-address-abi.rst. +config MITIGATE_SPECTRE_BRANCH_HISTORY + bool "Mitigate Spectre style attacks against branch history" if EXPERT + default y + depends on HARDEN_BRANCH_PREDICTOR || !KVM + help + Speculation attacks against some high-performance processors can + make use of branch history to influence future speculation. + When taking an exception from user-space, a sequence of branches + or a firmware call overwrites the branch history. + menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" depends on COMPAT diff --git a/arch/arm64/boot/dts/qcom/sm8150-sdxprairie-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sm8150-sdxprairie-audio-overlay.dtsi new file mode 100644 index 000000000000..30edbe98916a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-sdxprairie-audio-overlay.dtsi @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +&soc { + quat_mi2s_gpios: quat_mi2s_pinctrl { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&quat_mi2s_active + &quat_mi2s_sd0_active &quat_mi2s_sd1_active>; + pinctrl-1 = <&quat_mi2s_sleep + &quat_mi2s_sd0_sleep &quat_mi2s_sd1_sleep>; + }; + + audio_slimslave { + compatible = "qcom,audio-slimslave"; + elemental-addr = [00 01 50 02 17 02]; + }; +}; + +&snd_934x { + compatible = "qcom,sm8150-asoc-snd-hana55"; + qcom,model = "sm8150-hana55-snd-card"; + + qcom,quat-mi2s-gpios = <&quat_mi2s_gpios>; +}; + +&dai_mi2s3 { + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-sdxprairie-v3-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sm8150-sdxprairie-v3-mtp-overlay.dts index 124bf7a78078..159ad792c617 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-sdxprairie-v3-mtp-overlay.dts +++ b/arch/arm64/boot/dts/qcom/sm8150-sdxprairie-v3-mtp-overlay.dts @@ -23,6 +23,7 @@ #include "sdx5xm-external-soc.dtsi" #include "sm8150-sdxprairie-v2.dtsi" #include "sm8150-mtp-audio-overlay.dtsi" +#include "sm8150-sdxprairie-audio-overlay.dtsi" / { model = "SDXPRAIRIE V3 MTP"; diff --git a/arch/arm64/configs/vendor/trinket-perf_defconfig b/arch/arm64/configs/vendor/trinket-perf_defconfig index 02e826b42095..97d3041aaf1b 100644 --- a/arch/arm64/configs/vendor/trinket-perf_defconfig +++ b/arch/arm64/configs/vendor/trinket-perf_defconfig @@ -257,6 +257,7 @@ CONFIG_CFG80211_INTERNAL_REGDB=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_RFKILL=y CONFIG_NFC_NQ=y +CONFIG_NFC_MAX32560=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_REGMAP_WCD_IRQ=y CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y diff --git a/arch/arm64/configs/vendor/trinket_defconfig b/arch/arm64/configs/vendor/trinket_defconfig index d5f70a3fbd10..633e289f0a1d 100644 --- a/arch/arm64/configs/vendor/trinket_defconfig +++ b/arch/arm64/configs/vendor/trinket_defconfig @@ -266,6 +266,7 @@ CONFIG_CFG80211_INTERNAL_REGDB=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_RFKILL=y CONFIG_NFC_NQ=y +CONFIG_NFC_MAX32560=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_REGMAP_WCD_IRQ=y CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index a17280d58d70..80fd78e0b202 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -284,28 +284,6 @@ static int __init ssbd_cfg(char *buf) } early_param("ssbd", ssbd_cfg); -void __init arm64_update_smccc_conduit(struct alt_instr *alt, - __le32 *origptr, __le32 *updptr, - int nr_inst) -{ - u32 insn; - - BUG_ON(nr_inst != 1); - - switch (psci_ops.conduit) { - case PSCI_CONDUIT_HVC: - insn = aarch64_insn_get_hvc_value(); - break; - case PSCI_CONDUIT_SMC: - insn = aarch64_insn_get_smc_value(); - break; - default: - return; - } - - *updptr = cpu_to_le32(insn); -} - void arm64_set_ssbd_mitigation(bool state) { if (!IS_ENABLED(CONFIG_ARM64_SSBD)) { @@ -336,6 +314,31 @@ void arm64_set_ssbd_mitigation(bool state) } } +#if defined(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY) || \ + defined(CONFIG_ARM64_SSBD) +void __init arm64_update_smccc_conduit(struct alt_instr *alt, + __le32 *origptr, __le32 *updptr, + int nr_inst) +{ + u32 insn; + + BUG_ON(nr_inst != 1); + + switch (psci_ops.conduit) { + case PSCI_CONDUIT_HVC: + insn = aarch64_insn_get_hvc_value(); + break; + case PSCI_CONDUIT_SMC: + insn = aarch64_insn_get_smc_value(); + break; + default: + return; + } + + *updptr = cpu_to_le32(insn); +} +#endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY || CONFIG_ARM64_SSBD */ + static bool has_ssbd_mitigation(const struct arm64_cpu_capabilities *entry, int scope) { @@ -1180,4 +1183,4 @@ void __init spectre_bhb_patch_loop_iter(struct alt_instr *alt, AARCH64_INSN_VARIANT_64BIT, AARCH64_INSN_MOVEWIDE_ZERO); *updptr++ = cpu_to_le32(insn); -} \ No newline at end of file +} diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 1321f65d70eb..7c59112f9c32 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -75,7 +75,6 @@ .macro kernel_ventry, el, label, regsize = 64 .align 7 .Lventry_start\@: -#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is @@ -90,7 +89,6 @@ .endif .Lskip_tramp_vectors_cleanup\@: .endif -#endif sub sp, sp, #S_FRAME_SIZE #ifdef CONFIG_VMAP_STACK @@ -161,6 +159,9 @@ tbnz \tmp2, #TIF_SSBD, \targ mov w0, #ARM_SMCCC_ARCH_WORKAROUND_2 mov w1, #\state + alternative_cb arm64_update_smccc_conduit + nop // Patched to SMC/HVC #0 + alternative_cb_end smc #0 #endif .endm @@ -1066,13 +1067,41 @@ alternative_else_nop_endif sub \dst, \dst, PAGE_SIZE .endm - .macro tramp_ventry, vector_start, regsize, kpti + .macro tramp_data_read_var dst, var +#ifdef CONFIG_RANDOMIZE_BASE + tramp_data_page \dst + add \dst, \dst, #:lo12:__entry_tramp_data_\var + ldr \dst, [\dst] +#else + ldr \dst, =\var +#endif + .endm + +#define BHB_MITIGATION_NONE 0 +#define BHB_MITIGATION_LOOP 1 +#define BHB_MITIGATION_FW 2 +#define BHB_MITIGATION_INSN 3 + + .macro tramp_ventry, vector_start, regsize, kpti, bhb .align 7 1: .if \regsize == 64 msr tpidrro_el0, x30 // Restored in kernel_ventry .endif + .if \bhb == BHB_MITIGATION_LOOP + /* + * This sequence must appear before the first indirect branch. i.e. the + * ret out of tramp_ventry. It appears here because x30 is free. + */ + __mitigate_spectre_bhb_loop x30 + .endif // \bhb == BHB_MITIGATION_LOOP + + .if \bhb == BHB_MITIGATION_INSN + clearbhb + isb + .endif // \bhb == BHB_MITIGATION_INSN + .if \kpti == 1 /* * Defend against branch aliasing attacks by pushing a dummy @@ -1083,13 +1112,8 @@ alternative_else_nop_endif b . 2: tramp_map_kernel x30 -#ifdef CONFIG_RANDOMIZE_BASE - tramp_data_page x30 alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003 - ldr x30, [x30] -#else - ldr x30, =vectors -#endif + tramp_data_read_var x30, vectors prfm plil1strm, [x30, #(1b - \vector_start)] msr vbar_el1, x30 isb @@ -1097,13 +1121,29 @@ alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003 ldr x30, =vectors .endif // \kpti == 1 + .if \bhb == BHB_MITIGATION_FW + /* + * The firmware sequence must appear before the first indirect branch. + * i.e. the ret out of tramp_ventry. But it also needs the stack to be + * mapped to save/restore the registers the SMC clobbers. + */ + __mitigate_spectre_bhb_fw + .endif // \bhb == BHB_MITIGATION_FW + add x30, x30, #(1b - \vector_start + 4) ret .org 1b + 128 // Did we overflow the ventry slot? .endm .macro tramp_exit, regsize = 64 - adr x30, tramp_vectors + tramp_data_read_var x30, this_cpu_vector +alternative_if_not ARM64_HAS_VIRT_HOST_EXTN + mrs x29, tpidr_el1 +alternative_else + mrs x29, tpidr_el2 +alternative_endif + ldr x30, [x30, x29] + msr vbar_el1, x30 ldr lr, [sp, #S_LR] tramp_unmap_kernel x29 @@ -1114,26 +1154,33 @@ alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003 eret .endm - .macro generate_tramp_vector, kpti + .macro generate_tramp_vector, kpti, bhb .Lvector_start\@: .space 0x400 .rept 4 - tramp_ventry .Lvector_start\@, 64, \kpti + tramp_ventry .Lvector_start\@, 64, \kpti, \bhb .endr .rept 4 - tramp_ventry .Lvector_start\@, 32, \kpti + tramp_ventry .Lvector_start\@, 32, \kpti, \bhb .endr .endm #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 /* * Exception vectors trampoline. + * The order must match __bp_harden_el1_vectors and the + * arm64_bp_harden_el1_vectors enum. */ .pushsection ".entry.tramp.text", "ax" .align 11 ENTRY(tramp_vectors) - generate_tramp_vector kpti=1 +#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY + generate_tramp_vector kpti=1, bhb=BHB_MITIGATION_LOOP + generate_tramp_vector kpti=1, bhb=BHB_MITIGATION_FW + generate_tramp_vector kpti=1, bhb=BHB_MITIGATION_INSN +#endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */ + generate_tramp_vector kpti=1, bhb=BHB_MITIGATION_NONE END(tramp_vectors) ENTRY(tramp_exit_native) @@ -1167,7 +1214,7 @@ __entry_tramp_data_this_cpu_vector: * Exception vectors for spectre mitigations on entry from EL1 when * kpti is not in use. */ - .macro generate_el1_vector + .macro generate_el1_vector, bhb .Lvector_start\@: kernel_ventry 1, sync_invalid // Synchronous EL1t kernel_ventry 1, irq_invalid // IRQ EL1t @@ -1180,17 +1227,22 @@ __entry_tramp_data_this_cpu_vector: kernel_ventry 1, error_invalid // Error EL1h .rept 4 - tramp_ventry .Lvector_start\@, 64, kpti=0 + tramp_ventry .Lvector_start\@, 64, 0, \bhb .endr .rept 4 - tramp_ventry .Lvector_start\@, 32, kpti=0 + tramp_ventry .Lvector_start\@, 32, 0, \bhb .endr .endm +/* The order must match tramp_vecs and the arm64_bp_harden_el1_vectors enum. */ .pushsection ".entry.text", "ax" .align 11 ENTRY(__bp_harden_el1_vectors) - generate_el1_vector +#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY + generate_el1_vector bhb=BHB_MITIGATION_LOOP + generate_el1_vector bhb=BHB_MITIGATION_FW + generate_el1_vector bhb=BHB_MITIGATION_INSN +#endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */ END(__bp_harden_el1_vectors) .popsection diff --git a/drivers/clk/qcom/camcc-sm8150.c b/drivers/clk/qcom/camcc-sm8150.c index b44c1fe938a2..f633d85750fa 100644 --- a/drivers/clk/qcom/camcc-sm8150.c +++ b/drivers/clk/qcom/camcc-sm8150.c @@ -588,7 +588,6 @@ static struct clk_rcg2 cam_cc_cci_0_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_cci_0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_cci_0_clk_src", .parent_names = cam_cc_parent_names_0, @@ -609,7 +608,6 @@ static struct clk_rcg2 cam_cc_cci_1_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_cci_0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_cci_1_clk_src", .parent_names = cam_cc_parent_names_0, @@ -636,7 +634,6 @@ static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_cphy_rx_clk_src", .parent_names = cam_cc_parent_names_0, @@ -663,7 +660,6 @@ static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_csi0phytimer_clk_src", .parent_names = cam_cc_parent_names_0, @@ -684,7 +680,6 @@ static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_csi1phytimer_clk_src", .parent_names = cam_cc_parent_names_0, @@ -705,7 +700,6 @@ static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_csi2phytimer_clk_src", .parent_names = cam_cc_parent_names_0, @@ -726,7 +720,6 @@ static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_csi3phytimer_clk_src", .parent_names = cam_cc_parent_names_0, @@ -757,7 +750,6 @@ static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_fast_ahb_clk_src", .parent_names = cam_cc_parent_names_0, @@ -991,7 +983,6 @@ static struct clk_rcg2 cam_cc_ife_lite_0_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_ife_lite_0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_ife_lite_0_clk_src", .parent_names = cam_cc_parent_names_0, @@ -1038,7 +1029,6 @@ static struct clk_rcg2 cam_cc_ife_lite_1_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_ife_lite_0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_ife_lite_1_clk_src", .parent_names = cam_cc_parent_names_0, @@ -1193,7 +1183,6 @@ static struct clk_rcg2 cam_cc_mclk0_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_1, .freq_tbl = ftbl_cam_cc_mclk0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_mclk0_clk_src", .parent_names = cam_cc_parent_names_1, @@ -1214,7 +1203,6 @@ static struct clk_rcg2 cam_cc_mclk1_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_1, .freq_tbl = ftbl_cam_cc_mclk0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_mclk1_clk_src", .parent_names = cam_cc_parent_names_1, @@ -1235,7 +1223,6 @@ static struct clk_rcg2 cam_cc_mclk2_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_1, .freq_tbl = ftbl_cam_cc_mclk0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_mclk2_clk_src", .parent_names = cam_cc_parent_names_1, @@ -1256,7 +1243,6 @@ static struct clk_rcg2 cam_cc_mclk3_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_1, .freq_tbl = ftbl_cam_cc_mclk0_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_mclk3_clk_src", .parent_names = cam_cc_parent_names_1, @@ -1285,7 +1271,6 @@ static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { .hid_width = 5, .parent_map = cam_cc_parent_map_0, .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, - .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "cam_cc_qdss_debug_clk_src", .parent_names = cam_cc_parent_names_0, diff --git a/drivers/media/platform/msm/ais/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/ais/cam_sensor_module/cam_eeprom/cam_eeprom_core.c index 8eac9d54d07f..f324ab36777d 100644 --- a/drivers/media/platform/msm/ais/cam_sensor_module/cam_eeprom/cam_eeprom_core.c +++ b/drivers/media/platform/msm/ais/cam_sensor_module/cam_eeprom/cam_eeprom_core.c @@ -1,4 +1,5 @@ /* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -650,6 +651,13 @@ static int32_t cam_eeprom_init_pkt_parser(struct cam_eeprom_ctrl_t *e_ctrl, rc = -EINVAL; goto rel_cmd_buf; } + if ((num_map + 1) >= + (MSM_EEPROM_MAX_MEM_MAP_CNT * + MSM_EEPROM_MEMORY_MAP_MAX_SIZE)) { + CAM_ERR(CAM_EEPROM, "OOB error"); + rc = -EINVAL; + goto rel_cmd_buf; + } /* Configure the following map slave address */ map[num_map + 1].saddr = i2c_info->slave_addr; rc = cam_eeprom_update_slaveInfo(e_ctrl, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 8b263110f25b..9c9d4ff36879 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -6764,7 +6764,7 @@ struct msm_vidc_buffer *msm_comm_get_vidc_buffer(struct msm_vidc_inst *inst, while (planes) msm_smem_put_dma_buf((struct dma_buf *)dma_planes[--planes]); - return rc ? ERR_PTR(rc) : mbuf; + return rc ? ((rc == -EEXIST && !inst->batch.enable) ? ERR_PTR(rc):mbuf) : mbuf; } void msm_comm_put_vidc_buffer(struct msm_vidc_inst *inst, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_platform.c b/drivers/media/platform/msm/vidc/msm_vidc_platform.c index 02ffa85a77c1..ae49e6460adc 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_platform.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c @@ -926,6 +926,8 @@ static struct msm_vidc_platform_data sdmshrike_data = { .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, .efuse_data = NULL, .efuse_data_length = 0, + .heic_image_capability = &default_heic_image_capability, + .hevc_image_capability = &default_hevc_image_capability, .sku_version = 0, .vpu_ver = VPU_VERSION_5, }; diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index ce35eca4fc92..224da2fe2413 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -75,3 +75,5 @@ config NTAG_NQ This enables NTAG driver for NTx based devices. NTAG is NFC tags that combine passive NFC interface with contact i2c interface. This is for i2c connected version. + +source "drivers/nfc/max32560/Kconfig" diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index d8d45190599e..4e1db4e0cd1a 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -17,5 +17,6 @@ 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_NFC_MAX32560) += max32560/ obj-$(CONFIG_NFC_NQ) += nq-nci.o obj-$(CONFIG_NTAG_NQ) += nq-ntag.o diff --git a/drivers/nfc/max32560/Kconfig b/drivers/nfc/max32560/Kconfig new file mode 100644 index 000000000000..4a101a123f9d --- /dev/null +++ b/drivers/nfc/max32560/Kconfig @@ -0,0 +1,11 @@ +# +# MAX32560 NFC driver configuration +# +config NFC_MAX32560 + tristate "MAX32560 NFC SPI driver" + help + This module adds support for the MAX32560 NFC controller + spi interface. + + If unsure, say N. + diff --git a/drivers/nfc/max32560/Makefile b/drivers/nfc/max32560/Makefile new file mode 100644 index 000000000000..67e1234c2408 --- /dev/null +++ b/drivers/nfc/max32560/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the MAXIM mx32560 NFC driver. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_NFC_MAX32560) += max32560.o diff --git a/drivers/nfc/max32560/max32560.c b/drivers/nfc/max32560/max32560.c new file mode 100644 index 000000000000..97c544df4895 --- /dev/null +++ b/drivers/nfc/max32560/max32560.c @@ -0,0 +1,835 @@ +/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif + +#include "max32560.h" + +static struct maxim_dev *maxim_dev; + +MODULE_DEVICE_TABLE(of, msm_match_table); + +static void maxim_disable_irq(struct maxim_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->irq_enabled_lock, flags); + if (dev->irq_enabled) { + disable_irq_nosync(dev->client->irq); + dev->irq_enabled = false; + NFC_LOGD(&dev->client->dev, "%s\n", __func__); + } + spin_unlock_irqrestore(&dev->irq_enabled_lock, flags); +} + +static void maxim_enable_irq(struct maxim_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->irq_enabled_lock, flags); + if (!dev->irq_enabled) { + dev->irq_enabled = true; + enable_irq(dev->client->irq); + NFC_LOGD(&dev->client->dev, "%s\n", __func__); + } + spin_unlock_irqrestore(&dev->irq_enabled_lock, flags); +} + +static int32_t nfc_spi_write(struct maxim_dev *dev, + uint8_t *buf_write, uint16_t len) +{ + int32_t ret = 0; + struct spi_message m; + struct spi_transfer t = { + .len = len, + }; + + /* + * Wait for the end of the general response. Check 4 times in a + * row to avoid continuous responses. + */ + NFC_LOGD(&dev->client->dev, "%s ---> [%02X %02X %02X]\n", + __func__, + buf_write[0], buf_write[1], buf_write[2]); + mutex_lock(&dev->spi_mutex); + NFC_LOGD(&dev->client->dev, "nfc_spi_write_mutex ---> lock\n"); + memset(maxim_dev->send_xbuf, 0xFF, len+3); + memset(maxim_dev->send_rbuf, 0, len+3); + + memcpy(maxim_dev->send_xbuf, buf_write, len); + + t.len = len+3; + t.bits_per_word = 8; + t.speed_hz = maxim_dev->client->max_speed_hz; + t.tx_buf = maxim_dev->send_xbuf; + t.rx_buf = maxim_dev->send_rbuf; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + spi_sync(maxim_dev->client, &m); + mutex_unlock(&maxim_dev->spi_mutex); + NFC_LOGD(&dev->client->dev, "nfc_spi_write_mutex ---> lock end\n"); + + return ret; +} + +static bool nfc_rsp_error_check(uint8_t *rbuf) +{ + if (rbuf[2] != 0x00) { + if (rbuf[0] == 0x40) { + if (rbuf[1] >= 0x00 && rbuf[1] <= 0x05) + return true; + return false; + } else if (rbuf[0] == 0x41) { + if ((rbuf[1] >= 0x00 && rbuf[1] <= 0x06) || + (rbuf[1] == 0x08) || (rbuf[1] == 0x0B)) + return true; + return false; + } else if (rbuf[0] == 0x60) { + if ((rbuf[1] == 0x00) || (rbuf[1] == 0x06) || + (rbuf[1] == 0x07) || (rbuf[1] == 0x08)) + return true; + return false; + } else if (rbuf[0] == 0x61) { + if ((rbuf[1] == 0x02) || (rbuf[1] == 0x03) || + (rbuf[1] >= 0x05 && rbuf[1] <= 0x0A)) + return true; + return false; + } else if (rbuf[0] == 0x62) { + if (rbuf[1] == 0x01) + return true; + return false; + } else if (rbuf[0] == 0x00) { + if (rbuf[1] == 0x00) + return true; + return false; + } else + return false; + } else + return false; +} + +static int32_t nfc_spi_read(struct maxim_dev *dev, + uint8_t *buf_read, + uint16_t len) +{ + int32_t ret = 0; + + struct spi_message m; + struct spi_transfer t = { + .len = len, + }; + + NFC_LOGD(&dev->client->dev, "%s <--- wait lock count:%d\n", + __func__, len); + mutex_lock(&dev->spi_mutex); + NFC_LOGD(&dev->client->dev, "%s <--- lock\n", __func__); + + if (dev->read_check == false) + memset(dev->recv_xbuf, 0xFF, len); + else + memset(dev->recv_xbuf, 0xFE, len); + + memset(dev->recv_rbuf, 0, len); + + t.bits_per_word = 8; + t.speed_hz = dev->client->max_speed_hz; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + t.tx_buf = dev->recv_xbuf; + t.rx_buf = dev->recv_rbuf; + t.len = len; + + ret = spi_sync(dev->client, &m); + NFC_LOGD(&dev->client->dev, + "%s <--- [%02X %02X %02X]\n", + __func__, + dev->recv_rbuf[0], + dev->recv_rbuf[1], + dev->recv_rbuf[2]); + memcpy(buf_read, dev->recv_rbuf, len); + + if (dev->rsp_data == false) { + /* header error check */ + if (nfc_rsp_error_check(dev->recv_rbuf)) { + NFC_LOGD(&dev->client->dev, + "%s <--- set rsp_data = true\n", + __func__); + dev->rsp_data = true; + goto success; + } else + goto header_error; + } else { + /* The response is complete. */ + NFC_LOGD(&dev->client->dev, + "%s <--- set rsp_data = false\n", + __func__); + dev->rsp_data = false; + } +success: + mutex_unlock(&dev->spi_mutex); + NFC_LOGD(&dev->client->dev, "%s <--- lock end\n", __func__); + return ret; + +header_error: + mutex_unlock(&dev->spi_mutex); + dev->read_check = !dev->read_check; + ret = -1; + NFC_LOGD(&dev->client->dev, + "%s <--- header error read_check=%d\n", __func__, + dev->read_check); + return ret; +} + +static irqreturn_t maxim_dev_irq_handler(int irq, void *dev_id) +{ + NFC_LOGD(&maxim_dev->client->dev, "%s : --- enter\n", __func__); + + if (device_may_wakeup(&maxim_dev->client->dev)) + pm_wakeup_event(&maxim_dev->client->dev, WAKEUP_SRC_TIMEOUT); + + maxim_disable_irq(maxim_dev); + wake_up(&maxim_dev->read_wq); + NFC_LOGD(&maxim_dev->client->dev, "%s : --- exit\n", __func__); + + return IRQ_HANDLED; +} + +static int nfc_waiting_to_read(struct maxim_dev *dev) +{ + int ret = 0; + + while (1) { + if (!dev->irq_enabled) { + dev->irq_enabled = true; + wake_up(&dev->write_wq); + NFC_LOGD(&dev->client->dev, + "%s <--- wake_up write_wq and set irq_enabled=%d\n", + __func__, + dev->irq_enabled); + enable_irq(dev->client->irq); + } + if (!gpio_get_value(dev->irq_gpio)) { + NFC_LOGD(&dev->client->dev, + "%s <--- Start : wait_event_interruptible irq_enabled=%d\n", + __func__, + dev->irq_enabled); + ret = wait_event_interruptible(dev->read_wq, + !dev->irq_enabled); + } + if (ret) + return ret; + + maxim_disable_irq(dev); + + if (gpio_get_value(dev->irq_gpio)) + break; + + } + return ret; +} + +static ssize_t nfc_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + + unsigned char *tmp = NULL; + int ret; + int irq_gpio_val = 0; + + NFC_LOGD(&maxim_dev->client->dev, "%s <--- enter\n", __func__); + + if (!maxim_dev) { + ret = -ENODEV; + goto out; + } + + if (count > maxim_dev->kbuflen) + count = maxim_dev->kbuflen; + + mutex_lock(&maxim_dev->read_mutex); + +reload: + ret = 0; + if (maxim_dev->rsp_data == false) { + /* wait for maxim irq reset */ + NFC_LOGD(&maxim_dev->client->dev, + "%s <--- wait for next\n", + __func__); + msleep(20); + } + + if (maxim_dev->rsp_data == false) { + irq_gpio_val = gpio_get_value(maxim_dev->irq_gpio); + if (irq_gpio_val == 0) { + ret = nfc_waiting_to_read(maxim_dev); + if (ret) + goto err; + } + } + NFC_LOGD(&maxim_dev->client->dev, + "%s <--- End : wait_event_interruptible\n", __func__); + tmp = maxim_dev->recv_rbuf; + memset(tmp, 0x00, count); + + ret = 0; + + ret = nfc_spi_read(maxim_dev, tmp, count); + + if (ret == -1) { + NFC_LOGD(&maxim_dev->client->dev, + "%s <--- read error goto reload\n", __func__); + goto reload; + } + + ret = count; + + if (copy_to_user(buf, tmp, ret)) { + dev_warn(&maxim_dev->client->dev, + "%s :failed to copy to user space\n", __func__); + ret = -EFAULT; + goto err; + } + +err: + mutex_unlock(&maxim_dev->read_mutex); +out: + NFC_LOGD(&maxim_dev->client->dev, "%s <--- exit\n", __func__); + return ret; +} + +static ssize_t nfc_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + int ret = 0; + char *tmp = NULL; + + NFC_LOGD(&maxim_dev->client->dev, "%s ---> enter\n", __func__); + + if (!maxim_dev) { + ret = -ENODEV; + goto out; + } + + if (count > maxim_dev->kbuflen) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: out of memory\n", + __func__); + ret = -ENOMEM; + goto out; + } + + tmp = memdup_user(buf, count); + + if (maxim_dev->irq_enabled == 0 || + gpio_get_value(maxim_dev->irq_gpio)) { + NFC_LOGD(&maxim_dev->client->dev, + "%s --->wait_event_interruptible start irq_enabled=%d\n", + __func__, + maxim_dev->irq_enabled); + ret = wait_event_interruptible(maxim_dev->write_wq, + maxim_dev->irq_enabled); + + } + NFC_LOGD(&maxim_dev->client->dev, + "%s ---> wait_event_interruptible end\n", __func__); + + ret = count; + + nfc_spi_write(maxim_dev, tmp, count); + kfree(tmp); +out: + NFC_LOGD(&maxim_dev->client->dev, "%s <--- exit\n", __func__); + + return ret; +} + +static int nfc_open(struct inode *inode, struct file *filp) +{ + maxim_dev->rsp_data = false; + maxim_dev->read_check = false; + + mutex_lock(&maxim_dev->dev_ref_mutex); + + if (maxim_dev->dev_ref_count == 0) + maxim_enable_irq(maxim_dev); + + + maxim_dev->dev_ref_count = maxim_dev->dev_ref_count + 1; + + mutex_unlock(&maxim_dev->dev_ref_mutex); + + dev_dbg(&maxim_dev->client->dev, + "%s: %d,%d\n", __func__, imajor(inode), iminor(inode)); + return 0; +} + +static int nfc_close(struct inode *inode, struct file *filp) +{ + mutex_lock(&maxim_dev->dev_ref_mutex); + + if (maxim_dev->dev_ref_count == 1) + maxim_disable_irq(maxim_dev); + + if (maxim_dev->dev_ref_count > 0) + maxim_dev->dev_ref_count = maxim_dev->dev_ref_count - 1; + + mutex_unlock(&maxim_dev->dev_ref_mutex); + + filp->private_data = NULL; + + return 0; +} + +#ifdef CONFIG_COMPAT +static long nfc_compat_ioctl(struct file *pfile, unsigned int cmd, + unsigned long arg) +{ + /* RFU */ + return 0; +} +#endif + + +static long nfc_ioctl(struct file *pfile, unsigned int cmd, + unsigned long arg) +{ + /* RFU */ + return 0; +} + +static const struct file_operations nfc_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = nfc_read, + .write = nfc_write, + .open = nfc_open, + .release = nfc_close, + .unlocked_ioctl = nfc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = nfc_compat_ioctl +#endif +}; + +int32_t print_command(uint8_t *tmp, uint16_t len, uint16_t rw) +{ + char dmesgline[300] = {0}; + int n; + + for (n = 0 ; n < len ; n++) + snprintf(dmesgline, sizeof(dmesgline), "%s %02X", + dmesgline, tmp[n]); + if (rw == 1) { + NFC_LOGE(&maxim_dev->client->dev, + "Read TX command=[%s] count=[%d]\n", + dmesgline, len); + } else if (rw == 2) { + NFC_LOGE(&maxim_dev->client->dev, + "Read RX command=[%s] count=[%d]\n", + dmesgline, len); + } else if (rw == 3) { + NFC_LOGE(&maxim_dev->client->dev, + "Write TX command=[%s] count=[%d]\n", + dmesgline, len); + } else if (rw == 4) { + NFC_LOGE(&maxim_dev->client->dev, + "Write RX command=[%s] count=[%d]\n", + dmesgline, len); + } + + return 0; +} + +static ssize_t maxim_dev_reset(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + gpio_set_value(maxim_dev->enable_gpio, 0); + msleep(100); + gpio_set_value(maxim_dev->enable_gpio, 1); + return count; +} + +static DEVICE_ATTR(maxim_reset, 0664, NULL, maxim_dev_reset); + +static int nfc_parse_dt(struct device *dev) +{ + int r = 0; + struct device_node *np = dev->of_node; + + maxim_dev->irq_gpio = of_get_named_gpio(np, "maxim,irq-gpio", 0); + if ((!gpio_is_valid(maxim_dev->irq_gpio))) + return -EINVAL; + + maxim_dev->enable_gpio = of_get_named_gpio(np, "maxim,en-gpio", 0); + if ((!gpio_is_valid(maxim_dev->enable_gpio))) + return -EINVAL; + + maxim_dev->reset_gpio = of_get_named_gpio(np, "maxim,reset-gpio", 0); + if ((!gpio_is_valid(maxim_dev->reset_gpio))) + return -EINVAL; + + return r; +} + +static int maxim_probe(struct spi_device *client) +{ + int r = 0; + int irqn = 0; + int irq; + int cs; + int cpha, cpol, cs_high; + u32 max_speed; + + dev_err(&client->dev, "max32560 %s: enter\n", __func__); + + maxim_dev = kzalloc(sizeof(*maxim_dev), GFP_KERNEL); + + if (maxim_dev == NULL) + return -ENOMEM; + + maxim_dev->client = client; + + /* init SPI configuration */ + maxim_dev->send_xbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL); + if (maxim_dev->send_xbuf == NULL) { + r = -ENOMEM; + goto err_free_dev; + } + + maxim_dev->send_rbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL); + if (maxim_dev->send_rbuf == NULL) { + r = -ENOMEM; + goto err_free_send_xbuf; + } + + maxim_dev->recv_xbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL); + if (maxim_dev->recv_xbuf == NULL) { + r = -ENOMEM; + goto err_free_send_rbuf; + } + + maxim_dev->recv_rbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL); + if (maxim_dev->recv_rbuf == NULL) { + r = -ENOMEM; + goto err_free_recv_xbuf; + } + + maxim_dev->kbuflen = MAX_BUFFER_SIZE; + maxim_dev->client->bits_per_word = 8; + + /* ---Check SPI configuration--- */ + irq = maxim_dev->client->irq; + cs = maxim_dev->client->chip_select; + cpha = (maxim_dev->client->mode & SPI_CPHA) ? 1:0; + cpol = (maxim_dev->client->mode & SPI_CPOL) ? 1:0; + cs_high = (maxim_dev->client->mode & SPI_CS_HIGH) ? 1:0; + max_speed = maxim_dev->client->max_speed_hz; + NFC_LOGE(&maxim_dev->client->dev, + "irq [%d] cs [%x] CPHA [%x] CPOL [%x] CS_HIGH [%x] Max Speed [%d]\n", + irq, cs, cpha, cpol, cs_high, max_speed); + + /* init GPIO */ + r = nfc_parse_dt(&maxim_dev->client->dev); + if (r) + goto err_spi_setup; + + if (gpio_is_valid(maxim_dev->irq_gpio)) { + r = gpio_request(maxim_dev->irq_gpio, "nfc_irq_gpio"); + if (r) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: unable to request nfc irq gpio [%d]\n", + __func__, maxim_dev->irq_gpio); + goto err_free_recv_rbuf; + } + r = gpio_direction_input(maxim_dev->irq_gpio); + if (r) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: unable to set direction for nfc irq gpio [%d]\n", + __func__, + maxim_dev->irq_gpio); + goto err_irq_gpio; + } + irqn = gpio_to_irq(maxim_dev->irq_gpio); + if (irqn < 0) { + r = irqn; + goto err_irq_gpio; + } + maxim_dev->client->irq = irqn; + } else { + NFC_LOGE(&maxim_dev->client->dev, "%s: irq gpio not provided\n", + __func__); + goto err_free_recv_rbuf; + } + + /* Reset pin is low trigger. */ + if (gpio_is_valid(maxim_dev->reset_gpio)) { + r = gpio_request(maxim_dev->reset_gpio, "nfc_reset_gpio"); + if (r) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: unable to request nfc reset gpio [%d]\n", + __func__, maxim_dev->reset_gpio); + goto err_irq_gpio; + + } + + r = gpio_direction_output(maxim_dev->reset_gpio, 1); + if (r) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: unable to set direction for nfc reset gpio [%d]\n", + __func__, + maxim_dev->reset_gpio); + goto err_enable_gpio; + } + } else { + NFC_LOGE(&maxim_dev->client->dev, + "%s: reset gpio not provided\n", + __func__); + goto err_irq_gpio; + } + + if (gpio_is_valid(maxim_dev->enable_gpio)) { + r = gpio_request(maxim_dev->enable_gpio, "nfc_enable_gpio"); + if (r) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: unable to request nfc enable gpio [%d]\n", + __func__, maxim_dev->enable_gpio); + goto err_enable_gpio; + } + /* Enable 0 = off / 1 = on */ + r = gpio_direction_output(maxim_dev->enable_gpio, 1); + if (r) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: unable to set direction for nfc enable gpio [%d]\n", + __func__, maxim_dev->enable_gpio); + goto err_reset_gpio; + } + } else { + NFC_LOGE(&maxim_dev->client->dev, + "%s: enable gpio not provided\n", + __func__); + goto err_enable_gpio; + } + + /* Power */ + if (gpio_is_valid(maxim_dev->enable_gpio)) + gpio_set_value(maxim_dev->enable_gpio, 1); + + /* init mutex and queues */ + init_waitqueue_head(&maxim_dev->read_wq); + init_waitqueue_head(&maxim_dev->write_wq); + mutex_init(&maxim_dev->read_mutex); + mutex_init(&maxim_dev->spi_mutex); + mutex_init(&maxim_dev->dev_ref_mutex); + + spin_lock_init(&maxim_dev->irq_enabled_lock); + + r = alloc_chrdev_region(&maxim_dev->devno, 0, DEV_COUNT, DEVICE_NAME); + if (r < 0) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: failed to alloc chrdev region\n", __func__); + goto err_char_dev_register; + } + + maxim_dev->maxim_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(maxim_dev->maxim_class)) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: failed to register device class\n", __func__); + goto err_class_create; + } + + cdev_init(&maxim_dev->c_dev, &nfc_dev_fops); + r = cdev_add(&maxim_dev->c_dev, maxim_dev->devno, DEV_COUNT); + if (r < 0) { + NFC_LOGE(&maxim_dev->client->dev, "%s: failed to add cdev\n", + __func__); + goto err_cdev_add; + } + + maxim_dev->maxim_device = device_create(maxim_dev->maxim_class, NULL, + maxim_dev->devno, maxim_dev, DEVICE_NAME); + if (IS_ERR(maxim_dev->maxim_device)) { + NFC_LOGE(&client->dev, + "%s: failed to create the device\n", __func__); + goto err_device_create; + } + + /* NFC_INT IRQ */ + maxim_dev->irq_enabled = true; + r = devm_request_irq(&maxim_dev->client->dev, + maxim_dev->client->irq, + maxim_dev_irq_handler, + IRQF_TRIGGER_HIGH, + "MAXIM-nfc", + maxim_dev); + + if (r) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: request_irq failed\n", __func__); + goto err_request_irq_failed; + } + + maxim_disable_irq(maxim_dev); + + spi_set_drvdata(client, maxim_dev); + + NFC_LOGI(&maxim_dev->client->dev, + "max32560 %s: probing exited successfully\n", + __func__); + + /* Firmware download + * /sys/bus/spi/drivers/nq-nci/spi0.0/maxim_reset + */ + device_create_file(&maxim_dev->client->dev, &dev_attr_maxim_reset); + + return 0; + +err_request_irq_failed: + device_destroy(maxim_dev->maxim_class, maxim_dev->devno); +err_device_create: + cdev_del(&maxim_dev->c_dev); +err_cdev_add: + class_destroy(maxim_dev->maxim_class); +err_class_create: + unregister_chrdev_region(maxim_dev->devno, DEV_COUNT); +err_char_dev_register: + mutex_destroy(&maxim_dev->read_mutex); + mutex_destroy(&maxim_dev->spi_mutex); +err_enable_gpio: + gpio_free(maxim_dev->enable_gpio); +err_reset_gpio: + gpio_free(maxim_dev->reset_gpio); +err_irq_gpio: + gpio_free(maxim_dev->irq_gpio); +err_spi_setup: +err_free_recv_rbuf: + kfree(maxim_dev->recv_rbuf); + maxim_dev->recv_rbuf = NULL; +err_free_recv_xbuf: + kfree(maxim_dev->send_xbuf); + maxim_dev->send_xbuf = NULL; +err_free_send_rbuf: + kfree(maxim_dev->send_rbuf); + maxim_dev->send_rbuf = NULL; +err_free_send_xbuf: + kfree(maxim_dev->send_xbuf); + maxim_dev->send_xbuf = NULL; +err_free_dev: + kfree(maxim_dev); + maxim_dev = NULL; + + return r; +} + +static int maxim_remove(struct spi_device *client) +{ + int ret = 0; + + if (!maxim_dev) { + NFC_LOGE(&maxim_dev->client->dev, + "%s: device doesn't exist anymore\n", __func__); + ret = -ENODEV; + goto err; + } + + cdev_del(&maxim_dev->c_dev); + device_destroy(maxim_dev->maxim_class, maxim_dev->devno); + class_destroy(maxim_dev->maxim_class); + unregister_chrdev_region(maxim_dev->devno, DEV_COUNT); + + mutex_destroy(&maxim_dev->read_mutex); + mutex_destroy(&maxim_dev->dev_ref_mutex); + mutex_destroy(&maxim_dev->spi_mutex); + + gpio_free(maxim_dev->irq_gpio); + gpio_free(maxim_dev->enable_gpio); + gpio_free(maxim_dev->reset_gpio); + + kfree(maxim_dev->send_xbuf); + maxim_dev->send_xbuf = NULL; + kfree(maxim_dev->send_rbuf); + maxim_dev->send_rbuf = NULL; + kfree(maxim_dev->recv_xbuf); + maxim_dev->recv_xbuf = NULL; + kfree(maxim_dev->recv_rbuf); + maxim_dev->recv_rbuf = NULL; + + kfree(maxim_dev); + maxim_dev = NULL; +err: + return ret; +} + +static int maxim_suspend(struct device *device) +{ + gpio_set_value(maxim_dev->enable_gpio, 0); + return 0; +} + +static int maxim_resume(struct device *device) +{ + gpio_set_value(maxim_dev->enable_gpio, 1); + return 0; +} + +static const struct dev_pm_ops nfc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(maxim_suspend, maxim_resume) +}; + +static const struct of_device_id msm_match_table[] = { + {.compatible = "maxim,max32560"}, + {} +}; + +static struct spi_driver maxim = { + .probe = maxim_probe, + .remove = maxim_remove, + .driver = { + .owner = THIS_MODULE, + .name = "nq-nci", + .of_match_table = msm_match_table, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .pm = &nfc_pm_ops, + }, +}; + +static int __init maxim_dev_init(void) +{ + return spi_register_driver(&maxim); +} +module_init(maxim_dev_init); + +static void __exit maxim_dev_exit(void) +{ + spi_unregister_driver(&maxim); +} +module_exit(maxim_dev_exit); + +MODULE_DESCRIPTION("NFC MAX32560"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/nfc/max32560/max32560.h b/drivers/nfc/max32560/max32560.h new file mode 100644 index 000000000000..68714e538a1b --- /dev/null +++ b/drivers/nfc/max32560/max32560.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 __MAXIM_32560_H +#define __MAXIM_32560_H + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define DEV_COUNT 1 +#define DEVICE_NAME "nq-nci" +#define CLASS_NAME "maxim" +#define MAX_BUFFER_SIZE (320) +#define WAKEUP_SRC_TIMEOUT (2000) + +#define NFC_LOGI(...) dev_info(__VA_ARGS__) +#define NFC_LOGE(...) dev_err(__VA_ARGS__) + +#ifdef NFC_DEBUG_LOG +#define NFC_LOGD(...) dev_err(__VA_ARGS__) +#else +#define NFC_LOGD(...) +#endif + +struct maxim_dev { + struct spi_device *client; + + uint8_t *send_xbuf; + uint8_t *send_rbuf; + + uint8_t *recv_xbuf; + uint8_t *recv_rbuf; + + size_t kbuflen; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct mutex read_mutex; + struct mutex dev_ref_mutex; + struct mutex spi_mutex; + + /* NFC GPIO variables */ + unsigned int irq_gpio; + unsigned int enable_gpio; + unsigned int reset_gpio; + + dev_t devno; + struct class *maxim_class; + struct device *maxim_device; + struct cdev c_dev; + + /* NFC_IRQ state */ + bool irq_enabled; + + /* NFC_IRQ wake-up state */ + bool irq_wake_up; + spinlock_t irq_enabled_lock; + bool rsp_data; + //bool write_cmd; + + /* NFC_IRQ Count */ + unsigned int dev_ref_count; + + /* ERROR Check */ + bool read_check; + +}; + +int32_t print_command(uint8_t *tmp, uint16_t len, uint16_t rw); + +#endif +