ASoC: wcd-spi: add SPI bus arbitration logic
On some platforms, WCD SPI bus can be shared with other processors (ex: sensor processor). In such cases, there is need for software to arbitrate the bus control. Add functionality to perform SPI bus arbitration. Change-Id: I7df933f55ac5035a55173a04e74b74f7af1f7ece Signed-off-by: Bhalchandra Gajare <gajare@codeaurora.org>
This commit is contained in:

committed by
Gerrit - the friendly Code Review server

parent
e039f16fa4
commit
c5f43a1eec
@@ -14,8 +14,10 @@
|
|||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/component.h>
|
#include <linux/component.h>
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <sound/wcd-dsp-mgr.h>
|
#include <sound/wcd-dsp-mgr.h>
|
||||||
#include <sound/wcd-spi.h>
|
#include <sound/wcd-spi.h>
|
||||||
|
#include <soc/wcd-spi-ac.h>
|
||||||
#include "wcd-spi-registers.h"
|
#include "wcd-spi-registers.h"
|
||||||
|
|
||||||
/* Byte manipulations */
|
/* Byte manipulations */
|
||||||
@@ -157,6 +159,8 @@ struct wcd_spi_priv {
|
|||||||
/* DMA handles for transfer buffers */
|
/* DMA handles for transfer buffers */
|
||||||
dma_addr_t tx_dma;
|
dma_addr_t tx_dma;
|
||||||
dma_addr_t rx_dma;
|
dma_addr_t rx_dma;
|
||||||
|
/* Handle to child (qmi client) device */
|
||||||
|
struct device *ac_dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum xfer_request {
|
enum xfer_request {
|
||||||
@@ -567,6 +571,19 @@ static int wcd_spi_clk_enable(struct spi_device *spi)
|
|||||||
int ret;
|
int ret;
|
||||||
u32 rd_status = 0;
|
u32 rd_status = 0;
|
||||||
|
|
||||||
|
/* Get the SPI access first */
|
||||||
|
if (wcd_spi->ac_dev) {
|
||||||
|
ret = wcd_spi_access_ctl(wcd_spi->ac_dev,
|
||||||
|
WCD_SPI_ACCESS_REQUEST,
|
||||||
|
WCD_SPI_AC_DATA_TRANSFER);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"%s: Can't get spi access, err = %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = wcd_spi_cmd_nop(spi);
|
ret = wcd_spi_cmd_nop(spi);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n",
|
dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n",
|
||||||
@@ -620,6 +637,17 @@ static int wcd_spi_clk_disable(struct spi_device *spi)
|
|||||||
*/
|
*/
|
||||||
clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask);
|
clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask);
|
||||||
|
|
||||||
|
/* once the clock is released, SPI access can be released as well */
|
||||||
|
if (wcd_spi->ac_dev) {
|
||||||
|
ret = wcd_spi_access_ctl(wcd_spi->ac_dev,
|
||||||
|
WCD_SPI_ACCESS_RELEASE,
|
||||||
|
WCD_SPI_AC_DATA_TRANSFER);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"%s: SPI access release failed, err = %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,6 +972,18 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
|
|||||||
__func__, event);
|
__func__, event);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
case WDSP_EVENT_PRE_SHUTDOWN:
|
||||||
|
if (wcd_spi->ac_dev) {
|
||||||
|
ret = wcd_spi_access_ctl(wcd_spi->ac_dev,
|
||||||
|
WCD_SPI_ACCESS_REQUEST,
|
||||||
|
WCD_SPI_AC_REMOTE_DOWN);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"%s: request access failed %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WDSP_EVENT_POST_SHUTDOWN:
|
case WDSP_EVENT_POST_SHUTDOWN:
|
||||||
cancel_delayed_work_sync(&wcd_spi->clk_dwork);
|
cancel_delayed_work_sync(&wcd_spi->clk_dwork);
|
||||||
WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
|
WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
|
||||||
@@ -953,6 +993,18 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
|
|||||||
WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
|
WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WDSP_EVENT_POST_BOOTUP:
|
||||||
|
if (wcd_spi->ac_dev) {
|
||||||
|
ret = wcd_spi_access_ctl(wcd_spi->ac_dev,
|
||||||
|
WCD_SPI_ACCESS_RELEASE,
|
||||||
|
WCD_SPI_AC_REMOTE_DOWN);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"%s: release access failed %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WDSP_EVENT_PRE_DLOAD_CODE:
|
case WDSP_EVENT_PRE_DLOAD_CODE:
|
||||||
case WDSP_EVENT_PRE_DLOAD_DATA:
|
case WDSP_EVENT_PRE_DLOAD_DATA:
|
||||||
ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
|
ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
|
||||||
@@ -1299,10 +1351,50 @@ static struct regmap_config wcd_spi_regmap_cfg = {
|
|||||||
.readable_reg = wcd_spi_is_readable_reg,
|
.readable_reg = wcd_spi_is_readable_reg,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int wcd_spi_add_ac_dev(struct device *dev,
|
||||||
|
struct device_node *node)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = to_spi_device(dev);
|
||||||
|
struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
|
||||||
|
struct platform_device *pdev;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pdev = platform_device_alloc("wcd-spi-ac", -1);
|
||||||
|
if (IS_ERR_OR_NULL(pdev)) {
|
||||||
|
ret = PTR_ERR(pdev);
|
||||||
|
dev_err(dev, "%s: pdev alloc failed, ret = %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdev->dev.parent = dev;
|
||||||
|
pdev->dev.of_node = node;
|
||||||
|
|
||||||
|
ret = platform_device_add(pdev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "%s: pdev add failed, ret = %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
goto dealloc_pdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
wcd_spi->ac_dev = &pdev->dev;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dealloc_pdev:
|
||||||
|
platform_device_put(pdev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int wdsp_spi_init(struct device *dev, void *priv_data)
|
static int wdsp_spi_init(struct device *dev, void *priv_data)
|
||||||
{
|
{
|
||||||
struct spi_device *spi = to_spi_device(dev);
|
struct spi_device *spi = to_spi_device(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
struct device_node *node;
|
||||||
|
|
||||||
|
for_each_child_of_node(dev->of_node, node) {
|
||||||
|
if (!strcmp(node->name, "wcd_spi_ac"))
|
||||||
|
wcd_spi_add_ac_dev(dev, node);
|
||||||
|
}
|
||||||
|
|
||||||
ret = wcd_spi_init(spi);
|
ret = wcd_spi_init(spi);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
Reference in New Issue
Block a user