Files
kernel_xiaomi_sm8250/drivers/soc/qcom/smsm.c
Michael Bestas 8a4226a21a Merge tag 'ASB-2022-11-01_4.19-stable' of https://android.googlesource.com/kernel/common into android13-4.19-kona
https://source.android.com/docs/security/bulletin/2022-11-01

* tag 'ASB-2022-11-01_4.19-stable' of https://android.googlesource.com/kernel/common:
  Linux 4.19.264
  can: rcar_canfd: rcar_canfd_handle_global_receive(): fix IRQ storm on global FIFO receive
  net/mlx5e: Do not increment ESN when updating IPsec ESN state
  net: ehea: fix possible memory leak in ehea_register_port()
  openvswitch: switch from WARN to pr_warn
  ALSA: aoa: Fix I2S device accounting
  ALSA: aoa: i2sbus: fix possible memory leak in i2sbus_add_dev()
  PM: domains: Fix handling of unavailable/disabled idle states
  net: ksz884x: fix missing pci_disable_device() on error in pcidev_init()
  i40e: Fix flow-type by setting GL_HASH_INSET registers
  i40e: Fix VF hang when reset is triggered on another VF
  i40e: Fix ethtool rx-flow-hash setting for X722
  media: videodev2.h: V4L2_DV_BT_BLANKING_HEIGHT should check 'interlaced'
  media: v4l2-dv-timings: add sanity checks for blanking values
  media: vivid: dev->bitmap_cap wasn't freed in all cases
  media: vivid: s_fbuf: add more sanity checks
  PM: hibernate: Allow hybrid sleep to work with s2idle
  can: mscan: mpc5xxx: mpc5xxx_can_probe(): add missing put_clock() in error path
  tcp: fix indefinite deferral of RTO with SACK reneging
  net: lantiq_etop: don't free skb when returning NETDEV_TX_BUSY
  net: fix UAF issue in nfqnl_nf_hook_drop() when ops_init() failed
  kcm: annotate data-races around kcm->rx_wait
  kcm: annotate data-races around kcm->rx_psock
  amd-xgbe: add the bit rate quirk for Molex cables
  amd-xgbe: fix the SFP compliance codes check for DAC cables
  x86/unwind/orc: Fix unreliable stack dump with gcov
  net: netsec: fix error handling in netsec_register_mdio()
  tipc: fix a null-ptr-deref in tipc_topsrv_accept
  ALSA: ac97: fix possible memory leak in snd_ac97_dev_register()
  arc: iounmap() arg is volatile
  drm/msm: Fix return type of mdp4_lvds_connector_mode_valid
  net: ieee802154: fix error return code in dgram_bind()
  mm,hugetlb: take hugetlb_lock before decrementing h->resv_huge_pages
  xen/gntdev: Prevent leaking grants
  Xen/gntdev: don't ignore kernel unmapping error
  s390/futex: add missing EX_TABLE entry to __futex_atomic_op()
  perf auxtrace: Fix address filter symbol name match for modules
  kernfs: fix use-after-free in __kernfs_remove
  mmc: core: Fix kernel panic when remove non-standard SDIO card
  drm/msm/hdmi: fix memory corruption with too many bridges
  drm/msm/dsi: fix memory corruption with too many bridges
  mac802154: Fix LQI recording
  fbdev: smscufx: Fix several use-after-free bugs
  iio: light: tsl2583: Fix module unloading
  tools: iio: iio_utils: fix digit calculation
  xhci: Remove device endpoints from bandwidth list when freeing the device
  usb: xhci: add XHCI_SPURIOUS_SUCCESS to ASM1042 despite being a V0.96 controller
  usb: bdc: change state when port disconnected
  usb: dwc3: gadget: Don't set IMI for no_interrupt
  usb: dwc3: gadget: Stop processing more requests on IMI
  USB: add RESET_RESUME quirk for NVIDIA Jetson devices in RCM
  ALSA: au88x0: use explicitly signed char
  ALSA: Use del_timer_sync() before freeing timer
  can: kvaser_usb: Fix possible completions during init_completion
  mm: /proc/pid/smaps_rollup: fix no vma's null-deref
  hv_netvsc: Fix race between VF offering and VF association message from host
  Makefile.debug: re-enable debug info for .S files
  ACPI: video: Force backlight native for more TongFang devices
  media: v4l2-mem2mem: Apply DST_QUEUE_OFF_BASE on MMAP buffers across ioctls
  iommu/vt-d: Clean up si_domain in the init_dmars() error path
  net: hns: fix possible memory leak in hnae_ae_register()
  net: sched: cake: fix null pointer access issue when cake_init() fails
  net/atm: fix proc_mpc_write incorrect return value
  HID: magicmouse: Do not set BTN_MOUSE on double report
  tipc: fix an information leak in tipc_topsrv_kern_subscr
  tipc: Fix recognition of trial period
  ACPI: extlog: Handle multiple records
  btrfs: fix processing of delayed tree block refs during backref walking
  btrfs: fix processing of delayed data refs during backref walking
  r8152: add PID for the Lenovo OneLink+ Dock
  arm64: errata: Remove AES hwcap for COMPAT tasks
  media: venus: dec: Handle the case where find_format fails
  KVM: arm64: vgic: Fix exit condition in scan_its_table()
  ata: ahci: Match EM_MAX_SLOTS with SATA_PMP_MAX_PORTS
  ata: ahci-imx: Fix MODULE_ALIAS
  hwmon/coretemp: Handle large core ID value
  x86/microcode/AMD: Apply the patch early on every logical thread
  ocfs2: fix BUG when iput after ocfs2_mknod fails
  ocfs2: clear dinode links count in case of error
  UPSTREAM: once: fix section mismatch on clang builds
  Revert "serial: 8250: Fix restoring termios speed after suspend"
  UPSTREAM: ARM: 8788/1: ftrace: remove old mcount support
  Linux 4.19.263
  once: fix section mismatch on clang builds
  Linux 4.19.262
  gcov: support GCC 12.1 and newer compilers
  thermal: intel_powerclamp: Use first online CPU as control_cpu
  inet: fully convert sk->sk_rx_dst to RCU rules
  efi: libstub: drop pointless get_memory_map() call
  md: Replace snprintf with scnprintf
  ext4: continue to expand file system when the target size doesn't reach
  net/ieee802154: don't warn zero-sized raw_sendmsg()
  net: ieee802154: return -EINVAL for unknown addr type
  perf intel-pt: Fix segfault in intel_pt_print_info() with uClibc
  clk: bcm2835: Make peripheral PLLC critical
  usb: idmouse: fix an uninit-value in idmouse_open
  nvme: copy firmware_rev on each init
  Revert "usb: storage: Add quirk for Samsung Fit flash"
  usb: musb: Fix musb_gadget.c rxstate overflow bug
  usb: host: xhci: Fix potential memory leak in xhci_alloc_stream_info()
  md/raid5: Wait for MD_SB_CHANGE_PENDING in raid5d
  HID: roccat: Fix use-after-free in roccat_read()
  ata: libahci_platform: Sanity check the DT child nodes number
  staging: vt6655: fix potential memory leak
  power: supply: adp5061: fix out-of-bounds read in adp5061_get_chg_type()
  nbd: Fix hung when signal interrupts nbd_start_device_ioctl()
  scsi: 3w-9xxx: Avoid disabling device if failing to enable it
  media: cx88: Fix a null-ptr-deref bug in buffer_prepare()
  ARM: dts: imx6sx: add missing properties for sram
  ARM: dts: imx6sll: add missing properties for sram
  ARM: dts: imx6sl: add missing properties for sram
  ARM: dts: imx6qp: add missing properties for sram
  ARM: dts: imx6dl: add missing properties for sram
  ARM: dts: imx6q: add missing properties for sram
  ARM: dts: imx7d-sdb: config the max pressure for tsc2046
  drm/amdgpu: fix initial connector audio value
  platform/x86: msi-laptop: Change DMI match / alias strings to fix module autoloading
  drm: panel-orientation-quirks: Add quirk for Anbernic Win600
  drm/vc4: vec: Fix timings for VEC modes
  drm/amd/display: fix overflow on MIN_I64 definition
  drm: Prevent drm_copy_field() to attempt copying a NULL pointer
  drm: Use size_t type for len variable in drm_copy_field()
  r8152: Rate limit overflow messages
  Bluetooth: L2CAP: Fix user-after-free
  net: If sock is dead don't access sock's sk_wq in sk_stream_wait_memory
  wifi: rt2x00: correctly set BBP register 86 for MT7620
  wifi: rt2x00: set SoC wmac clock register
  wifi: rt2x00: set correct TX_SW_CFG1 MAC register for MT7620
  wifi: rt2x00: don't run Rt5592 IQ calibration on MT7620
  can: bcm: check the result of can_send() in bcm_can_tx()
  Bluetooth: hci_sysfs: Fix attempting to call device_add multiple times
  Bluetooth: L2CAP: initialize delayed works at l2cap_chan_create()
  wifi: brcmfmac: fix use-after-free bug in brcmf_netdev_start_xmit()
  xfrm: Update ipcomp_scratches with NULL when freed
  wifi: ath9k: avoid uninit memory read in ath9k_htc_rx_msg()
  tcp: annotate data-race around tcp_md5sig_pool_populated
  openvswitch: Fix overreporting of drops in dropwatch
  openvswitch: Fix double reporting of drops in dropwatch
  wifi: brcmfmac: fix invalid address access when enabling SCAN log level
  NFSD: Return nfserr_serverfault if splice_ok but buf->pages have data
  thermal: intel_powerclamp: Use get_cpu() instead of smp_processor_id() to avoid crash
  powercap: intel_rapl: fix UBSAN shift-out-of-bounds issue
  MIPS: BCM47XX: Cast memcmp() of function to (void *)
  ACPI: video: Add Toshiba Satellite/Portege Z830 quirk
  f2fs: fix race condition on setting FI_NO_EXTENT flag
  crypto: cavium - prevent integer overflow loading firmware
  iommu/iova: Fix module config properly
  iommu/omap: Fix buffer overflow in debugfs
  powerpc: Fix SPE Power ISA properties for e500v1 platforms
  powerpc/64s: Fix GENERIC_CPU build flags for PPC970 / G5
  x86/hyperv: Fix 'struct hv_enlightened_vmcs' definition
  powerpc/powernv: add missing of_node_put() in opal_export_attrs()
  powerpc/pci_dn: Add missing of_node_put()
  powerpc/sysdev/fsl_msi: Add missing of_node_put()
  powerpc/math_emu/efp: Include module.h
  mailbox: bcm-ferxrm-mailbox: Fix error check for dma_map_sg
  clk: ti: dra7-atl: Fix reference leak in of_dra7_atl_clk_probe
  clk: bcm2835: fix bcm2835_clock_rate_from_divisor declaration
  spmi: pmic-arb: correct duplicate APID to PPID mapping logic
  dmaengine: ioat: stop mod_timer from resurrecting deleted timer in __cleanup()
  mfd: sm501: Add check for platform_driver_register()
  mfd: lp8788: Fix an error handling path in lp8788_irq_init() and lp8788_irq_init()
  mfd: lp8788: Fix an error handling path in lp8788_probe()
  mfd: fsl-imx25: Fix an error handling path in mx25_tsadc_setup_irq()
  mfd: intel_soc_pmic: Fix an error handling path in intel_soc_pmic_i2c_probe()
  fsi: core: Check error number after calling ida_simple_get
  serial: 8250: Fix restoring termios speed after suspend
  firmware: google: Test spinlock on panic path to avoid lockups
  staging: vt6655: fix some erroneous memory clean-up loops
  phy: qualcomm: call clk_disable_unprepare in the error handling
  drivers: serial: jsm: fix some leaks in probe
  usb: gadget: function: fix dangling pnp_string in f_printer.c
  xhci: Don't show warning for reinit on known broken suspend
  md/raid5: Ensure stripe_fill happens on non-read IO with journal
  ata: fix ata_id_has_dipm()
  ata: fix ata_id_has_ncq_autosense()
  ata: fix ata_id_has_devslp()
  ata: fix ata_id_sense_reporting_enabled() and ata_id_has_sense_reporting()
  mtd: devices: docg3: check the return value of devm_ioremap() in the probe
  dyndbg: let query-modname override actual module name
  dyndbg: fix module.dyndbg handling
  RDMA/rxe: Fix the error caused by qp->sk
  RDMA/rxe: Fix "kernel NULL pointer dereference" error
  media: xilinx: vipp: Fix refcount leak in xvip_graph_dma_init
  tty: xilinx_uartps: Fix the ignore_status
  media: exynos4-is: fimc-is: Add of_node_put() when breaking out of loop
  HSI: omap_ssi_port: Fix dma_map_sg error check
  HSI: omap_ssi: Fix refcount leak in ssi_probe
  clk: tegra20: Fix refcount leak in tegra20_clock_init
  clk: tegra: Fix refcount leak in tegra114_clock_init
  clk: tegra: Fix refcount leak in tegra210_clock_init
  clk: berlin: Add of_node_put() for of_get_parent()
  clk: oxnas: Hold reference returned by of_get_parent()
  iio: ABI: Fix wrong format of differential capacitance channel ABI.
  iio: inkern: only release the device node when done with it
  iio: adc: at91-sama5d2_adc: check return status for pressure and touch
  iio: adc: at91-sama5d2_adc: fix AT91_SAMA5D2_MR_TRACKTIM_MAX
  ARM: dts: exynos: fix polarity of VBUS GPIO of Origen
  ARM: Drop CMDLINE_* dependency on ATAGS
  ARM: dts: exynos: correct s5k6a3 reset polarity on Midas family
  ARM: dts: kirkwood: lsxl: remove first ethernet port
  ARM: dts: kirkwood: lsxl: fix serial line
  ARM: dts: turris-omnia: Fix mpp26 pin name and comment
  soc: qcom: smem_state: Add refcounting for the 'state->of_node'
  soc: qcom: smsm: Fix refcount leak bugs in qcom_smsm_probe()
  memory: of: Fix refcount leak bug in of_get_ddr_timings()
  ASoC: wm5102: Fix PM disable depth imbalance in wm5102_probe
  ASoC: wm5110: Fix PM disable depth imbalance in wm5110_probe
  ASoC: wm8997: Fix PM disable depth imbalance in wm8997_probe
  mmc: wmt-sdmmc: Fix an error handling path in wmt_mci_probe()
  ALSA: dmaengine: increment buffer pointer atomically
  drm/msm/dpu: index dpu_kms->hw_vbif using vbif_idx
  ASoC: eureka-tlv320: Hold reference returned from of_find_xxx API
  mmc: au1xmmc: Fix an error handling path in au1xmmc_probe()
  drm/bridge: megachips: Fix a null pointer dereference bug
  platform/x86: msi-laptop: Fix resource cleanup
  platform/x86: msi-laptop: Fix old-ec check for backlight registering
  platform/chrome: fix double-free in chromeos_laptop_prepare()
  drm/mipi-dsi: Detach devices when removing the host
  drm: bridge: adv7511: fix CEC power down control register offset
  net: mvpp2: fix mvpp2 debugfs leak
  once: add DO_ONCE_SLOW() for sleepable contexts
  bnx2x: fix potential memory leak in bnx2x_tpa_stop()
  net: rds: don't hold sock lock when cancelling work from rds_tcp_reset_callbacks()
  tcp: fix tcp_cwnd_validate() to not forget is_cwnd_limited
  sctp: handle the error returned from sctp_auth_asoc_init_active_key
  mISDN: fix use-after-free bugs in l1oip timer handlers
  vhost/vsock: Use kvmalloc/kvfree for larger packets.
  spi: s3c64xx: Fix large transfers with DMA
  netfilter: nft_fib: Fix for rpath check with VRF devices
  spi/omap100k:Fix PM disable depth imbalance in omap1_spi100k_probe
  bpf: Ensure correct locking around vulnerable function find_vpid()
  net: fs_enet: Fix wrong check in do_pd_setup
  wifi: rtl8xxxu: gen2: Fix mistake in path B IQ calibration
  bpf: btf: fix truncated last_member_type_id in btf_struct_resolve
  wifi: rtl8xxxu: Fix skb misuse in TX queue selection
  spi: qup: add missing clk_disable_unprepare on error in spi_qup_pm_resume_runtime()
  spi: qup: add missing clk_disable_unprepare on error in spi_qup_resume()
  wifi: rtl8xxxu: tighten bounds checking in rtl8xxxu_read_efuse()
  spi: mt7621: Fix an error message in mt7621_spi_probe()
  bpftool: Fix a wrong type cast in btf_dumper_int
  wifi: mac80211: allow bw change during channel switch in mesh
  wifi: ath10k: add peer map clean up for peer delete in ath10k_sta_state()
  sh: machvec: Use char[] for section boundaries
  selinux: use "grep -E" instead of "egrep"
  KVM: nVMX: Unconditionally purge queued/injected events on nested "exit"
  KVM: x86/emulator: Fix handing of POP SS to correctly set interruptibility
  ring-buffer: Fix race between reset page and reading page
  ring-buffer: Check pending waiters when doing wake ups as well
  ring-buffer: Allow splice to read previous partially read pages
  ftrace: Properly unset FTRACE_HASH_FL_MOD
  livepatch: fix race between fork and KLP transition
  ext4: place buffer head allocation before handle start
  ext4: make ext4_lazyinit_thread freezable
  ext4: fix null-ptr-deref in ext4_write_info
  ext4: avoid crash when inline data creation follows DIO write
  nilfs2: fix use-after-free bug of struct nilfs_root
  riscv: fix build with binutils 2.38
  btrfs: fix race between quota enable and quota rescan ioctl
  fbdev: smscufx: Fix use-after-free in ufx_ops_open()
  PCI: Sanitise firmware BAR assignments behind a PCI-PCI bridge
  UM: cpuinfo: Fix a warning for CONFIG_CPUMASK_OFFSTACK
  riscv: Allow PROT_WRITE-only mmap()
  parisc: fbdev/stifb: Align graphics memory size to 4MB
  Revert "fs: check FMODE_LSEEK to control internal pipe splicing"
  regulator: qcom_rpm: Fix circular deferral regression
  quota: Check next/prev free block number after reading from quota file
  HID: multitouch: Add memory barriers
  fs: dlm: handle -EBUSY first in lock arg validation
  fs: dlm: fix race between test_bit() and queue_work()
  can: kvaser_usb_leaf: Fix CAN state after restart
  can: kvaser_usb_leaf: Fix TX queue out of sync after restart
  can: kvaser_usb_leaf: Fix overread with an invalid command
  can: kvaser_usb: Fix use of uninitialized completion
  usb: add quirks for Lenovo OneLink+ Dock
  iio: dac: ad5593r: Fix i2c read protocol requirements
  mtd: rawnand: atmel: Unmap streaming DMA mappings
  ALSA: hda/realtek: remove ALC289_FIXUP_DUAL_SPK for Dell 5530
  ALSA: usb-audio: Fix NULL dererence at error path
  ALSA: usb-audio: Fix potential memory leaks
  ALSA: rawmidi: Drop register_mutex in snd_rawmidi_free()
  ALSA: oss: Fix potential deadlock at unregistration
  Input: xpad - fix wireless 360 controller breaking after suspend
  Input: xpad - add supported devices as contributed on github
  wifi: mac80211_hwsim: avoid mac80211 warning on bad rate
  random: use expired timer rather than wq for mixing fast pool
  random: avoid reading two cache lines on irq randomness
  random: restore O_NONBLOCK support
  USB: serial: qcserial: add new usb-id for Dell branded EM7455
  scsi: stex: Properly zero out the passthrough command structure
  ALSA: hda: Fix position reporting on Poulsbo
  random: clamp credited irq bits to maximum mixed
  ceph: don't truncate file in atomic_open
  nilfs2: replace WARN_ONs by nilfs_error for checkpoint acquisition failure
  nilfs2: fix leak of nilfs_root in case of writer thread creation failure
  nilfs2: fix NULL pointer dereference at nilfs_bmap_lookup_at_level()
  rpmsg: qcom: glink: replace strncpy() with strscpy_pad()
  mmc: core: Terminate infinite loop in SD-UHS voltage switch
  mmc: core: Replace with already defined values for readability
  USB: serial: ftdi_sio: fix 300 bps rate for SIO
  usb: mon: make mmapped memory read only
  um: Cleanup compiler warning in arch/x86/um/tls_32.c
  um: Cleanup syscall_handler_t cast in syscalls_32.h
  net/ieee802154: fix uninit value bug in dgram_sendmsg
  scsi: qedf: Fix a UAF bug in __qedf_probe()
  ARM: dts: fix Moxa SDIO 'compatible', remove 'sdhci' misnomer
  dmaengine: xilinx_dma: Report error in case of dma_set_mask_and_coherent API failure
  dmaengine: xilinx_dma: cleanup for fetching xlnx,num-fstores property
  firmware: arm_scmi: Add SCMI PM driver remove routine
  fs: fix UAF/GPF bug in nilfs_mdt_destroy
  ARM: fix function graph tracer and unwinder dependencies
  docs: update mediator information in CoC docs
  Makefile.extrawarn: Move -Wcast-function-type-strict to W=1
  BACKPORT: arm64: compat: vdso: Use legacy syscalls as fallback
  ANDROID: Drop explicit 'CONFIG_INIT_STACK_ALL_ZERO=y' from gki_defconfig
  UPSTREAM: hardening: Remove Clang's enable flag for -ftrivial-auto-var-init=zero
  UPSTREAM: hardening: Avoid harmless Clang option under CONFIG_INIT_STACK_ALL_ZERO
  UPSTREAM: hardening: Clarify Kconfig text for auto-var-init
  ANDROID: Fix kenelci build-break for !CONFIG_PERF_EVENTS
  UPSTREAM: f2fs: guarantee to write dirty data when enabling checkpoint back

 Conflicts:
	arch/arm64/include/asm/cpucaps.h
	arch/arm64/kernel/cpu_errata.c
	drivers/rpmsg/qcom_glink_native.c
	scripts/Makefile.extrawarn

Change-Id: I88d2e91d5148d4d7ccd442213795c7544ecd8b0b
2022-11-24 03:35:09 +02:00

645 lines
17 KiB
C

/*
* Copyright (c) 2015, Sony Mobile Communications Inc.
* Copyright (c) 2012-2013,2019,2021 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.
*/
#include <linux/interrupt.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/regmap.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
/*
* This driver implements the Qualcomm Shared Memory State Machine, a mechanism
* for communicating single bit state information to remote processors.
*
* The implementation is based on two sections of shared memory; the first
* holding the state bits and the second holding a matrix of subscription bits.
*
* The state bits are structured in entries of 32 bits, each belonging to one
* system in the SoC. The entry belonging to the local system is considered
* read-write, while the rest should be considered read-only.
*
* The subscription matrix consists of N bitmaps per entry, denoting interest
* in updates of the entry for each of the N hosts. Upon updating a state bit
* each host's subscription bitmap should be queried and the remote system
* should be interrupted if they request so.
*
* The subscription matrix is laid out in entry-major order:
* entry0: [host0 ... hostN]
* .
* .
* entryM: [host0 ... hostN]
*
* A third, optional, shared memory region might contain information regarding
* the number of entries in the state bitmap as well as number of columns in
* the subscription matrix.
*/
/*
* Shared memory identifiers, used to acquire handles to respective memory
* region.
*/
#define SMEM_SMSM_SHARED_STATE 85
#define SMEM_SMSM_CPU_INTR_MASK 333
#define SMEM_SMSM_SIZE_INFO 419
/*
* Default sizes, in case SMEM_SMSM_SIZE_INFO is not found.
*/
#define SMSM_DEFAULT_NUM_ENTRIES 8
#define SMSM_DEFAULT_NUM_HOSTS 3
struct smsm_entry;
struct smsm_host;
/**
* struct qcom_smsm - smsm driver context
* @dev: smsm device pointer
* @local_host: column in the subscription matrix representing this system
* @num_hosts: number of columns in the subscription matrix
* @num_entries: number of entries in the state map and rows in the subscription
* matrix
* @local_state: pointer to the local processor's state bits
* @subscription: pointer to local processor's row in subscription matrix
* @state: smem state handle
* @lock: spinlock for read-modify-write of the outgoing state
* @entries: context for each of the entries
* @hosts: context for each of the hosts
*/
struct qcom_smsm {
struct device *dev;
u32 local_host;
u32 num_hosts;
u32 num_entries;
u32 *local_state;
u32 *subscription;
struct qcom_smem_state *state;
spinlock_t lock;
struct smsm_entry *entries;
struct smsm_host *hosts;
int irq;
};
/**
* struct smsm_entry - per remote processor entry context
* @smsm: back-reference to driver context
* @domain: IRQ domain for this entry, if representing a remote system
* @irq_enabled: bitmap of which state bits IRQs are enabled
* @irq_rising: bitmap tracking if rising bits should be propagated
* @irq_falling: bitmap tracking if falling bits should be propagated
* @last_value: snapshot of state bits last time the interrupts where propagated
* @remote_state: pointer to this entry's state bits
* @subscription: pointer to a row in the subscription matrix representing this
* entry
*/
struct smsm_entry {
struct qcom_smsm *smsm;
struct irq_domain *domain;
DECLARE_BITMAP(irq_enabled, 32);
DECLARE_BITMAP(irq_rising, 32);
DECLARE_BITMAP(irq_falling, 32);
unsigned long last_value;
u32 *remote_state;
u32 *subscription;
};
/**
* struct smsm_host - representation of a remote host
* @ipc_regmap: regmap for outgoing interrupt
* @ipc_offset: offset in @ipc_regmap for outgoing interrupt
* @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt
*/
struct smsm_host {
struct regmap *ipc_regmap;
int ipc_offset;
int ipc_bit;
};
/**
* smsm_update_bits() - change bit in outgoing entry and inform subscribers
* @data: smsm context pointer
* @offset: bit in the entry
* @value: new value
*
* Used to set and clear the bits in the outgoing/local entry and inform
* subscribers about the change.
*/
static int smsm_update_bits(void *data, u32 mask, u32 value)
{
struct qcom_smsm *smsm = data;
struct smsm_host *hostp;
unsigned long flags;
u32 changes;
u32 host;
u32 orig;
u32 val;
spin_lock_irqsave(&smsm->lock, flags);
/* Update the entry */
val = orig = readl(smsm->local_state);
val &= ~mask;
val |= value;
/* Don't signal if we didn't change the value */
changes = val ^ orig;
if (!changes) {
spin_unlock_irqrestore(&smsm->lock, flags);
goto done;
}
/* Write out the new value */
writel(val, smsm->local_state);
spin_unlock_irqrestore(&smsm->lock, flags);
/* Make sure the value update is ordered before any kicks */
wmb();
/* Iterate over all hosts to check whom wants a kick */
for (host = 0; host < smsm->num_hosts; host++) {
hostp = &smsm->hosts[host];
val = readl(smsm->subscription + host);
if (val & changes && hostp->ipc_regmap) {
regmap_write(hostp->ipc_regmap,
hostp->ipc_offset,
BIT(hostp->ipc_bit));
}
}
done:
return 0;
}
static const struct qcom_smem_state_ops smsm_state_ops = {
.update_bits = smsm_update_bits,
};
/**
* smsm_intr() - cascading IRQ handler for SMSM
* @irq: unused
* @data: entry related to this IRQ
*
* This function cascades an incoming interrupt from a remote system, based on
* the state bits and configuration.
*/
static irqreturn_t smsm_intr(int irq, void *data)
{
struct smsm_entry *entry = data;
unsigned i;
int irq_pin;
u32 changed;
u32 val;
val = readl(entry->remote_state);
changed = val ^ xchg(&entry->last_value, val);
for_each_set_bit(i, entry->irq_enabled, 32) {
if (!(changed & BIT(i)))
continue;
if (val & BIT(i)) {
if (test_bit(i, entry->irq_rising)) {
irq_pin = irq_find_mapping(entry->domain, i);
handle_nested_irq(irq_pin);
}
} else {
if (test_bit(i, entry->irq_falling)) {
irq_pin = irq_find_mapping(entry->domain, i);
handle_nested_irq(irq_pin);
}
}
}
return IRQ_HANDLED;
}
/**
* smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit
* @irqd: IRQ handle to be masked
*
* This un-subscribes the local CPU from interrupts upon changes to the defines
* status bit. The bit is also cleared from cascading.
*/
static void smsm_mask_irq(struct irq_data *irqd)
{
struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
irq_hw_number_t irq = irqd_to_hwirq(irqd);
struct qcom_smsm *smsm = entry->smsm;
u32 val;
if (entry->subscription) {
val = readl(entry->subscription + smsm->local_host);
val &= ~BIT(irq);
writel(val, entry->subscription + smsm->local_host);
}
clear_bit(irq, entry->irq_enabled);
}
/**
* smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit
* @irqd: IRQ handle to be unmasked
*
* This subscribes the local CPU to interrupts upon changes to the defined
* status bit. The bit is also marked for cascading.
*/
static void smsm_unmask_irq(struct irq_data *irqd)
{
struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
irq_hw_number_t irq = irqd_to_hwirq(irqd);
struct qcom_smsm *smsm = entry->smsm;
u32 val;
/* Make sure our last cached state is up-to-date */
if (readl(entry->remote_state) & BIT(irq))
set_bit(irq, &entry->last_value);
else
clear_bit(irq, &entry->last_value);
set_bit(irq, entry->irq_enabled);
if (entry->subscription) {
val = readl(entry->subscription + smsm->local_host);
val |= BIT(irq);
writel(val, entry->subscription + smsm->local_host);
}
}
/**
* smsm_set_irq_type() - updates the requested IRQ type for the cascading
* @irqd: consumer interrupt handle
* @type: requested flags
*/
static int smsm_set_irq_type(struct irq_data *irqd, unsigned int type)
{
struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
irq_hw_number_t irq = irqd_to_hwirq(irqd);
if (!(type & IRQ_TYPE_EDGE_BOTH))
return -EINVAL;
if (type & IRQ_TYPE_EDGE_RISING)
set_bit(irq, entry->irq_rising);
else
clear_bit(irq, entry->irq_rising);
if (type & IRQ_TYPE_EDGE_FALLING)
set_bit(irq, entry->irq_falling);
else
clear_bit(irq, entry->irq_falling);
return 0;
}
static struct irq_chip smsm_irq_chip = {
.name = "smsm",
.irq_mask = smsm_mask_irq,
.irq_unmask = smsm_unmask_irq,
.irq_set_type = smsm_set_irq_type,
};
/**
* smsm_irq_map() - sets up a mapping for a cascaded IRQ
* @d: IRQ domain representing an entry
* @irq: IRQ to set up
* @hw: unused
*/
static int smsm_irq_map(struct irq_domain *d,
unsigned int irq,
irq_hw_number_t hw)
{
struct smsm_entry *entry = d->host_data;
irq_set_chip_and_handler(irq, &smsm_irq_chip, handle_level_irq);
irq_set_chip_data(irq, entry);
irq_set_nested_thread(irq, 1);
irq_set_noprobe(irq);
irq_set_parent(irq, entry->smsm->irq);
irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
return 0;
}
static const struct irq_domain_ops smsm_irq_ops = {
.map = smsm_irq_map,
.xlate = irq_domain_xlate_twocell,
};
/**
* smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
* @smsm: smsm driver context
* @host_id: index of the remote host to be resolved
*
* Parses device tree to acquire the information needed for sending the
* outgoing interrupts to a remote host - identified by @host_id.
*/
static int smsm_parse_ipc(struct qcom_smsm *smsm, unsigned host_id)
{
struct device_node *syscon;
struct device_node *node = smsm->dev->of_node;
struct smsm_host *host = &smsm->hosts[host_id];
char key[16];
int ret;
snprintf(key, sizeof(key), "qcom,ipc-%d", host_id);
syscon = of_parse_phandle(node, key, 0);
if (!syscon)
return 0;
host->ipc_regmap = syscon_node_to_regmap(syscon);
of_node_put(syscon);
if (IS_ERR(host->ipc_regmap))
return PTR_ERR(host->ipc_regmap);
ret = of_property_read_u32_index(node, key, 1, &host->ipc_offset);
if (ret < 0) {
dev_err(smsm->dev, "no offset in %s\n", key);
return -EINVAL;
}
ret = of_property_read_u32_index(node, key, 2, &host->ipc_bit);
if (ret < 0) {
dev_err(smsm->dev, "no bit in %s\n", key);
return -EINVAL;
}
return 0;
}
/**
* smsm_inbound_entry() - parse DT and set up an entry representing a remote system
* @smsm: smsm driver context
* @entry: entry context to be set up
* @node: dt node containing the entry's properties
*/
static int smsm_inbound_entry(struct qcom_smsm *smsm,
struct smsm_entry *entry,
struct device_node *node)
{
int ret;
smsm->irq = irq_of_parse_and_map(node, 0);
if (!smsm->irq) {
dev_err(smsm->dev, "failed to parse smsm interrupt\n");
return -EINVAL;
}
ret = devm_request_threaded_irq(smsm->dev, smsm->irq,
NULL, smsm_intr,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
"smsm", (void *)entry);
if (ret) {
dev_err(smsm->dev, "failed to request interrupt\n");
return ret;
}
entry->domain = irq_domain_add_linear(node, 32, &smsm_irq_ops, entry);
if (!entry->domain) {
dev_err(smsm->dev, "failed to add irq_domain\n");
return -ENOMEM;
}
return 0;
}
/**
* smsm_get_size_info() - parse the optional memory segment for sizes
* @smsm: smsm driver context
*
* Attempt to acquire the number of hosts and entries from the optional shared
* memory location. Not being able to find this segment should indicate that
* we're on a older system where these values was hard coded to
* SMSM_DEFAULT_NUM_ENTRIES and SMSM_DEFAULT_NUM_HOSTS.
*
* Returns 0 on success, negative errno on failure.
*/
static int smsm_get_size_info(struct qcom_smsm *smsm)
{
size_t size;
struct {
u32 num_hosts;
u32 num_entries;
u32 reserved0;
u32 reserved1;
} *info;
info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SIZE_INFO, &size);
if (IS_ERR(info) && PTR_ERR(info) != -ENOENT) {
if (PTR_ERR(info) != -EPROBE_DEFER)
dev_err(smsm->dev, "unable to retrieve smsm size info\n");
return PTR_ERR(info);
} else if (IS_ERR(info) || size != sizeof(*info)) {
dev_warn(smsm->dev, "no smsm size info, using defaults\n");
smsm->num_entries = SMSM_DEFAULT_NUM_ENTRIES;
smsm->num_hosts = SMSM_DEFAULT_NUM_HOSTS;
return 0;
}
smsm->num_entries = info->num_entries;
smsm->num_hosts = info->num_hosts;
dev_dbg(smsm->dev,
"found custom size of smsm: %d entries %d hosts\n",
smsm->num_entries, smsm->num_hosts);
return 0;
}
static int qcom_smsm_probe(struct platform_device *pdev)
{
struct device_node *local_node;
struct device_node *node;
struct smsm_entry *entry;
struct qcom_smsm *smsm;
u32 *intr_mask;
size_t size;
u32 *states;
u32 id;
int ret;
smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL);
if (!smsm)
return -ENOMEM;
smsm->dev = &pdev->dev;
spin_lock_init(&smsm->lock);
ret = smsm_get_size_info(smsm);
if (ret)
return ret;
smsm->entries = devm_kcalloc(&pdev->dev,
smsm->num_entries,
sizeof(struct smsm_entry),
GFP_KERNEL);
if (!smsm->entries)
return -ENOMEM;
smsm->hosts = devm_kcalloc(&pdev->dev,
smsm->num_hosts,
sizeof(struct smsm_host),
GFP_KERNEL);
if (!smsm->hosts)
return -ENOMEM;
for_each_child_of_node(pdev->dev.of_node, local_node) {
if (of_find_property(local_node, "#qcom,smem-state-cells", NULL))
break;
}
if (!local_node) {
dev_err(&pdev->dev, "no state entry\n");
return -EINVAL;
}
of_property_read_u32(pdev->dev.of_node,
"qcom,local-host",
&smsm->local_host);
/* Parse the host properties */
for (id = 0; id < smsm->num_hosts; id++) {
ret = smsm_parse_ipc(smsm, id);
if (ret < 0)
goto out_put;
}
/* Acquire the main SMSM state vector */
ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE,
smsm->num_entries * sizeof(u32));
if (ret < 0 && ret != -EEXIST) {
dev_err(&pdev->dev, "unable to allocate shared state entry\n");
goto out_put;
}
states = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE, NULL);
if (IS_ERR(states)) {
dev_err(&pdev->dev, "Unable to acquire shared state entry\n");
ret = PTR_ERR(states);
goto out_put;
}
/* Acquire the list of interrupt mask vectors */
size = smsm->num_entries * smsm->num_hosts * sizeof(u32);
ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK, size);
if (ret < 0 && ret != -EEXIST) {
dev_err(&pdev->dev, "unable to allocate smsm interrupt mask\n");
goto out_put;
}
intr_mask = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK, NULL);
if (IS_ERR(intr_mask)) {
dev_err(&pdev->dev, "unable to acquire shared memory interrupt mask\n");
ret = PTR_ERR(intr_mask);
goto out_put;
}
/* Setup the reference to the local state bits */
smsm->local_state = states + smsm->local_host;
smsm->subscription = intr_mask + smsm->local_host * smsm->num_hosts;
/* Register the outgoing state */
smsm->state = qcom_smem_state_register(local_node, &smsm_state_ops, smsm);
if (IS_ERR(smsm->state)) {
dev_err(smsm->dev, "failed to register qcom_smem_state\n");
ret = PTR_ERR(smsm->state);
goto out_put;
}
/* Register handlers for remote processor entries of interest. */
for_each_available_child_of_node(pdev->dev.of_node, node) {
if (!of_property_read_bool(node, "interrupt-controller"))
continue;
ret = of_property_read_u32(node, "reg", &id);
if (ret || id >= smsm->num_entries) {
dev_err(&pdev->dev, "invalid reg of entry\n");
if (!ret)
ret = -EINVAL;
goto unwind_interfaces;
}
entry = &smsm->entries[id];
entry->smsm = smsm;
entry->remote_state = states + id;
/* Setup subscription pointers and unsubscribe to any kicks */
entry->subscription = intr_mask + id * smsm->num_hosts;
writel(0, entry->subscription + smsm->local_host);
ret = smsm_inbound_entry(smsm, entry, node);
if (ret < 0)
goto unwind_interfaces;
}
platform_set_drvdata(pdev, smsm);
of_node_put(local_node);
return 0;
unwind_interfaces:
of_node_put(node);
for (id = 0; id < smsm->num_entries; id++)
if (smsm->entries[id].domain)
irq_domain_remove(smsm->entries[id].domain);
qcom_smem_state_unregister(smsm->state);
out_put:
of_node_put(local_node);
return ret;
}
static int qcom_smsm_remove(struct platform_device *pdev)
{
struct qcom_smsm *smsm = platform_get_drvdata(pdev);
unsigned id;
for (id = 0; id < smsm->num_entries; id++)
if (smsm->entries[id].domain)
irq_domain_remove(smsm->entries[id].domain);
qcom_smem_state_unregister(smsm->state);
return 0;
}
static const struct of_device_id qcom_smsm_of_match[] = {
{ .compatible = "qcom,smsm" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_smsm_of_match);
static struct platform_driver qcom_smsm_driver = {
.probe = qcom_smsm_probe,
.remove = qcom_smsm_remove,
.driver = {
.name = "qcom-smsm",
.of_match_table = qcom_smsm_of_match,
},
};
module_platform_driver(qcom_smsm_driver);
MODULE_DESCRIPTION("Qualcomm Shared Memory State Machine driver");
MODULE_LICENSE("GPL v2");