From 6a464ae275505cc075405e57c42aac231f62038e Mon Sep 17 00:00:00 2001 From: Dan Sneddon Date: Mon, 28 Apr 2014 14:50:31 -0600 Subject: [PATCH] spi: spi_qsd: Add multi-ee support. Add support for the same spi controller to be programmed by the CPU and other execution environments rather than the controller hw being owned by only one ee. Change-Id: Id6560b9cb09ddf59045fdf48c713d14c5c013145 Signed-off-by: Dan Sneddon --- drivers/spi/spi_qsd.c | 124 ++++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 34 deletions(-) diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index 937d281cb008..1db434486fc6 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -1740,6 +1740,69 @@ error: msm_spi_free_cs_gpio(dd); } +static void reset_core(struct msm_spi *dd) +{ + msm_spi_register_init(dd); + /* + * The SPI core generates a bogus input overrun error on some targets, + * when a transition from run to reset state occurs and if the FIFO has + * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN + * bit. + */ + msm_spi_enable_error_flags(dd); + + writel_relaxed(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL); + msm_spi_set_state(dd, SPI_OP_STATE_RESET); +} + +static void put_local_resources(struct msm_spi *dd) +{ + msm_spi_disable_irqs(dd); + clk_disable_unprepare(dd->clk); + clk_disable_unprepare(dd->pclk); + + /* Free the spi clk, miso, mosi, cs gpio */ + if (dd->pdata && dd->pdata->gpio_release) + dd->pdata->gpio_release(); + + msm_spi_free_gpios(dd); +} + +static int get_local_resources(struct msm_spi *dd) +{ + int ret = -EINVAL; + /* Configure the spi clk, miso, mosi and cs gpio */ + if (dd->pdata->gpio_config) { + ret = dd->pdata->gpio_config(); + if (ret) { + dev_err(dd->dev, + "%s: error configuring GPIOs\n", + __func__); + return ret; + } + } + + ret = msm_spi_request_gpios(dd); + if (ret) + return ret; + + ret = clk_prepare_enable(dd->clk); + if (ret) + goto clk0_err; + ret = clk_prepare_enable(dd->pclk); + if (ret) + goto clk1_err; + msm_spi_enable_irqs(dd); + + return 0; + +clk1_err: + clk_disable_unprepare(dd->clk); +clk0_err: + msm_spi_free_gpios(dd); + return ret; +} + /** * msm_spi_transfer_one_message: To process one spi message at a time * @master: spi master controller reference @@ -1773,6 +1836,7 @@ static int msm_spi_transfer_one_message(struct spi_master *master, status_error = -EINVAL; msg->status = status_error; spi_finalize_current_message(master); + put_local_resources(dd); return 0; } } @@ -1783,6 +1847,21 @@ static int msm_spi_transfer_one_message(struct spi_master *master, dd->transfer_pending = 1; dd->cur_msg = msg; spin_unlock_irqrestore(&dd->queue_lock, flags); + /* + * get local resources for each transfer to ensure we're in a good + * state and not interfering with other EE's using this device + */ + if (get_local_resources(dd)) + return -EINVAL; + + reset_core(dd); + if (dd->use_dma) { + msm_spi_bam_pipe_connect(dd, &dd->bam.prod, + &dd->bam.prod.config); + msm_spi_bam_pipe_connect(dd, &dd->bam.cons, + &dd->bam.cons.config); + } + if (dd->suspended || !msm_spi_is_valid_state(dd)) { dev_err(dd->dev, "%s: SPI operational state not valid\n", __func__); @@ -1808,6 +1887,17 @@ static int msm_spi_transfer_one_message(struct spi_master *master, if (dd->suspended) wake_up_interruptible(&dd->continue_suspend); + /* + * Put local resources prior to calling finalize to ensure the hw + * is in a known state before notifying the calling thread (which is a + * different context since we're running in the spi kthread here) to + * prevent race conditions between us and any other EE's using this hw. + */ + put_local_resources(dd); + if (dd->use_dma) { + msm_spi_bam_pipe_disconnect(dd, &dd->bam.prod); + msm_spi_bam_pipe_disconnect(dd, &dd->bam.cons); + } dd->cur_msg->status = status_error; spi_finalize_current_message(master); return 0; @@ -2102,15 +2192,9 @@ static int msm_spi_bam_pipe_init(struct msm_spi *dd, memset(pipe_conf->desc.base, 0x00, pipe_conf->desc.size); pipe->handle = pipe_handle; - rc = msm_spi_bam_pipe_connect(dd, pipe, pipe_conf); - if (rc) - goto connect_err; return 0; -connect_err: - dma_free_coherent(dd->dev, pipe_conf->desc.size, - pipe_conf->desc.base, pipe_conf->desc.phys_base); config_err: sps_free_endpoint(pipe_handle); @@ -2666,18 +2750,9 @@ static int msm_spi_pm_suspend_runtime(struct device *device) wait_event_interruptible(dd->continue_suspend, !dd->transfer_pending); - msm_spi_disable_irqs(dd); - clk_disable_unprepare(dd->clk); - clk_disable_unprepare(dd->pclk); if (dd->pdata && !dd->pdata->active_only) msm_spi_clk_path_unvote(dd); - /* Free the spi clk, miso, mosi, cs gpio */ - if (dd->pdata && dd->pdata->gpio_release) - dd->pdata->gpio_release(); - - msm_spi_free_gpios(dd); - suspend_exit: return 0; } @@ -2687,7 +2762,6 @@ static int msm_spi_pm_resume_runtime(struct device *device) struct platform_device *pdev = to_platform_device(device); struct spi_master *master = platform_get_drvdata(pdev); struct msm_spi *dd; - int ret = 0; dev_dbg(device, "pm_runtime: resuming...\n"); if (!master) @@ -2699,27 +2773,9 @@ static int msm_spi_pm_resume_runtime(struct device *device) if (!dd->suspended) return 0; - /* Configure the spi clk, miso, mosi and cs gpio */ - if (dd->pdata->gpio_config) { - ret = dd->pdata->gpio_config(); - if (ret) { - dev_err(dd->dev, - "%s: error configuring GPIOs\n", - __func__); - return ret; - } - } - - ret = msm_spi_request_gpios(dd); - if (ret) - return ret; - msm_spi_clk_path_init(dd); if (!dd->pdata->active_only) msm_spi_clk_path_vote(dd); - clk_prepare_enable(dd->clk); - clk_prepare_enable(dd->pclk); - msm_spi_enable_irqs(dd); dd->suspended = 0; resume_exit: