-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEZH8oZUiU471FcZm+ONu9yGCSaT4FAmS38qMACgkQONu9yGCS aT56yQ//ZuDuw8Ev3HISVgZhE9FpuXC1RSYXiMCAvwA9rH3KnJ4wKVPEhEWLy9P4 jdJaatSLbLOvA7ME7JnwZxz2qahjBxo1tpx6u2S3zrzz4UlAPNLwCxTxxp4X07VI 3fBNvsmucqFSayCrA8t9xgkaJizuCvHZm7eSoyVIigPwbB5igc2b+bNSRcx1Zo+j SHl4Y4nGK8a47XU9RSlDLVKow0/6rrQLHQ9DLpxACArRHw3h451vD0DMcgOuU/Uv 6qq9u3COcdVw3oc5VENu9XklPmvQkxo3RaCUHyRadVstuc0H/BBUDvEhPn5PcVOV EdBWlTjmhsQo0aUziK4kotLNeX1VRgKa+rrIUBJn68OHv1SRRPZU/eJ8hkL81dCi FDPzXDOszixO7pPv1jj7O9kNcwKPuiHPmdaNPCY6jviOHhZnAEub44DpQamxWvU/ kb5MZRRY72wt9iWeI3kscCCSbf6eyjlmDMoYIeLuYn10n7gIDU80eUOBl9bqEsz/ X+OUxaY+XuKbCoucpNmSHHLmynJ5D0CXhl/5qnlgMoSo4UJ5BUIMj2e3ZqsKLfrR e/09MCRX79y9J+TxUunnQZfq5vBlH1tRsvUyhIfYfW4AaC9BrkOL2XZviQldKY6x FUmsxh62O3iGRtLOWDKQA5MwoJuD54qVcHr1iidWkO2G8T3ctCc= =kyUh -----END PGP SIGNATURE----- Merge 6.1.39 into android14-6.1-lts Changes in 6.1.39 drm: use mgr->dev in drm_dbg_kms in drm_dp_add_payload_part2 fs: pipe: reveal missing function protoypes block: Fix the type of the second bdev_op_is_zoned_write() argument erofs: clean up cached I/O strategies erofs: avoid tagged pointers to mark sync decompression erofs: remove tagged pointer helpers erofs: move zdata.h into zdata.c erofs: kill hooked chains to avoid loops on deduplicated compressed images x86/resctrl: Only show tasks' pid in current pid namespace blk-iocost: use spin_lock_irqsave in adjust_inuse_and_calc_cost x86/sev: Fix calculation of end address based on number of pages virt: sevguest: Add CONFIG_CRYPTO dependency blk-mq: fix potential io hang by wrong 'wake_batch' lockd: drop inappropriate svc_get() from locked_get() nvme-auth: rename __nvme_auth_[reset|free] to nvme_auth[reset|free]_dhchap nvme-auth: rename authentication work elements nvme-auth: remove symbol export from nvme_auth_reset nvme-auth: no need to reset chap contexts on re-authentication nvme-core: fix memory leak in dhchap_secret_store nvme-core: fix memory leak in dhchap_ctrl_secret nvme-auth: don't ignore key generation failures when initializing ctrl keys nvme-core: add missing fault-injection cleanup nvme-core: fix dev_pm_qos memleak md/raid10: check slab-out-of-bounds in md_bitmap_get_counter md/raid10: fix overflow of md/safe_mode_delay md/raid10: fix wrong setting of max_corr_read_errors md/raid10: fix null-ptr-deref of mreplace in raid10_sync_request md/raid10: fix io loss while replacement replace rdev md/raid1-10: factor out a helper to add bio to plug md/raid1-10: factor out a helper to submit normal write md/raid1-10: submit write io directly if bitmap is not enabled block: fix blktrace debugfs entries leakage irqchip/stm32-exti: Fix warning on initialized field overwritten irqchip/jcore-aic: Fix missing allocation of IRQ descriptors svcrdma: Prevent page release when nothing was received erofs: simplify iloc() erofs: fix compact 4B support for 16k block size posix-timers: Prevent RT livelock in itimer_delete() tick/rcu: Fix bogus ratelimit condition tracing/timer: Add missing hrtimer modes to decode_hrtimer_mode(). clocksource/drivers/cadence-ttc: Fix memory leak in ttc_timer_probe PM: domains: fix integer overflow issues in genpd_parse_state() perf/arm-cmn: Fix DTC reset x86/mm: Allow guest.enc_status_change_prepare() to fail x86/tdx: Fix race between set_memory_encrypted() and load_unaligned_zeropad() drivers/perf: hisi: Don't migrate perf to the CPU going to teardown powercap: RAPL: Fix CONFIG_IOSF_MBI dependency PM: domains: Move the verification of in-params from genpd_add_device() ARM: 9303/1: kprobes: avoid missing-declaration warnings cpufreq: intel_pstate: Fix energy_performance_preference for passive thermal/drivers/sun8i: Fix some error handling paths in sun8i_ths_probe() rcu: Make rcu_cpu_starting() rely on interrupts being disabled rcu-tasks: Stop rcu_tasks_invoke_cbs() from using never-onlined CPUs rcutorture: Correct name of use_softirq module parameter rcuscale: Move shutdown from wait_event() to wait_event_idle() rcu/rcuscale: Move rcu_scale_*() after kfree_scale_cleanup() rcu/rcuscale: Stop kfree_scale_thread thread(s) after unloading rcuscale kselftest: vDSO: Fix accumulation of uninitialized ret when CLOCK_REALTIME is undefined perf/ibs: Fix interface via core pmu events x86/mm: Fix __swp_entry_to_pte() for Xen PV guests locking/atomic: arm: fix sync ops evm: Complete description of evm_inode_setattr() evm: Fix build warnings ima: Fix build warnings pstore/ram: Add check for kstrdup igc: Enable and fix RX hash usage by netstack wifi: ath9k: fix AR9003 mac hardware hang check register offset calculation wifi: ath9k: avoid referencing uninit memory in ath9k_wmi_ctrl_rx libbpf: btf_dump_type_data_check_overflow needs to consider BTF_MEMBER_BITFIELD_SIZE samples/bpf: Fix buffer overflow in tcp_basertt spi: spi-geni-qcom: Correct CS_TOGGLE bit in SPI_TRANS_CFG wifi: wilc1000: fix for absent RSN capabilities WFA testcase wifi: mwifiex: Fix the size of a memory allocation in mwifiex_ret_802_11_scan() sctp: add bpf_bypass_getsockopt proto callback libbpf: fix offsetof() and container_of() to work with CO-RE bpf: Don't EFAULT for {g,s}setsockopt with wrong optlen spi: dw: Round of n_bytes to power of 2 nfc: llcp: fix possible use of uninitialized variable in nfc_llcp_send_connect() bpftool: JIT limited misreported as negative value on aarch64 bpf: Remove bpf trampoline selector bpf: Fix memleak due to fentry attach failure selftests/bpf: Do not use sign-file as testcase regulator: core: Fix more error checking for debugfs_create_dir() regulator: core: Streamline debugfs operations wifi: orinoco: Fix an error handling path in spectrum_cs_probe() wifi: orinoco: Fix an error handling path in orinoco_cs_probe() wifi: atmel: Fix an error handling path in atmel_probe() wifi: wl3501_cs: Fix an error handling path in wl3501_probe() wifi: ray_cs: Fix an error handling path in ray_probe() wifi: ath9k: don't allow to overwrite ENDPOINT0 attributes samples/bpf: xdp1 and xdp2 reduce XDPBUFSIZE to 60 wifi: ath10k: Trigger STA disconnect after reconfig complete on hardware restart wifi: mac80211: recalc min chandef for new STA links selftests/bpf: Fix check_mtu using wrong variable type wifi: rsi: Do not configure WoWlan in shutdown hook if not enabled wifi: rsi: Do not set MMC_PM_KEEP_POWER in shutdown ice: handle extts in the miscellaneous interrupt thread selftests: cgroup: fix unexpected failure on test_memcg_low watchdog/perf: define dummy watchdog_update_hrtimer_threshold() on correct config watchdog/perf: more properly prevent false positives with turbo modes kexec: fix a memory leak in crash_shrink_memory() mmc: mediatek: Avoid ugly error message when SDIO wakeup IRQ isn't used memstick r592: make memstick_debug_get_tpc_name() static wifi: ath9k: Fix possible stall on ath9k_txq_list_has_key() wifi: mac80211: Fix permissions for valid_links debugfs entry rtnetlink: extend RTEXT_FILTER_SKIP_STATS to IFLA_VF_INFO wifi: ath11k: Add missing check for ioremap wifi: iwlwifi: pull from TXQs with softirqs disabled wifi: iwlwifi: pcie: fix NULL pointer dereference in iwl_pcie_irq_rx_msix_handler() wifi: mac80211: Remove "Missing iftype sband data/EHT cap" spam wifi: cfg80211: rewrite merging of inherited elements wifi: cfg80211: drop incorrect nontransmitted BSS update code wifi: cfg80211: fix regulatory disconnect with OCB/NAN wifi: cfg80211/mac80211: Fix ML element common size calculation wifi: ieee80211: Fix the common size calculation for reconfiguration ML mmc: Add MMC_QUIRK_BROKEN_SD_CACHE for Kingston Canvas Go Plus from 11/2019 wifi: iwlwifi: mvm: indicate HW decrypt for beacon protection wifi: ath9k: convert msecs to jiffies where needed bpf: Factor out socket lookup functions for the TC hookpoint. bpf: Call __bpf_sk_lookup()/__bpf_skc_lookup() directly via TC hookpoint bpf: Fix bpf socket lookup from tc/xdp to respect socket VRF bindings can: length: fix bitstuffing count can: kvaser_pciefd: Add function to set skb hwtstamps can: kvaser_pciefd: Set hardware timestamp on transmitted packets net: stmmac: fix double serdes powerdown netlink: fix potential deadlock in netlink_set_err() netlink: do not hard code device address lenth in fdb dumps bonding: do not assume skb mac_header is set selftests: rtnetlink: remove netdevsim device after ipsec offload test gtp: Fix use-after-free in __gtp_encap_destroy(). net: axienet: Move reset before 64-bit DMA detection ocfs2: Fix use of slab data with sendpage sfc: fix crash when reading stats while NIC is resetting net: nfc: Fix use-after-free caused by nfc_llcp_find_local lib/ts_bm: reset initial match offset for every block of text netfilter: conntrack: dccp: copy entire header to stack buffer, not just basic one netfilter: nf_conntrack_sip: fix the ct_sip_parse_numerical_param() return value. ipvlan: Fix return value of ipvlan_queue_xmit() netlink: Add __sock_i_ino() for __netlink_diag_dump(). drm/amd/display: Add logging for display MALL refresh setting radeon: avoid double free in ci_dpm_init() drm/amd/display: Explicitly specify update type per plane info change drm/bridge: it6505: Move a variable assignment behind a null pointer check in receive_timing_debugfs_show() Input: drv260x - sleep between polling GO bit drm/bridge: ti-sn65dsi83: Fix enable error path drm/bridge: tc358768: always enable HS video mode drm/bridge: tc358768: fix PLL parameters computation drm/bridge: tc358768: fix PLL target frequency drm/bridge: tc358768: fix TCLK_ZEROCNT computation drm/bridge: tc358768: Add atomic_get_input_bus_fmts() implementation drm/bridge: tc358768: fix TCLK_TRAILCNT computation drm/bridge: tc358768: fix THS_ZEROCNT computation drm/bridge: tc358768: fix TXTAGOCNT computation drm/bridge: tc358768: fix THS_TRAILCNT computation drm/vram-helper: fix function names in vram helper doc ARM: dts: BCM5301X: Drop "clock-names" from the SPI node ARM: dts: meson8b: correct uart_B and uart_C clock references mm: call arch_swap_restore() from do_swap_page() clk: vc5: Use `clamp()` to restrict PLL range bootmem: remove the vmemmap pages from kmemleak in free_bootmem_page clk: vc5: Fix .driver_data content in i2c_device_id clk: vc7: Fix .driver_data content in i2c_device_id clk: rs9: Fix .driver_data content in i2c_device_id Input: adxl34x - do not hardcode interrupt trigger type drm: sun4i_tcon: use devm_clk_get_enabled in `sun4i_tcon_init_clocks` drm/panel: sharp-ls043t1le01: adjust mode settings driver: soc: xilinx: use _safe loop iterator to avoid a use after free ASoC: Intel: sof_sdw: remove SOF_SDW_TGL_HDMI for MeteorLake devices drm/vkms: isolate pixel conversion functionality drm: Add fixed-point helper to get rounded integer values drm/vkms: Fix RGB565 pixel conversion ARM: dts: stm32: Move ethernet MAC EEPROM from SoM to carrier boards bus: ti-sysc: Fix dispc quirk masking bool variables arm64: dts: microchip: sparx5: do not use PSCI on reference boards drm/bridge: tc358767: Switch to devm MIPI-DSI helpers clk: imx: scu: use _safe list iterator to avoid a use after free hwmon: (f71882fg) prevent possible division by zero RDMA/bnxt_re: Disable/kill tasklet only if it is enabled RDMA/bnxt_re: Fix to remove unnecessary return labels RDMA/bnxt_re: Use unique names while registering interrupts RDMA/bnxt_re: Remove a redundant check inside bnxt_re_update_gid RDMA/bnxt_re: Fix to remove an unnecessary log drm/msm/dsi: don't allow enabling 14nm VCO with unprogrammed rate drm/msm/disp/dpu: get timing engine status from intf status register drm/msm/dpu: Set DPU_DATA_HCTL_EN for in INTF_SC7180_MASK iommu/virtio: Detach domain on endpoint release iommu/virtio: Return size mapped for a detached domain clk: renesas: rzg2l: Fix CPG_SIPLL5_CLK1 register write ARM: dts: gta04: Move model property out of pinctrl node drm/bridge: anx7625: Convert to i2c's .probe_new() drm/bridge: anx7625: Prevent endless probe loop ARM: dts: qcom: msm8974: do not use underscore in node name (again) arm64: dts: qcom: msm8916: correct camss unit address arm64: dts: qcom: msm8916: correct MMC unit address arm64: dts: qcom: msm8994: correct SPMI unit address arm64: dts: qcom: msm8996: correct camss unit address arm64: dts: qcom: sdm630: correct camss unit address arm64: dts: qcom: sdm845: correct camss unit address arm64: dts: qcom: sm8350: Add GPI DMA compatible fallback arm64: dts: qcom: sm8350: correct DMA controller unit address arm64: dts: qcom: sdm845-polaris: add missing touchscreen child node reg arm64: dts: qcom: apq8016-sbc: Fix regulator constraints arm64: dts: qcom: apq8016-sbc: Fix 1.8V power rail on LS expansion drm/bridge: Introduce pre_enable_prev_first to alter bridge init order drm/bridge: ti-sn65dsi83: Fix enable/disable flow to meet spec drm/panel: simple: fix active size for Ampire AM-480272H3TMQW-T01H ARM: ep93xx: fix missing-prototype warnings ARM: omap2: fix missing tick_broadcast() prototype arm64: dts: qcom: pm7250b: add missing spmi-vadc include arm64: dts: qcom: apq8096: fix fixed regulator name property arm64: dts: mediatek: mt8183: Add mediatek,broken-save-restore-fw to kukui ARM: dts: stm32: Shorten the AV96 HDMI sound card name memory: brcmstb_dpfe: fix testing array offset after use ARM: dts: qcom: apq8074-dragonboard: Set DMA as remotely controlled ASoC: es8316: Increment max value for ALC Capture Target Volume control ASoC: es8316: Do not set rate constraints for unsupported MCLKs ARM: dts: meson8: correct uart_B and uart_C clock references soc/fsl/qe: fix usb.c build errors RDMA/irdma: avoid fortify-string warning in irdma_clr_wqes IB/hfi1: Fix wrong mmu_node used for user SDMA packet after invalidate RDMA/hns: Fix hns_roce_table_get return value ARM: dts: iwg20d-q7-common: Fix backlight pwm specifier arm64: dts: renesas: ulcb-kf: Remove flow control for SCIF1 drm/msm/dpu: set DSC flush bit correctly at MDP CTL flush register fbdev: omapfb: lcd_mipid: Fix an error handling path in mipid_spi_probe() arm64: dts: ti: k3-j7200: Fix physical address of pin Input: pm8941-powerkey - fix debounce on gen2+ PMICs ARM: dts: stm32: Fix audio routing on STM32MP15xx DHCOM PDK2 ARM: dts: stm32: fix i2s endpoint format property for stm32mp15xx-dkx hwmon: (gsc-hwmon) fix fan pwm temperature scaling hwmon: (pmbus/adm1275) Fix problems with temperature monitoring on ADM1272 ARM: dts: BCM5301X: fix duplex-full => full-duplex clk: Export clk_hw_forward_rate_request() drm/amd/display: Fix a test CalculatePrefetchSchedule() drm/amd/display: Fix a test dml32_rq_dlg_get_rq_reg() drm/amdkfd: Fix potential deallocation of previously deallocated memory. soc: mediatek: SVS: Fix MT8192 GPU node name drm/amd/display: Fix artifacting on eDP panels when engaging freesync video mode drm/radeon: fix possible division-by-zero errors HID: uclogic: Modular KUnit tests should not depend on KUNIT=y RDMA/rxe: Add ibdev_dbg macros for rxe RDMA/rxe: Replace pr_xxx by rxe_dbg_xxx in rxe_mw.c RDMA/rxe: Fix access checks in rxe_check_bind_mw amdgpu: validate offset_in_bo of drm_amdgpu_gem_va drm/msm/a5xx: really check for A510 in a5xx_gpu_init RDMA/bnxt_re: wraparound mbox producer index RDMA/bnxt_re: Avoid calling wake_up threads from spin_lock context clk: imx: clk-imxrt1050: fix memory leak in imxrt1050_clocks_probe clk: imx: clk-imx8mn: fix memory leak in imx8mn_clocks_probe clk: imx93: fix memory leak and missing unwind goto in imx93_clocks_probe clk: imx: clk-imx8mp: improve error handling in imx8mp_clocks_probe() arm64: dts: qcom: sdm845: Flush RSC sleep & wake votes arm64: dts: qcom: sm8250-edo: Panel framebuffer is 2.5k instead of 4k clk: bcm: rpi: Fix off by one in raspberrypi_discover_clocks() clk: clocking-wizard: Fix Oops in clk_wzrd_register_divider() clk: tegra: tegra124-emc: Fix potential memory leak ALSA: ac97: Fix possible NULL dereference in snd_ac97_mixer drm/msm/dpu: do not enable color-management if DSPPs are not available drm/msm/dpu: Fix slice_last_group_size calculation drm/msm/dsi: Use DSC slice(s) packet size to compute word count drm/msm/dsi: Flip greater-than check for slice_count and slice_per_intf drm/msm/dsi: Remove incorrect references to slice_count drm/msm/dp: Free resources after unregistering them arm64: dts: mediatek: Add cpufreq nodes for MT8192 arm64: dts: mediatek: mt8192: Fix CPUs capacity-dmips-mhz drm/amdgpu: Fix memcpy() in sienna_cichlid_append_powerplay_table function. drm/amdgpu: Fix usage of UMC fill record in RAS drm/msm/dpu: correct MERGE_3D length clk: vc5: check memory returned by kasprintf() clk: cdce925: check return value of kasprintf() clk: si5341: return error if one synth clock registration fails clk: si5341: check return value of {devm_}kasprintf() clk: si5341: free unused memory on probe failure clk: keystone: sci-clk: check return value of kasprintf() clk: ti: clkctrl: check return value of kasprintf() drivers: meson: secure-pwrc: always enable DMA domain ovl: update of dentry revalidate flags after copy up ASoC: imx-audmix: check return value of devm_kasprintf() clk: Fix memory leak in devm_clk_notifier_register() ARM: dts: lan966x: kontron-d10: fix board reset ARM: dts: lan966x: kontron-d10: fix SPI CS ASoC: amd: acp: clear pdm dma interrupt mask PCI: cadence: Fix Gen2 Link Retraining process PCI: vmd: Reset VMD config register between soft reboots scsi: qedf: Fix NULL dereference in error handling pinctrl: bcm2835: Handle gpiochip_add_pin_range() errors platform/x86: lenovo-yogabook: Fix work race on remove() platform/x86: lenovo-yogabook: Reprobe devices on remove() platform/x86: lenovo-yogabook: Set default keyboard backligh brightness on probe() PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free scsi: 3w-xxxx: Add error handling for initialization failure in tw_probe() PCI: pciehp: Cancel bringup sequence if card is not present PCI: ftpci100: Release the clock resources pinctrl: sunplus: Add check for kmalloc PCI: Add pci_clear_master() stub for non-CONFIG_PCI scsi: lpfc: Revise NPIV ELS unsol rcv cmpl logic to drop ndlp based on nlp_state perf bench: Add missing setlocale() call to allow usage of %'d style formatting pinctrl: cherryview: Return correct value if pin in push-pull mode platform/x86: think-lmi: mutex protection around multiple WMI calls platform/x86: think-lmi: Correct System password interface platform/x86: think-lmi: Correct NVME password handling pinctrl:sunplus: Add check for kmalloc pinctrl: npcm7xx: Add missing check for ioremap kcsan: Don't expect 64 bits atomic builtins from 32 bits architectures powerpc/interrupt: Don't read MSR from interrupt_exit_kernel_prepare() powerpc/signal32: Force inlining of __unsafe_save_user_regs() and save_tm_user_regs_unsafe() perf script: Fix allocation of evsel->priv related to per-event dump files platform/x86: thinkpad_acpi: Fix lkp-tests warnings for platform profiles perf dwarf-aux: Fix off-by-one in die_get_varname() platform/x86/dell/dell-rbtn: Fix resources leaking on error path perf tool x86: Consolidate is_amd check into single function perf tool x86: Fix perf_env memory leak powerpc/64s: Fix VAS mm use after free pinctrl: microchip-sgpio: check return value of devm_kasprintf() pinctrl: at91-pio4: check return value of devm_kasprintf() powerpc/powernv/sriov: perform null check on iov before dereferencing iov powerpc: simplify ppc_save_regs powerpc: update ppc_save_regs to save current r1 in pt_regs PCI: qcom: Remove PCIE20_ prefix from register definitions PCI: qcom: Sort and group registers and bitfield definitions PCI: qcom: Use lower case for hex PCI: qcom: Use DWC helpers for modifying the read-only DBI registers PCI: qcom: Disable write access to read only registers for IP v2.9.0 riscv: uprobes: Restore thread.bad_cause powerpc/book3s64/mm: Fix DirectMap stats in /proc/meminfo powerpc/mm/dax: Fix the condition when checking if altmap vmemap can cross-boundary PCI: endpoint: Fix Kconfig indent style PCI: endpoint: Fix a Kconfig prompt of vNTB driver PCI: endpoint: functions/pci-epf-test: Fix dma_chan direction PCI: vmd: Fix uninitialized variable usage in vmd_enable_domain() vfio/mdev: Move the compat_class initialization to module init hwrng: virtio - Fix race on data_avail and actual data modpost: remove broken calculation of exception_table_entry size crypto: nx - fix build warnings when DEBUG_FS is not enabled modpost: fix section mismatch message for R_ARM_ABS32 modpost: fix section mismatch message for R_ARM_{PC24,CALL,JUMP24} crypto: marvell/cesa - Fix type mismatch warning crypto: jitter - correct health test during initialization modpost: fix off by one in is_executable_section() ARC: define ASM_NL and __ALIGN(_STR) outside #ifdef __ASSEMBLY__ guard crypto: kpp - Add helper to set reqsize crypto: qat - Use helper to set reqsize crypto: qat - unmap buffer before free for DH crypto: qat - unmap buffers before free for RSA NFSv4.2: fix wrong shrinker_id NFSv4.1: freeze the session table upon receiving NFS4ERR_BADSESSION SMB3: Do not send lease break acknowledgment if all file handles have been closed dax: Fix dax_mapping_release() use after free dax: Introduce alloc_dev_dax_id() dax/kmem: Pass valid argument to memory_group_register_static hwrng: st - keep clock enabled while hwrng is registered kbuild: Disable GCOV for *.mod.o efi/libstub: Disable PCI DMA before grabbing the EFI memory map cifs: prevent use-after-free by freeing the cfile later cifs: do all necessary checks for credits within or before locking smb: client: fix broken file attrs with nodfs mounts ksmbd: avoid field overflow warning arm64: sme: Use STR P to clear FFR context field in streaming SVE mode x86/efi: Make efi_set_virtual_address_map IBT safe md/raid1-10: fix casting from randomized structure in raid1_submit_write() USB: serial: option: add LARA-R6 01B PIDs usb: dwc3: gadget: Propagate core init errors to UDC during pullup phy: tegra: xusb: Clear the driver reference in usb-phy dev iio: adc: ad7192: Fix null ad7192_state pointer access iio: adc: ad7192: Fix internal/external clock selection iio: accel: fxls8962af: errata bug only applicable for FXLS8962AF iio: accel: fxls8962af: fixup buffer scan element type Revert "drm/amd/display: edp do not add non-edid timings" mm/mmap: Fix VM_LOCKED check in do_vmi_align_munmap() ALSA: hda/realtek: Enable mute/micmute LEDs and limit mic boost on EliteBook ALSA: hda/realtek: Add quirk for Clevo NPx0SNx ALSA: jack: Fix mutex call in snd_jack_report() ALSA: pcm: Fix potential data race at PCM memory allocation helpers block: fix signed int overflow in Amiga partition support block: add overflow checks for Amiga partition support block: change all __u32 annotations to __be32 in affs_hardblocks.h block: increment diskseq on all media change events btrfs: fix race when deleting free space root from the dirty cow roots list SUNRPC: Fix UAF in svc_tcp_listen_data_ready() w1: w1_therm: fix locking behavior in convert_t w1: fix loop in w1_fini() dt-bindings: power: reset: qcom-pon: Only allow reboot-mode pre-pmk8350 f2fs: do not allow to defragment files have FI_COMPRESS_RELEASED sh: j2: Use ioremap() to translate device tree address into kernel memory usb: dwc2: platform: Improve error reporting for problems during .remove() usb: dwc2: Fix some error handling paths serial: 8250: omap: Fix freeing of resources on failed register clk: qcom: mmcc-msm8974: remove oxili_ocmemgx_clk clk: qcom: camcc-sc7180: Add parent dependency to all camera GDSCs clk: qcom: gcc-ipq6018: Use floor ops for sdcc clocks clk: qcom: gcc-qcm2290: Mark RCGs shared where applicable media: usb: Check az6007_read() return value media: amphion: drop repeated codec data for vc1l format media: amphion: drop repeated codec data for vc1g format media: amphion: initiate a drain of the capture queue in dynamic resolution change media: videodev2.h: Fix struct v4l2_input tuner index comment media: usb: siano: Fix warning due to null work_func_t function pointer media: i2c: Correct format propagation for st-mipid02 media: hi846: fix usage of pm_runtime_get_if_in_use() media: mediatek: vcodec: using decoder status instead of core work count clk: qcom: reset: support resetting multiple bits clk: qcom: ipq6018: fix networking resets clk: qcom: dispcc-qcm2290: Fix BI_TCXO_AO handling clk: qcom: dispcc-qcm2290: Fix GPLL0_OUT_DIV handling clk: qcom: mmcc-msm8974: use clk_rcg2_shared_ops for mdp_clk_src clock staging: vchiq_arm: mark vchiq_platform_init() static usb: dwc3: qcom: Fix potential memory leak usb: gadget: u_serial: Add null pointer check in gserial_suspend extcon: Fix kernel doc of property fields to avoid warnings extcon: Fix kernel doc of property capability fields to avoid warnings usb: phy: phy-tahvo: fix memory leak in tahvo_usb_probe() usb: hide unused usbfs_notify_suspend/resume functions usb: misc: eud: Fix eud sysfs path (use 'qcom_eud') serial: core: lock port for stop_rx() in uart_suspend_port() serial: 8250: lock port for stop_rx() in omap8250_irq() serial: core: lock port for start_rx() in uart_resume_port() serial: 8250: lock port for UART_IER access in omap8250_irq() kernfs: fix missing kernfs_idr_lock to remove an ID from the IDR lkdtm: replace ll_rw_block with submit_bh i3c: master: svc: fix cpu schedule in spin lock coresight: Fix loss of connection info when a module is unloaded mfd: rt5033: Drop rt5033-battery sub-device media: venus: helpers: Fix ALIGN() of non power of two media: atomisp: gmin_platform: fix out_len in gmin_get_config_dsm_var() sh: Avoid using IRQ0 on SH3 and SH4 gfs2: Fix duplicate should_fault_in_pages() call f2fs: fix potential deadlock due to unpaired node_write lock use f2fs: fix to avoid NULL pointer dereference f2fs_write_end_io() KVM: s390: fix KVM_S390_GET_CMMA_BITS for GFNs in memslot holes usb: dwc3: qcom: Release the correct resources in dwc3_qcom_remove() usb: dwc3: qcom: Fix an error handling path in dwc3_qcom_probe() usb: common: usb-conn-gpio: Set last role to unknown before initial detection usb: dwc3-meson-g12a: Fix an error handling path in dwc3_meson_g12a_probe() mfd: wcd934x: Fix an error handling path in wcd934x_slim_probe() mfd: intel-lpss: Add missing check for platform_get_resource Revert "usb: common: usb-conn-gpio: Set last role to unknown before initial detection" serial: 8250_omap: Use force_suspend and resume for system suspend device property: Fix documentation for fwnode_get_next_parent() device property: Clarify description of returned value in some functions drivers: fwnode: fix fwnode_irq_get[_byname]() nvmem: sunplus-ocotp: release otp->clk before return nvmem: rmem: Use NVMEM_DEVID_AUTO bus: fsl-mc: don't assume child devices are all fsl-mc devices mfd: stmfx: Fix error path in stmfx_chip_init mfd: stmfx: Nullify stmfx->vdd in case of error KVM: s390: vsie: fix the length of APCB bitmap KVM: s390/diag: fix racy access of physical cpu number in diag 9c handler cpufreq: mediatek: correct voltages for MT7622 and MT7623 misc: fastrpc: check return value of devm_kasprintf() clk: qcom: mmcc-msm8974: fix MDSS_GDSC power flags hwtracing: hisi_ptt: Fix potential sleep in atomic context mfd: stmpe: Only disable the regulators if they are enabled phy: tegra: xusb: check return value of devm_kzalloc() lib/bitmap: drop optimization of bitmap_{from,to}_arr64 pwm: imx-tpm: force 'real_period' to be zero in suspend pwm: sysfs: Do not apply state to already disabled PWMs pwm: ab8500: Fix error code in probe() pwm: mtk_disp: Fix the disable flow of disp_pwm md/raid10: fix the condition to call bio_end_io_acct() rtc: st-lpc: Release some resources in st_rtc_probe() in case of error drm/i915/psr: Use hw.adjusted mode when calculating io/fast wake times drm/i915/guc/slpc: Apply min softlimit correctly f2fs: check return value of freeze_super() media: cec: i2c: ch7322: also select REGMAP sctp: fix potential deadlock on &net->sctp.addr_wq_lock net/sched: act_ipt: add sanity checks on table name and hook locations net: add a couple of helpers for iph tot_len net/sched: act_ipt: add sanity checks on skb before calling target spi: spi-geni-qcom: enable SPI_CONTROLLER_MUST_TX for GPI DMA mode net: mscc: ocelot: don't report that RX timestamping is enabled by default net: mscc: ocelot: don't keep PTP configuration of all ports in single structure net: dsa: felix: don't drop PTP frames with tag_8021q when RX timestamping is disabled net: dsa: sja1105: always enable the INCL_SRCPT option net: dsa: tag_sja1105: always prefer source port information from INCL_SRCPT Add MODULE_FIRMWARE() for FIRMWARE_TG357766. Bluetooth: fix invalid-bdaddr quirk for non-persistent setup Bluetooth: ISO: use hci_sync for setting CIG parameters Bluetooth: MGMT: add CIS feature bits to controller information Bluetooth: MGMT: Use BIT macro when defining bitfields Bluetooth: MGMT: Fix marking SCAN_RSP as not connectable ibmvnic: Do not reset dql stats on NON_FATAL err net: dsa: vsc73xx: fix MTU configuration mlxsw: minimal: fix potential memory leak in mlxsw_m_linecards_init spi: bcm-qspi: return error if neither hif_mspi nor mspi is available drm/amdgpu: fix number of fence calculations drm/amd: Don't try to enable secure display TA multiple times mailbox: ti-msgmgr: Fill non-message tx data fields with 0x0 f2fs: fix error path handling in truncate_dnode() octeontx2-af: Fix mapping for NIX block from CGX connection octeontx2-af: Add validation before accessing cgx and lmac ntfs: Fix panic about slab-out-of-bounds caused by ntfs_listxattr() powerpc: allow PPC_EARLY_DEBUG_CPM only when SERIAL_CPM=y powerpc: dts: turris1x.dts: Fix PCIe MEM size for pci2 node net: bridge: keep ports without IFF_UNICAST_FLT in BR_PROMISC mode net: dsa: tag_sja1105: fix source port decoding in vlan_filtering=0 bridge mode net: fix net_dev_start_xmit trace event vs skb_transport_offset() tcp: annotate data races in __tcp_oow_rate_limited() bpf, btf: Warn but return no error for NULL btf from __register_btf_kfunc_id_set() xsk: Honor SO_BINDTODEVICE on bind net/sched: act_pedit: Add size check for TCA_PEDIT_PARMS_EX fanotify: disallow mount/sb marks on kernel internal pseudo fs riscv: move memblock_allow_resize() after linear mapping is ready pptp: Fix fib lookup calls. net: dsa: tag_sja1105: fix MAC DA patching from meta frames net: dsa: sja1105: always enable the send_meta options octeontx-af: fix hardware timestamp configuration afs: Fix accidental truncation when storing data s390/qeth: Fix vipa deletion sh: dma: Fix DMA channel offset calculation apparmor: fix missing error check for rhashtable_insert_fast i2c: xiic: Don't try to handle more interrupt events after error dm: fix undue/missing spaces dm: avoid split of quoted strings where possible dm ioctl: have constant on the right side of the test dm ioctl: Avoid double-fetch of version extcon: usbc-tusb320: Convert to i2c's .probe_new() extcon: usbc-tusb320: Unregister typec port on driver removal btrfs: do not BUG_ON() on tree mod log failure at balance_level() i2c: qup: Add missing unwind goto in qup_i2c_probe() irqchip/loongson-pch-pic: Fix potential incorrect hwirq assignment NFSD: add encoding of op_recall flag for write delegation irqchip/loongson-pch-pic: Fix initialization of HT vector register io_uring: wait interruptibly for request completions on exit mmc: core: disable TRIM on Kingston EMMC04G-M627 mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M mmc: mmci: Set PROBE_PREFER_ASYNCHRONOUS mmc: sdhci: fix DMA configure compatibility issue when 64bit DMA mode is used. wifi: cfg80211: fix regulatory disconnect for non-MLO wifi: ath10k: Serialize wake_tx_queue ops wifi: mt76: mt7921e: fix init command fail with enabled device bcache: fixup btree_cache_wait list damage bcache: Remove unnecessary NULL point check in node allocations bcache: Fix __bch_btree_node_alloc to make the failure behavior consistent watch_queue: prevent dangling pipe pointer um: Use HOST_DIR for mrproper integrity: Fix possible multiple allocation in integrity_inode_get() autofs: use flexible array in ioctl structure mm/damon/ops-common: atomically test and clear young on ptes and pmds shmem: use ramfs_kill_sb() for kill_sb method of ramfs-based tmpfs jffs2: reduce stack usage in jffs2_build_xattr_subsystem() fs: avoid empty option when generating legacy mount string ext4: Remove ext4 locking of moved directory Revert "f2fs: fix potential corruption when moving a directory" fs: Establish locking order for unrelated directories fs: Lock moved directories i2c: nvidia-gpu: Add ACPI property to align with device-tree i2c: nvidia-gpu: Remove ccgx,firmware-build property usb: typec: ucsi: Mark dGPUs as DEVICE scope ipvs: increase ip_vs_conn_tab_bits range for 64BIT btrfs: add handling for RAID1C23/DUP to btrfs_reduce_alloc_profile btrfs: delete unused BGs while reclaiming BGs btrfs: bail out reclaim process if filesystem is read-only btrfs: add block-group tree to lockdep classes btrfs: reinsert BGs failed to reclaim btrfs: fix race when deleting quota root from the dirty cow roots list btrfs: fix extent buffer leak after tree mod log failure at split_node() btrfs: do not BUG_ON() on tree mod log failure at __btrfs_cow_block() ASoC: mediatek: mt8173: Fix irq error path ASoC: mediatek: mt8173: Fix snd_soc_component_initialize error path regulator: tps65219: Fix matching interrupts for their regulators ARM: dts: qcom: ipq4019: fix broken NAND controller properties override ARM: orion5x: fix d2net gpio initialization leds: trigger: netdev: Recheck NETDEV_LED_MODE_LINKUP on dev rename blktrace: use inline function for blk_trace_remove() while blktrace is disabled fs: no need to check source xfs: explicitly specify cpu when forcing inodegc delayed work to run immediately xfs: check that per-cpu inodegc workers actually run on that cpu xfs: disable reaping in fscounters scrub xfs: fix xfs_inodegc_stop racing with mod_delayed_work mm/mmap: Fix extra maple tree write drm/i915: Fix TypeC mode initialization during system resume drm/i915/tc: Fix TC port link ref init for DP MST during HW readout drm/i915/tc: Fix system resume MST mode restore for DP-alt sinks mtd: parsers: refer to ARCH_BCMBCA instead of ARCH_BCM4908 netfilter: nf_tables: unbind non-anonymous set if rule construction fails netfilter: conntrack: Avoid nf_ct_helper_hash uses after free netfilter: nf_tables: do not ignore genmask when looking up chain by id netfilter: nf_tables: prevent OOB access in nft_byteorder_eval wireguard: queueing: use saner cpu selection wrapping wireguard: netlink: send staged packets when setting initial private key tty: serial: fsl_lpuart: add earlycon for imx8ulp platform block/partition: fix signedness issue for Amiga partitions sh: mach-r2d: Handle virq offset in cascaded IRL demux sh: mach-highlander: Handle virq offset in cascaded IRL demux sh: mach-dreamcast: Handle virq offset in cascaded IRQ demux sh: hd64461: Handle virq offset for offchip IRQ base and HD64461 IRQ io_uring: Use io_schedule* in cqring wait Linux 6.1.39 Change-Id: I5867c943c99c157fa599ecd08da961c632e58302 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
1111 lines
29 KiB
C
1111 lines
29 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/dmaengine.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/dma/qcom-gpi-dma.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/log2.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_opp.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/soc/qcom/geni-se.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
/* SPI SE specific registers and respective register fields */
|
|
#define SE_SPI_CPHA 0x224
|
|
#define CPHA BIT(0)
|
|
|
|
#define SE_SPI_LOOPBACK 0x22c
|
|
#define LOOPBACK_ENABLE 0x1
|
|
#define NORMAL_MODE 0x0
|
|
#define LOOPBACK_MSK GENMASK(1, 0)
|
|
|
|
#define SE_SPI_CPOL 0x230
|
|
#define CPOL BIT(2)
|
|
|
|
#define SE_SPI_DEMUX_OUTPUT_INV 0x24c
|
|
#define CS_DEMUX_OUTPUT_INV_MSK GENMASK(3, 0)
|
|
|
|
#define SE_SPI_DEMUX_SEL 0x250
|
|
#define CS_DEMUX_OUTPUT_SEL GENMASK(3, 0)
|
|
|
|
#define SE_SPI_TRANS_CFG 0x25c
|
|
#define CS_TOGGLE BIT(1)
|
|
|
|
#define SE_SPI_WORD_LEN 0x268
|
|
#define WORD_LEN_MSK GENMASK(9, 0)
|
|
#define MIN_WORD_LEN 4
|
|
|
|
#define SE_SPI_TX_TRANS_LEN 0x26c
|
|
#define SE_SPI_RX_TRANS_LEN 0x270
|
|
#define TRANS_LEN_MSK GENMASK(23, 0)
|
|
|
|
#define SE_SPI_PRE_POST_CMD_DLY 0x274
|
|
|
|
#define SE_SPI_DELAY_COUNTERS 0x278
|
|
#define SPI_INTER_WORDS_DELAY_MSK GENMASK(9, 0)
|
|
#define SPI_CS_CLK_DELAY_MSK GENMASK(19, 10)
|
|
#define SPI_CS_CLK_DELAY_SHFT 10
|
|
|
|
/* M_CMD OP codes for SPI */
|
|
#define SPI_TX_ONLY 1
|
|
#define SPI_RX_ONLY 2
|
|
#define SPI_TX_RX 7
|
|
#define SPI_CS_ASSERT 8
|
|
#define SPI_CS_DEASSERT 9
|
|
#define SPI_SCK_ONLY 10
|
|
/* M_CMD params for SPI */
|
|
#define SPI_PRE_CMD_DELAY BIT(0)
|
|
#define TIMESTAMP_BEFORE BIT(1)
|
|
#define FRAGMENTATION BIT(2)
|
|
#define TIMESTAMP_AFTER BIT(3)
|
|
#define POST_CMD_DELAY BIT(4)
|
|
|
|
#define GSI_LOOPBACK_EN BIT(0)
|
|
#define GSI_CS_TOGGLE BIT(3)
|
|
#define GSI_CPHA BIT(4)
|
|
#define GSI_CPOL BIT(5)
|
|
|
|
struct spi_geni_master {
|
|
struct geni_se se;
|
|
struct device *dev;
|
|
u32 tx_fifo_depth;
|
|
u32 fifo_width_bits;
|
|
u32 tx_wm;
|
|
u32 last_mode;
|
|
unsigned long cur_speed_hz;
|
|
unsigned long cur_sclk_hz;
|
|
unsigned int cur_bits_per_word;
|
|
unsigned int tx_rem_bytes;
|
|
unsigned int rx_rem_bytes;
|
|
const struct spi_transfer *cur_xfer;
|
|
struct completion cs_done;
|
|
struct completion cancel_done;
|
|
struct completion abort_done;
|
|
unsigned int oversampling;
|
|
spinlock_t lock;
|
|
int irq;
|
|
bool cs_flag;
|
|
bool abort_failed;
|
|
struct dma_chan *tx;
|
|
struct dma_chan *rx;
|
|
int cur_xfer_mode;
|
|
};
|
|
|
|
static int get_spi_clk_cfg(unsigned int speed_hz,
|
|
struct spi_geni_master *mas,
|
|
unsigned int *clk_idx,
|
|
unsigned int *clk_div)
|
|
{
|
|
unsigned long sclk_freq;
|
|
unsigned int actual_hz;
|
|
int ret;
|
|
|
|
ret = geni_se_clk_freq_match(&mas->se,
|
|
speed_hz * mas->oversampling,
|
|
clk_idx, &sclk_freq, false);
|
|
if (ret) {
|
|
dev_err(mas->dev, "Failed(%d) to find src clk for %dHz\n",
|
|
ret, speed_hz);
|
|
return ret;
|
|
}
|
|
|
|
*clk_div = DIV_ROUND_UP(sclk_freq, mas->oversampling * speed_hz);
|
|
actual_hz = sclk_freq / (mas->oversampling * *clk_div);
|
|
|
|
dev_dbg(mas->dev, "req %u=>%u sclk %lu, idx %d, div %d\n", speed_hz,
|
|
actual_hz, sclk_freq, *clk_idx, *clk_div);
|
|
ret = dev_pm_opp_set_rate(mas->dev, sclk_freq);
|
|
if (ret)
|
|
dev_err(mas->dev, "dev_pm_opp_set_rate failed %d\n", ret);
|
|
else
|
|
mas->cur_sclk_hz = sclk_freq;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void handle_fifo_timeout(struct spi_master *spi,
|
|
struct spi_message *msg)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
unsigned long time_left;
|
|
struct geni_se *se = &mas->se;
|
|
|
|
spin_lock_irq(&mas->lock);
|
|
reinit_completion(&mas->cancel_done);
|
|
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
|
mas->cur_xfer = NULL;
|
|
geni_se_cancel_m_cmd(se);
|
|
spin_unlock_irq(&mas->lock);
|
|
|
|
time_left = wait_for_completion_timeout(&mas->cancel_done, HZ);
|
|
if (time_left)
|
|
return;
|
|
|
|
spin_lock_irq(&mas->lock);
|
|
reinit_completion(&mas->abort_done);
|
|
geni_se_abort_m_cmd(se);
|
|
spin_unlock_irq(&mas->lock);
|
|
|
|
time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
|
|
if (!time_left) {
|
|
dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
|
|
|
|
/*
|
|
* No need for a lock since SPI core has a lock and we never
|
|
* access this from an interrupt.
|
|
*/
|
|
mas->abort_failed = true;
|
|
}
|
|
}
|
|
|
|
static void handle_gpi_timeout(struct spi_master *spi, struct spi_message *msg)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
|
|
dmaengine_terminate_sync(mas->tx);
|
|
dmaengine_terminate_sync(mas->rx);
|
|
}
|
|
|
|
static void spi_geni_handle_err(struct spi_master *spi, struct spi_message *msg)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
|
|
switch (mas->cur_xfer_mode) {
|
|
case GENI_SE_FIFO:
|
|
handle_fifo_timeout(spi, msg);
|
|
break;
|
|
case GENI_GPI_DMA:
|
|
handle_gpi_timeout(spi, msg);
|
|
break;
|
|
default:
|
|
dev_err(mas->dev, "Abort on Mode:%d not supported", mas->cur_xfer_mode);
|
|
}
|
|
}
|
|
|
|
static bool spi_geni_is_abort_still_pending(struct spi_geni_master *mas)
|
|
{
|
|
struct geni_se *se = &mas->se;
|
|
u32 m_irq, m_irq_en;
|
|
|
|
if (!mas->abort_failed)
|
|
return false;
|
|
|
|
/*
|
|
* The only known case where a transfer times out and then a cancel
|
|
* times out then an abort times out is if something is blocking our
|
|
* interrupt handler from running. Avoid starting any new transfers
|
|
* until that sorts itself out.
|
|
*/
|
|
spin_lock_irq(&mas->lock);
|
|
m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
|
|
m_irq_en = readl(se->base + SE_GENI_M_IRQ_EN);
|
|
spin_unlock_irq(&mas->lock);
|
|
|
|
if (m_irq & m_irq_en) {
|
|
dev_err(mas->dev, "Interrupts pending after abort: %#010x\n",
|
|
m_irq & m_irq_en);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* If we're here the problem resolved itself so no need to check more
|
|
* on future transfers.
|
|
*/
|
|
mas->abort_failed = false;
|
|
|
|
return false;
|
|
}
|
|
|
|
static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(slv->master);
|
|
struct spi_master *spi = dev_get_drvdata(mas->dev);
|
|
struct geni_se *se = &mas->se;
|
|
unsigned long time_left;
|
|
|
|
if (!(slv->mode & SPI_CS_HIGH))
|
|
set_flag = !set_flag;
|
|
|
|
if (set_flag == mas->cs_flag)
|
|
return;
|
|
|
|
pm_runtime_get_sync(mas->dev);
|
|
|
|
if (spi_geni_is_abort_still_pending(mas)) {
|
|
dev_err(mas->dev, "Can't set chip select\n");
|
|
goto exit;
|
|
}
|
|
|
|
spin_lock_irq(&mas->lock);
|
|
if (mas->cur_xfer) {
|
|
dev_err(mas->dev, "Can't set CS when prev xfer running\n");
|
|
spin_unlock_irq(&mas->lock);
|
|
goto exit;
|
|
}
|
|
|
|
mas->cs_flag = set_flag;
|
|
reinit_completion(&mas->cs_done);
|
|
if (set_flag)
|
|
geni_se_setup_m_cmd(se, SPI_CS_ASSERT, 0);
|
|
else
|
|
geni_se_setup_m_cmd(se, SPI_CS_DEASSERT, 0);
|
|
spin_unlock_irq(&mas->lock);
|
|
|
|
time_left = wait_for_completion_timeout(&mas->cs_done, HZ);
|
|
if (!time_left) {
|
|
dev_warn(mas->dev, "Timeout setting chip select\n");
|
|
handle_fifo_timeout(spi, NULL);
|
|
}
|
|
|
|
exit:
|
|
pm_runtime_put(mas->dev);
|
|
}
|
|
|
|
static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
|
|
unsigned int bits_per_word)
|
|
{
|
|
unsigned int pack_words;
|
|
bool msb_first = (mode & SPI_LSB_FIRST) ? false : true;
|
|
struct geni_se *se = &mas->se;
|
|
u32 word_len;
|
|
|
|
/*
|
|
* If bits_per_word isn't a byte aligned value, set the packing to be
|
|
* 1 SPI word per FIFO word.
|
|
*/
|
|
if (!(mas->fifo_width_bits % bits_per_word))
|
|
pack_words = mas->fifo_width_bits / bits_per_word;
|
|
else
|
|
pack_words = 1;
|
|
geni_se_config_packing(&mas->se, bits_per_word, pack_words, msb_first,
|
|
true, true);
|
|
word_len = (bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK;
|
|
writel(word_len, se->base + SE_SPI_WORD_LEN);
|
|
}
|
|
|
|
static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas,
|
|
unsigned long clk_hz)
|
|
{
|
|
u32 clk_sel, m_clk_cfg, idx, div;
|
|
struct geni_se *se = &mas->se;
|
|
int ret;
|
|
|
|
if (clk_hz == mas->cur_speed_hz)
|
|
return 0;
|
|
|
|
ret = get_spi_clk_cfg(clk_hz, mas, &idx, &div);
|
|
if (ret) {
|
|
dev_err(mas->dev, "Err setting clk to %lu: %d\n", clk_hz, ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* SPI core clock gets configured with the requested frequency
|
|
* or the frequency closer to the requested frequency.
|
|
* For that reason requested frequency is stored in the
|
|
* cur_speed_hz and referred in the consecutive transfer instead
|
|
* of calling clk_get_rate() API.
|
|
*/
|
|
mas->cur_speed_hz = clk_hz;
|
|
|
|
clk_sel = idx & CLK_SEL_MSK;
|
|
m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
|
|
writel(clk_sel, se->base + SE_GENI_CLK_SEL);
|
|
writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
|
|
|
|
/* Set BW quota for CPU as driver supports FIFO mode only. */
|
|
se->icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(mas->cur_speed_hz);
|
|
ret = geni_icc_set_bw(se);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int setup_fifo_params(struct spi_device *spi_slv,
|
|
struct spi_master *spi)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
struct geni_se *se = &mas->se;
|
|
u32 loopback_cfg = 0, cpol = 0, cpha = 0, demux_output_inv = 0;
|
|
u32 demux_sel;
|
|
|
|
if (mas->last_mode != spi_slv->mode) {
|
|
if (spi_slv->mode & SPI_LOOP)
|
|
loopback_cfg = LOOPBACK_ENABLE;
|
|
|
|
if (spi_slv->mode & SPI_CPOL)
|
|
cpol = CPOL;
|
|
|
|
if (spi_slv->mode & SPI_CPHA)
|
|
cpha = CPHA;
|
|
|
|
if (spi_slv->mode & SPI_CS_HIGH)
|
|
demux_output_inv = BIT(spi_slv->chip_select);
|
|
|
|
demux_sel = spi_slv->chip_select;
|
|
mas->cur_bits_per_word = spi_slv->bits_per_word;
|
|
|
|
spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word);
|
|
writel(loopback_cfg, se->base + SE_SPI_LOOPBACK);
|
|
writel(demux_sel, se->base + SE_SPI_DEMUX_SEL);
|
|
writel(cpha, se->base + SE_SPI_CPHA);
|
|
writel(cpol, se->base + SE_SPI_CPOL);
|
|
writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV);
|
|
|
|
mas->last_mode = spi_slv->mode;
|
|
}
|
|
|
|
return geni_spi_set_clock_and_bw(mas, spi_slv->max_speed_hz);
|
|
}
|
|
|
|
static void
|
|
spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
|
|
{
|
|
struct spi_master *spi = cb;
|
|
|
|
spi->cur_msg->status = -EIO;
|
|
if (result->result != DMA_TRANS_NOERROR) {
|
|
dev_err(&spi->dev, "DMA txn failed: %d\n", result->result);
|
|
spi_finalize_current_transfer(spi);
|
|
return;
|
|
}
|
|
|
|
if (!result->residue) {
|
|
spi->cur_msg->status = 0;
|
|
dev_dbg(&spi->dev, "DMA txn completed\n");
|
|
} else {
|
|
dev_err(&spi->dev, "DMA xfer has pending: %d\n", result->residue);
|
|
}
|
|
|
|
spi_finalize_current_transfer(spi);
|
|
}
|
|
|
|
static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas,
|
|
struct spi_device *spi_slv, struct spi_master *spi)
|
|
{
|
|
unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
|
|
struct dma_slave_config config = {};
|
|
struct gpi_spi_config peripheral = {};
|
|
struct dma_async_tx_descriptor *tx_desc, *rx_desc;
|
|
int ret;
|
|
|
|
config.peripheral_config = &peripheral;
|
|
config.peripheral_size = sizeof(peripheral);
|
|
peripheral.set_config = true;
|
|
|
|
if (xfer->bits_per_word != mas->cur_bits_per_word ||
|
|
xfer->speed_hz != mas->cur_speed_hz) {
|
|
mas->cur_bits_per_word = xfer->bits_per_word;
|
|
mas->cur_speed_hz = xfer->speed_hz;
|
|
}
|
|
|
|
if (xfer->tx_buf && xfer->rx_buf) {
|
|
peripheral.cmd = SPI_DUPLEX;
|
|
} else if (xfer->tx_buf) {
|
|
peripheral.cmd = SPI_TX;
|
|
peripheral.rx_len = 0;
|
|
} else if (xfer->rx_buf) {
|
|
peripheral.cmd = SPI_RX;
|
|
if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) {
|
|
peripheral.rx_len = ((xfer->len << 3) / mas->cur_bits_per_word);
|
|
} else {
|
|
int bytes_per_word = (mas->cur_bits_per_word / BITS_PER_BYTE) + 1;
|
|
|
|
peripheral.rx_len = (xfer->len / bytes_per_word);
|
|
}
|
|
}
|
|
|
|
peripheral.loopback_en = !!(spi_slv->mode & SPI_LOOP);
|
|
peripheral.clock_pol_high = !!(spi_slv->mode & SPI_CPOL);
|
|
peripheral.data_pol_high = !!(spi_slv->mode & SPI_CPHA);
|
|
peripheral.cs = spi_slv->chip_select;
|
|
peripheral.pack_en = true;
|
|
peripheral.word_len = xfer->bits_per_word - MIN_WORD_LEN;
|
|
|
|
ret = get_spi_clk_cfg(mas->cur_speed_hz, mas,
|
|
&peripheral.clk_src, &peripheral.clk_div);
|
|
if (ret) {
|
|
dev_err(mas->dev, "Err in get_spi_clk_cfg() :%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!xfer->cs_change) {
|
|
if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers))
|
|
peripheral.fragmentation = FRAGMENTATION;
|
|
}
|
|
|
|
if (peripheral.cmd & SPI_RX) {
|
|
dmaengine_slave_config(mas->rx, &config);
|
|
rx_desc = dmaengine_prep_slave_sg(mas->rx, xfer->rx_sg.sgl, xfer->rx_sg.nents,
|
|
DMA_DEV_TO_MEM, flags);
|
|
if (!rx_desc) {
|
|
dev_err(mas->dev, "Err setting up rx desc\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Prepare the TX always, even for RX or tx_buf being null, we would
|
|
* need TX to be prepared per GSI spec
|
|
*/
|
|
dmaengine_slave_config(mas->tx, &config);
|
|
tx_desc = dmaengine_prep_slave_sg(mas->tx, xfer->tx_sg.sgl, xfer->tx_sg.nents,
|
|
DMA_MEM_TO_DEV, flags);
|
|
if (!tx_desc) {
|
|
dev_err(mas->dev, "Err setting up tx desc\n");
|
|
return -EIO;
|
|
}
|
|
|
|
tx_desc->callback_result = spi_gsi_callback_result;
|
|
tx_desc->callback_param = spi;
|
|
|
|
if (peripheral.cmd & SPI_RX)
|
|
dmaengine_submit(rx_desc);
|
|
dmaengine_submit(tx_desc);
|
|
|
|
if (peripheral.cmd & SPI_RX)
|
|
dma_async_issue_pending(mas->rx);
|
|
|
|
dma_async_issue_pending(mas->tx);
|
|
return 1;
|
|
}
|
|
|
|
static bool geni_can_dma(struct spi_controller *ctlr,
|
|
struct spi_device *slv, struct spi_transfer *xfer)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(slv->master);
|
|
|
|
/* check if dma is supported */
|
|
return mas->cur_xfer_mode != GENI_SE_FIFO;
|
|
}
|
|
|
|
static int spi_geni_prepare_message(struct spi_master *spi,
|
|
struct spi_message *spi_msg)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
int ret;
|
|
|
|
switch (mas->cur_xfer_mode) {
|
|
case GENI_SE_FIFO:
|
|
if (spi_geni_is_abort_still_pending(mas))
|
|
return -EBUSY;
|
|
ret = setup_fifo_params(spi_msg->spi, spi);
|
|
if (ret)
|
|
dev_err(mas->dev, "Couldn't select mode %d\n", ret);
|
|
return ret;
|
|
|
|
case GENI_GPI_DMA:
|
|
/* nothing to do for GPI DMA */
|
|
return 0;
|
|
}
|
|
|
|
dev_err(mas->dev, "Mode not supported %d", mas->cur_xfer_mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int spi_geni_grab_gpi_chan(struct spi_geni_master *mas)
|
|
{
|
|
int ret;
|
|
|
|
mas->tx = dma_request_chan(mas->dev, "tx");
|
|
if (IS_ERR(mas->tx)) {
|
|
ret = dev_err_probe(mas->dev, PTR_ERR(mas->tx),
|
|
"Failed to get tx DMA ch\n");
|
|
goto err_tx;
|
|
}
|
|
|
|
mas->rx = dma_request_chan(mas->dev, "rx");
|
|
if (IS_ERR(mas->rx)) {
|
|
ret = dev_err_probe(mas->dev, PTR_ERR(mas->rx),
|
|
"Failed to get rx DMA ch\n");
|
|
goto err_rx;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_rx:
|
|
mas->rx = NULL;
|
|
dma_release_channel(mas->tx);
|
|
err_tx:
|
|
mas->tx = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static void spi_geni_release_dma_chan(struct spi_geni_master *mas)
|
|
{
|
|
if (mas->rx) {
|
|
dma_release_channel(mas->rx);
|
|
mas->rx = NULL;
|
|
}
|
|
|
|
if (mas->tx) {
|
|
dma_release_channel(mas->tx);
|
|
mas->tx = NULL;
|
|
}
|
|
}
|
|
|
|
static int spi_geni_init(struct spi_geni_master *mas)
|
|
{
|
|
struct geni_se *se = &mas->se;
|
|
unsigned int proto, major, minor, ver;
|
|
u32 spi_tx_cfg, fifo_disable;
|
|
int ret = -ENXIO;
|
|
|
|
pm_runtime_get_sync(mas->dev);
|
|
|
|
proto = geni_se_read_proto(se);
|
|
if (proto != GENI_SE_SPI) {
|
|
dev_err(mas->dev, "Invalid proto %d\n", proto);
|
|
goto out_pm;
|
|
}
|
|
mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se);
|
|
|
|
/* Width of Tx and Rx FIFO is same */
|
|
mas->fifo_width_bits = geni_se_get_tx_fifo_width(se);
|
|
|
|
/*
|
|
* Hardware programming guide suggests to configure
|
|
* RX FIFO RFR level to fifo_depth-2.
|
|
*/
|
|
geni_se_init(se, mas->tx_fifo_depth - 3, mas->tx_fifo_depth - 2);
|
|
/* Transmit an entire FIFO worth of data per IRQ */
|
|
mas->tx_wm = 1;
|
|
ver = geni_se_get_qup_hw_version(se);
|
|
major = GENI_SE_VERSION_MAJOR(ver);
|
|
minor = GENI_SE_VERSION_MINOR(ver);
|
|
|
|
if (major == 1 && minor == 0)
|
|
mas->oversampling = 2;
|
|
else
|
|
mas->oversampling = 1;
|
|
|
|
fifo_disable = readl(se->base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE;
|
|
switch (fifo_disable) {
|
|
case 1:
|
|
ret = spi_geni_grab_gpi_chan(mas);
|
|
if (!ret) { /* success case */
|
|
mas->cur_xfer_mode = GENI_GPI_DMA;
|
|
geni_se_select_mode(se, GENI_GPI_DMA);
|
|
dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n");
|
|
break;
|
|
} else if (ret == -EPROBE_DEFER) {
|
|
goto out_pm;
|
|
}
|
|
/*
|
|
* in case of failure to get dma channel, we can still do the
|
|
* FIFO mode, so fallthrough
|
|
*/
|
|
dev_warn(mas->dev, "FIFO mode disabled, but couldn't get DMA, fall back to FIFO mode\n");
|
|
fallthrough;
|
|
|
|
case 0:
|
|
mas->cur_xfer_mode = GENI_SE_FIFO;
|
|
geni_se_select_mode(se, GENI_SE_FIFO);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
/* We always control CS manually */
|
|
spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG);
|
|
spi_tx_cfg &= ~CS_TOGGLE;
|
|
writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
|
|
|
|
out_pm:
|
|
pm_runtime_put(mas->dev);
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas)
|
|
{
|
|
/*
|
|
* Calculate how many bytes we'll put in each FIFO word. If the
|
|
* transfer words don't pack cleanly into a FIFO word we'll just put
|
|
* one transfer word in each FIFO word. If they do pack we'll pack 'em.
|
|
*/
|
|
if (mas->fifo_width_bits % mas->cur_bits_per_word)
|
|
return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word,
|
|
BITS_PER_BYTE));
|
|
|
|
return mas->fifo_width_bits / BITS_PER_BYTE;
|
|
}
|
|
|
|
static bool geni_spi_handle_tx(struct spi_geni_master *mas)
|
|
{
|
|
struct geni_se *se = &mas->se;
|
|
unsigned int max_bytes;
|
|
const u8 *tx_buf;
|
|
unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas);
|
|
unsigned int i = 0;
|
|
|
|
/* Stop the watermark IRQ if nothing to send */
|
|
if (!mas->cur_xfer) {
|
|
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
|
return false;
|
|
}
|
|
|
|
max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word;
|
|
if (mas->tx_rem_bytes < max_bytes)
|
|
max_bytes = mas->tx_rem_bytes;
|
|
|
|
tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes;
|
|
while (i < max_bytes) {
|
|
unsigned int j;
|
|
unsigned int bytes_to_write;
|
|
u32 fifo_word = 0;
|
|
u8 *fifo_byte = (u8 *)&fifo_word;
|
|
|
|
bytes_to_write = min(bytes_per_fifo_word, max_bytes - i);
|
|
for (j = 0; j < bytes_to_write; j++)
|
|
fifo_byte[j] = tx_buf[i++];
|
|
iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1);
|
|
}
|
|
mas->tx_rem_bytes -= max_bytes;
|
|
if (!mas->tx_rem_bytes) {
|
|
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void geni_spi_handle_rx(struct spi_geni_master *mas)
|
|
{
|
|
struct geni_se *se = &mas->se;
|
|
u32 rx_fifo_status;
|
|
unsigned int rx_bytes;
|
|
unsigned int rx_last_byte_valid;
|
|
u8 *rx_buf;
|
|
unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas);
|
|
unsigned int i = 0;
|
|
|
|
rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS);
|
|
rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word;
|
|
if (rx_fifo_status & RX_LAST) {
|
|
rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK;
|
|
rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT;
|
|
if (rx_last_byte_valid && rx_last_byte_valid < 4)
|
|
rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid;
|
|
}
|
|
|
|
/* Clear out the FIFO and bail if nowhere to put it */
|
|
if (!mas->cur_xfer) {
|
|
for (i = 0; i < DIV_ROUND_UP(rx_bytes, bytes_per_fifo_word); i++)
|
|
readl(se->base + SE_GENI_RX_FIFOn);
|
|
return;
|
|
}
|
|
|
|
if (mas->rx_rem_bytes < rx_bytes)
|
|
rx_bytes = mas->rx_rem_bytes;
|
|
|
|
rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes;
|
|
while (i < rx_bytes) {
|
|
u32 fifo_word = 0;
|
|
u8 *fifo_byte = (u8 *)&fifo_word;
|
|
unsigned int bytes_to_read;
|
|
unsigned int j;
|
|
|
|
bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i);
|
|
ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1);
|
|
for (j = 0; j < bytes_to_read; j++)
|
|
rx_buf[i++] = fifo_byte[j];
|
|
}
|
|
mas->rx_rem_bytes -= rx_bytes;
|
|
}
|
|
|
|
static void setup_fifo_xfer(struct spi_transfer *xfer,
|
|
struct spi_geni_master *mas,
|
|
u16 mode, struct spi_master *spi)
|
|
{
|
|
u32 m_cmd = 0;
|
|
u32 len;
|
|
struct geni_se *se = &mas->se;
|
|
int ret;
|
|
|
|
/*
|
|
* Ensure that our interrupt handler isn't still running from some
|
|
* prior command before we start messing with the hardware behind
|
|
* its back. We don't need to _keep_ the lock here since we're only
|
|
* worried about racing with out interrupt handler. The SPI core
|
|
* already handles making sure that we're not trying to do two
|
|
* transfers at once or setting a chip select and doing a transfer
|
|
* concurrently.
|
|
*
|
|
* NOTE: we actually _can't_ hold the lock here because possibly we
|
|
* might call clk_set_rate() which needs to be able to sleep.
|
|
*/
|
|
spin_lock_irq(&mas->lock);
|
|
spin_unlock_irq(&mas->lock);
|
|
|
|
if (xfer->bits_per_word != mas->cur_bits_per_word) {
|
|
spi_setup_word_len(mas, mode, xfer->bits_per_word);
|
|
mas->cur_bits_per_word = xfer->bits_per_word;
|
|
}
|
|
|
|
/* Speed and bits per word can be overridden per transfer */
|
|
ret = geni_spi_set_clock_and_bw(mas, xfer->speed_hz);
|
|
if (ret)
|
|
return;
|
|
|
|
mas->tx_rem_bytes = 0;
|
|
mas->rx_rem_bytes = 0;
|
|
|
|
if (!(mas->cur_bits_per_word % MIN_WORD_LEN))
|
|
len = xfer->len * BITS_PER_BYTE / mas->cur_bits_per_word;
|
|
else
|
|
len = xfer->len / (mas->cur_bits_per_word / BITS_PER_BYTE + 1);
|
|
len &= TRANS_LEN_MSK;
|
|
|
|
mas->cur_xfer = xfer;
|
|
if (xfer->tx_buf) {
|
|
m_cmd |= SPI_TX_ONLY;
|
|
mas->tx_rem_bytes = xfer->len;
|
|
writel(len, se->base + SE_SPI_TX_TRANS_LEN);
|
|
}
|
|
|
|
if (xfer->rx_buf) {
|
|
m_cmd |= SPI_RX_ONLY;
|
|
writel(len, se->base + SE_SPI_RX_TRANS_LEN);
|
|
mas->rx_rem_bytes = xfer->len;
|
|
}
|
|
|
|
/*
|
|
* Lock around right before we start the transfer since our
|
|
* interrupt could come in at any time now.
|
|
*/
|
|
spin_lock_irq(&mas->lock);
|
|
geni_se_setup_m_cmd(se, m_cmd, FRAGMENTATION);
|
|
if (m_cmd & SPI_TX_ONLY) {
|
|
if (geni_spi_handle_tx(mas))
|
|
writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG);
|
|
}
|
|
spin_unlock_irq(&mas->lock);
|
|
}
|
|
|
|
static int spi_geni_transfer_one(struct spi_master *spi,
|
|
struct spi_device *slv,
|
|
struct spi_transfer *xfer)
|
|
{
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
|
|
if (spi_geni_is_abort_still_pending(mas))
|
|
return -EBUSY;
|
|
|
|
/* Terminate and return success for 0 byte length transfer */
|
|
if (!xfer->len)
|
|
return 0;
|
|
|
|
if (mas->cur_xfer_mode == GENI_SE_FIFO) {
|
|
setup_fifo_xfer(xfer, mas, slv->mode, spi);
|
|
return 1;
|
|
}
|
|
return setup_gsi_xfer(xfer, mas, slv, spi);
|
|
}
|
|
|
|
static irqreturn_t geni_spi_isr(int irq, void *data)
|
|
{
|
|
struct spi_master *spi = data;
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
struct geni_se *se = &mas->se;
|
|
u32 m_irq;
|
|
|
|
m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
|
|
if (!m_irq)
|
|
return IRQ_NONE;
|
|
|
|
if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
|
|
M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN |
|
|
M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN))
|
|
dev_warn(mas->dev, "Unexpected IRQ err status %#010x\n", m_irq);
|
|
|
|
spin_lock(&mas->lock);
|
|
|
|
if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
|
|
geni_spi_handle_rx(mas);
|
|
|
|
if (m_irq & M_TX_FIFO_WATERMARK_EN)
|
|
geni_spi_handle_tx(mas);
|
|
|
|
if (m_irq & M_CMD_DONE_EN) {
|
|
if (mas->cur_xfer) {
|
|
spi_finalize_current_transfer(spi);
|
|
mas->cur_xfer = NULL;
|
|
/*
|
|
* If this happens, then a CMD_DONE came before all the
|
|
* Tx buffer bytes were sent out. This is unusual, log
|
|
* this condition and disable the WM interrupt to
|
|
* prevent the system from stalling due an interrupt
|
|
* storm.
|
|
*
|
|
* If this happens when all Rx bytes haven't been
|
|
* received, log the condition. The only known time
|
|
* this can happen is if bits_per_word != 8 and some
|
|
* registers that expect xfer lengths in num spi_words
|
|
* weren't written correctly.
|
|
*/
|
|
if (mas->tx_rem_bytes) {
|
|
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
|
dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
|
|
mas->tx_rem_bytes, mas->cur_bits_per_word);
|
|
}
|
|
if (mas->rx_rem_bytes)
|
|
dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
|
|
mas->rx_rem_bytes, mas->cur_bits_per_word);
|
|
} else {
|
|
complete(&mas->cs_done);
|
|
}
|
|
}
|
|
|
|
if (m_irq & M_CMD_CANCEL_EN)
|
|
complete(&mas->cancel_done);
|
|
if (m_irq & M_CMD_ABORT_EN)
|
|
complete(&mas->abort_done);
|
|
|
|
/*
|
|
* It's safe or a good idea to Ack all of our interrupts at the end
|
|
* of the function. Specifically:
|
|
* - M_CMD_DONE_EN / M_RX_FIFO_LAST_EN: Edge triggered interrupts and
|
|
* clearing Acks. Clearing at the end relies on nobody else having
|
|
* started a new transfer yet or else we could be clearing _their_
|
|
* done bit, but everyone grabs the spinlock before starting a new
|
|
* transfer.
|
|
* - M_RX_FIFO_WATERMARK_EN / M_TX_FIFO_WATERMARK_EN: These appear
|
|
* to be "latched level" interrupts so it's important to clear them
|
|
* _after_ you've handled the condition and always safe to do so
|
|
* since they'll re-assert if they're still happening.
|
|
*/
|
|
writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR);
|
|
|
|
spin_unlock(&mas->lock);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int spi_geni_probe(struct platform_device *pdev)
|
|
{
|
|
int ret, irq;
|
|
struct spi_master *spi;
|
|
struct spi_geni_master *mas;
|
|
void __iomem *base;
|
|
struct clk *clk;
|
|
struct device *dev = &pdev->dev;
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0)
|
|
return irq;
|
|
|
|
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "could not set DMA mask\n");
|
|
|
|
base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(base))
|
|
return PTR_ERR(base);
|
|
|
|
clk = devm_clk_get(dev, "se");
|
|
if (IS_ERR(clk))
|
|
return PTR_ERR(clk);
|
|
|
|
spi = devm_spi_alloc_master(dev, sizeof(*mas));
|
|
if (!spi)
|
|
return -ENOMEM;
|
|
|
|
platform_set_drvdata(pdev, spi);
|
|
mas = spi_master_get_devdata(spi);
|
|
mas->irq = irq;
|
|
mas->dev = dev;
|
|
mas->se.dev = dev;
|
|
mas->se.wrapper = dev_get_drvdata(dev->parent);
|
|
mas->se.base = base;
|
|
mas->se.clk = clk;
|
|
|
|
ret = devm_pm_opp_set_clkname(&pdev->dev, "se");
|
|
if (ret)
|
|
return ret;
|
|
/* OPP table is optional */
|
|
ret = devm_pm_opp_of_add_table(&pdev->dev);
|
|
if (ret && ret != -ENODEV) {
|
|
dev_err(&pdev->dev, "invalid OPP table in device tree\n");
|
|
return ret;
|
|
}
|
|
|
|
spi->bus_num = -1;
|
|
spi->dev.of_node = dev->of_node;
|
|
spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
|
|
spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
|
spi->num_chipselect = 4;
|
|
spi->max_speed_hz = 50000000;
|
|
spi->prepare_message = spi_geni_prepare_message;
|
|
spi->transfer_one = spi_geni_transfer_one;
|
|
spi->can_dma = geni_can_dma;
|
|
spi->dma_map_dev = dev->parent;
|
|
spi->auto_runtime_pm = true;
|
|
spi->handle_err = spi_geni_handle_err;
|
|
spi->use_gpio_descriptors = true;
|
|
|
|
init_completion(&mas->cs_done);
|
|
init_completion(&mas->cancel_done);
|
|
init_completion(&mas->abort_done);
|
|
spin_lock_init(&mas->lock);
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 250);
|
|
pm_runtime_enable(dev);
|
|
|
|
ret = geni_icc_get(&mas->se, NULL);
|
|
if (ret)
|
|
goto spi_geni_probe_runtime_disable;
|
|
/* Set the bus quota to a reasonable value for register access */
|
|
mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ);
|
|
mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
|
|
|
|
ret = geni_icc_set_bw(&mas->se);
|
|
if (ret)
|
|
goto spi_geni_probe_runtime_disable;
|
|
|
|
ret = spi_geni_init(mas);
|
|
if (ret)
|
|
goto spi_geni_probe_runtime_disable;
|
|
|
|
/*
|
|
* check the mode supported and set_cs for fifo mode only
|
|
* for dma (gsi) mode, the gsi will set cs based on params passed in
|
|
* TRE
|
|
*/
|
|
if (mas->cur_xfer_mode == GENI_SE_FIFO)
|
|
spi->set_cs = spi_geni_set_cs;
|
|
|
|
/*
|
|
* TX is required per GSI spec, see setup_gsi_xfer().
|
|
*/
|
|
if (mas->cur_xfer_mode == GENI_GPI_DMA)
|
|
spi->flags = SPI_CONTROLLER_MUST_TX;
|
|
|
|
ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi);
|
|
if (ret)
|
|
goto spi_geni_release_dma;
|
|
|
|
ret = spi_register_master(spi);
|
|
if (ret)
|
|
goto spi_geni_probe_free_irq;
|
|
|
|
return 0;
|
|
spi_geni_probe_free_irq:
|
|
free_irq(mas->irq, spi);
|
|
spi_geni_release_dma:
|
|
spi_geni_release_dma_chan(mas);
|
|
spi_geni_probe_runtime_disable:
|
|
pm_runtime_disable(dev);
|
|
return ret;
|
|
}
|
|
|
|
static int spi_geni_remove(struct platform_device *pdev)
|
|
{
|
|
struct spi_master *spi = platform_get_drvdata(pdev);
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
|
|
/* Unregister _before_ disabling pm_runtime() so we stop transfers */
|
|
spi_unregister_master(spi);
|
|
|
|
spi_geni_release_dma_chan(mas);
|
|
|
|
free_irq(mas->irq, spi);
|
|
pm_runtime_disable(&pdev->dev);
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
|
|
{
|
|
struct spi_master *spi = dev_get_drvdata(dev);
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
int ret;
|
|
|
|
/* Drop the performance state vote */
|
|
dev_pm_opp_set_rate(dev, 0);
|
|
|
|
ret = geni_se_resources_off(&mas->se);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return geni_icc_disable(&mas->se);
|
|
}
|
|
|
|
static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
|
|
{
|
|
struct spi_master *spi = dev_get_drvdata(dev);
|
|
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
|
int ret;
|
|
|
|
ret = geni_icc_enable(&mas->se);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = geni_se_resources_on(&mas->se);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
|
|
}
|
|
|
|
static int __maybe_unused spi_geni_suspend(struct device *dev)
|
|
{
|
|
struct spi_master *spi = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
ret = spi_master_suspend(spi);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = pm_runtime_force_suspend(dev);
|
|
if (ret)
|
|
spi_master_resume(spi);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __maybe_unused spi_geni_resume(struct device *dev)
|
|
{
|
|
struct spi_master *spi = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
ret = pm_runtime_force_resume(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = spi_master_resume(spi);
|
|
if (ret)
|
|
pm_runtime_force_suspend(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct dev_pm_ops spi_geni_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend,
|
|
spi_geni_runtime_resume, NULL)
|
|
SET_SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, spi_geni_resume)
|
|
};
|
|
|
|
static const struct of_device_id spi_geni_dt_match[] = {
|
|
{ .compatible = "qcom,geni-spi" },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, spi_geni_dt_match);
|
|
|
|
static struct platform_driver spi_geni_driver = {
|
|
.probe = spi_geni_probe,
|
|
.remove = spi_geni_remove,
|
|
.driver = {
|
|
.name = "geni_spi",
|
|
.pm = &spi_geni_pm_ops,
|
|
.of_match_table = spi_geni_dt_match,
|
|
},
|
|
};
|
|
module_platform_driver(spi_geni_driver);
|
|
|
|
MODULE_DESCRIPTION("SPI driver for GENI based QUP cores");
|
|
MODULE_LICENSE("GPL v2");
|