From c5f43a1eec4aa25caabc4a799340ea0ca496040e Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Mon, 19 Mar 2018 14:35:42 -0700 Subject: [PATCH] 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 --- asoc/codecs/wcd-spi.c | 92 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/asoc/codecs/wcd-spi.c b/asoc/codecs/wcd-spi.c index 145c4df690..0173e151cb 100644 --- a/asoc/codecs/wcd-spi.c +++ b/asoc/codecs/wcd-spi.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include "wcd-spi-registers.h" /* Byte manipulations */ @@ -157,6 +159,8 @@ struct wcd_spi_priv { /* DMA handles for transfer buffers */ dma_addr_t tx_dma; dma_addr_t rx_dma; + /* Handle to child (qmi client) device */ + struct device *ac_dev; }; enum xfer_request { @@ -567,6 +571,19 @@ static int wcd_spi_clk_enable(struct spi_device *spi) int ret; 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); if (ret < 0) { 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); + /* 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; } @@ -944,6 +972,18 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, __func__, 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: cancel_delayed_work_sync(&wcd_spi->clk_dwork); 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); 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_DATA: 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, }; +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) { struct spi_device *spi = to_spi_device(dev); 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); if (ret < 0)