diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2011-07-18 13:33:41 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:47:51 -0800 |
commit | 8ba216b8b79af2c1e5ae7c9f268d08e7804d946f (patch) | |
tree | b24bfff3e966cf53486ef9b12e64008068656dbb /drivers/spi/spi_slave_tegra.c | |
parent | 65515cd7cb21733a2b1edc40ef7ce80e8d999689 (diff) |
spi: tegra: Select best clock source for required rate
Providing the different clock source option through platform data
to select best clock source based on required interface frequency.
bug 851642
Original-Change-Id: I18bf817b63cf1afac7db3969f266cc5fcaeee81e
Reviewed-on: http://git-master/r/41226
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Tested-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
Rebase-Id: Ra4e4573414ef2c4e72cdcb4cd5625e242cfb4ec6
Diffstat (limited to 'drivers/spi/spi_slave_tegra.c')
-rw-r--r-- | drivers/spi/spi_slave_tegra.c | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/drivers/spi/spi_slave_tegra.c b/drivers/spi/spi_slave_tegra.c index 05bbc6e9fa77..807a9be2b13b 100644 --- a/drivers/spi/spi_slave_tegra.c +++ b/drivers/spi/spi_slave_tegra.c @@ -236,6 +236,12 @@ struct spi_tegra_data { callback client_slave_ready_cb; void *client_data; + + struct spi_clk_parent *parent_clk_list; + int parent_clk_count; + unsigned long max_rate; + unsigned long max_parent_rate; + int min_div; }; static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, @@ -589,6 +595,64 @@ static int spi_tegra_start_cpu_based_transfer( return 0; } +static void set_best_clk_source(struct spi_tegra_data *tspi, + unsigned long speed) +{ + long new_rate; + unsigned long err_rate; + int rate = speed * 4; + unsigned int fin_err = speed * 4; + int final_index = -1; + int count; + int ret; + struct clk *pclk; + unsigned long prate, crate, nrate; + unsigned long cdiv; + + if (!tspi->parent_clk_count || !tspi->parent_clk_list) + return; + + /* make sure divisor is more than min_div */ + pclk = clk_get_parent(tspi->clk); + prate = clk_get_rate(pclk); + crate = clk_get_rate(tspi->clk); + cdiv = DIV_ROUND_UP(prate, crate); + if (cdiv < tspi->min_div) { + nrate = DIV_ROUND_UP(prate, tspi->min_div); + clk_set_rate(tspi->clk, nrate); + } + + for (count = 0; count < tspi->parent_clk_count; ++count) { + if (!tspi->parent_clk_list[count].parent_clk) + continue; + ret = clk_set_parent(tspi->clk, + tspi->parent_clk_list[count].parent_clk); + if (ret < 0) { + dev_warn(&tspi->pdev->dev, "Error in setting parent " + " clk src %s\n", + tspi->parent_clk_list[count].name); + continue; + } + + new_rate = clk_round_rate(tspi->clk, rate); + if (new_rate < 0) + continue; + + err_rate = abs(new_rate - rate); + if (err_rate < fin_err) { + final_index = count; + fin_err = err_rate; + } + } + + if (final_index >= 0) { + dev_info(&tspi->pdev->dev, "Setting clk_src %s\n", + tspi->parent_clk_list[final_index].name); + clk_set_parent(tspi->clk, + tspi->parent_clk_list[final_index].parent_clk); + } +} + static void spi_tegra_start_transfer(struct spi_device *spi, struct spi_transfer *t) { @@ -612,6 +676,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi, speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; if (speed != tspi->cur_speed) { + set_best_clk_source(tspi, speed); clk_set_rate(tspi->clk, speed * 4); tspi->cur_speed = speed; } @@ -1009,6 +1074,7 @@ static int __init spi_tegra_probe(struct platform_device *pdev) struct resource *r; struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; int ret; + int i; master = spi_alloc_master(&pdev->dev, sizeof *tspi); if (master == NULL) { @@ -1083,10 +1149,30 @@ static int __init spi_tegra_probe(struct platform_device *pdev) tspi->is_dma_allowed = pdata->is_dma_based; tspi->dma_buf_size = (pdata->max_dma_buffer) ? pdata->max_dma_buffer : DEFAULT_SPI_DMA_BUF_LEN; + tspi->parent_clk_count = pdata->parent_clk_count; + tspi->parent_clk_list = pdata->parent_clk_list; + tspi->max_rate = pdata->max_rate; } else { tspi->is_clkon_always = false; tspi->is_dma_allowed = true; tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN; + tspi->parent_clk_count = 0; + tspi->parent_clk_list = NULL; + tspi->max_rate = 0; + } + + tspi->max_parent_rate = 0; + tspi->min_div = 0; + + if (tspi->parent_clk_count) { + tspi->max_parent_rate = tspi->parent_clk_list[0].fixed_clk_rate; + for (i = 1; i < tspi->parent_clk_count; ++i) { + tspi->max_parent_rate = max(tspi->max_parent_rate, + tspi->parent_clk_list[i].fixed_clk_rate); + } + if (tspi->max_rate) + tspi->min_div = DIV_ROUND_UP(tspi->max_parent_rate, + tspi->max_rate); } tspi->max_buf_size = SLINK_FIFO_DEPTH << 2; |