• Richard Fitzgerald's avatar
    ASoC: cs35l56: Fix unintended bus access while resetting amp · d4884fd4
    Richard Fitzgerald authored
    Use the new regmap_read_bypassed() so that the regmap can be left
    in cache-only mode while it is booting, but the driver can still
    read boot-status and chip-id information during this time.
    
    This fixes race conditions where some writes could be issued to the
    silicon while it is still rebooting, before the driver has determined
    that the boot is complete.
    
    This is typically prevented by putting regmap into cache-only until the
    hardware is ready. But this assumes that the driver does not need to
    access device registers to determine when it is "ready". For cs35l56
    this involves polling a register and the original implementation relied
    on having special handlers to block racing callbacks until dsp_work()
    is complete. However, some cases were missed, most notably the ASP DAI
    functions.
    
    The regmap_read_bypassed() function allows the fix for this to be
    simplified to putting regmap into cache-only during the reset. The
    initial boot stages (poll HALO_STATE and read the chip ID) are all done
    bypassed. Only when the amp is seen to be booted is the cache-only
    revoked.
    
    Changes are:
    - cs35l56_system_reset() now leaves the regmap in cache-only status.
    
    - cs35l56_wait_for_firmware_boot() polls using regmap_read_bypassed().
    
    - cs35l56_init() revokes cache-only either via cs35l56_hw_init() or
      when firmware has rebooted after a soft reset.
    
    - cs35l56_hw_init() exits cache-only after it has determined that the
      amp has booted.
    
    - cs35l56_sdw_init() doesn't disable cache-only, since this must be
      deferred to cs35l56_init().
    
    - cs35l56_runtime_resume_common() waits for firmware boot before exiting
      cache-only.
    
    These changes cover three situations where the registers are not
    accessible:
    
    1) SoundWire first-time enumeration. The regmap is kept in cache-only
       until the chip is fully booted. The original code had to exit
       cache-only to read chip status in cs35l56_init() and cs35l56_hw_init()
       but this is now deferred to after the firmware has rebooted.
    
       In this case cs35l56_sdw_probe() leaves regmap in cache-only
       (unchanged behaviour) and cs35l56_hw_init() exits cache-only after the
       firmware is booted and the chip identified.
    
    2) Soft reset during first-time initialization. cs35l56_init() calls
       cs35l56_system_reset(), which puts regmap into cache-only.
       On I2C/SPI cs35l56_init() then flows through to call
       cs35l56_wait_for_firmware_boot() and exit cache-only. On SoundWire
       the re-enumeration will enter cs35l56_init() again, which then drops
       down to call cs35l56_wait_for_firmware_boot() and exit cache-only.
    
    3) Soft reset after firmware download. dsp_work() calls
       cs35l56_system_reset(), which puts regmap into cache-only. After this
       the flow is the same as (2).
    Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
    Fixes: 8a731fd3 ("ASoC: cs35l56: Move utility functions to shared file")
    Link: https://msgid.link/r/20240408101803.43183-4-rf@opensource.cirrus.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
    d4884fd4
cs35l56-shared.c 29.3 KB