Przeglądaj źródła

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 <[email protected]>
Bhalchandra Gajare 7 lat temu
rodzic
commit
c5f43a1eec
1 zmienionych plików z 92 dodań i 0 usunięć
  1. 92 0
      asoc/codecs/wcd-spi.c

+ 92 - 0
asoc/codecs/wcd-spi.c

@@ -14,8 +14,10 @@
 #include <linux/regmap.h>
 #include <linux/component.h>
 #include <linux/ratelimit.h>
+#include <linux/platform_device.h>
 #include <sound/wcd-dsp-mgr.h>
 #include <sound/wcd-spi.h>
+#include <soc/wcd-spi-ac.h>
 #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)