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 at the controller level, shared by all links, so that all
accesses to such registers are serialized, and follow a pattern of
read-modify-write.

GitHub issue: #1555
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
  • Loading branch information
plbossart committed Dec 5, 2019
1 parent 6270e2c commit ad96056
Show file tree
Hide file tree
Showing 3 changed files with 36 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 @@ -307,13 +307,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 @@ -328,6 +332,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 @@ -372,6 +378,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 @@ -384,13 +392,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 @@ -399,6 +409,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 @@ -408,6 +419,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 @@ -424,6 +437,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 @@ -651,11 +666,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 @@ -670,6 +689,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 @@ -681,9 +702,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 @@ -693,6 +715,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 @@ -27,6 +28,7 @@ struct sdw_intel_link_res {
struct device *dev;
struct sdw_cdns *cdns;
struct list_head list;
struct mutex *shim_lock; /* protect shared registers */
};

#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
Expand Down
3 changes: 3 additions & 0 deletions drivers/soundwire/intel_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,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 Down Expand Up @@ -290,6 +291,8 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
if (link_mask && !(link_mask & BIT(i)))
continue;

link->shim_lock = &ctx->shim_lock;

md = link->md;

md->driver->startup(md);
Expand Down

0 comments on commit ad96056

Please sign in to comment.