Skip to content

Commit

Permalink
soundwire: intel: add mutex to prevent concurrent access to SHIM regi…
Browse files Browse the repository at this point in the history
…sters

Some of the SHIM registers exposed fields that are link specific, and
in addition some of the power-related registers (SPA/CPA) take time to
be updated. Uncontrolled access leads to timeouts or errors.

Add a mutex, shared by all links, so that all accesses to such
registers are serialized, and follow a pattern of read-modify-write.

BugLink: thesofproject#1555
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
  • Loading branch information
plbossart authored and kv2019i committed May 15, 2020
1 parent d8d9063 commit a24b7aa
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 6 deletions.
37 changes: 31 additions & 6 deletions drivers/soundwire/intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,13 +284,17 @@ static int intel_link_power_up(struct sdw_intel *sdw)
int spa_mask, cpa_mask;
int link_control, ret;

mutex_lock(sdw->link_res->shim_lock);

/* Link power up sequence */
link_control = intel_readl(shim, SDW_SHIM_LCTL);
spa_mask = (SDW_SHIM_LCTL_SPA << link_id);
cpa_mask = (SDW_SHIM_LCTL_CPA << link_id);
link_control |= spa_mask;

ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
mutex_unlock(sdw->link_res->shim_lock);

if (ret < 0)
return ret;

Expand All @@ -305,6 +309,8 @@ static int intel_shim_init(struct sdw_intel *sdw)
int sync_reg, ret;
u16 ioctl = 0, act = 0;

mutex_lock(sdw->link_res->shim_lock);

/* Initialize Shim */
ioctl |= SDW_SHIM_IOCTL_BKE;
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
Expand Down Expand Up @@ -349,6 +355,8 @@ static int intel_shim_init(struct sdw_intel *sdw)
sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
SDW_SHIM_SYNC_SYNCCPU);
mutex_unlock(sdw->link_res->shim_lock);

if (ret < 0)
dev_err(sdw->cdns.dev, "Failed to set sync period: %d\n", ret);

Expand All @@ -361,13 +369,15 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
unsigned int link_id = sdw->instance;
u16 wake_en, wake_sts;

mutex_lock(sdw->link_res->shim_lock);
wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);

if (wake_enable) {
/* Enable the wakeup */
intel_writew(shim, SDW_SHIM_WAKEEN,
(SDW_SHIM_WAKEEN_ENABLE << link_id));
wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
} else {
/* Disable the wake up interrupt */
wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);
wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);
intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);

Expand All @@ -376,6 +386,7 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts);
}
mutex_unlock(sdw->link_res->shim_lock);
}

static int intel_link_power_down(struct sdw_intel *sdw)
Expand All @@ -385,6 +396,8 @@ static int intel_link_power_down(struct sdw_intel *sdw)
void __iomem *shim = sdw->link_res->shim;
u16 ioctl;

mutex_lock(sdw->link_res->shim_lock);

/* Glue logic */
ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
ioctl |= SDW_SHIM_IOCTL_BKE;
Expand All @@ -401,6 +414,8 @@ static int intel_link_power_down(struct sdw_intel *sdw)
link_control &= spa_mask;

ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
mutex_unlock(sdw->link_res->shim_lock);

if (ret < 0)
return ret;

Expand Down Expand Up @@ -628,11 +643,15 @@ static int intel_pre_bank_switch(struct sdw_bus *bus)
if (!bus->multi_link)
return 0;

mutex_lock(sdw->link_res->shim_lock);

/* Read SYNC register */
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance;
intel_writel(shim, SDW_SHIM_SYNC, sync_reg);

mutex_unlock(sdw->link_res->shim_lock);

return 0;
}

Expand All @@ -647,6 +666,8 @@ static int intel_post_bank_switch(struct sdw_bus *bus)
if (!bus->multi_link)
return 0;

mutex_lock(sdw->link_res->shim_lock);

/* Read SYNC register */
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);

Expand All @@ -658,9 +679,10 @@ static int intel_post_bank_switch(struct sdw_bus *bus)
*
* So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
*/
if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK))
return 0;

if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
ret = 0;
goto unlock;
}
/*
* Set SyncGO bit to synchronously trigger a bank switch for
* all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
Expand All @@ -670,6 +692,9 @@ static int intel_post_bank_switch(struct sdw_bus *bus)

ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
SDW_SHIM_SYNC_SYNCGO);
unlock:
mutex_unlock(sdw->link_res->shim_lock);

if (ret < 0)
dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);

Expand Down
2 changes: 2 additions & 0 deletions drivers/soundwire/intel.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* @irq: Interrupt line
* @ops: Shim callback ops
* @dev: device implementing hw_params and free callbacks
* @shim_lock: mutex to handle access to shared SHIM registers
*/
struct sdw_intel_link_res {
struct sdw_master_device *md;
Expand All @@ -25,6 +26,7 @@ struct sdw_intel_link_res {
int irq;
const struct sdw_intel_ops *ops;
struct device *dev;
struct mutex *shim_lock; /* protect shared registers */
};

#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
Expand Down
2 changes: 2 additions & 0 deletions drivers/soundwire/intel_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ static struct sdw_intel_ctx
ctx->mmio_base = res->mmio_base;
ctx->link_mask = res->link_mask;
ctx->handle = res->handle;
mutex_init(&ctx->shim_lock);

link = ctx->links;
link_mask = ctx->link_mask;
Expand All @@ -207,6 +208,7 @@ static struct sdw_intel_ctx
link->ops = res->ops;
link->dev = res->dev;
link->clock_stop_quirks = res->clock_stop_quirks;
link->shim_lock = &ctx->shim_lock;

md = sdw_master_device_add(res->parent,
acpi_fwnode_handle(adev),
Expand Down

0 comments on commit a24b7aa

Please sign in to comment.