From 2c45b9ceabce36f6873f227c09bedc2be9b3ca00 Mon Sep 17 00:00:00 2001 From: Kirill Artamonov Date: Mon, 30 Jan 2012 23:08:37 +0200 Subject: video: tegra: remove free memory check Used free memory check in allocation policy is not working, because it doesn't calculate available physical memory size in same way as android oom killer. It also breaks kernel build if swapping is enabled. Remove free memory check from allocation policy. Change-Id: I214d1829451f313dbace967e87ed4111e688865d Reviewed-on: http://git-master/r/85227 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/video/tegra/nvmap/nvmap_handle.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/nvmap/nvmap_handle.c b/drivers/video/tegra/nvmap/nvmap_handle.c index 2f24ba515862..2e2b8b3d46a1 100644 --- a/drivers/video/tegra/nvmap/nvmap_handle.c +++ b/drivers/video/tegra/nvmap/nvmap_handle.c @@ -38,11 +38,8 @@ #include #include -#include -#include #include #include - #include "nvmap.h" #include "nvmap_mru.h" #include "nvmap_common.h" @@ -751,10 +748,6 @@ static const unsigned int heap_policy_large[] = { 0, }; -/* Do not override single page policy if there is not much space to -avoid invoking system oom killer. */ -#define NVMAP_SMALL_POLICY_SYSMEM_THRESHOLD 50000000 - int nvmap_alloc_handle_id(struct nvmap_client *client, unsigned long id, unsigned int heap_mask, size_t align, unsigned int flags) @@ -792,15 +785,7 @@ int nvmap_alloc_handle_id(struct nvmap_client *client, if (heap_mask & NVMAP_HEAP_IOVMM) heap_mask |= NVMAP_HEAP_SYSMEM; else if (heap_mask & NVMAP_HEAP_CARVEOUT_GENERIC) { - /* Calculate size of free physical pages - * managed by kernel */ - unsigned long freeMem = - (global_page_state(NR_FREE_PAGES) + - global_page_state(NR_FILE_PAGES) - - total_swapcache_pages) << PAGE_SHIFT; - - if (freeMem > NVMAP_SMALL_POLICY_SYSMEM_THRESHOLD) - heap_mask |= NVMAP_HEAP_SYSMEM; + heap_mask |= NVMAP_HEAP_SYSMEM; } } #endif -- cgit v1.2.3 From 5abc30ce92eee652cdadc3f88f1496d6941affe6 Mon Sep 17 00:00:00 2001 From: Preetham Chandru Date: Mon, 16 Apr 2012 13:18:57 +0530 Subject: video: tegra: dsi: enable dsi panel B Enable dsi panel B by setting the first bit in APB_MISC_GP_MIPI_PAD_CTRL_0 register. Bug 935764 Signed-off-by: Preetham Chandru R Change-Id: I9e958e0c9d9e934edf77688fd6a987b5e863392b Reviewed-on: http://git-master/r/96672 Reviewed-by: Shashank Sharma Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Kiran Adduri Reviewed-by: Animesh Kishore Reviewed-by: Laxman Dewangan --- drivers/video/tegra/dc/dsi.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index 69cc60f70f1c..e402c416b779 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/dsi.c * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA Corporation. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "dc_reg.h" @@ -38,6 +39,9 @@ #include "dsi_regs.h" #include "dsi.h" +#define APB_MISC_GP_MIPI_PAD_CTRL_0 (TEGRA_APB_MISC_BASE + 0x820) +#define DSIB_MODE_ENABLE 0x2 + #define DSI_USE_SYNC_POINTS 1 #define S_TO_MS(x) (1000 * (x)) @@ -1621,6 +1625,15 @@ static void tegra_dsi_pad_calibration(struct tegra_dc_dsi_data *dsi) tegra_vi_csi_writel(val, CSI_CIL_PAD_CONFIG); } +static void tegra_dsi_panelB_enable() +{ + unsigned int val; + + val = readl(IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0)); + val |= DSIB_MODE_ENABLE; + writel(val, (IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0))); +} + static int tegra_dsi_init_hw(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi) { @@ -1634,7 +1647,7 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc, tegra_dsi_set_dsi_clk(dc, dsi, dsi->target_lp_clk_khz); if (dsi->info.dsi_instance) { - /* TODO:Set the misc register*/ + tegra_dsi_panelB_enable(); } /* TODO: only need to change the timing for bta */ -- cgit v1.2.3 From 2f8a84dcc28dfb3fc9638dadc6692b8f96c5bb77 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 23 May 2012 13:00:01 +0530 Subject: spi: tegra: cleanup in runtime_pm implementation. Cleaning up runtime pm implementation for the driver. There is lots of duplicate code which is not require as it is handled in the runtime framework. Change-Id: I4494cdd3518cbcb90f24fb3387f38c9859b4f957 Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/102206 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Bitan Biswas --- drivers/spi/spi-tegra.c | 158 ++++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 86 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index 4901114f746c..0a0a72f3437f 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -247,6 +247,9 @@ struct spi_tegra_data { struct work_struct spi_transfer_work; }; +static int tegra_spi_runtime_idle(struct device *dev); +static int tegra_spi_runtime_resume(struct device *dev); + static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, unsigned long reg) { @@ -731,12 +734,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi, command2 = tspi->def_command2_reg; if (is_first_of_msg) { - if (!tspi->is_clkon_always) { - if (!tspi->clk_state) { - pm_runtime_get_sync(&tspi->pdev->dev); - tspi->clk_state = 1; - } - } + pm_runtime_get_sync(&tspi->pdev->dev); spi_tegra_clear_status(tspi); @@ -848,6 +846,8 @@ static int spi_tegra_setup(struct spi_device *spi) return -EINVAL; } + pm_runtime_get_sync(&tspi->pdev->dev); + spin_lock_irqsave(&tspi->lock, flags); val = tspi->def_command_reg; if (spi->mode & SPI_CS_HIGH) @@ -855,20 +855,10 @@ static int spi_tegra_setup(struct spi_device *spi) else val &= ~cs_bit; tspi->def_command_reg = val; - - if (!tspi->is_clkon_always && !tspi->clk_state) { - spin_unlock_irqrestore(&tspi->lock, flags); - pm_runtime_get_sync(&tspi->pdev->dev); - spin_lock_irqsave(&tspi->lock, flags); - tspi->clk_state = 1; - } spi_tegra_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); - if (!tspi->is_clkon_always && tspi->clk_state) { - tspi->clk_state = 0; - spin_unlock_irqrestore(&tspi->lock, flags); - pm_runtime_put_sync(&tspi->pdev->dev); - } else - spin_unlock_irqrestore(&tspi->lock, flags); + spin_unlock_irqrestore(&tspi->lock, flags); + + pm_runtime_put_sync(&tspi->pdev->dev); return 0; } @@ -1010,19 +1000,11 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi, SLINK_COMMAND); spi_tegra_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); - if (!tspi->is_clkon_always) { - if (tspi->clk_state) { - /* Provide delay to stablize the signal - state */ - spin_unlock_irqrestore(&tspi->lock, - *irq_flags); - udelay(10); - pm_runtime_put_sync(&tspi->pdev->dev); - spin_lock_irqsave(&tspi->lock, - *irq_flags); - tspi->clk_state = 0; - } - } + /* Provide delay to stablize the signal state */ + spin_unlock_irqrestore(&tspi->lock, *irq_flags); + udelay(10); + pm_runtime_put_sync(&tspi->pdev->dev); + spin_lock_irqsave(&tspi->lock, *irq_flags); tspi->is_transfer_in_progress = false; /* Check if any new request has come between * clock disable */ @@ -1391,17 +1373,20 @@ static int __init spi_tegra_probe(struct platform_device *pdev) skip_dma_alloc: pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); - tspi->clk_state = 1; - master->dev.of_node = pdev->dev.of_node; - ret = spi_register_master(master); - if (!tspi->is_clkon_always) { - if (tspi->clk_state) { - pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_spi_runtime_resume(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "runtime resume failed %d", ret); + goto err_pm_disable; } } + /* Enable clock if it is require to be enable always */ + if (tspi->is_clkon_always) + pm_runtime_get_sync(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); goto fail_master_register; @@ -1424,6 +1409,15 @@ fail_workqueue: spi_unregister_master(master); fail_master_register: + if (tspi->is_clkon_always) + pm_runtime_put_sync(&pdev->dev); + + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_spi_runtime_idle(&pdev->dev); + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + if (tspi->tx_buf) dma_free_coherent(&pdev->dev, tspi->dma_buf_size, tspi->tx_buf, tspi->tx_buf_phys); @@ -1438,7 +1432,6 @@ fail_rx_buf_alloc: if (tspi->rx_dma) tegra_dma_free_channel(tspi->rx_dma); fail_rx_dma_alloc: - pm_runtime_disable(&pdev->dev); clk_put(tspi->sclk); fail_sclk_get: clk_put(tspi->clk); @@ -1474,12 +1467,14 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) if (tspi->rx_dma) tegra_dma_free_channel(tspi->rx_dma); - if (tspi->is_clkon_always) { + /* Disable clock if it is always enabled */ + if (tspi->is_clkon_always) pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; - } pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_spi_runtime_idle(&pdev->dev); + clk_put(tspi->sclk); clk_put(tspi->clk); iounmap(tspi->base); @@ -1493,20 +1488,18 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) +static int spi_tegra_suspend(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; - unsigned limit = 50; + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); + unsigned limit = 50; unsigned long flags; - master = dev_get_drvdata(&pdev->dev); - tspi = spi_master_get_devdata(master); spin_lock_irqsave(&tspi->lock, flags); /* Wait for all transfer completes */ if (!list_empty(&tspi->queue)) - dev_warn(&pdev->dev, "The transfer list is not empty " + dev_warn(dev, "The transfer list is not empty " "Waiting for time %d ms to complete transfer\n", limit * 20); @@ -1520,7 +1513,7 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) tspi->is_suspended = true; if (!list_empty(&tspi->queue)) { limit = 50; - dev_err(&pdev->dev, "All transfer has not completed, " + dev_err(dev, "All transfer has not completed, " "Waiting for %d ms current transfer to complete\n", limit * 20); while (tspi->is_transfer_in_progress && limit--) { @@ -1531,7 +1524,7 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) } if (tspi->is_transfer_in_progress) { - dev_err(&pdev->dev, + dev_err(dev, "Spi transfer is in progress Avoiding suspend\n"); tspi->is_suspended = false; spin_unlock_irqrestore(&tspi->lock, flags); @@ -1539,33 +1532,32 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) } spin_unlock_irqrestore(&tspi->lock, flags); - if (tspi->is_clkon_always) { - pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; - } + + /* Disable clock if it is always enabled */ + if (tspi->is_clkon_always) + pm_runtime_put_sync(dev); + return 0; } -static int spi_tegra_resume(struct platform_device *pdev) +static int spi_tegra_resume(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); struct spi_message *m; struct spi_device *spi; struct spi_transfer *t = NULL; int single_xfer = 0; unsigned long flags; - master = dev_get_drvdata(&pdev->dev); - tspi = spi_master_get_devdata(master); + /* Enable clock if it is always enabled */ + if (tspi->is_clkon_always) + pm_runtime_get_sync(dev); - pm_runtime_get_sync(&pdev->dev); - tspi->clk_state = 1; + pm_runtime_get_sync(dev); spi_tegra_writel(tspi, tspi->command_reg, SLINK_COMMAND); - if (!tspi->is_clkon_always) { - pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; - } + pm_runtime_put_sync(dev); + spin_lock_irqsave(&tspi->lock, flags); tspi->cur_speed = 0; @@ -1587,15 +1579,12 @@ static int spi_tegra_resume(struct platform_device *pdev) } #endif -#if defined(CONFIG_PM_RUNTIME) - static int tegra_spi_runtime_idle(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; - master = dev_get_drvdata(dev); - tspi = spi_master_get_devdata(master); + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); + tspi->clk_state = 0; clk_disable(tspi->clk); clk_disable(tspi->sclk); return 0; @@ -1603,22 +1592,25 @@ static int tegra_spi_runtime_idle(struct device *dev) static int tegra_spi_runtime_resume(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; - master = dev_get_drvdata(dev); - tspi = spi_master_get_devdata(master); + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); clk_enable(tspi->sclk); clk_enable(tspi->clk); + tspi->clk_state = 1; return 0; } static const struct dev_pm_ops tegra_spi_dev_pm_ops = { +#if defined(CONFIG_PM_RUNTIME) .runtime_idle = tegra_spi_runtime_idle, .runtime_resume = tegra_spi_runtime_resume, -}; - #endif +#ifdef CONFIG_PM + .suspend = spi_tegra_suspend, + .resume = spi_tegra_resume, +#endif +}; MODULE_ALIAS("platform:spi_tegra"); @@ -1636,16 +1628,10 @@ static struct platform_driver spi_tegra_driver = { .driver = { .name = "spi_tegra", .owner = THIS_MODULE, -#if defined(CONFIG_PM_RUNTIME) .pm = &tegra_spi_dev_pm_ops, -#endif .of_match_table = spi_tegra_of_match_table, }, .remove = __devexit_p(spi_tegra_remove), -#ifdef CONFIG_PM - .suspend = spi_tegra_suspend, - .resume = spi_tegra_resume, -#endif }; static int __init spi_tegra_init(void) -- cgit v1.2.3 From 9ef3e8ecc0f1c9a3bfad7c9f4d6a496c4a67924c Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 23 May 2012 19:08:23 +0530 Subject: spi: tegra: synchronize PPSB late write When any write is made to PPSB register, it take time to actual happen in the register due to ARM-PPSB design. Delay or readback is required to make sure that write is completed. There is no worst case guaranteed delay and hence doing the register read to make write completes actually. Change-Id: Iefd25115e1a9f02c64e83f11a4e249ad9d086d16 Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/102207 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bitan Biswas --- drivers/spi/spi-tegra.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index 0a0a72f3437f..070cc1581efd 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -264,6 +264,9 @@ static inline void spi_tegra_writel(struct spi_tegra_data *tspi, if (!tspi->clk_state) BUG(); writel(val, tspi->base + reg); + + /* Synchronize write by reading back the register */ + readl(tspi->base + SLINK_MAS_DATA); } static void cancel_dma(struct tegra_dma_channel *dma_chan, @@ -1584,6 +1587,9 @@ static int tegra_spi_runtime_idle(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct spi_tegra_data *tspi = spi_master_get_devdata(master); + /* Flush all write which are in PPSB queue by reading back */ + spi_tegra_readl(tspi, SLINK_MAS_DATA); + tspi->clk_state = 0; clk_disable(tspi->clk); clk_disable(tspi->sclk); -- cgit v1.2.3 From 1953361f8e492ea6ec5de23a5321a8eaf000563e Mon Sep 17 00:00:00 2001 From: Jon Mayo Date: Tue, 22 May 2012 17:24:27 -0700 Subject: video: tegra: detect fbmem alignment on probe Detect the stride size used by the bootloader. If DC is not enabled, fallback to a default stride size. Bug 973111 Change-Id: If04647ddf04a44987cd841062ff30e03fa4d6a02 Signed-off-by: Jon Mayo Reviewed-on: http://git-master/r/104031 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/video/tegra/dc/dc.c | 17 +++++++++++++++++ drivers/video/tegra/dc/dc_reg.h | 2 ++ drivers/video/tegra/fb.c | 12 ++++++++---- 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index fb171549f78d..935f18bc8cfd 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -583,6 +583,23 @@ static unsigned int tegra_dc_has_multiple_dc(void) return (cnt > 1); } +/* get the stride size of a window. + * return: stride size in bytes for window win. or 0 if unavailble. */ +int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win) +{ + u32 tmp; + u32 stride; + + if (!dc->enabled) + return 0; + BUG_ON(win > DC_N_WINDOWS); + tegra_dc_writel(dc, WINDOW_A_SELECT << win, + DC_CMD_DISPLAY_WINDOW_HEADER); + tmp = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE); + return GET_LINE_STRIDE(tmp); +} +EXPORT_SYMBOL(tegra_dc_get_stride); + struct tegra_dc *tegra_dc_get_dc(unsigned idx) { if (idx < TEGRA_MAX_DC) diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h index 8b84bff45eb3..5eac27adfbf8 100644 --- a/drivers/video/tegra/dc/dc_reg.h +++ b/drivers/video/tegra/dc/dc_reg.h @@ -430,6 +430,8 @@ #define DC_WIN_LINE_STRIDE 0x70a #define LINE_STRIDE(x) (x) #define UV_LINE_STRIDE(x) (((x) & 0xffff) << 16) +#define GET_LINE_STRIDE(x) ((x) & 0xffff) +#define GET_UV_LINE_STRIDE(x) (((x) >> 16) & 0xffff) #define DC_WIN_BUF_STRIDE 0x70b #define DC_WIN_UV_BUF_STRIDE 0x70c #define DC_WIN_BUFFER_ADDR_MODE 0x70d diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index 50aa9b383059..1193a2eb8c52 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -44,7 +44,7 @@ #include "dc/dc_priv.h" /* Pad pitch to 16-byte boundary. */ -#define TEGRA_LINEAR_PITCH_ALIGNMENT 16 +#define TEGRA_LINEAR_PITCH_ALIGNMENT 32 struct tegra_fb_info { struct tegra_dc_win *win; @@ -527,6 +527,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, unsigned long fb_size = 0; unsigned long fb_phys = 0; int ret = 0; + unsigned stride; win = tegra_dc_get_window(dc, fb_data->win); if (!win) { @@ -560,6 +561,11 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, tegra_fb->valid = true; } + stride = tegra_dc_get_stride(dc, 0); + if (!stride) /* default to pad the stride to 16-byte boundary. */ + stride = round_up(info->fix.line_length, + TEGRA_LINEAR_PITCH_ALIGNMENT); + info->fbops = &tegra_fb_ops; info->pseudo_palette = pseudo_palette; info->screen_base = fb_base; @@ -574,9 +580,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, info->fix.smem_start = fb_phys; info->fix.smem_len = fb_size; info->fix.line_length = fb_data->xres * fb_data->bits_per_pixel / 8; - /* Pad the stride to 16-byte boundary. */ - info->fix.line_length = round_up(info->fix.line_length, - TEGRA_LINEAR_PITCH_ALIGNMENT); + info->fix.line_length = stride; info->var.xres = fb_data->xres; info->var.yres = fb_data->yres; -- cgit v1.2.3 From 3a8779385b7d191287f2e6e81fcc2dbcc1188869 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Wed, 7 Mar 2012 18:11:54 -0800 Subject: net: wireless: bcm4329: Fix GCC 4.6 warning This module has -Werror turned on, so this was causing the build to break on GCC 4.6: drivers/net/wireless/bcm4329/wl_iw.c: In function 'wl_iw_set_pmksa': drivers/net/wireless/bcm4329/wl_iw.c:5149:5: error: array subscript is above array bounds [-Werror=array-bounds] drivers/net/wireless/bcm4329/wl_iw.c:5152:5: error: array subscript is above array bounds [-Werror=array-bounds] It's a partial 'backport' of a change made to the bcmdhd driver: commit 09a8dc7361d0e603d9935ec7f736fabaa2e6dc7a net: wireless: bcmdhd: Combined P2P fix Change-Id: Ie62ad82f884c213553772ac91eaf85e17a807503 Signed-off-by: Dan Willemsen Reviewed-on: http://git-master/r/88694 GVS: Gerrit_Virtual_Submit Reviewed-by: Stephen Warren --- drivers/net/wireless/bcm4329/wl_iw.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 434e584f830c..e71ab64c2caa 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -5109,6 +5109,7 @@ wl_iw_set_pmksa( uint i; int ret = 0; char eabuf[ETHER_ADDR_STR_LEN]; + pmkid_t * pmkid_array = pmkid_list.pmkids.pmkid; WL_WSEC(("%s: SIOCSIWPMKSA\n", dev->name)); CHECK_EXTRA_FOR_NULL(extra); @@ -5139,18 +5140,18 @@ wl_iw_set_pmksa( } for (i = 0; i < pmkid_list.pmkids.npmkid; i++) - if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_list.pmkids.pmkid[i].BSSID, + if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID, ETHER_ADDR_LEN)) break; if ((pmkid_list.pmkids.npmkid > 0) && (i < pmkid_list.pmkids.npmkid)) { - bzero(&pmkid_list.pmkids.pmkid[i], sizeof(pmkid_t)); + bzero(&pmkid_array[i], sizeof(pmkid_t)); for (; i < (pmkid_list.pmkids.npmkid - 1); i++) { - bcopy(&pmkid_list.pmkids.pmkid[i+1].BSSID, - &pmkid_list.pmkids.pmkid[i].BSSID, + bcopy(&pmkid_array[i+1].BSSID, + &pmkid_array[i].BSSID, ETHER_ADDR_LEN); - bcopy(&pmkid_list.pmkids.pmkid[i+1].PMKID, - &pmkid_list.pmkids.pmkid[i].PMKID, + bcopy(&pmkid_array[i+1].PMKID, + &pmkid_array[i].PMKID, WPA2_PMKID_LEN); } pmkid_list.pmkids.npmkid--; @@ -5161,14 +5162,14 @@ wl_iw_set_pmksa( else if (iwpmksa->cmd == IW_PMKSA_ADD) { for (i = 0; i < pmkid_list.pmkids.npmkid; i++) - if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_list.pmkids.pmkid[i].BSSID, + if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID, ETHER_ADDR_LEN)) break; if (i < MAXPMKID) { bcopy(&iwpmksa->bssid.sa_data[0], - &pmkid_list.pmkids.pmkid[i].BSSID, + &pmkid_array[i].BSSID, ETHER_ADDR_LEN); - bcopy(&iwpmksa->pmkid[0], &pmkid_list.pmkids.pmkid[i].PMKID, + bcopy(&iwpmksa->pmkid[0], &pmkid_array[i].PMKID, WPA2_PMKID_LEN); if (i == pmkid_list.pmkids.npmkid) pmkid_list.pmkids.npmkid++; @@ -5181,10 +5182,10 @@ wl_iw_set_pmksa( uint k; k = pmkid_list.pmkids.npmkid; WL_WSEC(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ", - bcm_ether_ntoa(&pmkid_list.pmkids.pmkid[k].BSSID, + bcm_ether_ntoa(&pmkid_array[k].BSSID, eabuf))); for (j = 0; j < WPA2_PMKID_LEN; j++) - WL_WSEC(("%02x ", pmkid_list.pmkids.pmkid[k].PMKID[j])); + WL_WSEC(("%02x ", pmkid_array[k].PMKID[j])); WL_WSEC(("\n")); } } @@ -5192,10 +5193,10 @@ wl_iw_set_pmksa( for (i = 0; i < pmkid_list.pmkids.npmkid; i++) { uint j; WL_WSEC(("PMKID[%d]: %s = ", i, - bcm_ether_ntoa(&pmkid_list.pmkids.pmkid[i].BSSID, + bcm_ether_ntoa(&pmkid_array[i].BSSID, eabuf))); for (j = 0; j < WPA2_PMKID_LEN; j++) - WL_WSEC(("%02x ", pmkid_list.pmkids.pmkid[i].PMKID[j])); + WL_WSEC(("%02x ", pmkid_array[i].PMKID[j])); WL_WSEC(("\n")); } WL_WSEC(("\n")); -- cgit v1.2.3 From 64bcd65388b34c8832125b44c0f9b95b0ca755a0 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Fri, 18 May 2012 14:00:51 -0700 Subject: mfd: tps80031: turn on backup battery charger circuit The backup battery for the RTC circuit needs to be manually turned on. This change turns it on when the driver is first probed, off during LP0 to prevent excess power draw, and back on again upon resume. Bug 986402 Change-Id: Id4768929d6a73546662806f04d98d714997174b0 Signed-off-by: Tom Cherry Reviewed-on: http://git-master/r/103425 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Laxman Dewangan --- drivers/mfd/tps80031.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index 94ba777bcdb3..f0f2ce1f6840 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -105,6 +105,9 @@ #define TPS80031_CFG_INPUT_PUPD3 0xF2 #define TPS80031_CFG_INPUT_PUPD4 0xF3 +#define TPS80031_BBSPOR_CFG 0xE6 +#define TPS80031_BBSPOR_CHG_EN 0x8 + struct tps80031_pupd_data { u8 reg; u8 pullup_bit; @@ -568,6 +571,17 @@ static void tps80031_pupd_init(struct tps80031 *tps80031, } } +static void tps80031_backup_battery_charger_control(struct tps80031 *tps80031, + int enable) +{ + if (enable) + tps80031_update(tps80031->dev, SLAVE_ID1, TPS80031_BBSPOR_CFG, + TPS80031_BBSPOR_CHG_EN, TPS80031_BBSPOR_CHG_EN); + else + tps80031_update(tps80031->dev, SLAVE_ID1, TPS80031_BBSPOR_CFG, + 0, TPS80031_BBSPOR_CHG_EN); +} + static void tps80031_init_ext_control(struct tps80031 *tps80031, struct tps80031_platform_data *pdata) { int ret; @@ -1287,6 +1301,8 @@ static int __devinit tps80031_i2c_probe(struct i2c_client *client, tps80031_debuginit(tps80031); + tps80031_backup_battery_charger_control(tps80031, 1); + if (pdata->use_power_off && !pm_power_off) pm_power_off = tps80031_power_off; @@ -1302,13 +1318,17 @@ fail: #ifdef CONFIG_PM static int tps80031_i2c_suspend(struct i2c_client *client, pm_message_t state) { + struct tps80031 *tps80031 = i2c_get_clientdata(client); if (client->irq) disable_irq(client->irq); + tps80031_backup_battery_charger_control(tps80031, 0); return 0; } static int tps80031_i2c_resume(struct i2c_client *client) { + struct tps80031 *tps80031 = i2c_get_clientdata(client); + tps80031_backup_battery_charger_control(tps80031, 1); if (client->irq) enable_irq(client->irq); return 0; -- cgit v1.2.3 From dfa3030e767dca384f5d159c6993d5ee25b416d7 Mon Sep 17 00:00:00 2001 From: Mayuresh Kulkarni Date: Mon, 9 Apr 2012 12:21:24 +0530 Subject: video: tegra: host: rewrite nvhost_job_pin/unpin APIs - remove usage of custom interface of nvmap to patch the relocs and and pin the gathers/relocs - convert code that references nvmap_handle to nvmap_handle_ref - add logic to pin and map only unique gathers and relocs - rename nvhost_channel_gather to nvhost_job_gather, because it's used in nvhost_job code only Bug 965206 Change-Id: Iaa7fbac9e4a7b08c0a7c1e184b8dd3566e1edfe2 Signed-off-by: Mayuresh Kulkarni Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/95299 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Juha Tukkinen --- drivers/video/tegra/host/bus_client.c | 17 +- drivers/video/tegra/host/chip_support.h | 4 +- drivers/video/tegra/host/gr3d/gr3d_t20.c | 2 +- drivers/video/tegra/host/gr3d/gr3d_t30.c | 2 +- drivers/video/tegra/host/host1x/host1x_cdma.c | 2 +- drivers/video/tegra/host/host1x/host1x_channel.c | 4 +- drivers/video/tegra/host/host1x/host1x_debug.c | 17 +- drivers/video/tegra/host/mpe/mpe.c | 2 +- drivers/video/tegra/host/nvhost_cdma.c | 12 +- drivers/video/tegra/host/nvhost_cdma.h | 4 +- drivers/video/tegra/host/nvhost_channel.h | 7 - drivers/video/tegra/host/nvhost_job.c | 285 +++++++++-------------- drivers/video/tegra/host/nvhost_job.h | 27 +-- 13 files changed, 157 insertions(+), 228 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index fd632a6ea9c5..822f8f3a456d 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -166,7 +166,7 @@ static int set_submit(struct nvhost_channel_userctx *ctx) return -EFAULT; } - ctx->job = nvhost_job_realloc(ctx->job, + ctx->job = nvhost_job_alloc(ctx->ch, ctx->hwctx, &ctx->hdr, ctx->nvmap, @@ -241,13 +241,17 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, consumed = sizeof(struct nvhost_reloc); if (remaining < consumed) break; - if (copy_from_user(&job->pinarray[job->num_pins], + if (copy_from_user(&job->pinarray[job->num_relocs], buf, consumed)) { err = -EFAULT; break; } - trace_nvhost_channel_write_reloc(chname); - job->num_pins++; + trace_nvhost_channel_write_reloc(chname, + job->pinarray[job->num_relocs].patch_mem, + job->pinarray[job->num_relocs].patch_offset, + job->pinarray[job->num_relocs].pin_mem, + job->pinarray[job->num_relocs].pin_offset); + job->num_relocs++; hdr->num_relocs--; } else if (hdr->num_waitchks) { int numwaitchks = @@ -269,7 +273,7 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, hdr->num_waitchks -= numwaitchks; } else if (priv->num_relocshifts) { int next_shift = - job->num_pins - priv->num_relocshifts; + job->num_relocs - priv->num_relocshifts; consumed = sizeof(struct nvhost_reloc_shift); if (remaining < consumed) break; @@ -337,6 +341,9 @@ static int nvhost_ioctl_channel_flush( if (err) nvhost_job_unpin(ctx->job); + nvhost_job_put(ctx->job); + ctx->job = NULL; + return err; } diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h index edc5f6a51574..173a36065458 100644 --- a/drivers/video/tegra/host/chip_support.h +++ b/drivers/video/tegra/host/chip_support.h @@ -31,7 +31,7 @@ struct nvhost_syncpt; struct nvhost_waitchk; struct nvhost_userctx_timeout; struct nvhost_channel; -struct nvmap_handle; +struct nvmap_handle_ref; struct nvmap_client; struct nvhost_hwctx; struct nvhost_cdma; @@ -77,7 +77,7 @@ struct nvhost_chip_support { void (*destroy)(struct push_buffer *); void (*push_to)(struct push_buffer *, struct nvmap_client *, - struct nvmap_handle *, + struct nvmap_handle_ref *, u32 op1, u32 op2); void (*pop_from)(struct push_buffer *, unsigned int slots); diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.c b/drivers/video/tegra/host/gr3d/gr3d_t20.c index c0efac03b882..5645f5b2b0c6 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t20.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t20.c @@ -138,7 +138,7 @@ static void save_push_v0(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) nvhost_cdma_push_gather(cdma, nvhost_get_host(nctx->channel->dev)->nvmap, - p->save_buf->handle, + p->save_buf, 0, nvhost_opcode_gather(p->save_size), p->save_phys); diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.c b/drivers/video/tegra/host/gr3d/gr3d_t30.c index 93d98dfa645c..57f4c779eff8 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t30.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t30.c @@ -145,7 +145,7 @@ static void save_push_v1(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) /* gather the save buffer */ nvhost_cdma_push_gather(cdma, nvhost_get_host(nctx->channel->dev)->nvmap, - p->save_buf->handle, + p->save_buf, 0, nvhost_opcode_gather(p->save_size), p->save_phys); diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.c b/drivers/video/tegra/host/host1x/host1x_cdma.c index fcb1f05f0025..4569c3d62494 100644 --- a/drivers/video/tegra/host/host1x/host1x_cdma.c +++ b/drivers/video/tegra/host/host1x/host1x_cdma.c @@ -137,7 +137,7 @@ static void push_buffer_destroy(struct push_buffer *pb) */ static void push_buffer_push_to(struct push_buffer *pb, struct nvmap_client *client, - struct nvmap_handle *handle, u32 op1, u32 op2) + struct nvmap_handle_ref *handle, u32 op1, u32 op2) { u32 cur = pb->cur; u32 *p = (u32 *)((u32)pb->mapped + cur); diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c index 8c4a7a5c74ad..0b4d07cb9e47 100644 --- a/drivers/video/tegra/host/host1x/host1x_channel.c +++ b/drivers/video/tegra/host/host1x/host1x_channel.c @@ -143,7 +143,7 @@ static void submit_ctxrestore(struct nvhost_job *job) /* Send restore buffer to channel */ nvhost_cdma_push_gather(&ch->cdma, host->nvmap, - nvmap_ref_to_handle(ctx->restore), + ctx->restore, 0, nvhost_opcode_gather(ctx->restore_size), ctx->restore_phys); @@ -188,7 +188,7 @@ void submit_gathers(struct nvhost_job *job) u32 op2 = job->gathers[i].mem; nvhost_cdma_push_gather(&job->ch->cdma, job->nvmap, - nvmap_id_to_handle(job->gathers[i].mem_id), + job->gathers[i].ref, job->gathers[i].offset, op1, op2); } diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c index 76483d82528b..a5574a0fb60a 100644 --- a/drivers/video/tegra/host/host1x/host1x_debug.c +++ b/drivers/video/tegra/host/host1x/host1x_debug.c @@ -169,32 +169,27 @@ static void show_channel_gather(struct output *o, u32 addr, struct push_buffer *pb = &cdma->push_buffer; u32 cur = addr - pb->phys; struct nvmap_client_handle *nvmap = &pb->nvmap[cur/8]; - struct nvmap_handle_ref ref; u32 *map_addr, offset; phys_addr_t pin_addr; int state, count, i; if (!nvmap->handle || !nvmap->client - || atomic_read(&nvmap->handle->ref) < 1) { + || atomic_read(&nvmap->handle->handle->ref) < 1) { nvhost_debug_output(o, "[already deallocated]\n"); return; } - /* Create a fake nvmap_handle_ref - nvmap requires it - * but accesses only the first field - nvmap_handle */ - ref.handle = nvmap->handle; - - map_addr = nvmap_mmap(&ref); + map_addr = nvmap_mmap(nvmap->handle); if (!map_addr) { nvhost_debug_output(o, "[could not mmap]\n"); return; } /* Get base address from nvmap */ - pin_addr = nvmap_pin(nvmap->client, &ref); + pin_addr = nvmap_pin(nvmap->client, nvmap->handle); if (IS_ERR_VALUE(pin_addr)) { nvhost_debug_output(o, "[couldn't pin]\n"); - nvmap_munmap(&ref, map_addr); + nvmap_munmap(nvmap->handle, map_addr); return; } @@ -215,8 +210,8 @@ static void show_channel_gather(struct output *o, u32 addr, *(map_addr + offset/4 + i), cdma); } - nvmap_unpin(nvmap->client, &ref); - nvmap_munmap(&ref, map_addr); + nvmap_unpin(nvmap->client, nvmap->handle); + nvmap_munmap(nvmap->handle, map_addr); #endif } diff --git a/drivers/video/tegra/host/mpe/mpe.c b/drivers/video/tegra/host/mpe/mpe.c index d8c9da7e9a76..3fe2fcd8bb50 100644 --- a/drivers/video/tegra/host/mpe/mpe.c +++ b/drivers/video/tegra/host/mpe/mpe.c @@ -502,7 +502,7 @@ static void ctxmpe_save_push(struct nvhost_hwctx *nctx, struct host1x_hwctx_handler *h = host1x_hwctx_handler(ctx); nvhost_cdma_push_gather(cdma, nvhost_get_host(nctx->channel->dev)->nvmap, - h->save_buf->handle, + h->save_buf, 0, nvhost_opcode_gather(h->save_size), h->save_phys); diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index a72e18f16ac7..b1f138317cc1 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -371,15 +371,13 @@ int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job) } static void trace_write_gather(struct nvhost_cdma *cdma, - struct nvmap_handle *handle, + struct nvmap_handle_ref *ref, u32 offset, u32 words) { - struct nvmap_handle_ref ref; void *mem = NULL; if (nvhost_debug_trace_cmdbuf) { - ref.handle = handle; - mem = nvmap_mmap(&ref); + mem = nvmap_mmap(ref); if (IS_ERR_OR_NULL(mem)) mem = NULL; }; @@ -393,12 +391,12 @@ static void trace_write_gather(struct nvhost_cdma *cdma, for (i = 0; i < words; i += TRACE_MAX_LENGTH) { trace_nvhost_cdma_push_gather( cdma_to_channel(cdma)->dev->name, - (u32)handle, + (u32)ref->handle, min(words - i, TRACE_MAX_LENGTH), offset + i * sizeof(u32), mem); } - nvmap_munmap(&ref, mem); + nvmap_munmap(ref, mem); } } @@ -421,7 +419,7 @@ void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2) */ void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, struct nvmap_client *client, - struct nvmap_handle *handle, + struct nvmap_handle_ref *handle, u32 offset, u32 op1, u32 op2) { u32 slots_free = cdma->slots_free; diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h index e6f51179150f..98393f0cc765 100644 --- a/drivers/video/tegra/host/nvhost_cdma.h +++ b/drivers/video/tegra/host/nvhost_cdma.h @@ -48,7 +48,7 @@ struct nvhost_job; struct nvmap_client_handle { struct nvmap_client *client; - struct nvmap_handle *handle; + struct nvmap_handle_ref *handle; }; struct push_buffer { @@ -113,7 +113,7 @@ int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job); void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2); void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, struct nvmap_client *client, - struct nvmap_handle *handle, u32 offset, u32 op1, u32 op2); + struct nvmap_handle_ref *handle, u32 offset, u32 op1, u32 op2); void nvhost_cdma_end(struct nvhost_cdma *cdma, struct nvhost_job *job); void nvhost_cdma_update(struct nvhost_cdma *cdma); diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h index eac51731547b..a8f16f0c5abc 100644 --- a/drivers/video/tegra/host/nvhost_channel.h +++ b/drivers/video/tegra/host/nvhost_channel.h @@ -36,13 +36,6 @@ struct nvhost_device; struct nvhost_channel; struct nvhost_hwctx; -struct nvhost_channel_gather { - u32 words; - phys_addr_t mem; - u32 mem_id; - int offset; -}; - struct nvhost_channel { int refcount; int chid; diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index 71f2ab0e751f..11d65964e2c9 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "nvhost_channel.h" #include "nvhost_job.h" #include "nvhost_hwctx.h" @@ -33,128 +34,41 @@ static int job_size(struct nvhost_submit_hdr_ext *hdr) { - int num_pins = hdr ? (hdr->num_relocs + hdr->num_cmdbufs)*2 : 0; + int num_relocs = hdr ? hdr->num_relocs : 0; int num_waitchks = hdr ? hdr->num_waitchks : 0; + int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; + int num_unpins = num_cmdbufs + num_relocs; return sizeof(struct nvhost_job) - + num_pins * sizeof(struct nvmap_pinarray_elem) - + num_pins * sizeof(struct nvmap_handle *) - + num_waitchks * sizeof(struct nvhost_waitchk); -} - -static int gather_size(int num_cmdbufs) -{ - return num_cmdbufs * sizeof(struct nvhost_channel_gather); -} - -static void free_gathers(struct nvhost_job *job) -{ - if (job->gathers) { - nvmap_munmap(job->gather_mem, job->gathers); - job->gathers = NULL; - } - if (job->gather_mem) { - nvmap_free(job->nvmap, job->gather_mem); - job->gather_mem = NULL; - } -} - -static int alloc_gathers(struct nvhost_job *job, - int num_cmdbufs) -{ - int err = 0; - - job->gather_mem = NULL; - job->gathers = NULL; - job->gather_mem_size = 0; - - if (num_cmdbufs) { - /* Allocate memory */ - job->gather_mem = nvmap_alloc(job->nvmap, - gather_size(num_cmdbufs), - 32, NVMAP_HANDLE_CACHEABLE, 0); - if (IS_ERR_OR_NULL(job->gather_mem)) { - err = job->gather_mem ? PTR_ERR(job->gather_mem) : -ENOMEM; - job->gather_mem = NULL; - goto error; - } - job->gather_mem_size = gather_size(num_cmdbufs); - - /* Map memory to kernel */ - job->gathers = nvmap_mmap(job->gather_mem); - if (IS_ERR_OR_NULL(job->gathers)) { - err = job->gathers ? PTR_ERR(job->gathers) : -ENOMEM; - job->gathers = NULL; - goto error; - } - } - - return 0; - -error: - free_gathers(job); - return err; -} - -static int realloc_gathers(struct nvhost_job *oldjob, - struct nvhost_job *newjob, - int num_cmdbufs) -{ - int err = 0; - - /* Check if we can reuse gather buffer */ - if (oldjob->gather_mem_size < gather_size(num_cmdbufs) - || oldjob->nvmap != newjob->nvmap) { - free_gathers(oldjob); - err = alloc_gathers(newjob, num_cmdbufs); - } else { - newjob->gather_mem = oldjob->gather_mem; - newjob->gathers = oldjob->gathers; - newjob->gather_mem_size = oldjob->gather_mem_size; - - oldjob->gather_mem = NULL; - oldjob->gathers = NULL; - oldjob->gather_mem_size = 0; - } - return err; + + num_relocs * sizeof(struct nvmap_pinarray_elem) + + num_unpins * sizeof(struct nvmap_handle_ref *) + + num_waitchks * sizeof(struct nvhost_waitchk) + + num_cmdbufs * sizeof(struct nvhost_job_gather); } static void init_fields(struct nvhost_job *job, struct nvhost_submit_hdr_ext *hdr, int priority, int clientid) { - int num_pins = hdr ? (hdr->num_relocs + hdr->num_cmdbufs)*2 : 0; + int num_relocs = hdr ? hdr->num_relocs : 0; int num_waitchks = hdr ? hdr->num_waitchks : 0; + int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; + int num_unpins = num_cmdbufs + num_relocs; void *mem = job; /* First init state to zero */ - job->num_gathers = 0; - job->num_pins = 0; - job->num_unpins = 0; - job->num_waitchk = 0; - job->waitchk_mask = 0; - job->syncpt_id = 0; - job->syncpt_incrs = 0; - job->syncpt_end = 0; job->priority = priority; job->clientid = clientid; - job->null_kickoff = false; - job->first_get = 0; - job->num_slots = 0; /* Redistribute memory to the structs */ mem += sizeof(struct nvhost_job); - if (num_pins) { - job->pinarray = mem; - mem += num_pins * sizeof(struct nvmap_pinarray_elem); - job->unpins = mem; - mem += num_pins * sizeof(struct nvmap_handle *); - } else { - job->pinarray = NULL; - job->unpins = NULL; - } - + job->pinarray = num_relocs ? mem : NULL; + mem += num_relocs * sizeof(struct nvmap_pinarray_elem); + job->unpins = num_unpins ? mem : NULL; + mem += num_unpins * sizeof(struct nvmap_handle_ref *); job->waitchk = num_waitchks ? mem : NULL; + mem += num_waitchks * sizeof(struct nvhost_waitchk); + job->gathers = num_cmdbufs ? mem : NULL; /* Copy information from header */ if (hdr) { @@ -172,8 +86,6 @@ struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, int clientid) { struct nvhost_job *job = NULL; - int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; - int err = 0; job = vzalloc(job_size(hdr)); if (!job) @@ -186,10 +98,6 @@ struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, hwctx->h->get(hwctx); job->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL; - err = alloc_gathers(job, num_cmdbufs); - if (err) - goto error; - init_fields(job, hdr, priority, clientid); return job; @@ -200,46 +108,6 @@ error: return NULL; } -struct nvhost_job *nvhost_job_realloc( - struct nvhost_job *oldjob, - struct nvhost_hwctx *hwctx, - struct nvhost_submit_hdr_ext *hdr, - struct nvmap_client *nvmap, - int priority, int clientid) -{ - struct nvhost_job *newjob = NULL; - int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; - int err = 0; - - newjob = vzalloc(job_size(hdr)); - if (!newjob) - goto error; - kref_init(&newjob->ref); - newjob->ch = oldjob->ch; - newjob->hwctx = hwctx; - if (hwctx) - newjob->hwctx->h->get(newjob->hwctx); - newjob->timeout = oldjob->timeout; - newjob->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL; - - err = realloc_gathers(oldjob, newjob, num_cmdbufs); - if (err) - goto error; - - nvhost_job_put(oldjob); - - init_fields(newjob, hdr, priority, clientid); - - return newjob; - -error: - if (newjob) - nvhost_job_put(newjob); - if (oldjob) - nvhost_job_put(oldjob); - return NULL; -} - void nvhost_job_get(struct nvhost_job *job) { kref_get(&job->ref); @@ -253,10 +121,6 @@ static void job_free(struct kref *ref) job->hwctxref->h->put(job->hwctxref); if (job->hwctx) job->hwctx->h->put(job->hwctx); - if (job->gathers) - nvmap_munmap(job->gather_mem, job->gathers); - if (job->gather_mem) - nvmap_free(job->nvmap, job->gather_mem); if (job->nvmap) nvmap_client_put(job->nvmap); vfree(job); @@ -280,42 +144,119 @@ void nvhost_job_put(struct nvhost_job *job) void nvhost_job_add_gather(struct nvhost_job *job, u32 mem_id, u32 words, u32 offset) { - struct nvmap_pinarray_elem *pin; - struct nvhost_channel_gather *cur_gather = + struct nvhost_job_gather *cur_gather = &job->gathers[job->num_gathers]; - pin = &job->pinarray[job->num_pins++]; - pin->patch_mem = (u32)nvmap_ref_to_handle(job->gather_mem); - pin->patch_offset = (void *)&(cur_gather->mem) - (void *)job->gathers; - pin->pin_mem = nvmap_convert_handle_u2k(mem_id); - pin->pin_offset = offset; cur_gather->words = words; cur_gather->mem_id = mem_id; cur_gather->offset = offset; job->num_gathers += 1; } -int nvhost_job_pin(struct nvhost_job *job) +static int do_relocs(struct nvhost_job *job, u32 patch_mem, void *patch_addr) { - int err = 0; + phys_addr_t pin_phys; + int i; + u32 mem_id = 0; + struct nvmap_handle_ref *pin_ref = NULL; + + /* pin & patch the relocs for one gather */ + for (i = 0; i < job->num_relocs; i++) { + struct nvmap_pinarray_elem *pin = &job->pinarray[i]; + + /* skip all other gathers */ + if (patch_mem != pin->patch_mem) + continue; + + /* check if pin-mem is same as previous */ + if (pin->pin_mem != mem_id) { + pin_ref = nvmap_duplicate_handle_id(job->nvmap, + pin->pin_mem); + if (IS_ERR(pin_ref)) + return PTR_ERR(pin_ref); + + pin_phys = nvmap_pin(job->nvmap, pin_ref); + if (IS_ERR((void *)pin_phys)) { + nvmap_free(job->nvmap, pin_ref); + return pin_phys; + } + + mem_id = pin->pin_mem; + job->unpins[job->num_unpins++] = pin_ref; + } + + __raw_writel((pin_phys + pin->pin_offset) >> pin->reloc_shift, + (patch_addr + pin->patch_offset)); + + /* Different gathers might have same mem_id. This ensures we + * perform reloc only once per gather memid. */ + pin->patch_mem = 0; + } + + return 0; +} - /* pin mem handles and patch physical addresses */ - job->num_unpins = nvmap_pin_array(job->nvmap, - nvmap_ref_to_handle(job->gather_mem), - job->pinarray, job->num_pins, - job->unpins); - if (job->num_unpins < 0) - err = job->num_unpins; +int nvhost_job_pin(struct nvhost_job *job) +{ + int err = 0, i = 0; + phys_addr_t gather_phys = 0; + void *gather_addr = NULL; + + /* pin gathers */ + for (i = 0; i < job->num_gathers; i++) { + struct nvhost_job_gather *g = &job->gathers[i]; + + /* process each gather mem only once */ + if (!g->ref) { + g->ref = nvmap_duplicate_handle_id(job->nvmap, + job->gathers[i].mem_id); + if (IS_ERR(g->ref)) { + err = PTR_ERR(g->ref); + g->ref = NULL; + break; + } + + gather_phys = nvmap_pin(job->nvmap, g->ref); + if (IS_ERR((void *)gather_phys)) { + nvmap_free(job->nvmap, g->ref); + err = gather_phys; + break; + } + + /* store the gather ref into unpin array */ + job->unpins[job->num_unpins++] = g->ref; + + gather_addr = nvmap_mmap(g->ref); + if (!gather_addr) { + err = -ENOMEM; + break; + } + + err = do_relocs(job, g->mem_id, gather_addr); + nvmap_munmap(g->ref, gather_addr); + + if (err) + break; + } + g->mem = gather_phys + g->offset; + } + wmb(); return err; } void nvhost_job_unpin(struct nvhost_job *job) { - nvmap_unpin_handles(job->nvmap, job->unpins, - job->num_unpins); + int i; + + for (i = 0; i < job->num_unpins; i++) { + nvmap_unpin(job->nvmap, job->unpins[i]); + nvmap_free(job->nvmap, job->unpins[i]); + } + memset(job->unpins, BAD_MAGIC, - job->num_unpins * sizeof(struct nvmap_handle *)); + job->num_unpins * sizeof(struct nvmap_handle_ref *)); + job->num_unpins = 0; } /** diff --git a/drivers/video/tegra/host/nvhost_job.h b/drivers/video/tegra/host/nvhost_job.h index ad9d1af60da1..48555a231412 100644 --- a/drivers/video/tegra/host/nvhost_job.h +++ b/drivers/video/tegra/host/nvhost_job.h @@ -29,6 +29,14 @@ struct nvmap_client; struct nvhost_waitchk; struct nvmap_handle; +struct nvhost_job_gather { + u32 words; + phys_addr_t mem; + u32 mem_id; + int offset; + struct nvmap_handle_ref *ref; +}; + /* * Each submit is tracked as a nvhost_job. */ @@ -50,10 +58,8 @@ struct nvhost_job { struct nvmap_client *nvmap; /* Gathers and their memory */ - struct nvmap_handle_ref *gather_mem; - struct nvhost_channel_gather *gathers; + struct nvhost_job_gather *gathers; int num_gathers; - int gather_mem_size; /* Wait checks to be processed at submit time */ struct nvhost_waitchk *waitchk; @@ -62,8 +68,8 @@ struct nvhost_job { /* Array of handles to be pinned & unpinned */ struct nvmap_pinarray_elem *pinarray; - int num_pins; - struct nvmap_handle **unpins; + int num_relocs; + struct nvmap_handle_ref **unpins; int num_unpins; /* Sync point id, number of increments and end related to the submit */ @@ -98,17 +104,6 @@ struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, struct nvmap_client *nvmap, int priority, int clientid); -/* - * Allocate memory for a job. Just enough memory will be allocated to - * accomodate the submit announced in submit header. Gather memory from - * oldjob will be reused, and nvhost_job_put() will be called to it. - */ -struct nvhost_job *nvhost_job_realloc(struct nvhost_job *oldjob, - struct nvhost_hwctx *hwctx, - struct nvhost_submit_hdr_ext *hdr, - struct nvmap_client *nvmap, - int priority, int clientid); - /* * Add a gather to a job. */ -- cgit v1.2.3 From ee8c9ce26661e83584f4cb5931b0815d38b2843a Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Wed, 16 May 2012 19:32:28 +0530 Subject: usb: gadget: android: add the handler for ptp class requests Adding the handler for control requests of ptp function driver. Bug 980195 Change-Id: I3ddfc44d7ec4a98d29c7358be1f1d34799f92be9 Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/103007 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Venkat Moganty --- drivers/usb/gadget/android.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index fbafe8a3bca4..4805670cf6e3 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -319,6 +319,13 @@ static int mtp_function_ctrlrequest(struct android_usb_function *f, return mtp_ctrlrequest(cdev, c); } +static int ptp_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + return mtp_ctrlrequest(cdev, c); +} + static struct android_usb_function mtp_function = { .name = "mtp", .init = mtp_function_init, @@ -333,6 +340,7 @@ static struct android_usb_function ptp_function = { .init = ptp_function_init, .cleanup = ptp_function_cleanup, .bind_config = ptp_function_bind_config, + .ctrlrequest = ptp_function_ctrlrequest, }; -- cgit v1.2.3 From be1e69e5163ba61068d6a6a61e33778f7cf90b42 Mon Sep 17 00:00:00 2001 From: Sudhir Vyas Date: Thu, 17 May 2012 18:51:37 +0530 Subject: media: video: tegra: Add ad5816 focuser driver - Implement new focuser ad5816 driver. - Complies to latest NVC framework. Bug 947792 Change-Id: I1661de027062d2a9b4112fd24dc255d810c7afa0 Signed-off-by: Sudhir Vyas Reviewed-on: http://git-master/r/103131 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Sachin Nikam --- drivers/media/video/tegra/Kconfig | 7 + drivers/media/video/tegra/Makefile | 1 + drivers/media/video/tegra/ad5816.c | 1204 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1212 insertions(+) create mode 100644 drivers/media/video/tegra/ad5816.c (limited to 'drivers') diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig index f5aea996e2d6..5099fe12c3e2 100644 --- a/drivers/media/video/tegra/Kconfig +++ b/drivers/media/video/tegra/Kconfig @@ -95,3 +95,10 @@ config VIDEO_AD5820 ---help--- This is a driver for the AD5820 focuser for use with the tegra isp. + +config VIDEO_AD5816 + tristate "AD5816 focuser support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the AD5816 focuser + for use with the tegra isp. diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile index 08b81e261611..3c1a0ce4638e 100644 --- a/drivers/media/video/tegra/Makefile +++ b/drivers/media/video/tegra/Makefile @@ -18,4 +18,5 @@ obj-$(CONFIG_TORCH_SSL3250A) += ssl3250a.o obj-$(CONFIG_TORCH_TPS61050) += tps61050.o obj-$(CONFIG_VIDEO_SH532U) += sh532u.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o +obj-$(CONFIG_VIDEO_AD5816) += ad5816.o diff --git a/drivers/media/video/tegra/ad5816.c b/drivers/media/video/tegra/ad5816.c new file mode 100644 index 000000000000..d95368f43cf6 --- /dev/null +++ b/drivers/media/video/tegra/ad5816.c @@ -0,0 +1,1204 @@ +/* Copyright (C) 2011-2012 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +/* This is a NVC kernel driver for a focuser device called + * ad5816. + */ +/* Implementation + * -------------- + * The board level details about the device need to be provided in the board + * file with the _platform_data structure. + * Standard among NVC kernel drivers in this structure is: + * .cfg = Use the NVC_CFG_ defines that are in nvc.h. + * Descriptions of the configuration options are with the defines. + * This value is typically 0. + * .num = The number of the instance of the device. This should start at 1 and + * and increment for each device on the board. This number will be + * appended to the MISC driver name, Example: /dev/focuser.1 + * If not used or 0, then nothing is appended to the name. + * .sync = If there is a need to synchronize two devices, then this value is + * the number of the device instance (.num above) this device is to + * sync to. For example: + * Device 1 platform entries = + * .num = 1, + * .sync = 2, + * Device 2 platfrom entries = + * .num = 2, + * .sync = 1, + * The above example sync's device 1 and 2. + * To disable sync, set .sync = 0. Note that the .num = 0 device is not + * allowed to be synced to. + * This is typically used for stereo applications. + * .dev_name = The MISC driver name the device registers as. If not used, + * then the part number of the device is used for the driver name. + * If using the NVC user driver then use the name found in this + * driver under _default_pdata. + * .gpio_count = The ARRAY_SIZE of the nvc_gpio_pdata table. + * .gpio = A pointer to the nvc_gpio_pdata structure's platform GPIO data. + * The GPIO mechanism works by cross referencing the .gpio_type key + * among the nvc_gpio_pdata GPIO data and the driver's nvc_gpio_init + * GPIO data to build a GPIO table the driver can use. The GPIO's + * defined in the device header file's _gpio_type enum are the + * gpio_type keys for the nvc_gpio_pdata and nvc_gpio_init structures. + * These need to be present in the board file's nvc_gpio_pdata + * structure for the GPIO's that are used. + * The driver's GPIO logic uses assert/deassert throughout until the + * low level _gpio_wr/rd calls where the .assert_high is used to + * convert the value to the correct signal level. + * See the GPIO notes in nvc.h for additional information. + * + * The following is specific to NVC kernel focus drivers: + * .nvc = Pointer to the nvc_focus_nvc structure. This structure needs to + * be defined and populated if overriding the driver defaults. + * .cap = Pointer to the nvc_focus_cap structure. This structure needs to + * be defined and populated if overriding the driver defaults. + * + * The following is specific to this NVC kernel focus driver: + * .info = Pointer to the ad5816_pdata_info structure. This structure does + * not need to be defined and populated unless overriding ROM data. + * + * Power Requirements: + * The device's header file defines the voltage regulators needed with the + * enumeration _vreg. The order these are enumerated is the order + * the regulators will be enabled when powering on the device. When the + * device is powered off the regulators are disabled in descending order. + * The _vregs table in this driver uses the nvc_regulator_init + * structure to define the regulator ID strings that go with the regulators + * defined with _vreg. These regulator ID strings (or supply names) + * will be used in the regulator_get function in the _vreg_init function. + * The board power file and _vregs regulator ID strings must match. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AD5816_ID 0x04 +#define AD5816_FOCAL_LENGTH (4.570f) +#define AD5816_FNUMBER (2.8f) +#define AD5816_ACTUATOR_RANGE 680 +#define AD5816_SETTLETIME 110 +#define AD5816_FOCUS_MACRO 810 +#define AD5816_FOCUS_INFINITY 50 /* Exact value needs to be decided */ +#define AD5816_POS_LOW_DEFAULT 220 +#define AD5816_POS_HIGH_DEFAULT 900 +/* Need to decide exact value of VCM_THRESHOLD and its use */ +/* define AD5816_VCM_THRESHOLD 20 */ + +static u8 ad5816_ids[] = { + 0x04, +}; + +static struct nvc_gpio_init ad5816_gpios[] = { + { AD5816_GPIO_RESET, GPIOF_OUT_INIT_LOW, "reset", false, true, }, + { AD5816_GPIO_I2CMUX, 0, "i2c_mux", 0, false}, + { AD5816_GPIO_GP1, 0, "gp1", 0, false}, + { AD5816_GPIO_GP2, 0, "gp2", 0, false}, + { AD5816_GPIO_GP3, 0, "gp3", 0, false}, +}; + +static struct nvc_regulator_init ad5816_vregs[] = { + { AD5816_VREG_VDD, "vdd"}, + { AD5816_VREG_VDD_AF, "vdd_af"}, + { AD5816_VREG_VDD_I2C, "vdd_i2c"}, +}; + +struct ad5816_info { + atomic_t in_use; + struct i2c_client *i2c_client; + struct ad5816_platform_data *pdata; + struct miscdevice miscdev; + struct list_head list; + struct nvc_gpio gpio[ARRAY_SIZE(ad5816_gpios)]; + struct nvc_regulator vreg[ARRAY_SIZE(ad5816_vregs)]; + int pwr_api; + int pwr_dev; + int id_minor; + u32 pos; + u8 s_mode; + bool reset_flag; + struct ad5816_info *s_info; + struct nvc_focus_nvc nvc; + struct nvc_focus_cap cap; + struct ad5816_pdata_info config; +}; + +/** + * The following are default values + */ + +static struct ad5816_pdata_info ad5816_default_info = { + .pos_low = AD5816_POS_LOW_DEFAULT, + .pos_high = AD5816_POS_HIGH_DEFAULT, +}; + +static struct nvc_focus_cap ad5816_default_cap = { + .version = NVC_FOCUS_CAP_VER2, + .actuator_range = AD5816_ACTUATOR_RANGE, + .settle_time = AD5816_SETTLETIME, + .focus_macro = AD5816_FOCUS_MACRO, + .focus_infinity = AD5816_FOCUS_INFINITY, +}; + +static struct nvc_focus_nvc ad5816_default_nvc = { + .focal_length = AD5816_FOCAL_LENGTH, + .fnumber = AD5816_FNUMBER, +}; + +static struct ad5816_platform_data ad5816_default_pdata = { + .cfg = 0, + .num = 0, + .sync = 0, + .dev_name = "focuser", +}; +static LIST_HEAD(ad5816_info_list); +static DEFINE_SPINLOCK(ad5816_spinlock); + +static int ad5816_i2c_rd8(struct ad5816_info *info, u8 addr, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[2]; + buf[0] = reg; + if (addr) { + msg[0].addr = addr; + msg[1].addr = addr; + } else { + msg[0].addr = info->i2c_client->addr; + msg[1].addr = info->i2c_client->addr; + } + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &buf[0]; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &buf[1]; + *val = 0; + if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) + return -EIO; + *val = buf[1]; + return 0; +} + +static int ad5816_i2c_wr8(struct ad5816_info *info, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + buf[0] = reg; + buf[1] = val; + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int ad5816_i2c_rd16(struct ad5816_info *info, u8 reg, u16 *val) +{ + struct i2c_msg msg[2]; + u8 buf[3]; + buf[0] = reg; + msg[0].addr = info->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &buf[0]; + msg[1].addr = info->i2c_client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = &buf[1]; + if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) + return -EIO; + *val = (((u16)buf[1] << 8) | (u16)buf[2]); + return 0; +} + +static int ad5816_i2c_wr16(struct ad5816_info *info, u8 reg, u16 val) +{ + struct i2c_msg msg; + u8 buf[3]; + buf[0] = reg; + buf[1] = (u8)(val >> 8); + buf[2] = (u8)(val & 0xff); + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int ad5816_gpio_wr(struct ad5816_info *info, + enum ad5816_gpio_types i, + int val) /* val: 0=deassert, 1=assert */ +{ + int err = -EINVAL; + if (info->gpio[i].valid) { + if (val) + val = 1; + if (!info->gpio[i].active_high) + val = !val; + val &= 1; + err = val; + gpio_set_value_cansleep(info->gpio[i].gpio, val); + dev_dbg(&info->i2c_client->dev, "%s %u %d\n", __func__, info->gpio[i].gpio, val); + } + return err; /* return value written or error */ +} + +static int ad5816_gpio_reset(struct ad5816_info *info, int val) +{ + int err = 0; + + if (val) { + if (!info->reset_flag) { + info->reset_flag = true; + err = ad5816_gpio_wr(info, AD5816_GPIO_RESET, 1); + if (err < 0) + return 0; /* flag no reset */ + + mdelay(1); + ad5816_gpio_wr(info, AD5816_GPIO_RESET, 0); + mdelay(10); /* startup delay needs to be modified*/ + err = 1; /* flag that a reset was done */ + } + } else { + info->reset_flag = false; + } + return err; +} + +static void ad5816_gpio_able(struct ad5816_info *info, int val) +{ + /** + * This is a feature that allows driver to control GPIOs + * that may be needed for the board (not the device). + * */ + if (val) { + ad5816_gpio_wr(info, AD5816_GPIO_GP1, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP2, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP3, val); + } else { + ad5816_gpio_wr(info, AD5816_GPIO_GP3, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP2, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP1, val); + } +} +static void ad5816_gpio_exit(struct ad5816_info *info) +{ + unsigned i; + for (i = 0; i <= ARRAY_SIZE(ad5816_gpios); i++) { + if (info->gpio[i].flag && info->gpio[i].own) { + gpio_free(info->gpio[i].gpio); + info->gpio[i].own = false; + } + } +} + +static void ad5816_gpio_init(struct ad5816_info *info) +{ + char label[32]; + unsigned long flags; + unsigned type; + unsigned i; + unsigned j; + int err; + for (i = 0; i < ARRAY_SIZE(ad5816_gpios); i++) + info->gpio[i].flag = false; + + if (!info->pdata->gpio_count || !info->pdata->gpio) + return; + + for (i = 0; i < ARRAY_SIZE(ad5816_gpios); i++) { + type = ad5816_gpios[i].gpio_type; + + for (j = 0; j < info->pdata->gpio_count; j++) { + if (type == info->pdata->gpio[j].gpio_type) + break; + } + + if (j == info->pdata->gpio_count) + continue; + info->gpio[type].gpio = info->pdata->gpio[j].gpio; + info->gpio[type].flag = true; + + if (ad5816_gpios[i].use_flags) { + flags = ad5816_gpios[i].flags; + info->gpio[type].active_high = ad5816_gpios[i].active_high; + } else { + info->gpio[type].active_high = info->pdata->gpio[j].active_high; + if (info->gpio[type].active_high) + flags = GPIOF_OUT_INIT_LOW; + else + flags = GPIOF_OUT_INIT_HIGH; + } + + if (!info->pdata->gpio[j].init_en) + continue; + snprintf(label, sizeof(label), "ad5816_%u_%s", + info->pdata->num, ad5816_gpios[i].label); + err = gpio_request_one(info->gpio[type].gpio, flags, label); + if (err) { + dev_err(&info->i2c_client->dev, "%s ERR %s %u\n", + __func__, label, info->gpio[type].gpio); + } else { + info->gpio[type].own = true; + dev_dbg(&info->i2c_client->dev, "%s %s %u\n", + __func__, label, info->gpio[type].gpio); + } + } +} + +static int ad5816_vreg_dis(struct ad5816_info *info, + enum ad5816_vreg i) +{ + int err = 0; + if (info->vreg[i].vreg_flag && (info->vreg[i].vreg != NULL)) { + err = regulator_disable(info->vreg[i].vreg); + if (!err) + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, info->vreg[i].vreg_name); + else + dev_err(&info->i2c_client->dev, "%s %s ERR\n", + __func__, info->vreg[i].vreg_name); + } + info->vreg[i].vreg_flag = false; + return err; +} + +static int ad5816_vreg_dis_all(struct ad5816_info *info) +{ + unsigned i; + int err = 0; + for (i = ARRAY_SIZE(ad5816_vregs); i > 0; i--) + err |= ad5816_vreg_dis(info, (i - 1)); + return err; +} + +static int ad5816_vreg_en(struct ad5816_info *info, + enum ad5816_vreg i) +{ + int err = 0; + if (!info->vreg[i].vreg_flag && (info->vreg[i].vreg != NULL)) { + err = regulator_enable(info->vreg[i].vreg); + + if (!err) { + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, info->vreg[i].vreg_name); + info->vreg[i].vreg_flag = true; + err = 1; /* flag regulator state change */ + } else { + dev_err(&info->i2c_client->dev, "%s %s ERR\n", + __func__, info->vreg[i].vreg_name); + } + + } + return err; +} + +static int ad5816_vreg_en_all(struct ad5816_info *info) +{ + unsigned i; + int err = 0; + for (i = 0; i < ARRAY_SIZE(ad5816_vregs); i++) + err |= ad5816_vreg_en(info, i); + return err; +} + +static void ad5816_vreg_exit(struct ad5816_info *info) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE(ad5816_vregs); i++) { + regulator_put(info->vreg[i].vreg); + info->vreg[i].vreg = NULL; + } +} + +static int ad5816_vreg_init(struct ad5816_info *info) +{ + unsigned i; + unsigned j; + int err = 0; + for (i = 0; i < ARRAY_SIZE(ad5816_vregs); i++) { + j = ad5816_vregs[i].vreg_num; + info->vreg[j].vreg_name = ad5816_vregs[i].vreg_name; + info->vreg[j].vreg_flag = false; + info->vreg[j].vreg = regulator_get(&info->i2c_client->dev, + info->vreg[j].vreg_name); + if (IS_ERR_OR_NULL(info->vreg[j].vreg)) { + dev_dbg(&info->i2c_client->dev, "%s %s ERR: %d\n", + __func__, info->vreg[j].vreg_name, + (int)info->vreg[j].vreg); + err |= PTR_ERR(info->vreg[j].vreg); + info->vreg[j].vreg = NULL; + } else { + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, info->vreg[j].vreg_name); + } + } + return err; +} + +void ad5816_set_power_down(struct ad5816_info *info) +{ + int err; + u16 data = 0x0000; + err = ad5816_i2c_wr16(info, VCM_CODE_MSB, data); + if (err) + dev_err(&info->i2c_client->dev, " %s: failed \n", + __func__); +} + +void ad5816_set_arc_mode(struct ad5816_info *info) +{ + int err = 0; + /* set ARC enable */ + err = ad5816_i2c_wr8(info, CONTROL, 0x02); + if (err) + dev_err(&info->i2c_client->dev, + "%s: CONTROL reg write failed \n", __func__); + + /* set the ARC RES2 */ + err = ad5816_i2c_wr8(info, MODE, 0x01); + if (err) + dev_err(&info->i2c_client->dev, + "%s: MODE reg write failed \n", __func__); + + /* set the VCM_FREQ to 12.8mS */ + err = ad5816_i2c_wr8(info, VCM_FREQ, 0x80); + if (err) + dev_err(&info->i2c_client->dev, + "%s: VCM_FREQ reg write failed \n", __func__); +} + +static int ad5816_pm_wr(struct ad5816_info *info, int pwr) +{ + int err = 0; + + if ((info->pdata->cfg & (NVC_CFG_OFF2STDBY | NVC_CFG_BOOT_INIT)) && + (pwr == NVC_PWR_OFF || + pwr == NVC_PWR_STDBY_OFF)) + pwr = NVC_PWR_STDBY; + + if (pwr == info->pwr_dev) + return 0; + + switch (pwr) + { + case NVC_PWR_OFF_FORCE: + case NVC_PWR_OFF: + err = ad5816_vreg_dis_all(info); + ad5816_gpio_able(info, 0); + ad5816_gpio_reset(info, 0); + break; + case NVC_PWR_STDBY_OFF: + case NVC_PWR_STDBY: + err = ad5816_vreg_en_all(info); + ad5816_gpio_able(info, 1); + ad5816_gpio_reset(info, 1); + break; + case NVC_PWR_COMM: + case NVC_PWR_ON: + err = ad5816_vreg_en_all(info); + ad5816_gpio_able(info, 1); + ad5816_gpio_reset(info, 1); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + dev_err(&info->i2c_client->dev, "%s err %d\n", __func__, err); + pwr = NVC_PWR_ERR; + } + + info->pwr_dev = pwr; + dev_dbg(&info->i2c_client->dev, "%s pwr_dev=%d\n", __func__, info->pwr_dev); + + if (err > 0) + return 0; + + return err; +} +static int ad5816_pm_wr_s(struct ad5816_info *info, int pwr) +{ + int err1 = 0; + int err2 = 0; + if ((info->s_mode == NVC_SYNC_OFF) || + (info->s_mode == NVC_SYNC_MASTER) || + (info->s_mode == NVC_SYNC_STEREO)) + err1 = ad5816_pm_wr(info, pwr); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + err2 = ad5816_pm_wr(info->s_info, pwr); + return err1 | err2; +} + +static int ad5816_pm_api_wr(struct ad5816_info *info, int pwr) +{ + int err = 0; + if (!pwr || (pwr > NVC_PWR_ON)) + return 0; + if (pwr > info->pwr_dev) { + err = ad5816_pm_wr_s(info, pwr); + } + if (!err) { + info->pwr_api = pwr; + } else + info->pwr_api = NVC_PWR_ERR; + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + return err; +} + +static int ad5816_pm_dev_wr(struct ad5816_info *info, int pwr) +{ + if (pwr < info->pwr_api) + pwr = info->pwr_api; + return ad5816_pm_wr(info, pwr); +} + +static void ad5816_pm_exit(struct ad5816_info *info) +{ + ad5816_pm_wr(info, NVC_PWR_OFF_FORCE); + ad5816_vreg_exit(info); + ad5816_gpio_exit(info); +} +static void ad5816_pm_init(struct ad5816_info *info) +{ + ad5816_gpio_init(info); + ad5816_vreg_init(info); +} + +static int ad5816_reset(struct ad5816_info *info, u32 level) +{ + int err; + if (level == NVC_RESET_SOFT) { + err = ad5816_pm_wr(info, NVC_PWR_COMM); + err |= ad5816_i2c_wr8(info, CONTROL, 0x01); /* SW reset */ + } else { + err = ad5816_pm_wr(info, NVC_PWR_OFF_FORCE); + } + err |= ad5816_pm_wr(info, info->pwr_api); + return err; +} + +static int ad5816_dev_id(struct ad5816_info *info) +{ + u16 val = 0; + unsigned i; + int err; + ad5816_pm_dev_wr(info, NVC_PWR_COMM); + err = ad5816_i2c_rd16(info, IC_INFO, &val); + if (!err) { + dev_dbg(&info->i2c_client->dev, "%s found devId: %x\n", __func__, val); + info->id_minor = 0; + val = val & 0xff; + for (i = 0; i < ARRAY_SIZE(ad5816_ids); i++) { + if (val == ad5816_ids[i]) { + info->id_minor = val; + break; + } + } + if (!info->id_minor) { + err = -ENODEV; + dev_dbg(&info->i2c_client->dev, "%s No devId match\n", __func__); + } + } + ad5816_pm_dev_wr(info, NVC_PWR_OFF); + return err; +} + +static void ad5816_sts_rd(struct ad5816_info *info) +{ + /** + * Device specific code for status + * + * TODO: Ad5816 has support to get status for over/under + * voltage conditions but currently this feature is not + * required. + */ +} +/** + * Below are device specific functions. + */ + +static int ad5816_position_rd(struct ad5816_info *info, unsigned *position) +{ + + u16 pos = 0; + u8 t1 = 0; + int err = 0; + + err = ad5816_i2c_rd8(info, 0, VCM_CODE_MSB, &t1); + pos = t1 & 0x03; + err = ad5816_i2c_rd8(info, 0, VCM_CODE_LSB, &t1); + pos = (pos << 8) | t1; + if(pos) + *position = pos - info->config.pos_low; + else + *position = info->config.pos_low; + + return 0; +} + +static int ad5816_position_wr(struct ad5816_info *info, unsigned position) +{ + position = position + info->config.pos_low; + + if(position > info->config.pos_high) + position = info->config.pos_high; + + u16 data = position & 0x03ff; + + return ad5816_i2c_wr16(info, VCM_CODE_MSB, data); +} + +static int ad5816_param_rd(struct ad5816_info *info, unsigned long arg) +{ + struct nvc_param params; + const void *data_ptr; + u32 data_size = 0; + u32 position; + int err; + if (copy_from_user(¶ms, + (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", __func__, __LINE__); + return -EFAULT; + } + if (info->s_mode == NVC_SYNC_SLAVE) + info = info->s_info; + switch (params.param) { + case NVC_PARAM_LOCUS: + ad5816_pm_dev_wr(info, NVC_PWR_COMM); + err = ad5816_position_rd(info, &position); + if (err && !(info->pdata->cfg & NVC_CFG_NOERR)) + return err; + data_ptr = &position; + data_size = sizeof(position); + ad5816_pm_dev_wr(info, NVC_PWR_STDBY); + dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n", + __func__, position); + break; + case NVC_PARAM_FOCAL_LEN: + info->nvc.focal_length = AD5816_FOCAL_LENGTH; + data_ptr = &info->nvc.focal_length; + data_size = sizeof(info->nvc.focal_length); + break; + case NVC_PARAM_MAX_APERTURE: + data_ptr = &info->nvc.max_aperature; + data_size = sizeof(info->nvc.max_aperature); + dev_dbg(&info->i2c_client->dev, "%s MAX_APERTURE: %x\n", + __func__, info->nvc.max_aperature); + break; + case NVC_PARAM_FNUMBER: + data_ptr = &info->nvc.fnumber; + data_size = sizeof(info->nvc.fnumber); + dev_dbg(&info->i2c_client->dev, "%s FNUMBER: %u\n", + __func__, info->nvc.fnumber); + break; + case NVC_PARAM_CAPS: + data_ptr = &info->cap; + /* there are different sizes depending on the version */ + /* send back just what's requested or our max size */ + if (params.sizeofvalue < sizeof(info->cap)) + data_size = params.sizeofvalue; + else + data_size = sizeof(info->cap); + dev_err(&info->i2c_client->dev, "%s CAPS\n", __func__); + break; + case NVC_PARAM_STS: + /*data_ptr = &info->sts; + data_size = sizeof(info->sts);*/ + dev_dbg(&info->i2c_client->dev, "%s \n", __func__); + break; + case NVC_PARAM_STEREO: + data_ptr = &info->s_mode; + data_size = sizeof(info->s_mode); + dev_err(&info->i2c_client->dev, "%s STEREO: %d\n", __func__, info->s_mode); + break; + default: + dev_err(&info->i2c_client->dev, "%s unsupported parameter: %d\n", + __func__, params.param); + return -EINVAL; + } + if (params.sizeofvalue < data_size) { + dev_err(&info->i2c_client->dev, + "%s data size mismatch %d != %d Param: %d\n", + __func__, params.sizeofvalue, data_size, params.param); + return -EINVAL; + } + if (copy_to_user((void __user *)params.p_value, data_ptr, data_size)) { + dev_err(&info->i2c_client->dev, "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + return 0; +} + +static int ad5816_param_wr_s(struct ad5816_info *info, + struct nvc_param *params, + u32 u32val) +{ + struct nvc_focus_cap cap; + u8 u8val; + int err = 0; + u8val = (u8)u32val; + switch (params->param) { + case NVC_PARAM_LOCUS: + dev_dbg(&info->i2c_client->dev, "%s LOCUS: %u\n", __func__, u32val); + err = ad5816_position_wr(info, u32val); + return err; + case NVC_PARAM_RESET: + err = ad5816_reset(info, u32val); + dev_dbg(&info->i2c_client->dev, "%s RESET: %d\n", __func__, err); + return err; + case NVC_PARAM_SELF_TEST: + err = 0; + dev_dbg(&info->i2c_client->dev, "%s SELF_TEST: %d\n", __func__, err); + return err; + default: + dev_dbg(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params->param); + return -EINVAL; + } +} + +static int ad5816_param_wr(struct ad5816_info *info, unsigned long arg) +{ + struct nvc_param params; + u8 u8val; + u32 u32val; + int err = 0; + if (copy_from_user(¶ms, (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, "%s copy_from_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + if (copy_from_user(&u32val, (const void __user *)params.p_value, sizeof(u32val))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", __func__, __LINE__); + return -EFAULT; + } + u8val = (u8)u32val; + /* parameters independent of sync mode */ + switch (params.param) { + case NVC_PARAM_STEREO: + dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", __func__, u8val); + if (u8val == info->s_mode) + return 0; + switch (u8val) { + case NVC_SYNC_OFF: + info->s_mode = u8val; + ad5816_gpio_wr(info, AD5816_GPIO_I2CMUX, 0); + if (info->s_info != NULL) { + info->s_info->s_mode = u8val; + ad5816_pm_wr(info->s_info, NVC_PWR_OFF); + } + break; + case NVC_SYNC_MASTER: + info->s_mode = u8val; + ad5816_gpio_wr(info, AD5816_GPIO_I2CMUX, 0); + if (info->s_info != NULL) + info->s_info->s_mode = u8val; + break; + case NVC_SYNC_SLAVE: + if (info->s_info != NULL) { + /* default slave lens position */ + err = ad5816_position_wr(info->s_info, + info->s_info->cap.focus_infinity); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + ad5816_gpio_wr(info, + AD5816_GPIO_I2CMUX, 0); + } + else { + if (info->s_mode != NVC_SYNC_STEREO) + ad5816_pm_wr(info->s_info, + NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + case NVC_SYNC_STEREO: + if (info->s_info != NULL) { + /* sync power */ + info->s_info->pwr_api = info->pwr_api; + /* move slave lens to master position */ + err = ad5816_position_wr(info->s_info, info->pos); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + ad5816_gpio_wr(info, AD5816_GPIO_I2CMUX, 1); + } + else { + if (info->s_mode != NVC_SYNC_SLAVE) + ad5816_pm_wr(info->s_info, NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + } + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + return err; + default: + /* parameters dependent on sync mode */ + switch (info->s_mode) { + case NVC_SYNC_OFF: + case NVC_SYNC_MASTER: + return ad5816_param_wr_s(info, ¶ms, u32val); + case NVC_SYNC_SLAVE: + return ad5816_param_wr_s(info->s_info, ¶ms, u32val); + case NVC_SYNC_STEREO: + err = ad5816_param_wr_s(info, ¶ms, u32val); + if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) + err |= ad5816_param_wr_s(info->s_info, + ¶ms, + u32val); + return err; + default: + dev_err(&info->i2c_client->dev, "%s %d internal err\n", + __func__, __LINE__); + return -EINVAL; + } + } +} + +static long ad5816_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ad5816_info *info = file->private_data; + int pwr; + int err = 0; + switch (cmd) { + case NVC_IOCTL_PARAM_WR: + err = ad5816_param_wr(info, arg); + return err; + case NVC_IOCTL_PARAM_RD: + err = ad5816_param_rd(info, arg); + return err; + case NVC_IOCTL_PWR_WR: + /* This is a Guaranteed Level of Service (GLOS) call */ + pwr = (int)arg * 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_WR: %d\n", + __func__, pwr); + err = ad5816_pm_api_wr(info, pwr); + return err; + case NVC_IOCTL_PWR_RD: + if (info->s_mode == NVC_SYNC_SLAVE) + pwr = info->s_info->pwr_api / 2; + else + pwr = info->pwr_api / 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", + __func__, pwr); + if (copy_to_user((void __user *)arg, (const void *)&pwr, sizeof(pwr))) { + dev_err(&info->i2c_client->dev, "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + return 0; + default: + dev_dbg(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", __func__, cmd); + } + return -EINVAL; +} + + +static void ad5816_sdata_init(struct ad5816_info *info) +{ + /* set defaults */ + memcpy(&info->config, &ad5816_default_info, sizeof(info->config)); + memcpy(&info->nvc, &ad5816_default_nvc, sizeof(info->nvc)); + memcpy(&info->cap, &ad5816_default_cap, sizeof(info->cap)); + + info->config.settle_time = AD5816_SETTLETIME; + info->config.focal_length = AD5816_FOCAL_LENGTH; + info->config.fnumber = AD5816_FNUMBER; + info->config.pos_low = AD5816_POS_LOW_DEFAULT; + info->config.pos_high = AD5816_POS_HIGH_DEFAULT; + + /* set to proper value */ + info->cap.actuator_range = info->config.pos_high - info->config.pos_low; + + /* set overrides if any */ + if (info->pdata->nvc) { + if (info->pdata->nvc->fnumber) + info->nvc.fnumber = info->pdata->nvc->fnumber; + if (info->pdata->nvc->focal_length) + info->nvc.focal_length = info->pdata->nvc->focal_length; + if (info->pdata->nvc->max_aperature) + info->nvc.max_aperature = info->pdata->nvc->max_aperature; + } + + if (info->pdata->cap) { + if (info->pdata->cap->actuator_range) + info->cap.actuator_range = info->pdata->cap->actuator_range; + if (info->pdata->cap->settle_time) + info->cap.settle_time = info->pdata->cap->settle_time; + if (info->pdata->cap->focus_macro) + info->cap.focus_macro = info->pdata->cap->focus_macro; + if (info->pdata->cap->focus_hyper) + info->cap.focus_hyper = info->pdata->cap->focus_hyper; + if (info->pdata->cap->focus_infinity) + info->cap.focus_infinity = info->pdata->cap->focus_infinity; + } +} + +static int ad5816_sync_en(unsigned num, unsigned sync) +{ + struct ad5816_info *master = NULL; + struct ad5816_info *slave = NULL; + struct ad5816_info *pos = NULL; + rcu_read_lock(); + list_for_each_entry_rcu(pos, &ad5816_info_list, list) { + if (pos->pdata->num == num) { + master = pos; + break; + } + } + pos = NULL; + list_for_each_entry_rcu(pos, &ad5816_info_list, list) { + if (pos->pdata->num == sync) { + slave = pos; + break; + } + } + rcu_read_unlock(); + if (master != NULL) + master->s_info = NULL; + if (slave != NULL) + slave->s_info = NULL; + if (!sync) + return 0; /* no err if sync disabled */ + if (num == sync) + return -EINVAL; /* err if sync instance is itself */ + if ((master != NULL) && (slave != NULL)) { + master->s_info = slave; + slave->s_info = master; + } + return 0; +} + +static int ad5816_sync_dis(struct ad5816_info *info) +{ + if (info->s_info != NULL) { + info->s_info->s_mode = 0; + info->s_info->s_info = NULL; + info->s_mode = 0; + info->s_info = NULL; + return 0; + } + return -EINVAL; +} + +static int ad5816_open(struct inode *inode, struct file *file) +{ + struct ad5816_info *info = NULL; + struct ad5816_info *pos = NULL; + int err; + rcu_read_lock(); + list_for_each_entry_rcu(pos, &ad5816_info_list, list) { + if (pos->miscdev.minor == iminor(inode)) { + info = pos; + break; + } + } + rcu_read_unlock(); + if (!info) + return -ENODEV; + err = ad5816_sync_en(info->pdata->num, info->pdata->sync); + if (err == -EINVAL) + dev_err(&info->i2c_client->dev, "%s err: invalid num (%u) and sync (%u) instance\n", + __func__, info->pdata->num, info->pdata->sync); + if (atomic_xchg(&info->in_use, 1)) + return -EBUSY; + if (info->s_info != NULL) { + if (atomic_xchg(&info->s_info->in_use, 1)) + return -EBUSY; + } + file->private_data = info; + ad5816_pm_dev_wr(info, NVC_PWR_ON); + /* set ARC Mode to ensure faster focus */ + ad5816_set_arc_mode(info); + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + + return 0; +} + +static int ad5816_release(struct inode *inode, struct file *file) +{ + struct ad5816_info *info = file->private_data; + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + ad5816_pm_wr_s(info, NVC_PWR_OFF); + file->private_data = NULL; + WARN_ON(!atomic_xchg(&info->in_use, 0)); + if (info->s_info != NULL) + WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); + ad5816_sync_dis(info); + return 0; +} + +static const struct file_operations ad5816_fileops = { + .owner = THIS_MODULE, + .open = ad5816_open, + .unlocked_ioctl = ad5816_ioctl, + .release = ad5816_release, +}; + +static void ad5816_del(struct ad5816_info *info) +{ + ad5816_pm_exit(info); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + ad5816_pm_exit(info->s_info); + + ad5816_sync_dis(info); + spin_lock(&ad5816_spinlock); + list_del_rcu(&info->list); + spin_unlock(&ad5816_spinlock); + synchronize_rcu(); +} + +static int ad5816_remove(struct i2c_client *client) +{ + struct ad5816_info *info = i2c_get_clientdata(client); + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + misc_deregister(&info->miscdev); + ad5816_del(info); + return 0; +} + +static int ad5816_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + pr_info("ad5816: probing focuser.\n"); + struct ad5816_info *info; + char dname[16]; + int err; + dev_dbg(&client->dev, "%s\n", __func__); + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&client->dev, "%s: kzalloc error\n", __func__); + return -ENOMEM; + } + info->i2c_client = client; + if (client->dev.platform_data) { + info->pdata = client->dev.platform_data; + } else { + info->pdata = &ad5816_default_pdata; + dev_dbg(&client->dev,"%s No platform data. Using defaults.\n", __func__); + } + + i2c_set_clientdata(client, info); + INIT_LIST_HEAD(&info->list); + spin_lock(&ad5816_spinlock); + list_add_rcu(&info->list, &ad5816_info_list); + spin_unlock(&ad5816_spinlock); + ad5816_pm_init(info); + ad5816_sdata_init(info); + + err = ad5816_dev_id(info); + if (err < 0) { + dev_err(&client->dev, "%s device not found\n", __func__); + ad5816_pm_wr(info, NVC_PWR_OFF); + if (info->pdata->cfg & NVC_CFG_NODEV) { + ad5816_del(info); + return -ENODEV; + } + } else { + dev_dbg(&client->dev, "%s device found\n", __func__); + if (info->pdata->cfg & NVC_CFG_BOOT_INIT) { + /* initial move causes full initialization */ + ad5816_pm_dev_wr(info, NVC_PWR_ON); + ad5816_position_wr(info, info->cap.focus_infinity); + ad5816_pm_dev_wr(info, NVC_PWR_OFF); + } + } + + if (info->pdata->dev_name != 0) + strcpy(dname, info->pdata->dev_name); + else + strcpy(dname, "ad5816"); + + if (info->pdata->num) + snprintf(dname, sizeof(dname), "%s.%u", dname, info->pdata->num); + + info->miscdev.name = dname; + info->miscdev.fops = &ad5816_fileops; + info->miscdev.minor = MISC_DYNAMIC_MINOR; + if (misc_register(&info->miscdev)) { + dev_err(&client->dev, "%s unable to register misc device %s\n", + __func__, dname); + ad5816_del(info); + return -ENODEV; + } + + return 0; +} + + +static const struct i2c_device_id ad5816_id[] = { + { "ad5816", 0 }, + { }, +}; + + +MODULE_DEVICE_TABLE(i2c, ad5816_id); + +static struct i2c_driver ad5816_i2c_driver = { + .driver = { + .name = "ad5816", + .owner = THIS_MODULE, + }, + .id_table = ad5816_id, + .probe = ad5816_probe, + .remove = ad5816_remove, +}; + +static int __init ad5816_init(void) +{ + return i2c_add_driver(&ad5816_i2c_driver); +} + +static void __exit ad5816_exit(void) +{ + i2c_del_driver(&ad5816_i2c_driver); +} + +module_init(ad5816_init); +module_exit(ad5816_exit); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2285ec5a2c09d0f3e0469a65291ffe983918d112 Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Tue, 22 May 2012 11:28:12 +0300 Subject: video: tegra: host: Improve timing of syncpt wait nvhost_syncpt_wait_timeout() relies on sync point interrupt to signal reaching the sync point value. With low timeout values, the resolution is not good enough and an error is returned even when the value is reached. Update syncpoint value from hardware when doing a sync point wait and do an explicit check for reaching the value even if wait returns a timeout. bug 984166 bug 986788 bug 987597 Change-Id: I640e32cdcdfa66b8977eac273dd32f62bd72abd6 Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/103827 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Mayuresh Kulkarni Reviewed-by: Juha Tukkinen --- drivers/video/tegra/host/nvhost_syncpt.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c index 4835d22881b8..1458a7cb5acc 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.c +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -129,6 +129,19 @@ void nvhost_syncpt_incr(struct nvhost_syncpt *sp, u32 id) nvhost_module_idle(syncpt_to_dev(sp)->dev); } +/** + * Updated sync point form hardware, and returns true if syncpoint is expired, + * false if we may need to wait + */ +static bool syncpt_update_min_is_expired( + struct nvhost_syncpt *sp, + u32 id, + u32 thresh) +{ + syncpt_op().update_min(sp, id); + return nvhost_syncpt_is_expired(sp, id, thresh); +} + /** * Main entrypoint for syncpoint value waits. */ @@ -190,9 +203,9 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, while (timeout) { u32 check = min_t(u32, SYNCPT_CHECK_PERIOD, timeout); int remain = wait_event_interruptible_timeout(wq, - nvhost_syncpt_is_expired(sp, id, thresh), + syncpt_update_min_is_expired(sp, id, thresh), check); - if (remain > 0) { + if (remain > 0 || nvhost_syncpt_is_expired(sp, id, thresh)) { if (value) *value = nvhost_syncpt_read_min(sp, id); err = 0; -- cgit v1.2.3 From d85b5e5703ef0113cbf10d4c5177a942c9b092ba Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Thu, 24 May 2012 14:59:02 +0530 Subject: usb: tegra: modify USB platform data structures Modify USB structures of platform data. Based on the new platform data structures modifying the initialization in board files. Bug 887361 Change-Id: Ie6347a078c9a596a4debe21a353e127ddde35220 Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/103597 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Rohan Somvanshi --- drivers/misc/tegra-baseband/bb-m7400.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/tegra-baseband/bb-m7400.c b/drivers/misc/tegra-baseband/bb-m7400.c index 5808a6e321cd..99698a860917 100644 --- a/drivers/misc/tegra-baseband/bb-m7400.c +++ b/drivers/misc/tegra-baseband/bb-m7400.c @@ -28,10 +28,12 @@ #include #include #include +#include #include #include #include #include + #include "bb-power.h" static struct tegra_bb_gpio_data m7400_gpios[] = { @@ -193,20 +195,17 @@ static int m7400_power(int code) static void m7400_ehci_customize(struct platform_device *pdev) { - struct tegra_ehci_platform_data *ehci_pdata; - struct tegra_uhsic_config *hsic_config; + struct tegra_usb_platform_data *ehci_pdata; - ehci_pdata = (struct tegra_ehci_platform_data *) + ehci_pdata = (struct usb_platform_data *) pdev->dev.platform_data; - hsic_config = (struct tegra_uhsic_config *) - ehci_pdata->phy_config; /* Register PHY callbacks */ - hsic_config->postsuspend = m7400_l2_suspend; - hsic_config->preresume = m7400_l2_resume; + ehci_pdata->ops->post_suspend = m7400_l2_suspend; + ehci_pdata->ops->pre_resume = m7400_l2_resume; /* Override required settings */ - ehci_pdata->power_down_on_bus_suspend = 0; + ehci_pdata->u_data.host.power_off_on_suspend = false; } static int m7400_attrib_write(struct device *dev, int value) -- cgit v1.2.3 From 4e8c072c5b697b9893273d054c82e12026cf2907 Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Tue, 22 May 2012 11:31:14 +0530 Subject: usb: host: tegra: update ehci to use common phy Following enhancements are done: a. Update driver with common phy interface b. Make host driver independent of phy type and remove unnecessary CONFIG variables. Bug 887361 Change-Id: Ibafa37a048df4377b73029039b04d04a53020bd4 Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/103599 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Venkat Moganty GVS: Gerrit_Virtual_Submit --- drivers/usb/host/ehci-hub.c | 12 +- drivers/usb/host/ehci-tegra.c | 1376 ++++++++--------------------------------- drivers/usb/host/ehci.h | 1 + 3 files changed, 264 insertions(+), 1125 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 25ed607aab9a..bf171c180987 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -145,7 +145,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, spin_lock_irqsave(&ehci->lock, flags); /* clear phy low-power mode before changing wakeup flags */ - if (ehci->has_hostpc) { + if (ehci->has_hostpc && !ehci->broken_hostpc_phcd) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg; @@ -181,7 +181,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, } /* enter phy low-power mode again */ - if (ehci->has_hostpc) { + if (ehci->has_hostpc && !ehci->broken_hostpc_phcd) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg; @@ -285,7 +285,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } } #ifdef CONFIG_ARCH_TEGRA_2x_SOC - if (changed && ehci->has_hostpc) { + if (changed && ehci->has_hostpc && !ehci->broken_hostpc_phcd) { spin_unlock_irq(&ehci->lock); msleep(5); /* 5 ms for HCD to enter low-power mode */ spin_lock_irq(&ehci->lock); @@ -389,7 +389,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); /* clear phy low-power mode before resume */ - if (ehci->bus_suspended && ehci->has_hostpc) { + if (ehci->bus_suspended && ehci->has_hostpc && !ehci->broken_hostpc_phcd) { i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { @@ -731,7 +731,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (hostpc_reg) { + if (hostpc_reg && !ehci->broken_hostpc_phcd) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -979,7 +979,7 @@ static int ehci_hub_control ( temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (hostpc_reg) { + if (hostpc_reg && !ehci->broken_hostpc_phcd) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 4e7277b2b889..82523bd200a6 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -2,7 +2,7 @@ * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs * * Copyright (C) 2010 Google, Inc. - * Copyright (C) 2009 - 2012 NVIDIA Corporation + * Copyright (C) 2009 - 2011 NVIDIA Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,7 +16,6 @@ * */ -#include #include #include #include @@ -24,191 +23,146 @@ #include #include -#define TEGRA_USB_PORTSC_PHCD (1 << 23) - -#define TEGRA_USB_SUSP_CTRL_OFFSET 0x400 -#define TEGRA_USB_SUSP_CLR (1 << 5) -#define TEGRA_USB_PHY_CLK_VALID (1 << 7) -#define TEGRA_USB_SRT (1 << 25) -#define TEGRA_USB_PHY_CLK_VALID_INT_ENB (1 << 9) -#define TEGRA_USB_PHY_CLK_VALID_INT_STS (1 << 8) - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -#define TEGRA_USB_PORTSC1_OFFSET 0x184 -#else -#define TEGRA_USB_PORTSC1_OFFSET 0x174 -#endif -#define TEGRA_USB_PORTSC1_WKCN (1 << 20) - -#define TEGRA_LVL2_CLK_GATE_OVRB 0xfc -#define TEGRA_USB2_CLK_OVR_ON (1 << 10) +static const char driver_name[] = "tegra-ehci"; #define TEGRA_USB_DMA_ALIGN 32 -#define STS_SRI (1<<7) /* SOF Recieved */ - -#define HOSTPC_REG_OFFSET 0x1b4 - -#define HOSTPC1_DEVLC_STS (1 << 28) -#define HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) - -#define USB1_PREFETCH_ID 6 -#define USB2_PREFETCH_ID 18 -#define USB3_PREFETCH_ID 17 - struct tegra_ehci_hcd { struct ehci_hcd *ehci; struct tegra_usb_phy *phy; - struct clk *clk; - struct clk *emc_clk; - struct clk *sclk_clk; +#ifdef CONFIG_USB_OTG_UTILS struct otg_transceiver *transceiver; - int host_resumed; - int bus_suspended; - int port_resuming; - int power_down_on_bus_suspend; - int default_enable; - enum tegra_usb_phy_port_speed port_speed; - struct work_struct clk_timer_work; - struct timer_list clk_timer; - bool clock_enabled; - bool timer_event; - struct mutex tegra_ehci_hcd_mutex; +#endif + struct mutex sync_lock; + bool port_resuming; unsigned int irq; bool bus_suspended_fail; }; -static void tegra_ehci_power_up(struct usb_hcd *hcd, bool is_dpd) +struct dma_align_buffer { + void *kmalloc_ptr; + void *old_xfer_buffer; + u8 data[0]; +}; + +static void free_align_buffer(struct urb *urb) { - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct dma_align_buffer *temp = container_of(urb->transfer_buffer, + struct dma_align_buffer, data); + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; + + /* In transaction, DMA from Device */ + if (usb_urb_dir_in(urb)) + memcpy(temp->old_xfer_buffer, temp->data, + urb->transfer_buffer_length); - if (!tegra->default_enable) - clk_enable(tegra->clk); - tegra_usb_phy_power_on(tegra->phy, is_dpd); - tegra->host_resumed = 1; + urb->transfer_buffer = temp->old_xfer_buffer; + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; + kfree(temp->kmalloc_ptr); } -static void tegra_ehci_power_down(struct usb_hcd *hcd, bool is_dpd) +static int alloc_align_buffer(struct urb *urb, gfp_t mem_flags) { - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct dma_align_buffer *temp, *kmalloc_ptr; + size_t kmalloc_size; + + if (urb->num_sgs || urb->sg || + urb->transfer_buffer_length == 0 || + !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) + return 0; + + /* Allocate a buffer with enough padding for alignment */ + kmalloc_size = urb->transfer_buffer_length + + sizeof(struct dma_align_buffer) + TEGRA_USB_DMA_ALIGN - 1; + kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); + + if (!kmalloc_ptr) + return -ENOMEM; - tegra->host_resumed = 0; - tegra_usb_phy_power_off(tegra->phy, is_dpd); - if (!tegra->default_enable) - clk_disable(tegra->clk); + /* Position our struct dma_align_buffer such that data is aligned */ + temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; + temp->kmalloc_ptr = kmalloc_ptr; + temp->old_xfer_buffer = urb->transfer_buffer; + /* OUT transaction, DMA to Device */ + if (!usb_urb_dir_in(urb)) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + + urb->transfer_buffer = temp->data; + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; + + return 0; } -static int tegra_ehci_internal_port_reset( - struct ehci_hcd *ehci, - u32 __iomem *portsc_reg -) +static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, + struct urb *urb, gfp_t mem_flags) { - u32 temp; - unsigned long flags; - int retval = 0; - int i, tries; - u32 saved_usbintr; + int ret; - spin_lock_irqsave(&ehci->lock, flags); - saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable); - /* disable USB interrupt */ - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - spin_unlock_irqrestore(&ehci->lock, flags); + ret = alloc_align_buffer(urb, mem_flags); + if (ret) + return ret; - /* - * Here we have to do Port Reset at most twice for - * Port Enable bit to be set. - */ - for (i = 0; i < 2; i++) { - temp = ehci_readl(ehci, portsc_reg); - temp |= PORT_RESET; - ehci_writel(ehci, temp, portsc_reg); - mdelay(10); - temp &= ~PORT_RESET; - ehci_writel(ehci, temp, portsc_reg); - mdelay(1); - tries = 100; - do { - mdelay(1); - /* - * Up to this point, Port Enable bit is - * expected to be set after 2 ms waiting. - * USB1 usually takes extra 45 ms, for safety, - * we take 100 ms as timeout. - */ - temp = ehci_readl(ehci, portsc_reg); - } while (!(temp & PORT_PE) && tries--); - if (temp & PORT_PE) - break; + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + + /* Control packets over dma */ + if (urb->setup_dma) + dma_sync_single_for_device(hcd->self.controller, + urb->setup_dma, sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + + /* urb buffers over dma */ + if (urb->transfer_dma) { + enum dma_data_direction dir; + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + dma_sync_single_for_device(hcd->self.controller, + urb->transfer_dma, urb->transfer_buffer_length, dir); } - if (i == 2) - retval = -ETIMEDOUT; - - /* - * Clear Connect Status Change bit if it's set. - * We can't clear PORT_PEC. It will also cause PORT_PE to be cleared. - */ - if (temp & PORT_CSC) - ehci_writel(ehci, PORT_CSC, portsc_reg); - - /* - * Write to clear any interrupt status bits that might be set - * during port reset. - */ - temp = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, temp, &ehci->regs->status); - - /* restore original interrupt enable bits */ - ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable); - return retval; + + if (ret) + free_align_buffer(urb); + + return ret; } -static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) +static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, + struct urb *urb) +{ + usb_hcd_unmap_urb_for_dma(hcd, urb); + free_align_buffer(urb); + + if (urb->transfer_dma) { + enum dma_data_direction dir; + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + if (dir == DMA_FROM_DEVICE) + dma_sync_single_for_cpu(hcd->self.controller, + urb->transfer_dma, urb->transfer_buffer_length, + DMA_FROM_DEVICE); + } +} + +static irqreturn_t tegra_ehci_irq(struct usb_hcd *hcd) { - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - struct ehci_regs __iomem *hw = ehci->regs; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - u32 val; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); irqreturn_t irq_status; bool pmc_remote_wakeup = false; - /* Fence read for coherency of AHB master intiated writes */ - if (tegra->phy->instance == 0) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); - else if (tegra->phy->instance == 1) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB2_PREFETCH_ID)); - else if (tegra->phy->instance == 2) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB3_PREFETCH_ID)); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc)) { - /* check if there is any remote wake event */ - if (tegra_usb_phy_is_remotewake_detected(tegra->phy)) { - pmc_remote_wakeup = true; - spin_lock (&ehci->lock); - usb_hcd_resume_root_hub(hcd); - spin_unlock (&ehci->lock); - } - } - if (tegra->phy->hotplug) { - spin_lock(&ehci->lock); - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - if ((val & TEGRA_USB_PHY_CLK_VALID_INT_STS)) { - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB | - TEGRA_USB_PHY_CLK_VALID_INT_STS; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - - val = readl(&hw->status); - if (!(val & STS_PCD)) { - spin_unlock(&ehci->lock); - return 0; - } - val = readl(hcd->regs + TEGRA_USB_PORTSC1_OFFSET); - val &= ~(TEGRA_USB_PORTSC1_WKCN | PORT_RWC_BITS); - writel(val , (hcd->regs + TEGRA_USB_PORTSC1_OFFSET)); - } + spin_lock(&ehci->lock); + irq_status = tegra_usb_phy_irq(tegra->phy); + if (irq_status == IRQ_NONE) { spin_unlock(&ehci->lock); + return irq_status; } + if (tegra_usb_phy_remote_wakeup(tegra->phy)) { + ehci_info(ehci, "remote wakeup detected\n"); + pmc_remote_wakeup = true; + usb_hcd_resume_root_hub(hcd); + } + spin_unlock(&ehci->lock); irq_status = ehci_irq(hcd); @@ -220,561 +174,112 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) ehci->controller_remote_wakeup = false; /* disable interrupts */ ehci_writel(ehci, 0, &ehci->regs->intr_enable); - tegra_usb_phy_preresume(tegra->phy, true); + tegra_usb_phy_pre_resume(tegra->phy, true); tegra->port_resuming = 1; } return irq_status; } + static int tegra_ehci_hub_control( struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength ) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int ports = HCS_N_PORTS(ehci->hcs_params); - u32 temp, status, cmd_run; - u32 __iomem *status_reg; - u32 usbsts_reg; - + struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; - int retval = 0; - unsigned selector; - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - bool hsic = false; + int retval = 0; + u32 __iomem *status_reg; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if (!tegra->host_resumed) { + if (!tegra_usb_phy_hw_accessible(tegra->phy)) { if (buf) - memset (buf, 0, wLength); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + memset(buf, 0, wLength); return retval; } - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; spin_lock_irqsave(&ehci->lock, flags); - - /* - * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits - * that are write on clear, by writing back the register read value, so - * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits - */ - if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { - temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; - ehci_writel(ehci, temp & ~PORT_PE, status_reg); - goto done; - } else if (typeReq == GetPortStatus) { - temp = ehci_readl(ehci, status_reg); - /* check port is in resume state */ - if (tegra->port_resuming) { - int delay = ehci->reset_done[wIndex-1] - jiffies; - /* Sometimes it seems we get called too soon... In that case, wait.*/ - if (delay > 0) { - ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); - mdelay(jiffies_to_msecs(delay)); - } - /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ - if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { - pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); + /* Do tegra phy specific actions based on the type request */ + switch (typeReq) { + case GetPortStatus: + if (time_after_eq(jiffies, ehci->reset_done[wIndex - 1])) { + if (tegra->port_resuming) { + int delay = ehci->reset_done[wIndex-1] - jiffies; + /* Sometimes it seems we get called too soon... In that case, wait.*/ + if (delay > 0) { + ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); + mdelay(jiffies_to_msecs(delay)); + } + /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ + if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { + pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); + } + tegra_usb_phy_post_resume(tegra->phy); + tegra->port_resuming = 0; + /* If run bit is not set by now enable it */ + if (ehci->command & CMD_RUN) { + ehci->command |= CMD_RUN; + ehci_writel(ehci, ehci->command, + &ehci->regs->command); + } } - tegra->port_resuming = 0; - tegra_usb_phy_postresume(tegra->phy, false); - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - ehci->command |= CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run |= CMD_RUN; - /* - * ehci run bit is disabled to avoid SOF. - * 2LS WAR is executed by now enable the run bit. - */ - ehci_writel(ehci, cmd_run, &ehci->regs->command); - /* Now we can safely re-enable irqs */ - ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); - } - } - - } else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { - temp = ehci_readl(ehci, status_reg); - if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { - retval = -EPIPE; - goto done; } - - temp &= ~PORT_WKCONN_E; - temp |= PORT_WKDISC_E | PORT_WKOC_E; - ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - - /* Need a 4ms delay before the controller goes to suspend */ - mdelay(4); - - /* - * If a transaction is in progress, there may be a delay in - * suspending the port. Poll until the port is suspended. - */ - if (handshake(ehci, status_reg, PORT_SUSPEND, - PORT_SUSPEND, 5000)) - pr_err("%s: timeout waiting for SUSPEND\n", __func__); - - set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); - /* - * If RUN bit is disabled interrupt is not generated after suspend. - * This change on T20 will allow ASE interrupt generated after suspend - * which will unlink the qheads. - */ -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* Disable RUN bit. */ - ehci->command &= ~CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run &= ~CMD_RUN; - ehci_writel(ehci, cmd_run, &ehci->regs->command); - if (handshake (ehci, &ehci->regs->status, - STS_HALT, STS_HALT, 16 * 125)) - pr_err("%s() timeout waiting for STS_HALT\n", __func__); + break; + case ClearPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + tegra_usb_phy_pre_resume(tegra->phy, false); + tegra->port_resuming = 1; } -#endif - tegra_usb_phy_postsuspend(tegra->phy, false); - - goto done; - } - - /* For USB1 port we need to issue Port Reset twice internally */ - if (tegra->phy->instance == 0 && - (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { - spin_unlock_irqrestore(&ehci->lock, flags); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return tegra_ehci_internal_port_reset(ehci, status_reg); + break; } + spin_unlock_irqrestore(&ehci->lock, flags); - /* - * Tegra host controller will time the resume operation to clear the bit - * when the port control state switches to HS or FS Idle. This behavior - * is different from EHCI where the host controller driver is required - * to set this bit to a zero after the resume duration is timed in the - * driver. - */ - else if (typeReq == ClearPortFeature && - wValue == USB_PORT_FEAT_SUSPEND) { - temp = ehci_readl(ehci, status_reg); - if ((temp & PORT_RESET) || !(temp & PORT_PE)) { - retval = -EPIPE; - goto done; - } - - if (!(temp & PORT_SUSPEND)) - goto done; - - tegra->port_resuming = 1; - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* disable interrupts */ - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - /* Disable RUN bit. */ - ehci->command &= ~CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run &= ~CMD_RUN; - ehci_writel(ehci, cmd_run, &ehci->regs->command); - if (handshake (ehci, &ehci->regs->status, - STS_HALT, STS_HALT, 16 * 125)) - pr_err("%s() timeout waiting for STS_HALT\n", __func__); - } - - /* Disable disconnect detection during port resume */ - tegra_usb_phy_preresume(tegra->phy, false); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_UTMIP) { -#endif - ehci_dbg(ehci, "%s:USBSTS = 0x%x", __func__, - ehci_readl(ehci, &ehci->regs->status)); - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, usbsts_reg, &ehci->regs->status); - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - udelay(20); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) - pr_err("%s: timeout set for STS_SRI\n", __func__); - - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, usbsts_reg, &ehci->regs->status); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, 0, 2000)) - pr_err("%s: timeout clear STS_SRI\n", __func__); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) - pr_err("%s: timeout set STS_SRI\n", __func__); - - udelay(20); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - } -#endif - temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); - /* start resume signaling */ - ehci_writel(ehci, temp | PORT_RESUME, status_reg); - - ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); - /* whoever resumes must GetPortStatus to complete it!! */ - goto done; - } + /* handle ehci hub control request */ + retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); - /* Handle port reset here */ - if ((hsic) && (typeReq == SetPortFeature) && - ((wValue == USB_PORT_FEAT_RESET) || (wValue == USB_PORT_FEAT_POWER))) { - selector = wIndex >> 8; - wIndex &= 0xff; - if (!wIndex || wIndex > ports) { - retval = -EPIPE; - goto done; - } - wIndex--; - status = 0; - temp = ehci_readl(ehci, status_reg); - if (temp & PORT_OWNER) - goto done; - temp &= ~PORT_RWC_BITS; - - switch (wValue) { - case USB_PORT_FEAT_RESET: - { - if (temp & PORT_RESUME) { - retval = -EPIPE; - goto done; - } - /* line status bits may report this as low speed, - * which can be fine if this root hub has a - * transaction translator built in. - */ - if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT - && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { - ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); - temp |= PORT_OWNER; - ehci_writel(ehci, temp, status_reg); - } else { - ehci_vdbg(ehci, "port %d reset\n", wIndex + 1); - temp &= ~PORT_PE; - /* - * caller must wait, then call GetPortStatus - * usb 2.0 spec says 50 ms resets on root - */ - ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(50); - ehci_writel(ehci, temp, status_reg); - if (hsic && (wIndex == 0)) + /* do tegra phy specific actions based on the type request */ + if (!retval) { + spin_lock_irqsave(&ehci->lock, flags); + switch (typeReq) { + case SetPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + /* Need a 4ms delay for controller to suspend */ + mdelay(4); + tegra_usb_phy_post_suspend(tegra->phy); + } else if (wValue == USB_PORT_FEAT_RESET) { + if (ehci->reset_done[0] && wIndex == 0) tegra_usb_phy_bus_reset(tegra->phy); + } else if (wValue == USB_PORT_FEAT_POWER) { + if (wIndex == 1) + tegra_usb_phy_port_power(tegra->phy); } - break; } - case USB_PORT_FEAT_POWER: - { - if (HCS_PPC(ehci->hcs_params)) - ehci_writel(ehci, temp | PORT_POWER, status_reg); - if (hsic && (wIndex == 0)) - tegra_usb_phy_bus_connect(tegra->phy); - break; - } - } - goto done; + spin_unlock_irqrestore(&ehci->lock, flags); } - spin_unlock_irqrestore(&ehci->lock, flags); - - /* Handle the hub control events here */ - retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return retval; -done: - spin_unlock_irqrestore(&ehci->lock, flags); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); return retval; } -#ifdef CONFIG_PM -static void tegra_ehci_restart(struct usb_hcd *hcd, bool is_dpd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - unsigned int temp; - - ehci->controller_resets_phy = 0; - tegra_ehci_pre_reset(tegra->phy, false); - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) - ehci->controller_resets_phy = 1; - - /* setup the frame list and Async q heads */ - ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); - ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); - /* setup the command register and set the controller in RUN mode */ - ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - /* dont start RS here for HSIC, it will be set by bus_reset */ - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_HSIC) -#endif - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - - /* Enable the root Port Power */ - if (HCS_PPC(ehci->hcs_params)) { - temp = ehci_readl(ehci, &ehci->regs->port_status[0]); - ehci_writel(ehci, temp | PORT_POWER, &ehci->regs->port_status[0]); - } - - down_write(&ehci_cf_port_reset_rwsem); - if(is_dpd) - hcd->state = HC_STATE_SUSPENDED; - else - hcd->state = HC_STATE_RUNNING; - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - /* flush posted writes */ - ehci_readl(ehci, &ehci->regs->command); - up_write(&ehci_cf_port_reset_rwsem); - - /* Turn On Interrupts */ - ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); -} - -static int tegra_usb_suspend(struct usb_hcd *hcd, bool is_dpd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_regs __iomem *hw = tegra->ehci->regs; - unsigned long flags; - int hsic = 0; - - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - - spin_lock_irqsave(&tegra->ehci->lock, flags); - - if (tegra->ehci->has_hostpc) - tegra->port_speed = (readl(hcd->regs + HOSTPC_REG_OFFSET) >> 25) & 0x3; - else - tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; - ehci_halt(tegra->ehci); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* - * Ehci run bit is disabled by now read this into command variable - * so that bus resume will not enable run bit immedialty. - * this is required for 2LS WAR on UTMIP interface. - */ - tegra->ehci->command = ehci_readl(tegra->ehci, - &tegra->ehci->regs->command); - } -#endif - - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - tegra_ehci_power_down(hcd, is_dpd); - return 0; -} - -static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct ehci_regs __iomem *hw = ehci->regs; - unsigned long val; - bool hsic; - bool null_ulpi; - bool utmip_remote_wakeup = false; - - null_ulpi = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI); - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - - tegra_ehci_power_up(hcd, is_dpd); - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - if ((tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) || (hsic) || - (null_ulpi)) - goto restart; - - /* Force the phy to keep data lines in suspend state */ - tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - ehci_reset(ehci); - } - - /* Enable host mode */ - tdi_reset(ehci); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc)) { - val = readl(hcd->regs + HOSTPC_REG_OFFSET); - val &= ~HOSTPC1_DEVLC_PTS(~0); - val |= HOSTPC1_DEVLC_STS; - writel(val, hcd->regs + HOSTPC_REG_OFFSET); - } - - /* Enable Port Power */ - val = readl(&hw->port_status[0]); - val |= PORT_POWER; - writel(val, &hw->port_status[0]); - udelay(10); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc) && (tegra->phy->remote_wakeup)) { - utmip_remote_wakeup = true; - } - - /* Check if the phy resume from LP0. When the phy resume from LP0 - * USB register will be reset. */ - if (!readl(&hw->async_next)) { -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Start the controller */ - val = readl(&hw->command); - writel((val | CMD_RUN), &hw->command); -#endif - /* Program the field PTC based on the saved speed mode */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH) - val |= PORT_TEST_FORCE; - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) - val |= PORT_TEST(6); - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= PORT_TEST(7); - writel(val, &hw->port_status[0]); - udelay(10); - - /* Disable test mode by setting PTC field to NORMAL_OP */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - writel(val, &hw->port_status[0]); - udelay(10); - } - - /* Poll until CCS is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, - PORT_CONNECT, 2000)) { - pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); - goto restart; - } - - /* Poll until PE is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_PE, - PORT_PE, 2000)) { - pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); - goto restart; - } - - /* Clear the PCI status, to avoid an interrupt taken upon resume */ - val = readl(&hw->status); - val |= STS_PCD; - writel(val, &hw->status); - - /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */ - val = readl(&hw->port_status[0]); - if ((val & PORT_POWER) && (val & PORT_PE)) { - val |= PORT_SUSPEND; - writel(val, &hw->port_status[0]); - - /* Need a 4ms delay before the controller goes to suspend */ - mdelay(4); - - /* Wait until port suspend completes */ - if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, - PORT_SUSPEND, 1000)) { - pr_err("%s: timeout waiting for PORT_SUSPEND\n", - __func__); - goto restart; - } - } - - tegra_ehci_phy_restore_end(tegra->phy); - if (utmip_remote_wakeup) { - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - } - return 0; - -restart: - if (null_ulpi) { - bool LP0 = !readl(&hw->async_next); - - if (LP0) { - static int cnt = 1; - - pr_info("LP0 restart %d\n", cnt++); - tegra_ehci_phy_restore_start(tegra->phy, - tegra->port_speed); - } - - val = readl(&hw->port_status[0]); - if (!((val & PORT_POWER) && (val & PORT_PE))) { - tegra_ehci_restart(hcd, is_dpd); - } - - if (LP0) - tegra_ehci_phy_restore_end(tegra->phy); - - return 0; - } - - if ((tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) && (!hsic)) - tegra_ehci_phy_restore_end(tegra->phy); - if (hsic) { - val = readl(&hw->port_status[0]); - if (!((val & PORT_POWER) && (val & PORT_PE))) - tegra_ehci_restart(hcd, false); - - tegra_usb_phy_bus_idle(tegra->phy); - if (!tegra_usb_phy_is_device_connected(tegra->phy)) - pr_err("%s: no hsic device conenction\n", __func__); - } else { - tegra_ehci_restart(hcd, false); - } - - return 0; -} -#endif - -/* - * Disable PHY clock valid interrupts and wait for the interrupt handler to - * finish. - * - * Requires a lock on tegra_ehci_hcd_mutex - * Must not be called with a lock on ehci->lock - */ -static void tegra_ehci_disable_phy_interrupt(struct usb_hcd *hcd) { - struct tegra_ehci_hcd *tegra; - u32 val; - if (hcd->irq >= 0) { - tegra = dev_get_drvdata(hcd->self.controller); - if (tegra->phy->hotplug) { - /* Disable PHY clock valid interrupts */ - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - } - /* Wait for the interrupt handler to finish */ - synchronize_irq(hcd->irq); - } -} - static void tegra_ehci_shutdown(struct usb_hcd *hcd) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - tegra_ehci_disable_phy_interrupt(hcd); - /* ehci_shutdown touches the USB controller registers, make sure - * controller has clocks to it */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, false); - - ehci_shutdown(hcd); - - /* we are ready to shut down, powerdown the phy */ - tegra_ehci_power_down(hcd, false); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + mutex_lock(&tegra->sync_lock); + del_timer_sync(&ehci->watchdog); + del_timer_sync(&ehci->iaa_watchdog); + if (tegra_usb_phy_hw_accessible(tegra->phy)) { + spin_lock_irq(&ehci->lock); + ehci_silence_controller(ehci); + spin_unlock_irq(&ehci->lock); + } + mutex_unlock(&tegra->sync_lock); } static int tegra_ehci_setup(struct usb_hcd *hcd) @@ -786,24 +291,18 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci->has_hostpc = tegra_usb_phy_has_hostpc(tegra->phy) ? 1 : 0; + ehci->broken_hostpc_phcd = true; -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - ehci->has_hostpc = 1; -#endif hcd->has_tt = 1; - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_NULL_ULPI) { - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - } - retval = ehci_halt(ehci); if (retval) return retval; @@ -815,267 +314,46 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) ehci->sbrn = 0x20; ehci->controller_remote_wakeup = false; - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) { - tegra_ehci_pre_reset(tegra->phy, false); - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - - /* - * Resetting the controller has the side effect of resetting the PHY. - * So, never reset the controller after the calling - * tegra_ehci_reinit API. - */ - ehci->controller_resets_phy = 1; - } + ehci_reset(ehci); + tegra_usb_phy_reset(tegra->phy); ehci_port_power(ehci, 1); return retval; } + #ifdef CONFIG_PM static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - int error_status = 0; + int err = 0; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); + mutex_lock(&tegra->sync_lock); tegra->bus_suspended_fail = false; - tegra_ehci_disable_phy_interrupt(hcd); - /* ehci_shutdown touches the USB controller registers, make sure - * controller has clocks to it */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, false); - error_status = ehci_bus_suspend(hcd); - if (error_status) + err = ehci_bus_suspend(hcd); + if (err) tegra->bus_suspended_fail = true; - if (!error_status && tegra->power_down_on_bus_suspend) { - tegra_usb_suspend(hcd, false); - tegra->bus_suspended = 1; - } - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + else + tegra_usb_phy_suspend(tegra->phy); + mutex_unlock(&tegra->sync_lock); - return error_status; + return err; } static int tegra_ehci_bus_resume(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - int ehci_bus_resumed; + int err = 0; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if (tegra->bus_suspended && tegra->power_down_on_bus_suspend) { - tegra_usb_resume(hcd, false); - tegra->bus_suspended = 0; - } + mutex_lock(&tegra->sync_lock); + tegra_usb_phy_resume(tegra->phy); + err = ehci_bus_resume(hcd); + mutex_unlock(&tegra->sync_lock); - ehci_bus_resumed = ehci_bus_resume(hcd); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ehci_bus_resumed; + return err; } #endif -struct dma_aligned_buffer { - void *kmalloc_ptr; - void *old_xfer_buffer; - u8 data[0]; -}; - -static void free_dma_aligned_buffer(struct urb *urb) -{ - struct dma_aligned_buffer *temp = container_of(urb->transfer_buffer, - struct dma_aligned_buffer, data); - - if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) - return; - - if(usb_urb_dir_in(urb)) - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); - urb->transfer_buffer = temp->old_xfer_buffer; - kfree(temp->kmalloc_ptr); - urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; -} - -static int alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags) -{ - struct dma_aligned_buffer *temp, *kmalloc_ptr; - size_t kmalloc_size; - - if (urb->num_sgs || urb->sg || - urb->transfer_buffer_length == 0 || - !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) - return 0; - - /* Allocate a buffer with enough padding for alignment */ - kmalloc_size = urb->transfer_buffer_length + - sizeof(struct dma_aligned_buffer) + TEGRA_USB_DMA_ALIGN - 1; - - kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); - if (!kmalloc_ptr) - return -ENOMEM; - - /* Position our struct dma_aligned_buffer such that data is aligned */ - temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; - temp->kmalloc_ptr = kmalloc_ptr; - temp->old_xfer_buffer = urb->transfer_buffer; - if (!usb_urb_dir_in(urb)) - memcpy(temp->data, urb->transfer_buffer, - urb->transfer_buffer_length); - urb->transfer_buffer = temp->data; - urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; - - return 0; -} - -static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, - gfp_t mem_flags) -{ - int ret; - - ret = alloc_dma_aligned_buffer(urb, mem_flags); - if (ret) - return ret; - - ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); - - /* control packets over dma */ - if (urb->setup_dma) - dma_sync_single_for_device(hcd->self.controller, - urb->setup_dma, sizeof(struct usb_ctrlrequest), - DMA_TO_DEVICE); - - /* urb buffers over dma */ - if (urb->transfer_dma) { - enum dma_data_direction dir; - dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - dma_sync_single_for_device(hcd->self.controller, - urb->transfer_dma, urb->transfer_buffer_length, dir); - } - - if (ret) - free_dma_aligned_buffer(urb); - - return ret; -} - -static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - - /* Fence read for coherency of AHB master intiated writes */ - if (tegra->phy->instance == 0) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); - else if (tegra->phy->instance == 1) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB2_PREFETCH_ID)); - else if (tegra->phy->instance == 2) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB3_PREFETCH_ID)); - - if (urb->transfer_dma) { - enum dma_data_direction dir; - dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - if (dir == DMA_FROM_DEVICE) - dma_sync_single_for_cpu(hcd->self.controller, - urb->transfer_dma, urb->transfer_buffer_length, - DMA_FROM_DEVICE); - } - - usb_hcd_unmap_urb_for_dma(hcd, urb); - free_dma_aligned_buffer(urb); -} - -void clk_timer_callback(unsigned long data) -{ - struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd*) data; - unsigned long flags; - - if (!timer_pending(&tegra->clk_timer)) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->timer_event = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - schedule_work(&tegra->clk_timer_work); - } -} - -static void clk_timer_work_handler(struct work_struct* clk_timer_work) { - struct tegra_ehci_hcd *tegra = container_of(clk_timer_work, - struct tegra_ehci_hcd, clk_timer_work); - int ret; - unsigned long flags; - bool clock_enabled, timer_event; - - spin_lock_irqsave(&tegra->ehci->lock, flags); - clock_enabled = tegra->clock_enabled; - timer_event = tegra->timer_event; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - if (timer_event) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 0; - tegra->timer_event = 0; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - clk_disable(tegra->emc_clk); - clk_disable(tegra->sclk_clk); - return; - } - - if ((!clock_enabled)) { - ret = mod_timer(&tegra->clk_timer, jiffies + msecs_to_jiffies(2000)); - if (ret) - pr_err("tegra_ehci_urb_enqueue timer modify failed \n"); - clk_enable(tegra->emc_clk); - clk_enable(tegra->sclk_clk); - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - } else { - if (timer_pending(&tegra->clk_timer)) { - mod_timer_pending (&tegra->clk_timer, jiffies - + msecs_to_jiffies(2000)); - } - } -} - -static int tegra_ehci_urb_enqueue ( - struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags) -{ - struct tegra_ehci_hcd *pdata; - int xfertype; - int transfer_buffer_length; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - pdata = dev_get_drvdata(hcd->self.controller); - - xfertype = usb_endpoint_type(&urb->ep->desc); - transfer_buffer_length = urb->transfer_buffer_length; - spin_lock_irqsave(&ehci->lock,flags); - /* Turn on the USB busy hints */ - switch (xfertype) { - case USB_ENDPOINT_XFER_INT: - if (transfer_buffer_length < 255) { - /* Do nothing for interrupt buffers < 255 */ - } else { - /* signal to set the busy hints */ - schedule_work(&pdata->clk_timer_work); - } - break; - case USB_ENDPOINT_XFER_ISOC: - case USB_ENDPOINT_XFER_BULK: - /* signal to set the busy hints */ - schedule_work(&pdata->clk_timer_work); - break; - case USB_ENDPOINT_XFER_CONTROL: - default: - /* Do nothing special here */ - break; - } - spin_unlock_irqrestore(&ehci->lock,flags); - return ehci_urb_enqueue(hcd, urb, mem_flags); -} - static const struct hc_driver tegra_ehci_hc_driver = { .description = hcd_name, .product_desc = "Tegra EHCI Host Controller", @@ -1085,9 +363,10 @@ static const struct hc_driver tegra_ehci_hc_driver = { /* standard ehci functions */ .start = ehci_run, .stop = ehci_stop, + .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, + .endpoint_reset = ehci_endpoint_reset, .get_frame_number = ehci_get_frame, .hub_status_data = ehci_hub_status_data, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, @@ -1101,10 +380,9 @@ static const struct hc_driver tegra_ehci_hc_driver = { .map_urb_for_dma = tegra_ehci_map_urb_for_dma, .unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma, .hub_control = tegra_ehci_hub_control, - .urb_enqueue = tegra_ehci_urb_enqueue, #ifdef CONFIG_PM - .bus_suspend = tegra_ehci_bus_suspend, - .bus_resume = tegra_ehci_bus_resume, + .bus_suspend = tegra_ehci_bus_suspend, + .bus_resume = tegra_ehci_bus_resume, #endif }; @@ -1113,76 +391,30 @@ static int tegra_ehci_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; struct tegra_ehci_hcd *tegra; - struct tegra_ehci_platform_data *pdata; int err = 0; int irq; - int instance = pdev->id; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "Platform data missing\n"); - return -EINVAL; - } tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL); - if (!tegra) + if (!tegra) { + dev_err(&pdev->dev, "memory alloc failed\n"); return -ENOMEM; + } - mutex_init(&tegra->tegra_ehci_hcd_mutex); + mutex_init(&tegra->sync_lock); hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { - dev_err(&pdev->dev, "Unable to create HCD\n"); + dev_err(&pdev->dev, "unable to create HCD\n"); err = -ENOMEM; goto fail_hcd; } platform_set_drvdata(pdev, tegra); - tegra->default_enable = pdata->default_enable; - - tegra->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(tegra->clk)) { - dev_err(&pdev->dev, "Can't get ehci clock\n"); - err = PTR_ERR(tegra->clk); - goto fail_clk; - } - - err = clk_enable(tegra->clk); - if (err) - goto fail_clken; - - - tegra->sclk_clk = clk_get(&pdev->dev, "sclk"); - if (IS_ERR(tegra->sclk_clk)) { - dev_err(&pdev->dev, "Can't get sclk clock\n"); - err = PTR_ERR(tegra->sclk_clk); - goto fail_sclk_clk; - } - - clk_set_rate(tegra->sclk_clk, 80000000); - - tegra->emc_clk = clk_get(&pdev->dev, "emc"); - if (IS_ERR(tegra->emc_clk)) { - dev_err(&pdev->dev, "Can't get emc clock\n"); - err = PTR_ERR(tegra->emc_clk); - goto fail_emc_clk; - } - init_timer(&tegra->clk_timer); - tegra->clk_timer.function = clk_timer_callback; - tegra->clk_timer.data = (unsigned long) tegra; - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Set DDR busy hints to 150MHz. For Tegra 2x SOC, DDR rate is half of EMC rate */ - clk_set_rate(tegra->emc_clk, 300000000); -#else - /* Set DDR busy hints to 100MHz. For Tegra 3x SOC DDR rate equals to EMC rate */ - clk_set_rate(tegra->emc_clk, 100000000); -#endif res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&pdev->dev, "Failed to get I/O memory\n"); + dev_err(&pdev->dev, "failed to get I/O memory\n"); err = -ENXIO; goto fail_io; } @@ -1190,179 +422,103 @@ static int tegra_ehci_probe(struct platform_device *pdev) hcd->rsrc_len = resource_size(res); hcd->regs = ioremap(res->start, resource_size(res)); if (!hcd->regs) { - dev_err(&pdev->dev, "Failed to remap I/O memory\n"); + dev_err(&pdev->dev, "failed to remap I/O memory\n"); err = -ENOMEM; goto fail_io; } - INIT_WORK(&tegra->clk_timer_work, clk_timer_work_handler); + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + err = -ENODEV; + goto fail_irq; + } + set_irq_flags(irq, IRQF_VALID); + tegra->irq = irq; - tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config, - TEGRA_USB_PHY_MODE_HOST, pdata->phy_type); + tegra->phy = tegra_usb_phy_open(pdev); if (IS_ERR(tegra->phy)) { - dev_err(&pdev->dev, "Failed to open USB phy\n"); + dev_err(&pdev->dev, "failed to open USB phy\n"); err = -ENXIO; - goto fail_phy; + goto fail_irq; } - tegra->phy->hotplug = pdata->hotplug; - err = tegra_usb_phy_power_on(tegra->phy, true); + err = tegra_usb_phy_power_on(tegra->phy); if (err) { - dev_err(&pdev->dev, "Failed to power on the phy\n"); - goto fail; - } - - tegra->host_resumed = 1; - tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend; - tegra->ehci = hcd_to_ehci(hcd); - - irq = platform_get_irq(pdev, 0); - if (!irq) { - dev_err(&pdev->dev, "Failed to get IRQ\n"); - err = -ENODEV; - goto fail; + dev_err(&pdev->dev, "failed to power on the phy\n"); + goto fail_phy; } - set_irq_flags(irq, IRQF_VALID); - tegra->irq = irq; -#ifdef CONFIG_USB_OTG_UTILS - if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = otg_get_transceiver(); - if (tegra->transceiver) - otg_set_host(tegra->transceiver, &hcd->self); + err = tegra_usb_phy_init(tegra->phy); + if (err) { + dev_err(&pdev->dev, "failed to init the phy\n"); + goto fail_phy; } -#endif err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (err) { - dev_err(&pdev->dev, "Failed to add USB HCD error = %d\n", err); - goto fail; + dev_err(&pdev->dev, "Failed to add USB HCD, error=%d\n", err); + goto fail_phy; } err = enable_irq_wake(tegra->irq); if (err < 0) { dev_warn(&pdev->dev, - "Couldn't enable USB host mode wakeup, irq=%d, " - "error=%d\n", tegra->irq, err); + "Couldn't enable USB host mode wakeup, irq=%d, " + "error=%d\n", irq, err); err = 0; tegra->irq = 0; } - return err; + tegra->ehci = hcd_to_ehci(hcd); -fail: #ifdef CONFIG_USB_OTG_UTILS - if (tegra->transceiver) { - otg_set_host(tegra->transceiver, NULL); - otg_put_transceiver(tegra->transceiver); + if (tegra_usb_phy_otg_supported(tegra->phy)) { + tegra->transceiver = otg_get_transceiver(); + if (tegra->transceiver) + otg_set_host(tegra->transceiver, &hcd->self); } #endif - tegra_usb_phy_close(tegra->phy); + return err; + fail_phy: + tegra_usb_phy_close(tegra->phy); +fail_irq: iounmap(hcd->regs); fail_io: - clk_disable(tegra->emc_clk); - clk_put(tegra->emc_clk); -fail_emc_clk: - clk_disable(tegra->sclk_clk); - clk_put(tegra->sclk_clk); -fail_sclk_clk: - clk_disable(tegra->clk); -fail_clken: - clk_put(tegra->clk); -fail_clk: usb_put_hcd(hcd); fail_hcd: kfree(tegra); + return err; } + #ifdef CONFIG_PM static int tegra_ehci_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - if (tegra->default_enable) - clk_enable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - if (tegra->default_enable) - clk_enable(tegra->clk); - - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; + return tegra_usb_phy_power_on(tegra->phy); } -static int tegra_ehci_resume(struct device *dev) +static int tegra_ehci_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - int ret; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - ret = tegra_usb_resume(hcd, true); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ret; -} - -static int tegra_ehci_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - int ret; - u32 val; - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - /* if bus suspend is failed means there is remote wakeup resume, - then abort the PM suspend */ - if (tegra->bus_suspended_fail) { - tegra->bus_suspended_fail = false; - pr_err("%s: bus suspend failed, aborting driver suspend\n", __func__); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + /* bus suspend could have failed because of remote wakeup resume */ + if (tegra->bus_suspended_fail) return -EBUSY; - } - if (tegra->phy->hotplug) { - /* Disable PHY clock valid interrupts while going into suspend*/ - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - } - - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - if (tegra->default_enable) - clk_disable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - if (time_before(jiffies, tegra->ehci->next_statechange)) - msleep(10); - - ret = tegra_usb_suspend(hcd, true); - if (tegra->default_enable) - clk_disable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ret; + else + return tegra_usb_phy_power_off(tegra->phy); } static struct dev_pm_ops tegra_ehci_dev_pm_ops = { - .suspend = tegra_ehci_suspend, - .resume = tegra_ehci_resume, - .resume_noirq = tegra_ehci_resume_noirq, + .suspend_noirq = tegra_ehci_suspend_noirq, + .resume_noirq = tegra_ehci_resume_noirq, }; - #endif static int tegra_ehci_remove(struct platform_device *pdev) @@ -1372,9 +528,6 @@ static int tegra_ehci_remove(struct platform_device *pdev) if (tegra == NULL || hcd == NULL) return -EINVAL; - /* make sure controller is on as we will touch its registers */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, true); #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { @@ -1383,30 +536,15 @@ static int tegra_ehci_remove(struct platform_device *pdev) } #endif - /* Turn Off Interrupts */ - ehci_writel(tegra->ehci, 0, &tegra->ehci->regs->intr_enable); - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (tegra->irq) disable_irq_wake(tegra->irq); usb_remove_hcd(hcd); usb_put_hcd(hcd); - tegra_usb_phy_power_off(tegra->phy, true); + tegra_usb_phy_power_off(tegra->phy); tegra_usb_phy_close(tegra->phy); iounmap(hcd->regs); - - del_timer_sync(&tegra->clk_timer); - - clk_disable(tegra->clk); - clk_put(tegra->clk); - - if (tegra->clock_enabled) { - clk_disable(tegra->sclk_clk); - clk_disable(tegra->emc_clk); - } - clk_put(tegra->sclk_clk); - clk_put(tegra->emc_clk); - kfree(tegra); + return 0; } @@ -1421,12 +559,12 @@ static void tegra_ehci_hcd_shutdown(struct platform_device *pdev) static struct platform_driver tegra_ehci_driver = { .probe = tegra_ehci_probe, - .remove = tegra_ehci_remove, + .remove = tegra_ehci_remove, .shutdown = tegra_ehci_hcd_shutdown, - .driver = { - .name = "tegra-ehci", + .driver = { + .name = driver_name, #ifdef CONFIG_PM - .pm = &tegra_ehci_dev_pm_ops, + .pm = &tegra_ehci_dev_pm_ops, #endif } }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 509934ceb4a9..cfbdf32ec0b2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -143,6 +143,7 @@ struct ehci_hcd { /* one per controller */ #ifdef CONFIG_USB_EHCI_TEGRA unsigned controller_resets_phy:1; unsigned controller_remote_wakeup:1; + unsigned broken_hostpc_phcd:1; #endif /* required for usb32 quirk */ -- cgit v1.2.3 From 86c5f5fb289804f915fcd23ee812a1013be2e525 Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Mon, 21 May 2012 16:35:11 +0530 Subject: usb: gadget: tegra: update udc driver to use common phy Update the udc driver to use common phy. Bug 887361 Change-Id: I013c3df22fd7d41718debb96dc8db78f56d73bd1 Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/103600 Reviewed-by: Venkat Moganty GVS: Gerrit_Virtual_Submit --- drivers/usb/gadget/Makefile | 1 - drivers/usb/gadget/fsl_tegra_udc.c | 170 ------------------------------------- drivers/usb/gadget/tegra_udc.c | 119 ++++---------------------- 3 files changed, 16 insertions(+), 274 deletions(-) delete mode 100644 drivers/usb/gadget/fsl_tegra_udc.c (limited to 'drivers') diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index fedd8eef4ae2..c8c7c687010d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,7 +1,6 @@ # # USB peripheral controller drivers # -GCOV_PROFILE_fsl_tegra_udc.o := y GCOV_PROFILE_tegra_udc.o := y ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG diff --git a/drivers/usb/gadget/fsl_tegra_udc.c b/drivers/usb/gadget/fsl_tegra_udc.c deleted file mode 100644 index 31b476ad2279..000000000000 --- a/drivers/usb/gadget/fsl_tegra_udc.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Description: - * Helper functions to support the tegra USB controller - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include - -static struct tegra_usb_phy *phy; -static struct clk *udc_clk; -static struct clk *emc_clk; -static struct clk *sclk_clk; -static void *udc_base; - -int fsl_udc_clk_init(struct platform_device *pdev) -{ - struct resource *res; - int err; - int instance; - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - - - udc_clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(udc_clk)) { - dev_err(&pdev->dev, "Can't get udc clock\n"); - return PTR_ERR(udc_clk); - } - - clk_enable(udc_clk); - - sclk_clk = clk_get(&pdev->dev, "sclk"); - if (IS_ERR(sclk_clk)) { - dev_err(&pdev->dev, "Can't get sclk clock\n"); - err = PTR_ERR(sclk_clk); - goto err_sclk; - } - - clk_set_rate(sclk_clk, 80000000); - clk_enable(sclk_clk); - - emc_clk = clk_get(&pdev->dev, "emc"); - if (IS_ERR(emc_clk)) { - dev_err(&pdev->dev, "Can't get emc clock\n"); - err = PTR_ERR(emc_clk); - goto err_emc; - } - - clk_enable(emc_clk); -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Set DDR busy hints to 150MHz. For Tegra 2x SOC, DDR rate is half of EMC rate */ - clk_set_rate(emc_clk, 300000000); -#else - /* Set DDR busy hints to 100MHz. For Tegra 3x SOC DDR rate equals to EMC rate */ - clk_set_rate(emc_clk, 100000000); -#endif - - /* we have to remap the registers ourselves as fsl_udc does not - * export them for us. - */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -ENXIO; - goto err0; - } - udc_base = ioremap(res->start, resource_size(res)); - if (!udc_base) { - err = -ENOMEM; - goto err0; - } - - instance = pdev->id; - if (instance == -1) - instance = 0; - - phy = tegra_usb_phy_open(instance, udc_base, pdata->phy_config, - TEGRA_USB_PHY_MODE_DEVICE, pdata->usb_phy_type); - if (IS_ERR(phy)) { - dev_err(&pdev->dev, "Can't open phy\n"); - err = PTR_ERR(phy); - goto err1; - } - tegra_usb_phy_power_on(phy, true); - - return 0; -err1: - iounmap(udc_base); -err0: - clk_disable(emc_clk); - clk_put(emc_clk); -err_emc: - clk_disable(sclk_clk); - clk_put(sclk_clk); -err_sclk: - clk_disable(udc_clk); - clk_put(udc_clk); - return err; -} - -void fsl_udc_clk_finalize(struct platform_device *pdev) -{ -} - -void fsl_udc_clk_release(void) -{ - tegra_usb_phy_close(phy); - - iounmap(udc_base); - - clk_disable(udc_clk); - clk_put(udc_clk); - - clk_disable(sclk_clk); - clk_put(sclk_clk); - - clk_disable(emc_clk); - clk_put(emc_clk); -} - -void fsl_udc_clk_suspend(bool is_dpd) -{ - tegra_usb_phy_power_off(phy, is_dpd); - clk_disable(udc_clk); - clk_disable(sclk_clk); - clk_disable(emc_clk); -} - -void fsl_udc_clk_resume(bool is_dpd) -{ - clk_enable(emc_clk); - clk_enable(sclk_clk); - clk_enable(udc_clk); - tegra_usb_phy_power_on(phy, is_dpd); -} - -void fsl_udc_clk_enable(void) -{ - clk_enable(udc_clk); -} - -void fsl_udc_clk_disable(void) -{ - clk_disable(udc_clk); -} - -bool fsl_udc_charger_detect(void) -{ - return tegra_usb_phy_charger_detect(phy); -} - -void fsl_udc_dtd_prepare(void) -{ - /* When we are programming two DTDs very close to each other, - * the second DTD is being prefetched before it is actually written - * to DDR. To prevent this, we disable prefetcher before programming - * any new DTD and re-enable it before priming endpoint. - */ - tegra_usb_phy_memory_prefetch_off(phy); -} - -void fsl_udc_ep_barrier(void) -{ - tegra_usb_phy_memory_prefetch_on(phy); -} diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c index 978db7ece1de..dc14c612b38f 100644 --- a/drivers/usb/gadget/tegra_udc.c +++ b/drivers/usb/gadget/tegra_udc.c @@ -37,7 +37,6 @@ #include #include #include -#include /* check this */ #include #include @@ -50,14 +49,10 @@ #include "tegra_udc.h" -/* #define IS_NEW_PHY_DRIVER 1 */ - -#ifndef IS_NEW_PHY_DRIVER -#include "fsl_tegra_udc.c" -#endif #define DRIVER_DESC "Nvidia Tegra High-Speed USB SOC \ - Device Controller driver" + Device Controller driver" + #define DRIVER_AUTHOR "Venkat Moganty/Rakesh Bodla" #define DRIVER_VERSION "Apr 30, 2012" #define USB1_PREFETCH_ID 6 @@ -73,7 +68,6 @@ & USB_DIR_IN) == USB_DIR_IN) - static const char driver_name[] = "tegra-udc"; static const char driver_desc[] = DRIVER_DESC; @@ -733,11 +727,7 @@ static void tegra_queue_td(struct tegra_ep *ep, struct tegra_req *req) | EP_QUEUE_HEAD_STATUS_HALT)); dQH->size_ioc_int_sts &= temp; -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_memory_prefetch_on(udc->phy); -#else - fsl_udc_ep_barrier(); -#endif /* Ensure that updates to the QH will occur before priming. */ wmb(); @@ -828,11 +818,7 @@ static int tegra_req_to_dtd(struct tegra_req *req, gfp_t gfp_flags) struct ep_td_struct *last_dtd = NULL, *dtd; dma_addr_t dma; -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_memory_prefetch_off(the_udc->phy); -#else - fsl_udc_dtd_prepare(); -#endif do { dtd = tegra_build_dtd(req, &count, &dma, &is_last, gfp_flags); @@ -1229,7 +1215,7 @@ static int tegra_set_selfpowered(struct usb_gadget *gadget, int is_on) } /** - * Notify controller that VBUS is powered, Called by whatever + * Notify controller that VBUS is powered, called by whatever * detects VBUS sessions */ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) @@ -1251,22 +1237,14 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) udc->vbus_active = 0; udc->usb_state = USB_STATE_DEFAULT; spin_unlock(&udc->lock); -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_power_off(udc->phy); -#else - fsl_udc_clk_suspend(false); -#endif if (udc->vbus_reg) { /* set the current limit to 0mA */ regulator_set_current_limit( udc->vbus_reg, 0, 0); } } else if (!udc->vbus_active && is_active) { -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_power_on(udc->phy); -#else - fsl_udc_clk_resume(false); -#endif /* setup the controller in the device mode */ dr_controller_setup(udc); /* setup EP0 for setup packet */ @@ -1348,12 +1326,7 @@ static void tegra_udc_release(struct device *dev) struct tegra_udc *udc = platform_get_drvdata(pdev); complete(udc->done); -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_close(udc->phy); -#else - fsl_udc_clk_suspend(false); -#endif - kfree(udc); } @@ -2109,15 +2082,18 @@ static void tegra_udc_irq_work(struct work_struct *irq_work) { struct tegra_udc *udc = container_of(irq_work, struct tegra_udc, irq_work); + DBG("%s(%d) BEGIN\n", __func__, __LINE__); /* Check whether cable is connected*/ if (vbus_enabled(udc)) tegra_vbus_session(&udc->gadget, 1); else tegra_vbus_session(&udc->gadget, 0); + + DBG("%s(%d) END\n", __func__, __LINE__); } -/* +/** * If VBUS is detected and setup packet is not received in 100ms then * work thread starts and checks for the USB charger detection. */ @@ -2127,11 +2103,7 @@ static void tegra_udc_charger_detect_work(struct work_struct *work) DBG("%s(%d) BEGIN\n", __func__, __LINE__); /* check for the platform charger detection */ -#ifdef IS_NEW_PHY_DRIVER if (tegra_usb_phy_charger_detected(udc->phy)) { -#else - if (fsl_udc_charger_detect()) { -#endif printk(KERN_INFO "USB compliant charger detected\n"); /* check udc regulator is available for drawing vbus current*/ if (udc->vbus_reg) { @@ -2271,8 +2243,6 @@ static int tegra_udc_start(struct usb_gadget_driver *driver, if (!udc) return -ENODEV; - - if (!driver || (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) || !bind || !driver->disconnect @@ -2404,7 +2374,7 @@ static int tegra_udc_setup_qh(struct tegra_udc *udc) /* FIXME: tegra_alloc_request() ignores ep argument */ udc->status_req = container_of(tegra_alloc_request(NULL, GFP_KERNEL), struct tegra_req, req); - /* allocate a small amount of memory to get valid address */ + /* Allocate a small amount of memory to get valid address */ udc->status_req->req.buf = dma_alloc_coherent(&udc->pdev->dev, STATUS_BUFFER_SIZE, &udc->status_req->req.dma, GFP_KERNEL); @@ -2552,7 +2522,6 @@ static int __init tegra_udc_probe(struct platform_device *pdev) goto err_iounmap; } -#ifdef IS_NEW_PHY_DRIVER udc->phy = tegra_usb_phy_open(pdev); if (IS_ERR(udc->phy)) { dev_err(&pdev->dev, "failed to open USB phy\n"); @@ -2571,23 +2540,10 @@ static int __init tegra_udc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to init the phy\n"); goto err_phy; } -#else - /* Initialize USB clocks */ - fsl_udc_clk_init(pdev); -#endif spin_lock_init(&udc->lock); udc->stopped = 1; udc->pdev = pdev; -#ifdef IS_NEW_PHY_DRIVER udc->has_hostpc = tegra_usb_phy_has_hostpc(udc->phy) ? 1 : 0; -#else - #ifdef CONFIG_ARCH_TEGRA_2x_SOC - udc->has_hostpc = 0; - #else - udc->has_hostpc = 1; - #endif -#endif - platform_set_drvdata(pdev, udc); /* Initialize the udc structure including QH members */ @@ -2597,7 +2553,7 @@ static int __init tegra_udc_probe(struct platform_device *pdev) goto err_phy; } - /* initialize usb hw reg except for regs for EP, + /* Initialize usb hw reg except for regs for EP, * leave usbintr reg untouched */ err = dr_controller_setup(udc); if (err) { @@ -2617,7 +2573,7 @@ static int __init tegra_udc_probe(struct platform_device *pdev) goto err_unregister; } - /* use dma_pool for TD management */ + /* Use dma_pool for TD management */ udc->td_pool = dma_pool_create("udc_td", &pdev->dev, sizeof(struct ep_td_struct), DTD_ALIGNMENT, UDC_DMA_BOUNDARY); @@ -2638,9 +2594,9 @@ static int __init tegra_udc_probe(struct platform_device *pdev) PM_QOS_DEFAULT_VALUE); #endif - /* create a work for controlling clocks to the phy if otg is disabled */ + /* Create work for controlling clocks to the phy if otg is disabled */ INIT_WORK(&udc->irq_work, tegra_udc_irq_work); - /* create a delayed work for detecting the USB charger */ + /* Create a delayed work for detecting the USB charger */ INIT_DELAYED_WORK(&udc->work, tegra_udc_charger_detect_work); INIT_WORK(&udc->charger_work, tegra_udc_set_current_limit_work); @@ -2654,35 +2610,21 @@ static int __init tegra_udc_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - -#ifdef IS_NEW_PHY_DRIVER if (tegra_usb_phy_otg_supported(udc->phy)) udc->transceiver = otg_get_transceiver(); -#else - udc->transceiver = otg_get_transceiver(); -#endif if (udc->transceiver) { dr_controller_stop(udc); dr_controller_reset(udc); -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_power_off(udc->phy); -#else - fsl_udc_clk_suspend(false); -#endif udc->vbus_active = 0; udc->usb_state = USB_STATE_DEFAULT; otg_set_peripheral(udc->transceiver, &udc->gadget); } #else /* Power down the phy if cable is not connected */ - if (!vbus_enabled()) { -#ifdef IS_NEW_PHY_DRIVER + if (!vbus_enabled()) tegra_usb_phy_power_off(udc->phy); -#else - fsl_udc_clk_suspend(false); -#endif - } #endif DBG("%s(%d) END\n", __func__, __LINE__); @@ -2694,15 +2636,11 @@ err_del_udc: err_unregister: device_unregister(&udc->gadget.dev); -#ifdef IS_NEW_PHY_DRIVER err_phy: tegra_usb_phy_close(udc->phy); err_irq: free_irq(udc->irq, udc); -#else -err_phy: -#endif err_iounmap: iounmap(udc->regs); @@ -2743,9 +2681,6 @@ static int __exit tegra_udc_remove(struct platform_device *pdev) if (udc->transceiver) otg_set_peripheral(udc->transceiver, NULL); -#ifndef IS_NEW_PHY_DRIVER - fsl_udc_clk_release(); -#endif /* Free allocated memory */ dma_free_coherent(&pdev->dev, STATUS_BUFFER_SIZE, @@ -2760,7 +2695,7 @@ static int __exit tegra_udc_remove(struct platform_device *pdev) release_mem_region(res->start, res->end - res->start + 1); device_unregister(&udc->gadget.dev); - /* free udc --wait for the release() finished */ + /* Free udc -- wait for the release() finished */ wait_for_completion(&done); return 0; @@ -2771,7 +2706,7 @@ static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) struct tegra_udc *udc = platform_get_drvdata(pdev); DBG("%s(%d) BEGIN\n", __func__, __LINE__); - /* if it controller is in otg mode, return */ + /* If the controller is in otg mode, return */ if (udc->transceiver) return 0; @@ -2783,16 +2718,12 @@ static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) udc->usb_state = USB_STATE_DEFAULT; spin_unlock(&udc->lock); } - /* stop the controller and turn off the clocks */ + /* Stop the controller and turn off the clocks */ dr_controller_stop(udc); if (udc->transceiver) udc->transceiver->state = OTG_STATE_UNDEFINED; -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_power_off(udc->phy); -#else - fsl_udc_clk_suspend(true); -#endif DBG("%s(%d) END\n", __func__, __LINE__); return 0; @@ -2803,36 +2734,18 @@ static int tegra_udc_resume(struct platform_device *pdev) struct tegra_udc *udc = platform_get_drvdata(pdev); DBG("%s(%d) BEGIN\n", __func__, __LINE__); -#ifndef IS_NEW_PHY_DRIVER -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Work around to get UTMIP_OTGPD, UTMIP_BIASPD values correctly */ - fsl_udc_clk_resume(true); - fsl_udc_clk_suspend(true); -#endif -#endif - if (udc->transceiver) return 0; -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_power_on(udc->phy); -#else - fsl_udc_clk_resume(true); - fsl_udc_clk_resume(true); -#endif tegra_udc_restart(udc); /* Power down the phy if cable is not connected */ if (!vbus_enabled(udc)) { udc->vbus_active = 0; -#ifdef IS_NEW_PHY_DRIVER tegra_usb_phy_power_off(udc->phy); -#else - fsl_udc_clk_suspend(true); -#endif } - DBG("%s(%d) END\n", __func__, __LINE__); return 0; } -- cgit v1.2.3 From 0d88e1dc78fd422c07b8ef385871223bf7dbc9da Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Mon, 21 May 2012 16:36:19 +0530 Subject: usb: otg: tegra: enhance tegra otg driver Following enhancements are done: a. Removed unnecessary apis. b. Update the new platform data structures. c. Removed unnecessary function call overheads. Bug 887361 Change-Id: I148f2c0adb617c6f3100b84854bbd4ed1e953ecd Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/103601 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Venkat Moganty GVS: Gerrit_Virtual_Submit --- drivers/usb/otg/tegra-otg.c | 153 ++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 99 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index d95d238fd9d6..5258b3ec8992 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -41,7 +40,6 @@ #define USB_VBUS_INT_EN (1 << 8) #define USB_VBUS_INT_STATUS (1 << 9) #define USB_VBUS_STATUS (1 << 10) -#define USB_INTS (USB_VBUS_INT_STATUS | USB_ID_INT_STATUS) #define USB_INT_EN (USB_VBUS_INT_EN | USB_ID_INT_EN | \ USB_VBUS_WAKEUP_EN | USB_ID_PIN_WAKEUP_EN) @@ -67,9 +65,9 @@ struct tegra_otg_data { bool clk_enabled; callback_t charger_cb; void *charger_cb_data; - bool interrupt_mode; }; + static struct tegra_otg_data *tegra_clone; static inline unsigned long otg_readl(struct tegra_otg_data *tegra, @@ -84,20 +82,6 @@ static inline void otg_writel(struct tegra_otg_data *tegra, unsigned long val, writel(val, tegra->regs + offset); } -static void tegra_otg_enable_clk(void) -{ - if (!tegra_clone->clk_enabled) - clk_enable(tegra_clone->clk); - tegra_clone->clk_enabled = true; -} - -static void tegra_otg_disable_clk(void) -{ - if (tegra_clone->clk_enabled) - clk_disable(tegra_clone->clk); - tegra_clone->clk_enabled = false; -} - static const char *tegra_state_name(enum usb_otg_state state) { switch (state) { @@ -132,78 +116,69 @@ static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en) return val; } -static struct platform_device * -tegra_usb_otg_host_register(struct platform_device *ehci_device, - struct tegra_ehci_platform_data *pdata) +static void tegra_start_host(struct tegra_otg_data *tegra) { - struct platform_device *pdev; + struct tegra_usb_otg_data *pdata = tegra->otg.dev->platform_data; + struct platform_device *pdev, *ehci_device = pdata->ehci_device; void *platform_data; int val; + DBG("%s(%d) Begin\n", __func__, __LINE__); + if (tegra->pdev) + return ; + + /* prepare device structure for registering host*/ pdev = platform_device_alloc(ehci_device->name, ehci_device->id); if (!pdev) - return NULL; + return ; val = platform_device_add_resources(pdev, ehci_device->resource, ehci_device->num_resources); if (val) goto error; - pdev->dev.dma_mask = ehci_device->dev.dma_mask; + pdev->dev.dma_mask = ehci_device->dev.dma_mask; pdev->dev.coherent_dma_mask = ehci_device->dev.coherent_dma_mask; - platform_data = kmalloc(sizeof(struct tegra_ehci_platform_data), - GFP_KERNEL); + platform_data = kmalloc(sizeof(struct tegra_usb_platform_data), GFP_KERNEL); if (!platform_data) goto error; - memcpy(platform_data, pdata, sizeof(struct tegra_ehci_platform_data)); + memcpy(platform_data, pdata->ehci_pdata, + sizeof(struct tegra_usb_platform_data)); pdev->dev.platform_data = platform_data; val = platform_device_add(pdev); if (val) goto error_add; - return pdev; + tegra->pdev = pdev; + DBG("%s(%d) End\n", __func__, __LINE__); + return ; error_add: kfree(platform_data); error: pr_err("%s: failed to add the host controller device\n", __func__); platform_device_put(pdev); - return NULL; -} - -static void tegra_usb_otg_host_unregister(struct platform_device *pdev) -{ - kfree(pdev->dev.platform_data); - pdev->dev.platform_data = NULL; - platform_device_unregister(pdev); + tegra->pdev = NULL; } -void tegra_start_host(struct tegra_otg_data *tegra) +static void tegra_stop_host(struct tegra_otg_data *tegra) { - DBG("%s(%d) BEGIN\n", __func__, __LINE__); - - struct tegra_otg_platform_data *pdata = tegra->otg.dev->platform_data; - if (!tegra->pdev) { - tegra->pdev = tegra_usb_otg_host_register(pdata->ehci_device, - pdata->ehci_pdata); - } - - DBG("%s(%d) END\n", __func__, __LINE__); -} + struct platform_device *pdev = tegra->pdev; -void tegra_stop_host(struct tegra_otg_data *tegra) -{ - DBG("%s(%d) BEGIN\n", __func__, __LINE__); + DBG("%s(%d) Begin\n", __func__, __LINE__); - if (tegra->pdev) { - tegra_usb_otg_host_unregister(tegra->pdev); + if (pdev) { + /* unregister host from otg */ + kfree(pdev->dev.platform_data); + pdev->dev.platform_data = NULL; + platform_device_unregister(pdev); tegra->pdev = NULL; } - DBG("%s(%d) END\n", __func__, __LINE__); + DBG("%s(%d) End\n", __func__, __LINE__); } int register_otg_callback(callback_t cb, void *args) @@ -263,10 +238,7 @@ static void irq_work(struct work_struct *work) unsigned long flags; unsigned long status; - clk_enable(tegra->clk); - spin_lock_irqsave(&tegra->lock, flags); - status = tegra->int_status; /* Debug prints */ @@ -290,8 +262,6 @@ static void irq_work(struct work_struct *work) spin_unlock_irqrestore(&tegra->lock, flags); tegra_change_otg_state(tegra, to); - clk_disable(tegra->clk); - tegra_otg_disable_clk(); } static irqreturn_t tegra_otg_irq(int irq, void *data) @@ -301,8 +271,9 @@ static irqreturn_t tegra_otg_irq(int irq, void *data) unsigned long val; spin_lock_irqsave(&tegra->lock, flags); - val = otg_readl(tegra, USB_PHY_WAKEUP); + DBG("%s(%d) interrupt val = 0x%x\n", __func__, __LINE__, val); + if (val & (USB_VBUS_INT_EN | USB_ID_INT_EN)) { DBG("%s(%d) PHY_WAKEUP = 0x%x\n", __func__, __LINE__, val); otg_writel(tegra, val, USB_PHY_WAKEUP); @@ -311,17 +282,11 @@ static irqreturn_t tegra_otg_irq(int irq, void *data) schedule_work(&tegra->work); } } - spin_unlock_irqrestore(&tegra->lock, flags); return IRQ_HANDLED; } -void tegra_otg_check_vbus_detection(void) -{ - tegra_otg_enable_clk(); -} -EXPORT_SYMBOL(tegra_otg_check_vbus_detection); static int tegra_otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) @@ -344,7 +309,7 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg, if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) { tegra->int_status = val; - schedule_work (&tegra->work); + schedule_work(&tegra->work); } DBG("%s(%d) END\n", __func__, __LINE__); @@ -364,7 +329,6 @@ static int tegra_otg_set_host(struct otg_transceiver *otg, clk_enable(tegra->clk); val = otg_readl(tegra, USB_PHY_WAKEUP); val &= ~(USB_VBUS_INT_STATUS | USB_ID_INT_STATUS); - val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); otg_writel(tegra, val, USB_PHY_WAKEUP); clk_disable(tegra->clk); @@ -426,8 +390,6 @@ static DEVICE_ATTR(enable_host, 0644, show_host_en, store_host_en); static int tegra_otg_probe(struct platform_device *pdev) { struct tegra_otg_data *tegra; - struct tegra_otg_platform_data *otg_pdata; - struct tegra_ehci_platform_data *ehci_pdata; struct resource *res; int err; @@ -436,8 +398,6 @@ static int tegra_otg_probe(struct platform_device *pdev) return -ENOMEM; tegra->otg.dev = &pdev->dev; - otg_pdata = tegra->otg.dev->platform_data; - ehci_pdata = otg_pdata->ehci_pdata; tegra->otg.label = "tegra-otg"; tegra->otg.state = OTG_STATE_UNDEFINED; tegra->otg.set_host = tegra_otg_set_host; @@ -448,7 +408,6 @@ static int tegra_otg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tegra); tegra_clone = tegra; - tegra->clk_enabled = false; tegra->interrupt_mode = true; tegra->clk = clk_get(&pdev->dev, NULL); @@ -496,10 +455,8 @@ static int tegra_otg_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register IRQ\n"); goto err_irq; } - INIT_WORK (&tegra->work, irq_work); + INIT_WORK(&tegra->work, irq_work); - if (!ehci_pdata->default_enable) - clk_disable(tegra->clk); dev_info(&pdev->dev, "otg transceiver registered\n"); err = device_create_file(&pdev->dev, &dev_attr_enable_host); @@ -543,59 +500,57 @@ static int __exit tegra_otg_remove(struct platform_device *pdev) static int tegra_otg_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); - struct otg_transceiver *otg = &tegra_otg->otg; + struct tegra_otg_data *tegra = platform_get_drvdata(pdev); + struct otg_transceiver *otg = &tegra->otg; int val; DBG("%s(%d) BEGIN state : %s\n", __func__, __LINE__, tegra_state_name(otg->state)); - clk_enable(tegra_otg->clk); - val = readl(tegra_otg->regs + USB_PHY_WAKEUP); + clk_enable(tegra->clk); + val = readl(tegra->regs + USB_PHY_WAKEUP); val &= ~USB_INT_EN; - writel(val, tegra_otg->regs + USB_PHY_WAKEUP); - clk_disable(tegra_otg->clk); + writel(val, tegra->regs + USB_PHY_WAKEUP); + clk_disable(tegra->clk); - /* suspend peripheral mode, host mode is taken care by host driver */ + /* Suspend peripheral mode, host mode is taken care by host driver */ if (otg->state == OTG_STATE_B_PERIPHERAL) - tegra_change_otg_state(tegra_otg, OTG_STATE_A_SUSPEND); + tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND); DBG("%s(%d) END\n", __func__, __LINE__); - tegra_otg_disable_clk(); return 0; } static void tegra_otg_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); - struct otg_transceiver *otg = &tegra_otg->otg; - + struct tegra_otg_data *tegra = platform_get_drvdata(pdev); + struct otg_transceiver *otg = &tegra->otg; int val; unsigned long flags; DBG("%s(%d) BEGIN\n", __func__, __LINE__); /* Clear pending interrupts */ - clk_enable(tegra_otg->clk); - val = readl(tegra_otg->regs + USB_PHY_WAKEUP); - writel(val, tegra_otg->regs + USB_PHY_WAKEUP); + clk_enable(tegra->clk); + val = readl(tegra->regs + USB_PHY_WAKEUP); + writel(val, tegra->regs + USB_PHY_WAKEUP); DBG("%s(%d) PHY WAKEUP register : 0x%x\n", __func__, __LINE__, val); - clk_disable(tegra_otg->clk); + clk_disable(tegra->clk); /* Handle if host cable is replaced with device during suspend state */ if (otg->state == OTG_STATE_A_HOST && (val & USB_ID_STATUS)) - tegra_change_otg_state(tegra_otg, OTG_STATE_A_SUSPEND); + tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND); /* Enable interrupt and call work to set to appropriate state */ - spin_lock_irqsave(&tegra_otg->lock, flags); - tegra_otg->int_status = (val | USB_INT_EN); - spin_unlock_irqrestore(&tegra_otg->lock, flags); - irq_work(&tegra_otg->work); + spin_lock_irqsave(&tegra->lock, flags); + tegra->int_status = (val | USB_INT_EN); + spin_unlock_irqrestore(&tegra->lock, flags); + irq_work(&tegra->work); - clk_enable(tegra_otg->clk); - val = readl(tegra_otg->regs + USB_PHY_WAKEUP); + clk_enable(tegra->clk); + val = readl(tegra->regs + USB_PHY_WAKEUP); val |= USB_INT_EN; - writel(val, tegra_otg->regs + USB_PHY_WAKEUP); - clk_disable(tegra_otg->clk); + writel(val, tegra->regs + USB_PHY_WAKEUP); + clk_disable(tegra->clk); DBG("%s(%d) END\n", __func__, __LINE__); } -- cgit v1.2.3 From c20772878b3e88a31ef3021e8030d2c20a2daa19 Mon Sep 17 00:00:00 2001 From: Krishna Reddy Date: Tue, 22 May 2012 11:36:03 -0700 Subject: video: tegra: nvmap: Change function declaration order. Change function declaration order to avoid multiple CONFIG_TEGRA_NVMAP #ifdef's. Change-Id: Icb10380f8da4b8037b90e787f961a28e8528814f Signed-off-by: Krishna Reddy Reviewed-on: http://git-master/r/103990 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Bo Yan Reviewed-by: Jon Mayo --- drivers/video/tegra/nvmap/nvmap.h | 86 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/nvmap/nvmap.h b/drivers/video/tegra/nvmap/nvmap.h index b0fb70f64a5c..25403f5e7098 100644 --- a/drivers/video/tegra/nvmap/nvmap.h +++ b/drivers/video/tegra/nvmap/nvmap.h @@ -37,6 +37,8 @@ struct nvmap_device; struct page; struct tegra_iovmm_area; +void _nvmap_handle_free(struct nvmap_handle *h); + #if defined(CONFIG_TEGRA_NVMAP) #define nvmap_err(_client, _fmt, ...) \ dev_err(nvmap_client_to_device(_client), \ @@ -162,7 +164,46 @@ static inline void nvmap_ref_unlock(struct nvmap_client *priv) { mutex_unlock(&priv->ref_lock); } -#endif /* CONFIG_TEGRA_NVMAP */ + +static inline struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h) +{ + if (unlikely(atomic_inc_return(&h->ref) <= 1)) { + pr_err("%s: %s getting a freed handle\n", + __func__, current->group_leader->comm); + if (atomic_read(&h->ref) <= 0) + return NULL; + } + return h; +} + +static inline void nvmap_handle_put(struct nvmap_handle *h) +{ + int cnt = atomic_dec_return(&h->ref); + + if (WARN_ON(cnt < 0)) { + pr_err("%s: %s put to negative references\n", + __func__, current->comm); + } else if (cnt == 0) + _nvmap_handle_free(h); +} + +static inline pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot) +{ + if (h->flags == NVMAP_HANDLE_UNCACHEABLE) + return pgprot_noncached(prot); + else if (h->flags == NVMAP_HANDLE_WRITE_COMBINE) + return pgprot_writecombine(prot); + else if (h->flags == NVMAP_HANDLE_INNER_CACHEABLE) + return pgprot_inner_writeback(prot); + return prot; +} + +#else /* CONFIG_TEGRA_NVMAP */ +struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h); +void nvmap_handle_put(struct nvmap_handle *h); +pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot); + +#endif /* !CONFIG_TEGRA_NVMAP */ struct device *nvmap_client_to_device(struct nvmap_client *client); @@ -216,51 +257,10 @@ int nvmap_pin_ids(struct nvmap_client *client, void nvmap_unpin_ids(struct nvmap_client *priv, unsigned int nr, const unsigned long *ids); -void _nvmap_handle_free(struct nvmap_handle *h); - int nvmap_handle_remove(struct nvmap_device *dev, struct nvmap_handle *h); void nvmap_handle_add(struct nvmap_device *dev, struct nvmap_handle *h); -#if defined(CONFIG_TEGRA_NVMAP) -static inline struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h) -{ - if (unlikely(atomic_inc_return(&h->ref) <= 1)) { - pr_err("%s: %s getting a freed handle\n", - __func__, current->group_leader->comm); - if (atomic_read(&h->ref) <= 0) - return NULL; - } - return h; -} - -static inline void nvmap_handle_put(struct nvmap_handle *h) -{ - int cnt = atomic_dec_return(&h->ref); - - if (WARN_ON(cnt < 0)) { - pr_err("%s: %s put to negative references\n", - __func__, current->comm); - } else if (cnt == 0) - _nvmap_handle_free(h); -} - -static inline pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot) -{ - if (h->flags == NVMAP_HANDLE_UNCACHEABLE) - return pgprot_noncached(prot); - else if (h->flags == NVMAP_HANDLE_WRITE_COMBINE) - return pgprot_writecombine(prot); - else if (h->flags == NVMAP_HANDLE_INNER_CACHEABLE) - return pgprot_inner_writeback(prot); - return prot; -} -#else /* CONFIG_TEGRA_NVMAP */ -struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h); -void nvmap_handle_put(struct nvmap_handle *h); -pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot); -#endif /* !CONFIG_TEGRA_NVMAP */ - int is_nvmap_vma(struct vm_area_struct *vma); struct nvmap_handle_ref *nvmap_alloc_iovm(struct nvmap_client *client, @@ -268,4 +268,4 @@ struct nvmap_handle_ref *nvmap_alloc_iovm(struct nvmap_client *client, void nvmap_free_iovm(struct nvmap_client *client, struct nvmap_handle_ref *r); -#endif +#endif /* __VIDEO_TEGRA_NVMAP_NVMAP_H */ -- cgit v1.2.3 From 9d85ccb8c12126fdb48f00a8951860d4ed070653 Mon Sep 17 00:00:00 2001 From: Krishna Reddy Date: Tue, 22 May 2012 15:46:04 -0700 Subject: video: tegra: nvmap: Clean up carveout to iovmm conversion. Clean up carveout to iovmm conversion code. Fixed bug in converting iovmm requests to carveout. Change-Id: I35a5238c955d0478f0e1a295e501bae9ee52b0a8 Signed-off-by: Krishna Reddy Reviewed-on: http://git-master/r/103991 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Yu-Huan Hsu --- drivers/video/tegra/nvmap/nvmap_handle.c | 58 ++++++++------------------------ 1 file changed, 14 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/nvmap/nvmap_handle.c b/drivers/video/tegra/nvmap/nvmap_handle.c index 2e2b8b3d46a1..56e2dab1820c 100644 --- a/drivers/video/tegra/nvmap/nvmap_handle.c +++ b/drivers/video/tegra/nvmap/nvmap_handle.c @@ -44,13 +44,6 @@ #include "nvmap_mru.h" #include "nvmap_common.h" -#define PRINT_CARVEOUT_CONVERSION 0 -#if PRINT_CARVEOUT_CONVERSION -#define PR_INFO pr_info -#else -#define PR_INFO(...) -#endif - #define NVMAP_SECURE_HEAPS (NVMAP_HEAP_CARVEOUT_IRAM | NVMAP_HEAP_IOVMM | \ NVMAP_HEAP_CARVEOUT_VPR) #ifdef CONFIG_NVMAP_HIGHMEM_ONLY @@ -643,36 +636,19 @@ fail: static void alloc_handle(struct nvmap_client *client, struct nvmap_handle *h, unsigned int type) { + unsigned int carveout_mask = NVMAP_HEAP_CARVEOUT_MASK; + unsigned int iovmm_mask = NVMAP_HEAP_IOVMM; + BUG_ON(type & (type - 1)); #ifdef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM -#define __NVMAP_HEAP_CARVEOUT (NVMAP_HEAP_CARVEOUT_IRAM | NVMAP_HEAP_CARVEOUT_VPR) -#define __NVMAP_HEAP_IOVMM (NVMAP_HEAP_IOVMM | NVMAP_HEAP_CARVEOUT_GENERIC) - if (type & NVMAP_HEAP_CARVEOUT_GENERIC) { -#ifdef CONFIG_NVMAP_ALLOW_SYSMEM - if (h->size <= PAGE_SIZE) { - PR_INFO("###CARVEOUT CONVERTED TO SYSMEM " - "0x%x bytes %s(%d)###\n", - h->size, current->comm, current->pid); - goto sysheap; - } -#endif - PR_INFO("###CARVEOUT CONVERTED TO IOVM " - "0x%x bytes %s(%d)###\n", - h->size, current->comm, current->pid); - } -#else -#define __NVMAP_HEAP_CARVEOUT NVMAP_HEAP_CARVEOUT_MASK -#define __NVMAP_HEAP_IOVMM NVMAP_HEAP_IOVMM + /* Convert generic carveout requests to iovmm requests. */ + carveout_mask &= ~NVMAP_HEAP_CARVEOUT_GENERIC; + iovmm_mask |= NVMAP_HEAP_CARVEOUT_GENERIC; #endif - if (type & __NVMAP_HEAP_CARVEOUT) { + if (type & carveout_mask) { struct nvmap_heap_block *b; -#ifdef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM - PR_INFO("###IRAM REQUEST RETAINED " - "0x%x bytes %s(%d)###\n", - h->size, current->comm, current->pid); -#endif /* Protect handle from relocation */ nvmap_usecount_inc(h); @@ -686,7 +662,7 @@ static void alloc_handle(struct nvmap_client *client, } nvmap_usecount_dec(h); - } else if (type & __NVMAP_HEAP_IOVMM) { + } else if (type & iovmm_mask) { size_t reserved = PAGE_ALIGN(h->size); int commit = 0; int ret; @@ -710,10 +686,6 @@ static void alloc_handle(struct nvmap_client *client, } } else if (type & NVMAP_HEAP_SYSMEM) { -#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM) && \ - defined(CONFIG_NVMAP_ALLOW_SYSMEM) -sysheap: -#endif if (handle_page_alloc(client, h, true) == 0) { BUG_ON(!h->pgalloc.contig); h->heap_pgalloc = true; @@ -772,24 +744,22 @@ int nvmap_alloc_handle_id(struct nvmap_client *client, h->align = max_t(size_t, align, L1_CACHE_BYTES); #ifndef CONFIG_TEGRA_IOVMM + /* convert iovmm requests to generic carveout. */ if (heap_mask & NVMAP_HEAP_IOVMM) { - heap_mask &= NVMAP_HEAP_IOVMM; - heap_mask |= NVMAP_HEAP_CARVEOUT_GENERIC; + heap_mask = heap_mask & ~NVMAP_HEAP_IOVMM | + NVMAP_HEAP_CARVEOUT_GENERIC; } #endif -#ifndef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM #ifdef CONFIG_NVMAP_ALLOW_SYSMEM /* Allow single pages allocations in system memory to save * carveout space and avoid extra iovm mappings */ if (nr_page == 1) { - if (heap_mask & NVMAP_HEAP_IOVMM) + if (heap_mask & + (NVMAP_HEAP_IOVMM | NVMAP_HEAP_CARVEOUT_GENERIC)) heap_mask |= NVMAP_HEAP_SYSMEM; - else if (heap_mask & NVMAP_HEAP_CARVEOUT_GENERIC) { - heap_mask |= NVMAP_HEAP_SYSMEM; - } } #endif - +#ifndef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM /* This restriction is deprecated as alignments greater than PAGE_SIZE are now correctly handled, but it is retained for AP20 compatibility. */ -- cgit v1.2.3 From 043165e17e77abf771aae9ba03aff4467770cfc1 Mon Sep 17 00:00:00 2001 From: Krishna Reddy Date: Wed, 23 May 2012 14:58:52 -0700 Subject: video: tegra: nvmap: Fix debug allocations data shown. Debug allocations data for iovmm has carvout allocations also and vice versa. Fixed it to show only iovmm for iovmm and carveout for carveout. Add missing "FLAGS" print for iovmm allocations. Change-Id: I0fd271be24d0d2d3924ca473fd32476776fdcf84 Signed-off-by: Krishna Reddy Reviewed-on: http://git-master/r/104246 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Yu-Huan Hsu Reviewed-by: Jon Mayo --- drivers/video/tegra/nvmap/nvmap_dev.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/nvmap/nvmap_dev.c b/drivers/video/tegra/nvmap/nvmap_dev.c index c78818711f74..9ecce7eeeb17 100644 --- a/drivers/video/tegra/nvmap/nvmap_dev.c +++ b/drivers/video/tegra/nvmap/nvmap_dev.c @@ -973,20 +973,17 @@ static void client_stringify(struct nvmap_client *client, struct seq_file *s) } static void allocations_stringify(struct nvmap_client *client, - struct seq_file *s) + struct seq_file *s, bool iovmm) { - unsigned long base = 0; struct rb_node *n = rb_first(&client->handle_refs); for (; n != NULL; n = rb_next(n)) { struct nvmap_handle_ref *ref = rb_entry(n, struct nvmap_handle_ref, node); struct nvmap_handle *handle = ref->handle; - if (handle->alloc && !handle->heap_pgalloc) { - seq_printf(s, "%-18s %-18s %8lx %10u %8x\n", "", "", - (unsigned long)(handle->carveout->base), - handle->size, handle->userflags); - } else if (handle->alloc && handle->heap_pgalloc) { + if (handle->alloc && handle->heap_pgalloc == iovmm) { + unsigned long base = iovmm ? 0: + (unsigned long)(handle->carveout->base); seq_printf(s, "%-18s %-18s %8lx %10u %8x\n", "", "", base, handle->size, handle->userflags); } @@ -1010,7 +1007,7 @@ static int nvmap_debug_allocations_show(struct seq_file *s, void *unused) get_client_from_carveout_commit(node, commit); client_stringify(client, s); seq_printf(s, " %10u\n", commit->commit); - allocations_stringify(client, s); + allocations_stringify(client, s, false); seq_printf(s, "\n"); total += commit->commit; } @@ -1111,14 +1108,14 @@ static int nvmap_debug_iovmm_allocations_show(struct seq_file *s, void *unused) struct nvmap_device *dev = s->private; spin_lock_irqsave(&dev->clients_lock, flags); - seq_printf(s, "%-18s %18s %8s %10s\n", "CLIENT", "PROCESS", "PID", - "SIZE"); + seq_printf(s, "%-18s %18s %8s %10s %8s\n", "CLIENT", "PROCESS", "PID", + "SIZE", "FLAGS"); seq_printf(s, "%-18s %18s %8s %10s\n", "", "", "BASE", "SIZE"); list_for_each_entry(client, &dev->clients, list) { client_stringify(client, s); seq_printf(s, " %10u\n", atomic_read(&client->iovm_commit)); - allocations_stringify(client, s); + allocations_stringify(client, s, true); seq_printf(s, "\n"); total += atomic_read(&client->iovm_commit); } -- cgit v1.2.3 From eff5dd61e05e1b01c396609e2129a8ab433d2666 Mon Sep 17 00:00:00 2001 From: Yen Lin Date: Mon, 12 Mar 2012 17:07:58 -0700 Subject: arm: tegra: ahci/sata: Add SATA driver Upgrade the Tegra 3 SATA driver to support kernel 3.1. The driver supports runtime_pm when doing power-gating during idle. A new CONFIG_TEGRA_SATA_IDLE_POWERGATE is provided to enable/disable power-gating during idle. When sata clocks (sata, sata-oob and pll_e clocks) are in the core_dvfs_table[] table, CONFIG_TEGRA_SATA_IDLE_POWERGATE must not be enabled. Currently, sata clocks are in the core_dvfs_table[]. Those clocks will not be in that table in the future. By then, CONFIG_TEGRA_SATA_IDLE_POWERGATE can then be enabled. Change-Id: I15b585713bfd891f8827fd028b21bf3e5c2b80d9 Signed-off-by: Yen Lin Reviewed-on: http://git-master/r/89614 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jon Mayo Reviewed-by: Yu-Huan Hsu --- drivers/ata/Kconfig | 17 + drivers/ata/Makefile | 1 + drivers/ata/ahci-tegra.c | 2154 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2172 insertions(+) create mode 100644 drivers/ata/ahci-tegra.c (limited to 'drivers') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 5987e0ba8c2d..3807e572aef7 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -83,6 +83,23 @@ config SATA_AHCI_PLATFORM If unsure, say N. +config SATA_AHCI_TEGRA + tristate "TEGRA AHCI SATA support" + depends on ARCH_TEGRA_3x_SOC + help + This option enables support for TEGRA AHCI Serial ATA. + + If unsure, say N. + +config TEGRA_SATA_IDLE_POWERGATE + bool "TEGRA SATA idle power-gating" + depends on SATA_AHCI_TEGRA && PM && PM_RUNTIME + help + This option enables power-gating during SATA idling. + This option should not be enabled if sata clocks are in dvfs_table. + + If unsure, say N. + config SATA_FSL tristate "Freescale 3.0Gbps SATA support" depends on FSL_SOC diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 9550d691fd19..4245ecf30f05 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_ATA) += libata.o # non-SFF interface obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o +obj-$(CONFIG_SATA_AHCI_TEGRA) += ahci-tegra.o libahci.o obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o obj-$(CONFIG_SATA_FSL) += sata_fsl.o diff --git a/drivers/ata/ahci-tegra.c b/drivers/ata/ahci-tegra.c new file mode 100644 index 000000000000..5c58da143c5a --- /dev/null +++ b/drivers/ata/ahci-tegra.c @@ -0,0 +1,2154 @@ +/* + * ahci-tegra.c - AHCI SATA support for TEGRA AHCI device + * + * Copyright (c) 2011-2012, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * AHCI hardware documentation: + * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf + * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ahci.h" + +#include +#include +#include +#include +#include + +#define DRV_NAME "tegra-sata" +#define DRV_VERSION "1.0" + +#define ENABLE_AHCI_DBG_PRINT 0 +#if ENABLE_AHCI_DBG_PRINT +#define AHCI_DBG_PRINT(fmt, arg...) printk(KERN_ERR fmt, ## arg) +#else +#define AHCI_DBG_PRINT(fmt, arg...) do {} while (0) +#endif + +/* number of AHCI ports */ +#define TEGRA_AHCI_NUM_PORTS 1 + +/* idle timeout for PM in msec */ +#define TEGRA_AHCI_MIN_IDLE_TIME 1000 +#define TEGRA_AHCI_DEFAULT_IDLE_TIME 2000 + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE +static u32 tegra_ahci_idle_time = TEGRA_AHCI_DEFAULT_IDLE_TIME; +#endif + +/* Bit 0 (EN_FPCI) to allow FPCI accesses to SATA */ +#define SATA_CONFIGURATION_0_OFFSET 0x180 +#define EN_FPCI (1 << 0) + +#define SATA_INTR_MASK_0_OFFSET 0x188 +#define IP_INT_MASK (1 << 16) + +/* Need to write 0x00400200 to 0x70020094 */ +#define SATA_FPCI_BAR5_0_OFFSET 0x094 +#define PRI_ICTLR_CPU_IER_SET_0_OFFSET 0x024 +#define CPU_IER_SATA_CTL (1 << 23) + +#define AHCI_BAR5_CONFIG_LOCATION 0x24 +#define TEGRA_SATA_BAR5_INIT_PROGRAM 0xFFFFFFFF +#define TEGRA_SATA_BAR5_FINAL_PROGRAM 0x40020000 + +#define FUSE_SATA_CALIB_OFFSET 0x224 +#define FUSE_SATA_CALIB_MASK 0x3 + +#define T_SATA0_CFG_PHY_REG 0x120 +#define PHY_USE_7BIT_ALIGN_DET_FOR_SPD_MASK (1 << 11) + +#define T_SATA0_CFG_POWER_GATE 0x4ac +#define POWER_GATE_SSTS_RESTORED_MASK (1 << 23) +#define POWER_GATE_SSTS_RESTORED_YES (1 << 23) +#define POWER_GATE_SSTS_RESTORED_NO (0 << 23) + +#define T_SATA0_DBG0_OFFSET 0x550 + +#define T_SATA0_INDEX_OFFSET 0x680 +#define SATA0_NONE_SELECTED 0 +#define SATA0_CH1_SELECTED (1 << 0) + +#define T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET 0x690 +#define SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT 0 +#define SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK (0xff << 0) +#define SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT 8 +#define SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK (0xff << 8) + +#define T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET 0x694 +#define SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT 0 +#define SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK (0xff << 0) +#define SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT 12 +#define SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK (0xff << 12) +#define SATA0_CHX_PHY_CTRL1_GEN2_RX_EQ_SHIFT 24 +#define SATA0_CHX_PHY_CTRL1_GEN2_RX_EQ_MASK (0xf << 24) + +/* AHCI config space defines */ +#define TEGRA_PRIVATE_AHCI_CC_BKDR 0x4a4 +#define TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE 0x54c +#define TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN (1 << 12) +#define TEGRA_PRIVATE_AHCI_CC_BKDR_PGM 0x01060100 + +/* AHCI HBA_CAP */ +#define TEGRA_PRIVATE_AHCI_CAP_BKDR 0xa0 +#define T_SATA0_AHCI_HBA_CAP_BKDR 0x300 + +#define TEGRA_SATA_IO_SPACE_OFFSET 4 +#define TEGRA_SATA_ENABLE_IO_SPACE (1 << 0) +#define TEGRA_SATA_ENABLE_MEM_SPACE (1 << 1) +#define TEGRA_SATA_ENABLE_BUS_MASTER (1 << 2) +#define TEGRA_SATA_ENABLE_SERR (1 << 8) + +#define TEGRA_SATA_CORE_CLOCK_FREQ_HZ (108*1000*1000) +#define TEGRA_SATA_OOB_CLOCK_FREQ_HZ (216*1000*1000) + +#define APB_PMC_SATA_PWRGT_0_REG 0x1ac +#define CLK_RST_SATA_PLL_CFG0_REG 0x490 +#define CLK_RST_SATA_PLL_CFG1_REG 0x494 +#define SATA_AUX_PAD_PLL_CNTL_1_REG 0x1100 +#define SATA_AUX_MISC_CNTL_1_REG 0x1108 + +/* for APB_PMC_SATA_PWRGT_0_REG */ +#define PG_INFO_MASK (1 << 6) +#define PG_INFO_ON (1 << 6) +#define PG_INFO_OFF (0 << 6) +#define PLLE_IDDQ_SWCTL_MASK (1 << 4) +#define PLLE_IDDQ_SWCTL_ON (1 << 4) +#define PLLE_IDDQ_SWCTL_OFF (0 << 4) +#define PADPHY_IDDQ_OVERRIDE_VALUE_MASK (1 << 3) +#define PADPHY_IDDQ_OVERRIDE_VALUE_ON (1 << 3) +#define PADPHY_IDDQ_OVERRIDE_VALUE_OFF (0 << 3) +#define PADPHY_IDDQ_SWCTL_MASK (1 << 2) +#define PADPHY_IDDQ_SWCTL_ON (1 << 2) +#define PADPHY_IDDQ_SWCTL_OFF (0 << 2) +#define PADPLL_IDDQ_OVERRIDE_VALUE_MASK (1 << 1) +#define PADPLL_IDDQ_OVERRIDE_VALUE_ON (1 << 1) +#define PADPLL_IDDQ_OVERRIDE_VALUE_OFF (0 << 1) +#define PADPLL_IDDQ_SWCTL_MASK (1 << 0) +#define PADPLL_IDDQ_SWCTL_ON (1 << 0) +#define PADPLL_IDDQ_SWCTL_OFF (0 << 0) + +/* for CLK_RST_SATA_PLL_CFG0_REG */ +#define PADPLL_RESET_OVERRIDE_VALUE_MASK (1 << 1) +#define PADPLL_RESET_OVERRIDE_VALUE_ON (1 << 1) +#define PADPLL_RESET_OVERRIDE_VALUE_OFF (0 << 1) +#define PADPLL_RESET_SWCTL_MASK (1 << 0) +#define PADPLL_RESET_SWCTL_ON (1 << 0) +#define PADPLL_RESET_SWCTL_OFF (0 << 0) + +/* for CLK_RST_SATA_PLL_CFG1_REG */ +#define IDDQ2LANE_SLUMBER_DLY_MASK (0xffL << 16) +#define IDDQ2LANE_SLUMBER_DLY_SHIFT 16 +#define IDDQ2LANE_SLUMBER_DLY_3MS (3 << 16) +#define IDDQ2LANE_IDDQ_DLY_SHIFT 0 +#define IDDQ2LANE_IDDQ_DLY_MASK (0xffL << 0) + +/* for SATA_AUX_PAD_PLL_CNTL_1_REG */ +#define REFCLK_SEL_MASK (3 << 11) +#define REFCLK_SEL_INT_CML (0 << 11) +#define LOCKDET_FIELD (1 << 6) + +/* for SATA_AUX_MISC_CNTL_1_REG */ +#define NVA2SATA_OOB_ON_POR_MASK (1 << 7) +#define NVA2SATA_OOB_ON_POR_YES (1 << 7) +#define NVA2SATA_OOB_ON_POR_NO (0 << 7) +#define L0_RX_IDLE_T_SAX_SHIFT 5 +#define L0_RX_IDLE_T_SAX_MASK (3 << 5) +#define L0_RX_IDLE_T_NPG_SHIFT 3 +#define L0_RX_IDLE_T_NPG_MASK (3 << 3) +#define L0_RX_IDLE_T_MUX_MASK (1 << 2) +#define L0_RX_IDLE_T_MUX_FROM_APB_MISC (1 << 2) +#define L0_RX_IDLE_T_MUX_FROM_SATA (0 << 2) + +#define SSTAT_IPM_STATE_MASK 0xF00 +#define SSTAT_IPM_SLUMBER_STATE 0x600 + +enum { + AHCI_PCI_BAR = 5, +}; + +enum port_idle_status { + PORT_IS_NOT_IDLE, + PORT_IS_IDLE, + PORT_IS_IDLE_NOT_SLUMBER, + PORT_IS_SLUMBER, +}; + +enum sata_state { + SATA_ON, + SATA_OFF, + SATA_GOING_ON, + SATA_GOING_OFF, + SATA_ABORT_OFF, +}; + +char *sata_power_rails[] = { + "avdd_sata", + "vdd_sata", + "hvdd_sata", + "avdd_sata_pll" +}; + +#define NUM_SATA_POWER_RAILS ARRAY_SIZE(sata_power_rails) + +struct tegra_qc_list { + struct list_head list; + struct ata_queued_cmd *qc; +}; + +/* + * tegra_ahci_host_priv is the extension of ahci_host_priv + * with extra fields: idle_timer, pg_save, pg_state, etc. + */ +struct tegra_ahci_host_priv { + struct ahci_host_priv ahci_host_priv; + struct regulator *power_rails[NUM_SATA_POWER_RAILS]; + void __iomem *bars_table[6]; + struct ata_host *host; + struct timer_list idle_timer; + struct device *dev; + void *pg_save; + enum sata_state pg_state; + struct list_head qc_list; +}; + +static int tegra_ahci_init_one(struct platform_device *pdev); +static int tegra_ahci_remove_one(struct platform_device *pdev); + +#ifdef CONFIG_PM +static bool tegra_ahci_power_un_gate(struct ata_host *host); +static bool tegra_ahci_power_gate(struct ata_host *host); +static void tegra_ahci_abort_power_gate(struct ata_host *host); +static int tegra_ahci_controller_suspend(struct platform_device *pdev); +static int tegra_ahci_controller_resume(struct platform_device *pdev); +static int tegra_ahci_suspend(struct platform_device *pdev, pm_message_t mesg); +static int tegra_ahci_resume(struct platform_device *pdev); +static enum port_idle_status tegra_ahci_is_port_idle(struct ata_port *ap); +static enum port_idle_status tegra_ahci_is_port_slumber(struct ata_port *ap); +static bool tegra_ahci_are_all_ports_idle(struct ata_host *host); +static bool tegra_ahci_are_all_ports_slumber(struct ata_host *host); +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE +static unsigned int tegra_ahci_qc_issue(struct ata_queued_cmd *qc); +static int tegra_ahci_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); +static int tegra_ahci_runtime_suspend(struct device *dev); +static int tegra_ahci_runtime_resume(struct device *dev); +static void tegra_ahci_idle_timer(unsigned long arg); +static int tegra_ahci_queue_one_qc(struct tegra_ahci_host_priv *tegra_hpriv, + struct ata_queued_cmd *qc); +static void tegra_ahci_dequeue_qcs(struct tegra_ahci_host_priv *tegra_hpriv); +#endif +#else +#define tegra_ahci_controller_suspend NULL +#define tegra_ahci_controller_resume NULL +#define tegra_ahci_suspend NULL +#define tegra_ahci_resume NULL +#endif + +static struct scsi_host_template ahci_sht = { + AHCI_SHT("tegra-sata"), +}; + +static struct ata_port_operations tegra_ahci_ops = { + .inherits = &ahci_ops, +#ifdef CONFIG_PM +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE + .qc_issue = tegra_ahci_qc_issue, + .hardreset = tegra_ahci_hardreset, +#endif +#endif +}; + +static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = ATA_UDMA6, + .port_ops = &tegra_ahci_ops, +}; + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE +static const struct dev_pm_ops tegra_ahci_dev_rt_ops = { + .runtime_suspend = tegra_ahci_runtime_suspend, + .runtime_resume = tegra_ahci_runtime_resume, +}; +#endif + +static struct platform_driver tegra_platform_ahci_driver = { + .probe = tegra_ahci_init_one, + .remove = __devexit_p(tegra_ahci_remove_one), +#ifdef CONFIG_PM + .suspend = tegra_ahci_suspend, + .resume = tegra_ahci_resume, + .driver = { + .name = DRV_NAME, +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE + .pm = &tegra_ahci_dev_rt_ops, +#endif + } +#else + .driver = { + .name = DRV_NAME, + } +#endif +}; + +struct tegra_ahci_host_priv *g_tegra_hpriv; + +static inline u32 pmc_readl(u32 offset) +{ + u32 val; + val = readl(IO_ADDRESS(TEGRA_PMC_BASE + offset)); + AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", TEGRA_PMC_BASE+offset, val); + return val; +} + +static inline void pmc_writel(u32 val, u32 offset) +{ + AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", TEGRA_PMC_BASE+offset, val); + writel(val, IO_ADDRESS(TEGRA_PMC_BASE + offset)); +} + +static inline u32 clk_readl(u32 offset) +{ + u32 val; + + val = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + offset)); + AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", TEGRA_CLK_RESET_BASE+offset, val); + return val; +} + +static inline void clk_writel(u32 val, u32 offset) +{ + AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", TEGRA_CLK_RESET_BASE+offset, val); + writel(val, IO_ADDRESS(TEGRA_CLK_RESET_BASE + offset)); +} + +static inline u32 misc_readl(u32 offset) +{ + u32 val; + + val = readl(IO_ADDRESS(TEGRA_APB_MISC_BASE + offset)); + AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", TEGRA_APB_MISC_BASE+offset, val); + return val; +} + +static inline void misc_writel(u32 val, u32 offset) +{ + AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", TEGRA_APB_MISC_BASE+offset, val); + writel(val, IO_ADDRESS(TEGRA_APB_MISC_BASE + offset)); +} + +static inline u32 sata_readl(u32 offset) +{ + u32 val; + + val = readl(IO_ADDRESS(TEGRA_SATA_BASE + offset)); + AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", TEGRA_SATA_BASE+offset, val); + return val; +} + +static inline void sata_writel(u32 val, u32 offset) +{ + AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", TEGRA_SATA_BASE+offset, val); + writel(val, IO_ADDRESS(TEGRA_SATA_BASE + offset)); +} + +static inline u32 scfg_readl(u32 offset) +{ + u32 val; + + val = readl(IO_ADDRESS(TEGRA_SATA_CONFIG_BASE + offset)); + AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", TEGRA_SATA_CONFIG_BASE+offset, + val); + return val; +} + +static inline void scfg_writel(u32 val, u32 offset) +{ + AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", TEGRA_SATA_CONFIG_BASE+offset, + val); + writel(val, IO_ADDRESS(TEGRA_SATA_CONFIG_BASE + offset)); +} + +static inline u32 pictlr_readl(u32 offset) +{ + u32 val; + + val = readl(IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE + offset)); + AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", TEGRA_PRIMARY_ICTLR_BASE+offset, + val); + return val; +} + +static inline void pictlr_writel(u32 val, u32 offset) +{ + AHCI_DBG_PRINT("[0x%x] <= 0x%08x\n", TEGRA_PRIMARY_ICTLR_BASE+offset, + val); + writel(val, IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE + offset)); +} + +static inline u32 fuse_readl(u32 offset) +{ + u32 val; + + val = readl(IO_ADDRESS(TEGRA_FUSE_BASE + offset)); + AHCI_DBG_PRINT("[0x%x] => 0x%08x\n", TEGRA_FUSE_BASE+offset, val); + + return val; +} + +/* Sata Pad Cntrl Values */ +struct sata_pad_cntrl { + u8 gen1_tx_amp; + u8 gen1_tx_peak; + u8 gen2_tx_amp; + u8 gen2_tx_peak; +}; + +static const struct sata_pad_cntrl sata_calib_pad_val[] = { + { /* SATA_CALIB[1:0] = 00 */ + 0x0c, + 0x04, + 0x0e, + 0x0a + }, + { /* SATA_CALIB[1:0] = 01 */ + 0x0e, + 0x04, + 0x14, + 0x0a + }, + { /* SATA_CALIB[1:0] = 10 */ + 0x0e, + 0x07, + 0x1a, + 0x0e + }, + { /* SATA_CALIB[1:0] = 11 */ + 0x14, + 0x0e, + 0x1a, + 0x0e + } +}; + +static void tegra_ahci_set_pad_cntrl_regs(void) +{ + int calib_val; + int val; + int i; + + calib_val = fuse_readl(FUSE_SATA_CALIB_OFFSET) & FUSE_SATA_CALIB_MASK; + + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { + scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); + + val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); + val &= ~SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK; + val |= (sata_calib_pad_val[calib_val].gen1_tx_amp << + SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT); + scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); + + val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); + val &= ~SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK; + val |= (sata_calib_pad_val[calib_val].gen1_tx_peak << + SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT); + scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN1_OFFSET); + + val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); + val &= ~SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK; + val |= (sata_calib_pad_val[calib_val].gen2_tx_amp << + SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT); + scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); + + val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); + val &= ~SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK; + val |= (sata_calib_pad_val[calib_val].gen2_tx_peak << + SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT); + scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); + + /* set 2 to SATA0_CHX_PHY_CTRL1_GEN2_RX_EQ field */ + val = scfg_readl(T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); + val &= ~SATA0_CHX_PHY_CTRL1_GEN2_RX_EQ_MASK; + val |= (2 << SATA0_CHX_PHY_CTRL1_GEN2_RX_EQ_SHIFT); + scfg_writel(val, T_SATA0_CHX_PHY_CTRL1_GEN2_OFFSET); + } + scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); +} + +int tegra_ahci_get_rails(struct regulator *regulators[]) +{ + struct regulator *reg; + int i; + int ret = 0; + + for (i = 0; i < NUM_SATA_POWER_RAILS; ++i) { + reg = regulator_get(NULL, sata_power_rails[i]); + if (IS_ERR_OR_NULL(reg)) { + pr_err("%s: can't get regulator %s\n", + __func__, sata_power_rails[i]); + WARN_ON(1); + ret = PTR_ERR(reg); + goto exit; + } + regulators[i] = reg; + } +exit: + return ret; +} + +void tegra_ahci_put_rails(struct regulator *regulators[]) +{ + int i; + + for (i = 0; i < NUM_SATA_POWER_RAILS; ++i) + regulator_put(regulators[i]); +} + +int tegra_ahci_power_on_rails(struct regulator *regulators[]) +{ + struct regulator *reg; + int i; + int ret = 0; + + for (i = 0; i < NUM_SATA_POWER_RAILS; ++i) { + reg = regulators[i]; + ret = regulator_enable(reg); + if (ret) { + pr_err("%s: can't enable regulator[%d]\n", + __func__, i); + WARN_ON(1); + goto exit; + } + } + +exit: + return ret; +} + +int tegra_ahci_power_off_rails(struct regulator *regulators[]) +{ + struct regulator *reg; + int i; + int ret = 0; + + for (i = 0; i < NUM_SATA_POWER_RAILS; ++i) { + reg = regulators[i]; + if (!IS_ERR_OR_NULL(reg)) { + ret = regulator_disable(reg); + if (ret) { + pr_err("%s: can't disable regulator[%d]\n", + __func__, i); + WARN_ON(1); + goto exit; + } + } + } + +exit: + return ret; +} +static int tegra_ahci_controller_init(struct tegra_ahci_host_priv *tegra_hpriv) +{ + int err; + struct clk *clk_sata = NULL; + struct clk *clk_sata_oob = NULL; + struct clk *clk_sata_cold = NULL; + struct clk *clk_pllp = NULL; + u32 val; + u32 timeout; + + err = tegra_ahci_get_rails(tegra_hpriv->power_rails); + if (err) { + pr_err("%s: fails to get rails (%d)\n", __func__, err); + goto exit; + } + + err = tegra_ahci_power_on_rails(tegra_hpriv->power_rails); + if (err) { + pr_err("%s: fails to power on rails (%d)\n", __func__, err); + goto exit; + } + + /* pll_p is the parent of tegra_sata and tegra_sata_oob */ + clk_pllp = clk_get_sys(NULL, "pll_p"); + if (IS_ERR_OR_NULL(clk_pllp)) { + pr_err("%s: unable to get PLL_P clock\n", __func__); + err = -ENODEV; + goto exit; + } + + clk_sata = clk_get_sys("tegra_sata", NULL); + if (IS_ERR_OR_NULL(clk_sata)) { + pr_err("%s: unable to get SATA clock\n", __func__); + err = -ENODEV; + goto exit; + } + + clk_sata_oob = clk_get_sys("tegra_sata_oob", NULL); + if (IS_ERR_OR_NULL(clk_sata_oob)) { + pr_err("%s: unable to get SATA OOB clock\n", __func__); + err = -ENODEV; + goto exit; + } + + clk_sata_cold = clk_get_sys("tegra_sata_cold", NULL); + if (IS_ERR_OR_NULL(clk_sata_cold)) { + pr_err("%s: unable to get SATA COLD clock\n", __func__); + err = -ENODEV; + goto exit; + } + + tegra_periph_reset_assert(clk_sata); + tegra_periph_reset_assert(clk_sata_oob); + tegra_periph_reset_assert(clk_sata_cold); + udelay(10); + + /* need to establish both clocks divisors before setting clk sources */ + clk_set_rate(clk_sata, clk_get_rate(clk_sata)/10); + clk_set_rate(clk_sata_oob, clk_get_rate(clk_sata_oob)/10); + + /* set SATA clk and SATA_OOB clk source */ + clk_set_parent(clk_sata, clk_pllp); + clk_set_parent(clk_sata_oob, clk_pllp); + + /* Configure SATA clocks */ + /* Core clock runs at 108MHz */ + if (clk_set_rate(clk_sata, TEGRA_SATA_CORE_CLOCK_FREQ_HZ)) { + err = -ENODEV; + goto exit; + } + /* OOB clock runs at 216MHz */ + if (clk_set_rate(clk_sata_oob, TEGRA_SATA_OOB_CLOCK_FREQ_HZ)) { + err = -ENODEV; + goto exit; + } + + /**** Init the SATA PAD PLL ****/ + /* SATA_PADPLL_IDDQ_SWCTL=1 and SATA_PADPLL_IDDQ_OVERRIDE_VALUE=1 */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~(PADPLL_IDDQ_SWCTL_MASK | PADPLL_IDDQ_OVERRIDE_VALUE_MASK); + val |= (PADPLL_IDDQ_SWCTL_ON | PADPLL_IDDQ_OVERRIDE_VALUE_ON); + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + /* SATA_PADPLL_RESET_OVERRIDE_VALUE=1 and SATA_PADPLL_RESET_SWCTL=1 */ + val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); + val &= ~(PADPLL_RESET_OVERRIDE_VALUE_MASK | PADPLL_RESET_SWCTL_MASK); + val |= (PADPLL_RESET_OVERRIDE_VALUE_ON | PADPLL_RESET_SWCTL_ON); + clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); + + /* SATA_PADPHY_IDDQ_OVERRIDE_VALUE and SATA_PADPHY_IDDQ_SWCTL = 1 */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~(PADPHY_IDDQ_OVERRIDE_VALUE_MASK | PADPHY_IDDQ_SWCTL_MASK); + val |= (PADPHY_IDDQ_OVERRIDE_VALUE_ON | PADPHY_IDDQ_SWCTL_ON); + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + /* Get SATA pad PLL out of IDDQ mode */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~PADPLL_IDDQ_OVERRIDE_VALUE_MASK; + val |= PADPLL_IDDQ_OVERRIDE_VALUE_OFF; + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + udelay(3); + + /* select internal CML ref clk + * select PLLE as input to IO phy */ + val = misc_readl(SATA_AUX_PAD_PLL_CNTL_1_REG); + val &= ~REFCLK_SEL_MASK; + val |= REFCLK_SEL_INT_CML; + misc_writel(val, SATA_AUX_PAD_PLL_CNTL_1_REG); + + /* wait for SATA_PADPLL_IDDQ2LANE_SLUMBER_DLY = 3 microseconds. */ + val = clk_readl(CLK_RST_SATA_PLL_CFG1_REG); + val &= ~IDDQ2LANE_SLUMBER_DLY_MASK; + val |= IDDQ2LANE_SLUMBER_DLY_3MS; + clk_writel(val, CLK_RST_SATA_PLL_CFG1_REG); + udelay(3); + + /* de-assert IDDQ mode signal going to PHY */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~PADPHY_IDDQ_OVERRIDE_VALUE_MASK; + val |= PADPHY_IDDQ_OVERRIDE_VALUE_OFF; + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + err = tegra_unpowergate_partition_with_clk_on(TEGRA_POWERGATE_SATA); + if (err) { + pr_err("%s: ** failed to turn-on SATA (0x%x) **\n", + __func__, err); + goto exit; + } + + /* + * place SATA Pad PLL out of reset by writing + * SATA_PADPLL_RST_OVERRIDE_VALUE = 0 + */ + val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); + val &= ~PADPLL_RESET_OVERRIDE_VALUE_MASK; + val |= PADPLL_RESET_OVERRIDE_VALUE_OFF; + clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); + + /* + * Wait for SATA_AUX_PAD_PLL_CNTL_1_0_LOCKDET to turn 1 with a timeout + * of 15 us. + */ + timeout = 15; + while (timeout--) { + udelay(1); + val = misc_readl(SATA_AUX_PAD_PLL_CNTL_1_REG); + if (val & LOCKDET_FIELD) + break; + } + if (timeout == 0) + pr_err("%s: AUX_PAD_PLL_CNTL_1 (0x%x) is not locked in 15us.\n", + __func__, val); + + /* clear SW control of SATA PADPLL, SATA PHY and PLLE */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~(PADPLL_IDDQ_SWCTL_MASK | PADPHY_IDDQ_SWCTL_MASK | + PLLE_IDDQ_SWCTL_MASK); + val |= (PADPLL_IDDQ_SWCTL_OFF | PADPHY_IDDQ_SWCTL_OFF | + PLLE_IDDQ_SWCTL_OFF); + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); + val &= ~PADPLL_RESET_SWCTL_MASK; + val |= PADPLL_RESET_SWCTL_OFF; + clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); + + /* clear NVA2SATA_OOB_ON_POR in SATA_AUX_MISC_CNTL_1_REG */ + val = misc_readl(SATA_AUX_MISC_CNTL_1_REG); + val &= ~NVA2SATA_OOB_ON_POR_MASK; + misc_writel(val, SATA_AUX_MISC_CNTL_1_REG); + + val = sata_readl(SATA_CONFIGURATION_0_OFFSET); + val |= EN_FPCI; + sata_writel(val, SATA_CONFIGURATION_0_OFFSET); + + /* program sata pad control based on the fuse */ + tegra_ahci_set_pad_cntrl_regs(); + + /* + * clear bit T_SATA0_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD of + * T_SATA0_CFG_PHY_0 + */ + val = scfg_readl(T_SATA0_CFG_PHY_REG); + val &= ~PHY_USE_7BIT_ALIGN_DET_FOR_SPD_MASK; + scfg_writel(val, T_SATA0_CFG_PHY_REG); + + /* + * WAR: Before enabling SATA PLL shutdown, lockdet needs to be ignored. + * To ignore lockdet, T_SATA0_DBG0_OFFSET register bit 10 needs to + * be 1, and bit 8 needs to be 0. + */ + val = scfg_readl(T_SATA0_DBG0_OFFSET); + val |= (1 << 10); + val &= ~(1 << 8); + scfg_writel(val, T_SATA0_DBG0_OFFSET); + + /* program class code and programming interface for AHCI */ + val = scfg_readl(TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); + val |= TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; + scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); + scfg_writel(TEGRA_PRIVATE_AHCI_CC_BKDR_PGM, TEGRA_PRIVATE_AHCI_CC_BKDR); + val &= ~TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; + scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); + + /* Program config space registers: */ + + /* Enable BUS_MASTER+MEM+IO space, and SERR */ + val = scfg_readl(TEGRA_SATA_IO_SPACE_OFFSET); + val |= TEGRA_SATA_ENABLE_IO_SPACE | TEGRA_SATA_ENABLE_MEM_SPACE | + TEGRA_SATA_ENABLE_BUS_MASTER | TEGRA_SATA_ENABLE_SERR; + scfg_writel(val, TEGRA_SATA_IO_SPACE_OFFSET); + + /* program bar5 space, by first writing 1's to bar5 register */ + scfg_writel(TEGRA_SATA_BAR5_INIT_PROGRAM, AHCI_BAR5_CONFIG_LOCATION); + /* flush */ + val = scfg_readl(AHCI_BAR5_CONFIG_LOCATION); + + /* then, write the BAR5_FINAL_PROGRAM address */ + scfg_writel(TEGRA_SATA_BAR5_FINAL_PROGRAM, AHCI_BAR5_CONFIG_LOCATION); + /* flush */ + scfg_readl(AHCI_BAR5_CONFIG_LOCATION); + + sata_writel((TEGRA_SATA_BAR5_FINAL_PROGRAM >> 8), + SATA_FPCI_BAR5_0_OFFSET); + + val = scfg_readl(T_SATA0_AHCI_HBA_CAP_BKDR); + val |= (HOST_CAP_ALPM | HOST_CAP_SSC | HOST_CAP_PART); + scfg_writel(val, T_SATA0_AHCI_HBA_CAP_BKDR); + + /* enable Interrupt channel */ + val = pictlr_readl(PRI_ICTLR_CPU_IER_SET_0_OFFSET); + val |= CPU_IER_SATA_CTL; + pictlr_writel(val, PRI_ICTLR_CPU_IER_SET_0_OFFSET); + + /* set IP_INT_MASK */ + val = sata_readl(SATA_INTR_MASK_0_OFFSET); + val |= IP_INT_MASK; + sata_writel(val, SATA_INTR_MASK_0_OFFSET); + +exit: + if (!IS_ERR_OR_NULL(clk_pllp)) + clk_put(clk_pllp); + if (!IS_ERR_OR_NULL(clk_sata)) + clk_put(clk_sata); + if (!IS_ERR_OR_NULL(clk_sata_oob)) + clk_put(clk_sata_oob); + if (!IS_ERR_OR_NULL(clk_sata_cold)) + clk_put(clk_sata_cold); + + if (err) { + /* turn off all SATA power rails; ignore returned status */ + tegra_ahci_power_off_rails(tegra_hpriv->power_rails); + /* return regulators to system */ + tegra_ahci_put_rails(tegra_hpriv->power_rails); + } + + return err; +} + +static void tegra_ahci_save_initial_config(struct platform_device *pdev, + struct ahci_host_priv *hpriv) +{ + ahci_save_initial_config(&pdev->dev, hpriv, 0, 0); +} + +static void tegra_ahci_controller_remove(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct tegra_ahci_host_priv *tegra_hpriv; + int status; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + +#ifdef CONFIG_PM + /* call tegra_ahci_controller_suspend() to power-down the SATA */ + status = tegra_ahci_controller_suspend(pdev); + if (status) + dev_err(host->dev, "remove: error suspend SATA (0x%x)\n", + status); +#else + /* power off the sata */ + status = tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_SATA); + if (status) + dev_err(host->dev, "remove: error turn-off SATA (0x%x)\n", + status); + tegra_ahci_power_off_rails(tegra_hpriv->power_rails); +#endif + + /* return system resources */ + tegra_ahci_put_rails(tegra_hpriv->power_rails); +} + +#ifdef CONFIG_PM +static int tegra_ahci_controller_suspend(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct tegra_ahci_host_priv *tegra_hpriv; + unsigned long flags; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + /* stop the idle timer */ + if (timer_pending(&tegra_hpriv->idle_timer)) + del_timer_sync(&tegra_hpriv->idle_timer); + + spin_lock_irqsave(&host->lock, flags); + if (tegra_hpriv->pg_state == SATA_OFF) + dev_dbg(host->dev, "suspend: SATA already power gated\n"); + else { + bool pg_ok; + + dev_dbg(host->dev, "suspend: power gating SATA...\n"); + pg_ok = tegra_ahci_power_gate(host); + if (pg_ok) { + tegra_hpriv->pg_state = SATA_OFF; + dev_dbg(host->dev, "suspend: SATA is power gated\n"); + } else { + dev_err(host->dev, "suspend: abort power gating\n"); + tegra_ahci_abort_power_gate(host); + spin_unlock_irqrestore(&host->lock, flags); + return -EBUSY; + } + } + spin_unlock_irqrestore(&host->lock, flags); + + return tegra_ahci_power_off_rails(tegra_hpriv->power_rails); +} + +static int tegra_ahci_controller_resume(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct tegra_ahci_host_priv *tegra_hpriv; + unsigned long flags; + int err; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + err = tegra_ahci_power_on_rails(tegra_hpriv->power_rails); + if (err) { + pr_err("%s: fails to power on rails (%d)\n", __func__, err); + return err; + } + + spin_lock_irqsave(&host->lock, flags); + if (!tegra_hpriv->pg_state == SATA_ON) { + dev_dbg(host->dev, "resume: SATA already powered on\n"); + } else { + dev_dbg(host->dev, "resume: powering on SATA...\n"); + tegra_ahci_power_un_gate(host); + tegra_hpriv->pg_state = SATA_ON; + } + spin_unlock_irqrestore(&host->lock, flags); + + return 0; +} + +static int tegra_ahci_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; + u32 ctl; + int rc; + + dev_dbg(host->dev, "** entering %s: **\n", __func__); + if (mesg.event & PM_EVENT_SLEEP) { + /* + * AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + rc = ata_host_suspend(host, mesg); + if (rc) + return rc; + + return tegra_ahci_controller_suspend(pdev); +} + +static int tegra_ahci_resume(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + int rc; + + dev_dbg(host->dev, "** entering %s: **\n", __func__); + rc = tegra_ahci_controller_resume(pdev); + if (rc) + return rc; + + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + } + + ata_host_resume(host); + return 0; +} + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE +static int tegra_ahci_runtime_suspend(struct device *dev) +{ + struct ata_host *host; + struct tegra_ahci_host_priv *tegra_hpriv; + bool pg_ok; + unsigned long flags; + int err = 0; + + host = dev_get_drvdata(dev); + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + spin_lock_irqsave(&host->lock, flags); + + switch (tegra_hpriv->pg_state) { + case SATA_OFF: + dev_dbg(dev, "** rt-suspend: already power gated **\n"); + break; + + case SATA_ABORT_OFF: + dev_dbg(dev, "** rt-suspend: abort suspend **\n"); + pm_runtime_get_noresume(dev); + tegra_hpriv->pg_state = SATA_ON; + tegra_ahci_dequeue_qcs(tegra_hpriv); + err = -EBUSY; + break; + + case SATA_ON: + case SATA_GOING_OFF: + if (tegra_ahci_are_all_ports_idle(host)) { + /* if all ports are in idle, do power-gate */ + dev_dbg(dev, "** rt-suspend: power-down sata (%u) **\n", + tegra_hpriv->pg_state); + pg_ok = tegra_ahci_power_gate(host); + dev_dbg(dev, "** rt-suspend: done **\n"); + if (pg_ok) { + tegra_hpriv->pg_state = SATA_OFF; + } else { + dev_err(dev, "** rt-suspend: abort pg **\n"); + tegra_ahci_abort_power_gate(host); + tegra_hpriv->pg_state = SATA_ON; + err = -EBUSY; + } + } else { + dev_dbg(dev, "** rt-suspend: port not idle (%u) **\n", + tegra_hpriv->pg_state); + err = -EBUSY; + } + break; + + case SATA_GOING_ON: + default: + dev_err(dev, "** rt-suspend: bad state (%u) **\n", + tegra_hpriv->pg_state); + WARN_ON(1); + err = -EBUSY; + break; + + } + + spin_unlock_irqrestore(&host->lock, flags); + + return err; +} + +static int tegra_ahci_runtime_resume(struct device *dev) +{ + struct ata_host *host; + struct tegra_ahci_host_priv *tegra_hpriv; + unsigned long flags; + int err = 0; + + host = dev_get_drvdata(dev); + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + spin_lock_irqsave(&host->lock, flags); + + if (tegra_hpriv->pg_state == SATA_ON) { + dev_dbg(dev, "** rt-resume: already power ungated **\n"); + goto exit; + } + + if ((tegra_hpriv->pg_state == SATA_OFF) || + (tegra_hpriv->pg_state == SATA_GOING_ON)) { + dev_dbg(dev, "** rt-resume: power-up sata (%u) **\n", + tegra_hpriv->pg_state); + tegra_ahci_power_un_gate(host); + dev_dbg(dev, "** rt-resume: done **\n"); + tegra_hpriv->pg_state = SATA_ON; + + /* now qc_issue all qcs in the qc_list */ + tegra_ahci_dequeue_qcs(tegra_hpriv); + } else { + dev_err(dev, "** rt-resume: bad state (%u) **\n", + tegra_hpriv->pg_state); + WARN_ON(1); + err = -EBUSY; + } + +exit: + spin_unlock_irqrestore(&host->lock, flags); + return err; +} +#endif + +static u16 pg_save_bar5_registers[] = { + 0x018, /* T_AHCI_HBA_CCC_PORTS */ + 0x004, /* T_AHCI_HBA_GHC */ + 0x014, /* T_AHCI_HBA_CCC_CTL - OP (optional) */ + 0x01C, /* T_AHCI_HBA_EM_LOC */ + 0x020 /* T_AHCI_HBA_EM_CTL - OP */ +}; + +static u16 pg_save_bar5_port_registers[] = { + 0x100, /* T_AHCI_PORT_PXCLB */ + 0x104, /* T_AHCI_PORT_PXCLBU */ + 0x108, /* T_AHCI_PORT_PXFB */ + 0x10C, /* T_AHCI_PORT_PXFBU */ + 0x114, /* T_AHCI_PORT_PXIE */ + 0x118, /* T_AHCI_PORT_PXCMD */ + 0x12C /* T_AHCI_PORT_PXSCTL */ +}; + +/* + * pg_save_bar5_bkdr_registers: + * These registers in BAR5 are read only. + * To restore back those register values, write the saved value + * to the registers specified in pg_restore_bar5_bkdr_registers[]. + * These pg_restore_bar5_bkdr_registers[] are in SATA_CONFIG space. + */ +static u16 pg_save_bar5_bkdr_registers[] = { + /* Save and restore via bkdr writes */ + 0x000, /* T_AHCI_HBA_CAP */ + 0x00C, /* T_AHCI_HBA_PI */ + 0x024 /* T_AHCI_HBA_CAP2 */ +}; + +static u16 pg_restore_bar5_bkdr_registers[] = { + /* Save and restore via bkdr writes */ + 0x300, /* BKDR of T_AHCI_HBA_CAP */ + 0x33c, /* BKDR of T_AHCI_HBA_PI */ + 0x330 /* BKDR of T_AHCI_HBA_CAP2 */ +}; + +/* These registers are saved for each port */ +static u16 pg_save_bar5_bkdr_port_registers[] = { + 0x120, /* NV_PROJ__SATA0_CHX_AHCI_PORT_PXTFD */ + 0x124, /* NV_PROJ__SATA0_CHX_AHCI_PORT_PXSIG */ + 0x128 /* NV_PROJ__SATA0_CHX_AHCI_PORT_PXSSTS */ +}; + +static u16 pg_restore_bar5_bkdr_port_registers[] = { + /* Save and restore via bkdr writes */ + 0x790, /* BKDR of NV_PROJ__SATA0_CHX_AHCI_PORT_PXTFD */ + 0x794, /* BKDR of NV_PROJ__SATA0_CHX_AHCI_PORT_PXSIG */ + 0x798 /* BKDR of NV_PROJ__SATA0_CHX_AHCI_PORT_PXSSTS */ +}; + +static u16 pg_save_config_registers[] = { + 0x004, /* T_SATA0_CFG_1 */ + 0x00C, /* T_SATA0_CFG_3 */ + 0x024, /* T_SATA0_CFG_9 */ + 0x028, /* T_SATA0_CFG_10 */ + 0x030, /* T_SATA0_CFG_12 */ + 0x034, /* T_SATA0_CFG_13 */ + 0x038, /* T_SATA0_CFG_14 */ + 0x03C, /* T_SATA0_CFG_15 */ + 0x040, /* T_SATA0_CFG_16 */ + 0x044, /* T_SATA0_CFG_17 */ + 0x048, /* T_SATA0_CFG_18 */ + 0x0B0, /* T_SATA0_MSI_CTRL */ + 0x0B4, /* T_SATA0_MSI_ADDR1 */ + 0x0B8, /* T_SATA0_MSI_ADDR2 */ + 0x0BC, /* T_SATA0_MSI_DATA */ + 0x0C0, /* T_SATA0_MSI_QUEUE */ + 0x0EC, /* T_SATA0_MSI_MAP */ + 0x124, /* T_SATA0_CFG_PHY_POWER */ + 0x128, /* T_SATA0_CFG_PHY_POWER_1 */ + 0x12C, /* T_SATA0_CFG_PHY_1 */ + 0x174, /* T_SATA0_CFG_LINK_0 */ + 0x178, /* T_SATA0_CFG_LINK_1 */ + 0x1D0, /* MCP_SATA0_CFG_TRANS_0 */ + 0x238, /* T_SATA0_ALPM_CTRL */ + 0x30C, /* T_SATA0_AHCI_HBA_CYA_0 */ + 0x320, /* T_SATA0_AHCI_HBA_SPARE_1 */ + 0x324, /* T_SATA0_AHCI_HBA_SPARE_2 */ + 0x328, /* T_SATA0_AHCI_HBA_DYN_CLK_CLAMP */ + 0x32C, /* T_SATA0_AHCI_CFG_ERR_CTRL */ + 0x338, /* T_SATA0_AHCI_HBA_CYA_1 */ + 0x340, /* T_SATA0_AHCI_HBA_PRE_STAGING_CONTROL */ + 0x430, /* T_SATA0_CFG_FPCI_0 */ + 0x494, /* T_SATA0_CFG_ESATA_CTRL */ + 0x4A0, /* T_SATA0_CYA1 */ + 0x4B0, /* T_SATA0_CFG_GLUE */ + 0x534, /* T_SATA0_PHY_CTRL */ + 0x540, /* T_SATA0_CTRL */ + 0x550, /* T_SATA0_DBG0 */ + 0x554 /* T_SATA0_LOW_POWER_COUNT */ +}; + +static u16 pg_save_config_port_registers[] = { + /* Save and restore per port */ + /* need to have port selected */ + 0x530, /* T_SATA0_CHXCFG1 */ + 0x684, /* T_SATA0_CHX_MISC */ + 0x700, /* T_SATA0_CHXCFG3 */ + 0x704, /* T_SATA0_CHXCFG4_CHX */ + 0x690, /* T_SATA0_CHX_PHY_CTRL1_GEN1 */ + 0x694, /* T_SATA0_CHX_PHY_CTRL1_GEN2 */ + 0x698, /* T_SATA0_CHX_PHY_CTRL1_GEN3 */ + 0x69C, /* T_SATA0_CHX_PHY_CTRL_2 */ + 0x6B0, /* T_SATA0_CHX_PHY_CTRL_3 */ + 0x6B4, /* T_SATA0_CHX_PHY_CTRL_4 */ + 0x6B8, /* T_SATA0_CHX_PHY_CTRL_5 */ + 0x6BC, /* T_SATA0_CHX_PHY_CTRL_6 */ + 0x714, /* T_SATA0_PRBS_CHX - OP */ + 0x750, /* T_SATA0_CHX_LINK0 */ + 0x7F0 /* T_SATA0_CHX_GLUE */ +}; + +static u16 pg_save_ipfs_registers[] = { + 0x094, /* SATA_FPCI_BAR5_0 */ + 0x0C0, /* SATA_MSI_BAR_SZ_0 */ + 0x0C4, /* SATA_MSI_AXI_BAR_ST_0 */ + 0x0C8, /* SATA_MSI_FPCI_BAR_ST_0 */ + 0x140, /* SATA_MSI_EN_VEC0_0 */ + 0x144, /* SATA_MSI_EN_VEC1_0 */ + 0x148, /* SATA_MSI_EN_VEC2_0 */ + 0x14C, /* SATA_MSI_EN_VEC3_0 */ + 0x150, /* SATA_MSI_EN_VEC4_0 */ + 0x154, /* SATA_MSI_EN_VEC5_0 */ + 0x158, /* SATA_MSI_EN_VEC6_0 */ + 0x15C, /* SATA_MSI_EN_VEC7_0 */ + 0x180, /* SATA_CONFIGURATION_0 */ + 0x184, /* SATA_FPCI_ERROR_MASKS_0 */ + 0x188, /* SATA_INTR_MASK_0 */ + 0x1A0, /* SATA_CFG_REVID_0 */ + 0x198, /* SATA_IPFS_INTR_ENABLE_0 */ + 0x1BC, /* SATA_CLKGATE_HYSTERSIS_0 */ + 0x1DC /* SATA_SATA_MCCIF_FIFOCTRL_0 */ +}; + +static void tegra_ahci_save_regs(u32 **save_addr, + u32 reg_base, + u16 reg_array[], + u32 regs) +{ + u32 i; + u32 *dest = (u32 *)*save_addr; + u32 base = (u32)IO_ADDRESS(reg_base); + + for (i = 0; i < regs; ++i, ++dest) { + *dest = readl(base + (u32)reg_array[i]); + AHCI_DBG_PRINT("save: [0x%x]=0x%08x\n", + (reg_base+(u32)reg_array[i]), *dest); + } + *save_addr = dest; +} + +static void tegra_ahci_restore_regs(void **save_addr, + u32 reg_base, + u16 reg_array[], + u32 regs) +{ + u32 i; + u32 *src = (u32 *)*save_addr; + u32 base = (u32)IO_ADDRESS(reg_base); + + for (i = 0; i < regs; ++i, ++src) { + writel(*src, base + (u32)reg_array[i]); + AHCI_DBG_PRINT("restore: [0x%x]=0x%08x\n", + (reg_base+(u32)reg_array[i]), *src); + } + *save_addr = src; +} + +static void tegra_ahci_pg_save_registers(struct ata_host *host) +{ + struct tegra_ahci_host_priv *tegra_hpriv; + u32 *pg_save; + u32 regs; + int i; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + pg_save = tegra_hpriv->pg_save; + + /* + * Driver should save/restore the registers in the order of + * IPFS, CFG, Ext CFG, BAR5. + */ + + /* save IPFS registers */ + regs = ARRAY_SIZE(pg_save_ipfs_registers); + tegra_ahci_save_regs(&pg_save, TEGRA_SATA_BASE, + pg_save_ipfs_registers, regs); + /* after the call, pg_save should point to the next address to save */ + + /* save CONFIG registers */ + regs = ARRAY_SIZE(pg_save_config_registers); + tegra_ahci_save_regs(&pg_save, TEGRA_SATA_CONFIG_BASE, + pg_save_config_registers, regs); + + /* save CONFIG per port registers */ + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { + scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); + regs = ARRAY_SIZE(pg_save_config_port_registers); + tegra_ahci_save_regs(&pg_save, TEGRA_SATA_CONFIG_BASE, + pg_save_config_port_registers, regs); + } + scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); + + /* save BAR5 registers */ + regs = ARRAY_SIZE(pg_save_bar5_registers); + tegra_ahci_save_regs(&pg_save, TEGRA_SATA_BAR5_BASE, + pg_save_bar5_registers, regs); + + /* save BAR5 port_registers */ + regs = ARRAY_SIZE(pg_save_bar5_port_registers); + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) + tegra_ahci_save_regs(&pg_save, TEGRA_SATA_BAR5_BASE + (0x80*i), + pg_save_bar5_port_registers, regs); + + /* save bkdr registers */ + regs = ARRAY_SIZE(pg_save_bar5_bkdr_registers); + tegra_ahci_save_regs(&pg_save, TEGRA_SATA_BAR5_BASE, + pg_save_bar5_bkdr_registers, regs); + + /* and save bkdr per_port registers */ + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { + scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); + regs = ARRAY_SIZE(pg_save_bar5_bkdr_port_registers); + tegra_ahci_save_regs(&pg_save, TEGRA_SATA_BAR5_BASE + (0x80*i), + pg_save_bar5_bkdr_port_registers, + regs); + } + scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); +} + +static void tegra_ahci_pg_restore_registers(struct ata_host *host) +{ + struct tegra_ahci_host_priv *tegra_hpriv; + void *pg_save; + u32 regs, val; + int i; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + pg_save = tegra_hpriv->pg_save; + + /* + * Driver should restore the registers in the order of + * IPFS, CFG, Ext CFG, BAR5. + */ + + /* restore IPFS registers */ + regs = ARRAY_SIZE(pg_save_ipfs_registers); + tegra_ahci_restore_regs(&pg_save, TEGRA_SATA_BASE, + pg_save_ipfs_registers, regs); + /* after the call, pg_save should point to the next addr to restore */ + + /* restore CONFIG registers */ + regs = ARRAY_SIZE(pg_save_config_registers); + tegra_ahci_restore_regs(&pg_save, TEGRA_SATA_CONFIG_BASE, + pg_save_config_registers, regs); + + /* restore CONFIG per port registers */ + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { + scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); + regs = ARRAY_SIZE(pg_save_config_port_registers); + tegra_ahci_restore_regs(&pg_save, TEGRA_SATA_CONFIG_BASE, + pg_save_config_port_registers, + regs); + } + scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); + + /* restore BAR5 registers */ + regs = ARRAY_SIZE(pg_save_bar5_registers); + tegra_ahci_restore_regs(&pg_save, TEGRA_SATA_BAR5_BASE, + pg_save_bar5_registers, regs); + + /* restore BAR5 port_registers */ + regs = ARRAY_SIZE(pg_save_bar5_port_registers); + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) + tegra_ahci_restore_regs(&pg_save, TEGRA_SATA_BAR5_BASE+(0x80*i), + pg_save_bar5_port_registers, regs); + + /* restore bkdr registers */ + regs = ARRAY_SIZE(pg_restore_bar5_bkdr_registers); + tegra_ahci_restore_regs(&pg_save, TEGRA_SATA_CONFIG_BASE, + pg_restore_bar5_bkdr_registers, regs); + + /* and restore BAR5 bkdr per_port registers */ + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { + scfg_writel((1 << i), T_SATA0_INDEX_OFFSET); + regs = ARRAY_SIZE(pg_restore_bar5_bkdr_port_registers); + tegra_ahci_restore_regs(&pg_save, TEGRA_SATA_CONFIG_BASE, + pg_restore_bar5_bkdr_port_registers, + regs); + } + scfg_writel(SATA0_NONE_SELECTED, T_SATA0_INDEX_OFFSET); + + /* program class code and programming interface for AHCI */ + val = scfg_readl(TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); + val |= TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; + scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); + scfg_writel(TEGRA_PRIVATE_AHCI_CC_BKDR_PGM, TEGRA_PRIVATE_AHCI_CC_BKDR); + val &= ~TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE_EN; + scfg_writel(val, TEGRA_PRIVATE_AHCI_CC_BKDR_OVERRIDE); +} + +static u32 tegra_ahci_port_error(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + u32 err_status; + + err_status = readl(port_mmio + PORT_IRQ_STAT); + /* excludes PhyRdy and Connect Change status */ + err_status &= (PORT_IRQ_ERROR & (~(PORT_IRQ_PHYRDY|PORT_IRQ_CONNECT))); + return err_status; +} + +static bool tegra_ahci_check_errors(struct ata_host *host) +{ int i; + struct ata_port *ap; + u32 err; + + for (i = 0; i < host->n_ports; i++) { + ap = host->ports[i]; + err = tegra_ahci_port_error(ap); + if (err) { + dev_err(host->dev, + "pg-chk-err = 0x%08x on port %d\n", err, i); + return true; + } + } + return false; +} + +static void tegra_ahci_abort_power_gate(struct ata_host *host) +{ + u32 val; + + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~PG_INFO_MASK; + val |= PG_INFO_OFF; + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); +} + +static bool tegra_ahci_power_gate(struct ata_host *host) +{ + u32 val; + u32 dat; + struct tegra_ahci_host_priv *tegra_hpriv; + int status; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~PG_INFO_MASK; + val |= PG_INFO_ON; + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + tegra_ahci_pg_save_registers(host); + + /* + * Read SATA_AUX_MISC_CNTL_1_0 register L0_RX_IDLE_T_SAX field and + * write that value into same register L0_RX_IDLE_T_NPG field. + * And write 1 to L0_RX_IDLE_T_MUX field. + */ + val = misc_readl(SATA_AUX_MISC_CNTL_1_REG); + dat = val; + dat &= L0_RX_IDLE_T_SAX_MASK; + dat >>= L0_RX_IDLE_T_SAX_SHIFT; + dat <<= L0_RX_IDLE_T_NPG_SHIFT; + val &= ~L0_RX_IDLE_T_NPG_MASK; + val |= dat; + val &= ~L0_RX_IDLE_T_MUX_MASK; + val |= L0_RX_IDLE_T_MUX_FROM_APB_MISC; + misc_writel(val, SATA_AUX_MISC_CNTL_1_REG); + + /* abort PG if there are errors occurred */ + if (tegra_ahci_check_errors(host)) { + dev_err(host->dev, "** pg: errors; abort power gating **\n"); + return false; + } + /* make sure all ports have no outstanding commands and are idle. */ + if (!tegra_ahci_are_all_ports_idle(host)) { + dev_err(host->dev, "** pg: cmds; abort power gating **\n"); + return false; + } + + /* + * Hw wake up is not needed: + * Driver/RM shall place the SATA PHY and SATA PADPLL in IDDQ. + * SATA_PADPLL_RESET_SWCTL =1 + * SATA_PADPLL_RESET_OVERRIDE_VALUE=1 + * SATA_PADPHY_IDDQ_SWCTL=1 + * SATA_PADPHY_IDDQ_OVERRIDE_VALUE=1 + */ + val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); + val &= ~(PADPLL_RESET_SWCTL_MASK | PADPLL_RESET_OVERRIDE_VALUE_MASK); + val |= (PADPLL_RESET_SWCTL_ON | PADPLL_RESET_OVERRIDE_VALUE_ON); + clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~(PADPHY_IDDQ_OVERRIDE_VALUE_MASK | PADPHY_IDDQ_SWCTL_MASK); + val |= (PADPHY_IDDQ_SWCTL_ON | PADPHY_IDDQ_OVERRIDE_VALUE_ON); + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + /* Wait for time specified in SATA_LANE_IDDQ2_PADPLL_IDDQ */ + val = clk_readl(CLK_RST_SATA_PLL_CFG1_REG); + dat = (val & IDDQ2LANE_IDDQ_DLY_MASK) >> IDDQ2LANE_IDDQ_DLY_SHIFT; + udelay(dat); + + /* SATA_PADPLL_IDDQ_SWCTL=1 & SATA_PADPLL_IDDQ_OVERRIDE_VALUE=1 */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~(PADPLL_IDDQ_OVERRIDE_VALUE_MASK | PADPLL_IDDQ_SWCTL_MASK); + val |= (PADPLL_IDDQ_SWCTL_ON | PADPLL_IDDQ_OVERRIDE_VALUE_ON); + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + /* power off the sata */ + status = tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_SATA); + if (status) + dev_err(host->dev, "** failed to turn-off SATA (0x%x) **\n", + status); + + return true; +} + +static bool tegra_ahci_power_un_gate(struct ata_host *host) +{ + u32 val; + u32 dat; + u32 timeout; + struct tegra_ahci_host_priv *tegra_hpriv; + int status; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + /* get sata phy and pll out of iddq: */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~PADPLL_IDDQ_OVERRIDE_VALUE_MASK; + val |= PADPLL_IDDQ_OVERRIDE_VALUE_OFF; + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + /* wait for delay of IDDQ2LAND_SLUMBER_DLY */ + val = clk_readl(CLK_RST_SATA_PLL_CFG1_REG); + dat = (val & IDDQ2LANE_SLUMBER_DLY_MASK) >> IDDQ2LANE_SLUMBER_DLY_SHIFT; + udelay(dat); + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~PADPHY_IDDQ_OVERRIDE_VALUE_MASK; + val |= PADPHY_IDDQ_OVERRIDE_VALUE_OFF; + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + status = tegra_unpowergate_partition_with_clk_on(TEGRA_POWERGATE_SATA); + if (status) + dev_err(host->dev, "** failed to turn-on SATA (0x%x) **\n", + status); + + /* deasset PADPLL and wait until it locks. */ + val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); + val &= ~PADPLL_RESET_OVERRIDE_VALUE_MASK; + val |= PADPLL_RESET_OVERRIDE_VALUE_OFF; + clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); + + /* + * Wait for SATA_AUX_PAD_PLL_CNTL_1_0_LOCKDET to turn 1 with a timeout + * of 15 us. + */ + timeout = 15; + while (timeout--) { + udelay(1); + val = misc_readl(SATA_AUX_PAD_PLL_CNTL_1_REG); + if (val & LOCKDET_FIELD) + break; + } + if (timeout == 0) + pr_err("%s: SATA_PAD_PLL is not locked in 15us.\n", __func__); + + /* restore registers */ + tegra_ahci_pg_restore_registers(host); + + /* + * During the restoration of the registers, the driver would now need to + * restore the register T_SATA0_CFG_POWER_GATE_SSTS_RESTORED after the + * ssts_det, ssts_spd are restored. This register is used to tell the + * controller whether a drive existed earlier or not and move the PHY + * state machines into either HR_slumber or not. + */ + val = scfg_readl(T_SATA0_CFG_POWER_GATE); + val &= ~POWER_GATE_SSTS_RESTORED_MASK; + val |= POWER_GATE_SSTS_RESTORED_YES; + scfg_writel(val, T_SATA0_CFG_POWER_GATE); + + /* + * Driver needs to switch the rx_idle_t driven source back to from + * Sata controller after SAX is power-ungated. + */ + val = misc_readl(SATA_AUX_MISC_CNTL_1_REG); + val &= ~L0_RX_IDLE_T_MUX_MASK; + val |= L0_RX_IDLE_T_MUX_FROM_SATA; + misc_writel(val, SATA_AUX_MISC_CNTL_1_REG); + + /* + * Driver can start to use main SATA interrupt instead of the + * rx_stat_t interrupt. + */ + val = pictlr_readl(PRI_ICTLR_CPU_IER_SET_0_OFFSET); + val |= CPU_IER_SATA_CTL; + pictlr_writel(val, PRI_ICTLR_CPU_IER_SET_0_OFFSET); + + /* Set the bits in the CAR to allow HW based low power sequencing. */ + val = clk_readl(CLK_RST_SATA_PLL_CFG0_REG); + val &= ~PADPLL_RESET_SWCTL_MASK; + val |= PADPLL_RESET_SWCTL_OFF; + clk_writel(val, CLK_RST_SATA_PLL_CFG0_REG); + + /* + * power un-gating process is complete by clearing + * APBDEV_PMC_SATA_PWRGT_0.Pmc2sata_pg_info = 0 + */ + val = pmc_readl(APB_PMC_SATA_PWRGT_0_REG); + val &= ~PG_INFO_MASK; + val |= PG_INFO_OFF; + pmc_writel(val, APB_PMC_SATA_PWRGT_0_REG); + + return true; +} + +static enum port_idle_status tegra_ahci_is_port_idle(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + + if (readl(port_mmio + PORT_CMD_ISSUE) || + readl(port_mmio + PORT_SCR_ACT)) + return PORT_IS_NOT_IDLE; + return PORT_IS_IDLE; +} + +static enum port_idle_status tegra_ahci_is_port_slumber(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + u32 sstat; + + if (tegra_ahci_is_port_idle(ap) == PORT_IS_NOT_IDLE) + return PORT_IS_NOT_IDLE; + + /* return 1 if PORT_SCR_STAT is in IPM_SLUMBER_STATE */ + sstat = readl(port_mmio + PORT_SCR_STAT); + if ((sstat & SSTAT_IPM_STATE_MASK) == SSTAT_IPM_SLUMBER_STATE) + return PORT_IS_SLUMBER; + return PORT_IS_IDLE_NOT_SLUMBER; +} + +/* check if all supported ports are idle (no outstanding commands) */ +static bool tegra_ahci_are_all_ports_idle(struct ata_host *host) +{ int i; + struct ata_port *ap; + + for (i = 0; i < host->n_ports; i++) { + ap = host->ports[i]; + if (ap && (tegra_ahci_is_port_idle(ap) == PORT_IS_NOT_IDLE)) + return false; + } + return true; +} + +/* check if all supported ports are in slumber */ +static bool tegra_ahci_are_all_ports_slumber(struct ata_host *host) +{ int i; + struct ata_port *ap; + + for (i = 0; i < host->n_ports; i++) { + ap = host->ports[i]; + if (ap && (tegra_ahci_is_port_slumber(ap) != PORT_IS_SLUMBER)) + return false; + } + return true; +} + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE +static void tegra_ahci_to_add_idle_timer(struct ata_host *host) +{ + struct tegra_ahci_host_priv *tegra_hpriv; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + /* note: the routine is called from interrupt context */ + spin_lock(&host->lock); + /* start idle-timer if all ports have no outstanding commands */ + if (tegra_ahci_are_all_ports_idle(host)) { + /* adjust tegra_ahci_idle_time to minimum if it is too small */ + tegra_ahci_idle_time = max((u32)TEGRA_AHCI_MIN_IDLE_TIME, + tegra_ahci_idle_time); + tegra_hpriv->idle_timer.expires = + ata_deadline(jiffies, tegra_ahci_idle_time); + mod_timer(&tegra_hpriv->idle_timer, + tegra_hpriv->idle_timer.expires); + } + spin_unlock(&host->lock); +} + +static void tegra_ahci_idle_timer(unsigned long arg) +{ + struct ata_host *host = (void *)arg; + struct tegra_ahci_host_priv *tegra_hpriv; + unsigned long flags; + + tegra_hpriv = (struct tegra_ahci_host_priv *)host->private_data; + + spin_lock_irqsave(&host->lock, flags); + if (tegra_hpriv->pg_state == SATA_ON) + tegra_hpriv->pg_state = SATA_GOING_OFF; + else { + dev_err(host->dev, "idle_timer: bad state (%u)\n", + tegra_hpriv->pg_state); + WARN_ON(1); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + spin_unlock_irqrestore(&host->lock, flags); + + pm_runtime_put(tegra_hpriv->dev); +} + +static int tegra_ahci_queue_one_qc(struct tegra_ahci_host_priv *tegra_hpriv, + struct ata_queued_cmd *qc) +{ + struct tegra_qc_list *qc_list; + + qc_list = kmalloc(sizeof(struct tegra_qc_list), GFP_ATOMIC); + if (!qc_list) { + dev_err(tegra_hpriv->dev, "failed to alloc qc_list\n"); + return AC_ERR_SYSTEM; + } + qc_list->qc = qc; + list_add_tail(&(qc_list->list), &(tegra_hpriv->qc_list)); + dev_dbg(tegra_hpriv->dev, "queuing qc=%x\n", (unsigned int)qc); + return 0; +} + +static void tegra_ahci_dequeue_qcs(struct tegra_ahci_host_priv *tegra_hpriv) +{ + struct list_head *list, *next; + struct tegra_qc_list *qc_list; + struct ata_queued_cmd *qc; + + /* now qc_issue all qcs in the qc_list */ + list_for_each_safe(list, next, &tegra_hpriv->qc_list) { + qc_list = list_entry(list, struct tegra_qc_list, list); + qc = qc_list->qc; + dev_dbg(tegra_hpriv->dev, "dequeue qc=%x\n", (unsigned int)qc); + ahci_ops.qc_issue(qc); + list_del(list); + kfree(qc_list); + } +} + +static unsigned int tegra_ahci_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_host *host = ap->host; + struct tegra_ahci_host_priv *tegra_hpriv = host->private_data; + int rc; + + /* stop the idle timer */ + if (timer_pending(&tegra_hpriv->idle_timer)) + del_timer_sync(&tegra_hpriv->idle_timer); + + /* note: host->lock is locked */ + switch (tegra_hpriv->pg_state) { + case SATA_ON: + /* normal case, issue the qc */ + return ahci_ops.qc_issue(qc); + case SATA_GOING_OFF: + case SATA_ABORT_OFF: + /* SATA is going OFF, let's abort the suspend */ + dev_dbg(host->dev, "** qc_issue: going OFF **\n"); + tegra_hpriv->pg_state = SATA_ABORT_OFF; + return tegra_ahci_queue_one_qc(tegra_hpriv, qc); + case SATA_OFF: + dev_dbg(host->dev, "** qc_issue: request power-up sata **\n"); + rc = pm_runtime_get(tegra_hpriv->dev); + /* rc == 0 means the request has been queued successfully */ + if (rc) { + dev_err(host->dev, "** qc_issue: rt_get()=%d **\n", + rc); + WARN_ON(1); + return AC_ERR_SYSTEM; + } + tegra_hpriv->pg_state = SATA_GOING_ON; + /* continue with the following code to queue the qc */ + case SATA_GOING_ON: + return tegra_ahci_queue_one_qc(tegra_hpriv, qc); + default: + dev_err(host->dev, "** qc_issue: bad state (%u) **\n", + tegra_hpriv->pg_state); + WARN_ON(1); + return AC_ERR_SYSTEM; + } +} + +static int tegra_ahci_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct ata_host *host = ap->host; + struct tegra_ahci_host_priv *tegra_hpriv = host->private_data; + int rc; + + if (tegra_hpriv->pg_state == SATA_OFF) { + dev_dbg(host->dev, "** hreset: request power-up sata **\n"); + rc = pm_runtime_get_sync(tegra_hpriv->dev); + /* rc == 0 means the request has been run successfully */ + if (rc) { + dev_err(host->dev, "** hreset: rt_get()=%d **\n", rc); + WARN_ON(1); + return AC_ERR_SYSTEM; + } + tegra_hpriv->pg_state = SATA_ON; + } + + return ahci_ops.hardreset(link, class, deadline); +} + +static irqreturn_t tegra_ahci_interrupt(int irq, void *dev_instance) +{ + irqreturn_t irq_retval; + + irq_retval = ahci_interrupt(irq, dev_instance); + if (irq_retval == IRQ_NONE) + return IRQ_NONE; + +#ifdef CONFIG_PM + tegra_ahci_to_add_idle_timer((struct ata_host *)dev_instance); +#endif + + return irq_retval; +} +#endif +#endif + +static int __devexit tegra_ahci_remove_one(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ahci_host_priv *hpriv; + + BUG_ON(host == NULL); + BUG_ON(host->iomap[AHCI_PCI_BAR] == NULL); + hpriv = host->private_data; + + tegra_ahci_controller_remove(pdev); + + devm_iounmap(&pdev->dev, host->iomap[AHCI_PCI_BAR]); + devres_free(host); + +#ifdef CONFIG_PM + /* Free PG save/restore area */ + devm_kfree(&pdev->dev, ((struct tegra_ahci_host_priv *)hpriv)->pg_save); + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE + pm_runtime_disable(&pdev->dev); +#endif +#endif + + devm_kfree(&pdev->dev, hpriv); + + return 0; +} + +static int __devinit tegra_ahci_init_one(struct platform_device *pdev) +{ + struct ata_port_info pi = ahci_port_info; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv = NULL; + struct tegra_ahci_host_priv *tegra_hpriv; + struct ata_host *host = NULL; + int n_ports, i, rc; + struct resource *res, *irq_res; + void __iomem *mmio; + u32 save_size; + irq_handler_t irq_handler = ahci_interrupt; + + VPRINTK("ENTER\n"); + + WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS); + + ata_print_version_once(&pdev->dev, DRV_VERSION); + + /* Simple resource validation */ + if (pdev->num_resources != 3) { + dev_err(dev, "invalid number of resources\n"); + dev_err(dev, "not enough SATA resources\n"); + return -EINVAL; + } + + /* acquire bar resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -EINVAL; + + /* acquire IRQ resource */ + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (irq_res == NULL) + return -EINVAL; + if (irq_res->start <= 0) + return -EINVAL; + + /* allocate sizeof tegra_ahci_host_priv, which contains extra fields */ + hpriv = devm_kzalloc(dev, sizeof(struct tegra_ahci_host_priv), + GFP_KERNEL); + if (!hpriv) { + rc = -ENOMEM; + goto fail; + } + hpriv->flags |= (unsigned long)pi.private_data; + tegra_hpriv = (struct tegra_ahci_host_priv *)hpriv; + g_tegra_hpriv = tegra_hpriv; + + /* Call tegra init routine */ + rc = tegra_ahci_controller_init(tegra_hpriv); + if (rc != 0) { + dev_err(dev, "TEGRA SATA init failed\n"); + goto fail; + } + + /* + * We reserve a table of 6 BARs in tegra_hpriv to store BARs. + * Save the mapped AHCI_PCI_BAR address to the table. + */ + mmio = devm_ioremap(dev, res->start, (res->end-res->start+1)); + tegra_hpriv->bars_table[AHCI_PCI_BAR] = mmio; + hpriv->mmio = mmio; + + /* save initial config */ + tegra_ahci_save_initial_config(pdev, hpriv); + dev_dbg(dev, "past save init config\n"); + + /* prepare host */ + if (hpriv->cap & HOST_CAP_NCQ) { + pi.flags |= ATA_FLAG_NCQ; + pi.flags |= ATA_FLAG_FPDMA_AA; + } + + /* + * CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) { + rc = -ENOMEM; + goto fail; + } + host->private_data = hpriv; + tegra_hpriv->host = host; + tegra_hpriv->dev = dev; + host->iomap = tegra_hpriv->bars_table; + + if (!(hpriv->cap & HOST_CAP_SSS)) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n"); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + /* set initial link pm policy */ + ap->target_lpm_policy = ATA_LPM_UNKNOWN; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + else + ap->target_lpm_policy = ATA_LPM_MIN_POWER; + } + + rc = ahci_reset_controller(host); + if (rc) { + dev_err(dev, "Reset controller failed! (rc=%d)\n", rc); + goto fail; + } + + ahci_init_controller(host); + ahci_print_info(host, "TEGRA-SATA"); + dev_dbg(dev, "controller init okay\n"); + +#ifdef CONFIG_PM + /* Setup PG save/restore area: */ + + /* calculate the size */ + save_size = ARRAY_SIZE(pg_save_ipfs_registers) + + ARRAY_SIZE(pg_save_config_registers) + + ARRAY_SIZE(pg_save_bar5_registers) + + ARRAY_SIZE(pg_save_bar5_bkdr_registers); + + /* and add save port_registers for all the ports */ + save_size += TEGRA_AHCI_NUM_PORTS * + (ARRAY_SIZE(pg_save_config_port_registers) + + ARRAY_SIZE(pg_save_bar5_port_registers) + + ARRAY_SIZE(pg_save_bar5_bkdr_port_registers)); + + /* + * save_size is number of registers times number of bytes per + * register to get total save size. + */ + save_size *= sizeof(u32); + tegra_hpriv->pg_save = devm_kzalloc(dev, save_size, GFP_KERNEL); + if (!tegra_hpriv->pg_save) { + rc = -ENOMEM; + goto fail; + } + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE + pm_runtime_set_active(dev); + pm_suspend_ignore_children(dev, true); + pm_runtime_enable(dev); + + tegra_hpriv->pg_state = SATA_ON; + + /* setup sata idle timer */ + init_timer_deferrable(&tegra_hpriv->idle_timer); + tegra_hpriv->idle_timer.function = tegra_ahci_idle_timer; + tegra_hpriv->idle_timer.data = (unsigned long)host; + + INIT_LIST_HEAD(&tegra_hpriv->qc_list); + + /* use our own irq handler */ + irq_handler = tegra_ahci_interrupt; +#endif + +#endif + + rc = ata_host_activate(host, irq_res->start, irq_handler, 0, &ahci_sht); + if (rc == 0) + return 0; + + /* Free PG save/restore area */ + devm_kfree(dev, tegra_hpriv->pg_save); + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE + pm_runtime_put(dev); + pm_runtime_enable(dev); +#endif + +fail: + if (host) { + if (host->iomap[AHCI_PCI_BAR]) + devm_iounmap(dev, host->iomap[AHCI_PCI_BAR]); + devres_free(host); + } + if (hpriv) + devm_kfree(dev, hpriv); + + return rc; +} + +static int __init ahci_init(void) +{ + return platform_driver_register(&tegra_platform_ahci_driver); +} + +static void __exit ahci_exit(void) +{ + platform_driver_unregister(&tegra_platform_ahci_driver); +} + + +#ifdef CONFIG_DEBUG_FS + +#include +#include + +static void dbg_ahci_dump_regs(struct seq_file *s, u32 *ptr, u32 base, u32 regs) +{ +#define REGS_PER_LINE 4 + + u32 i, j; + u32 lines = regs / REGS_PER_LINE; + + for (i = 0; i < lines; i++) { + seq_printf(s, "0x%08x: ", base+(i*16)); + for (j = 0; j < REGS_PER_LINE; ++j) { + seq_printf(s, "0x%08x ", readl(ptr)); + ++ptr; + } + seq_printf(s, "\n"); + } +#undef REGS_PER_LINE +} + +static int dbg_ahci_dump_show(struct seq_file *s, void *unused) +{ + u32 base; + u32 *ptr; + u32 i; + + base = TEGRA_SATA_CONFIG_BASE; + ptr = (u32 *)IO_TO_VIRT(base); + seq_printf(s, "SATA CONFIG Registers:\n"); + seq_printf(s, "----------------------\n"); + dbg_ahci_dump_regs(s, ptr, base, 0x200); + + base = TEGRA_SATA_BAR5_BASE; + ptr = (u32 *)IO_TO_VIRT(base); + seq_printf(s, "\nAHCI HBA Registers:\n"); + seq_printf(s, "-------------------\n"); + dbg_ahci_dump_regs(s, ptr, base, 64); + + for (i = 0; i < TEGRA_AHCI_NUM_PORTS; ++i) { + base = TEGRA_SATA_BAR5_BASE + 0x100 + (0x80*i); + ptr = (u32 *)IO_TO_VIRT(base); + seq_printf(s, "\nPort %u Registers:\n", i); + seq_printf(s, "---------------\n"); + dbg_ahci_dump_regs(s, ptr, base, 16); + } + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE + /* adjust tegra_ahci_idle_time to minimum if it is too small */ + tegra_ahci_idle_time = max((u32)TEGRA_AHCI_MIN_IDLE_TIME, + tegra_ahci_idle_time); + seq_printf(s, "\nIdle Timeout = %u milli-seconds.\n", + tegra_ahci_idle_time); +#endif + + if (tegra_powergate_is_powered(TEGRA_POWERGATE_SATA)) + seq_printf(s, "\n=== SATA controller is powered on ===\n\n"); + else + seq_printf(s, "\n=== SATA controller is powered off ===\n\n"); + + return 0; +} + +static int dbg_ahci_dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_ahci_dump_show, &inode->i_private); +} + +static const struct file_operations debug_fops = { + .open = dbg_ahci_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init tegra_ahci_dump_debuginit(void) +{ + (void) debugfs_create_file("tegra_ahci", S_IRUGO, + NULL, NULL, &debug_fops); +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE + (void) debugfs_create_u32("tegra_ahci_idle_ms", S_IRWXUGO, + NULL, &tegra_ahci_idle_time); +#endif + return 0; +} +late_initcall(tegra_ahci_dump_debuginit); +#endif + +MODULE_AUTHOR("NVIDIA"); +MODULE_DESCRIPTION("Tegra AHCI SATA low-level driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); + +module_init(ahci_init); +module_exit(ahci_exit); -- cgit v1.2.3 From 168971ab0977d04e958671651c0be4be116fee01 Mon Sep 17 00:00:00 2001 From: Sang-Hun Lee Date: Tue, 15 May 2012 16:04:41 -0700 Subject: tegra: usb: disable interrupts when locking Problem description: - tegra_udc_irq uses udc->lock - Some functions running in the process context was not disabling interrupts when locking udc->lock - If a function gets interrupted by tegra_udc_irq after locking udc->lock, a deadlock occurs, as tegra_udc_irq would also try to lock Fix description: - Use an interruption disabling variant of spin_lock Bug 983958 Change-Id: Ib774847212da64f1f727a207a4821860ffa7b4a8 Signed-off-by: Sang-Hun Lee Reviewed-on: http://git-master/r/102693 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani Reviewed-by: Venkat Moganty GVS: Gerrit_Virtual_Submit --- drivers/usb/gadget/tegra_udc.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c index dc14c612b38f..f978f0f2d1e7 100644 --- a/drivers/usb/gadget/tegra_udc.c +++ b/drivers/usb/gadget/tegra_udc.c @@ -130,7 +130,7 @@ static void done(struct tegra_ep *ep, struct tegra_req *req, int status) unsigned char stopped = ep->stopped; struct ep_td_struct *curr_td, *next_td; int j; - + BUG_ON(!(in_irq() || irqs_disabled())); udc = (struct tegra_udc *)ep->udc; /* Removed the req from tegra_ep->queue */ list_del_init(&req->queue); @@ -180,19 +180,20 @@ static void done(struct tegra_ep *ep, struct tegra_req *req, int status) } #endif - spin_unlock(&ep->udc->lock); /* complete() is from gadget layer, * eg fsg->bulk_in_complete() */ - if (req->req.complete) + if (req->req.complete) { + spin_unlock(&ep->udc->lock); req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->udc->lock); + } - spin_lock(&ep->udc->lock); ep->stopped = stopped; } /* * nuke(): delete all requests related to this ep - * called with spinlock held + * Must be called with spinlock held and interrupt disabled */ static void nuke(struct tegra_ep *ep, int status) { @@ -1221,14 +1222,14 @@ static int tegra_set_selfpowered(struct usb_gadget *gadget, int is_on) static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) { struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); - + unsigned long flags; DBG("%s(%d) turn VBUS state from %s to %s", __func__, __LINE__, udc->vbus_active ? "on" : "off", is_active ? "on" : "off"); if (udc->vbus_active && !is_active) { /* If cable disconnected, cancel any delayed work */ cancel_delayed_work(&udc->work); - spin_lock(&udc->lock); + spin_lock_irqsave(&udc->lock, flags); /* reset all internal Queues and inform client driver */ reset_queues(udc); /* stop the controller and turn off the clocks */ @@ -1236,7 +1237,7 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) dr_controller_reset(udc); udc->vbus_active = 0; udc->usb_state = USB_STATE_DEFAULT; - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock,flags); tegra_usb_phy_power_off(udc->phy); if (udc->vbus_reg) { /* set the current limit to 0mA */ @@ -2704,6 +2705,7 @@ static int __exit tegra_udc_remove(struct platform_device *pdev) static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) { struct tegra_udc *udc = platform_get_drvdata(pdev); + unsigned long flags; DBG("%s(%d) BEGIN\n", __func__, __LINE__); /* If the controller is in otg mode, return */ @@ -2711,12 +2713,12 @@ static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) return 0; if (udc->vbus_active) { - spin_lock(&udc->lock); + spin_lock_irqsave(&udc->lock, flags); /* Reset all internal Queues and inform client driver */ reset_queues(udc); udc->vbus_active = 0; udc->usb_state = USB_STATE_DEFAULT; - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); } /* Stop the controller and turn off the clocks */ dr_controller_stop(udc); -- cgit v1.2.3 From f745e7ea486bcbf9665071df2e818141ed90ac07 Mon Sep 17 00:00:00 2001 From: Wen Yi Date: Wed, 23 May 2012 17:41:03 -0700 Subject: Revert "video: tegra: host: t30: use max 2d clock" This reverts commit 5bdd03b21f625d0a07c66e4894b79e557287a3a1. The reverted commit kept vcore at 1.2 volts whenever 2D engine is on and increased power consumption for use cases that utilize 2D but doesn't require its full speed. Bug 979545 Change-Id: I4297ab1fb83558501ff620952284c8590dc5f1dd Signed-off-by: Wen Yi Reviewed-on: http://git-master/r/104293 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Donghan Ryu Tested-by: Donghan Ryu GVS: Gerrit_Virtual_Submit --- drivers/video/tegra/host/t30/t30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/t30/t30.c b/drivers/video/tegra/host/t30/t30.c index 257ba0849277..b2768741546a 100644 --- a/drivers/video/tegra/host/t30/t30.c +++ b/drivers/video/tegra/host/t30/t30.c @@ -95,7 +95,7 @@ struct nvhost_device t30_devices[] = { .waitbases = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1), .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) | BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B), - .clocks = { {"gr2d", UINT_MAX}, + .clocks = { {"gr2d", 0}, {"epp", 0}, {"emc", 300000000} }, NVHOST_MODULE_NO_POWERGATE_IDS, -- cgit v1.2.3 From 839b2b9c17da5b96ecccf0acf5e8c9d3ae3816af Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Thu, 24 May 2012 11:30:30 +0300 Subject: video: tegra: host: Merge waitchk and relocation Job pinning and relocation already maps the gather buffers to kernel memory. Move waitchk to be done at the same time so that we do not need to re-map the memory to patch expired waits. Bug 965206 Change-Id: I23634b501a45de080200e57d3debf267b39fea38 Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/104415 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Juha Tukkinen Reviewed-by: Mayuresh Kulkarni --- drivers/video/tegra/host/bus_client.c | 8 +-- drivers/video/tegra/host/chip_support.h | 8 +-- drivers/video/tegra/host/host1x/host1x_channel.c | 16 ------ drivers/video/tegra/host/host1x/host1x_syncpt.c | 64 +++--------------------- drivers/video/tegra/host/nvhost_channel.h | 1 - drivers/video/tegra/host/nvhost_job.c | 58 ++++++++++++++++++++- drivers/video/tegra/host/nvhost_job.h | 7 ++- drivers/video/tegra/host/nvhost_syncpt.c | 13 ++--- drivers/video/tegra/host/nvhost_syncpt.h | 18 +------ 9 files changed, 81 insertions(+), 112 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index 822f8f3a456d..397cd21f555d 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -306,7 +306,7 @@ static int nvhost_ioctl_channel_flush( struct nvhost_get_param_args *args, int null_kickoff) { - struct device *device = &ctx->ch->dev->dev; + struct nvhost_device *ndev = to_nvhost_device(&ctx->ch->dev->dev); int err; trace_nvhost_ioctl_channel_flush(ctx->ch->dev->name); @@ -316,13 +316,13 @@ static int nvhost_ioctl_channel_flush( ctx->hdr.num_cmdbufs || ctx->hdr.num_waitchks) { reset_submit(ctx); - dev_err(device, "channel submit out of sync\n"); + dev_err(&ndev->dev, "channel submit out of sync\n"); return -EFAULT; } - err = nvhost_job_pin(ctx->job); + err = nvhost_job_pin(ctx->job, &nvhost_get_host(ndev)->syncpt); if (err) { - dev_warn(device, "nvhost_job_pin failed: %d\n", err); + dev_warn(&ndev->dev, "nvhost_job_pin failed: %d\n", err); return err; } diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h index 173a36065458..d69e1c4bccb9 100644 --- a/drivers/video/tegra/host/chip_support.h +++ b/drivers/video/tegra/host/chip_support.h @@ -28,7 +28,6 @@ struct output; struct nvhost_master; struct nvhost_intr; struct nvhost_syncpt; -struct nvhost_waitchk; struct nvhost_userctx_timeout; struct nvhost_channel; struct nvmap_handle_ref; @@ -106,11 +105,8 @@ struct nvhost_chip_support { void (*read_wait_base)(struct nvhost_syncpt *, u32 id); u32 (*update_min)(struct nvhost_syncpt *, u32 id); void (*cpu_incr)(struct nvhost_syncpt *, u32 id); - int (*wait_check)(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 waitchk_mask, - struct nvhost_waitchk *wait, - int num_waitchk); + int (*patch_wait)(struct nvhost_syncpt *sp, + void *patch_addr); void (*debug)(struct nvhost_syncpt *); const char * (*name)(struct nvhost_syncpt *, u32 id); int (*mutex_try_lock)(struct nvhost_syncpt *, diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c index 0b4d07cb9e47..c72e6478b806 100644 --- a/drivers/video/tegra/host/host1x/host1x_channel.c +++ b/drivers/video/tegra/host/host1x/host1x_channel.c @@ -242,22 +242,6 @@ int host1x_channel_submit(struct nvhost_job *job) goto error; } - /* remove stale waits */ - if (job->num_waitchk) { - err = nvhost_syncpt_wait_check(sp, - job->nvmap, - job->waitchk_mask, - job->waitchk, - job->num_waitchk); - if (err) { - dev_warn(&ch->dev->dev, - "nvhost_syncpt_wait_check failed: %d\n", err); - mutex_unlock(&ch->submitlock); - nvhost_module_idle(ch->dev); - goto error; - } - } - /* begin a CDMA submit */ err = nvhost_cdma_begin(&ch->cdma, job); if (err) { diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c index b7d6587acc61..4cc8e9e212fa 100644 --- a/drivers/video/tegra/host/host1x/host1x_syncpt.c +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c @@ -103,62 +103,14 @@ static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) wmb(); } -/* check for old WAITs to be removed (avoiding a wrap) */ -static int t20_syncpt_wait_check(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 waitchk_mask, - struct nvhost_waitchk *wait, - int num_waitchk) +/* remove a wait pointed to by patch_addr */ +static int host1x_syncpt_patch_wait(struct nvhost_syncpt *sp, + void *patch_addr) { - u32 idx; - int err = 0; - - /* get current syncpt values */ - for (idx = 0; idx < NV_HOST1X_SYNCPT_NB_PTS; idx++) { - if (BIT(idx) & waitchk_mask) - nvhost_syncpt_update_min(sp, idx); - } - - BUG_ON(!wait && !num_waitchk); - - /* compare syncpt vs wait threshold */ - while (num_waitchk) { - u32 override; - - BUG_ON(wait->syncpt_id >= NV_HOST1X_SYNCPT_NB_PTS); - trace_nvhost_syncpt_wait_check(wait->mem, wait->offset, - wait->syncpt_id, wait->thresh); - if (nvhost_syncpt_is_expired(sp, - wait->syncpt_id, wait->thresh)) { - /* - * NULL an already satisfied WAIT_SYNCPT host method, - * by patching its args in the command stream. The - * method data is changed to reference a reserved - * (never given out or incr) NVSYNCPT_GRAPHICS_HOST - * syncpt with a matching threshold value of 0, so - * is guaranteed to be popped by the host HW. - */ - dev_dbg(&syncpt_to_dev(sp)->dev->dev, - "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n", - wait->syncpt_id, - syncpt_op().name(sp, wait->syncpt_id), - wait->thresh, - nvhost_syncpt_read_min(sp, wait->syncpt_id)); - - /* patch the wait */ - override = nvhost_class_host_wait_syncpt( - NVSYNCPT_GRAPHICS_HOST, 0); - err = nvmap_patch_word(nvmap, - (struct nvmap_handle *)wait->mem, - wait->offset, override); - if (err) - break; - } - - wait++; - num_waitchk--; - } - return err; + u32 override = nvhost_class_host_wait_syncpt( + NVSYNCPT_GRAPHICS_HOST, 0); + __raw_writel(override, patch_addr); + return 0; } @@ -241,7 +193,7 @@ int host1x_init_syncpt_support(struct nvhost_master *host, op->syncpt.read_wait_base = t20_syncpt_read_wait_base; op->syncpt.update_min = t20_syncpt_update_min; op->syncpt.cpu_incr = t20_syncpt_cpu_incr; - op->syncpt.wait_check = t20_syncpt_wait_check; + op->syncpt.patch_wait = host1x_syncpt_patch_wait; op->syncpt.debug = t20_syncpt_debug; op->syncpt.name = t20_syncpt_name; op->syncpt.mutex_try_lock = syncpt_mutex_try_lock; diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h index a8f16f0c5abc..b3a904d5a3ee 100644 --- a/drivers/video/tegra/host/nvhost_channel.h +++ b/drivers/video/tegra/host/nvhost_channel.h @@ -31,7 +31,6 @@ #define NVHOST_MAX_POWERGATE_IDS 2 struct nvhost_master; -struct nvhost_waitchk; struct nvhost_device; struct nvhost_channel; struct nvhost_hwctx; diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index 11d65964e2c9..6d8c3bd2bd50 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -27,6 +27,7 @@ #include "nvhost_channel.h" #include "nvhost_job.h" #include "nvhost_hwctx.h" +#include "nvhost_syncpt.h" #include "dev.h" /* Magic to use to fill freed handle slots */ @@ -196,11 +197,63 @@ static int do_relocs(struct nvhost_job *job, u32 patch_mem, void *patch_addr) return 0; } -int nvhost_job_pin(struct nvhost_job *job) +/* + * Check driver supplied waitchk structs for syncpt thresholds + * that have already been satisfied and NULL the comparison (to + * avoid a wrap condition in the HW). + */ +static int do_waitchks(struct nvhost_job *job, struct nvhost_syncpt *sp, + u32 patch_mem, void *patch_addr) +{ + int i; + + /* compare syncpt vs wait threshold */ + for (i = 0; i < job->num_waitchk; i++) { + struct nvhost_waitchk *wait = &job->waitchk[i]; + + /* skip all other gathers */ + if (patch_mem != wait->mem) + continue; + + trace_nvhost_syncpt_wait_check(wait->mem, wait->offset, + wait->syncpt_id, wait->thresh); + if (nvhost_syncpt_is_expired(sp, + wait->syncpt_id, wait->thresh)) { + /* + * NULL an already satisfied WAIT_SYNCPT host method, + * by patching its args in the command stream. The + * method data is changed to reference a reserved + * (never given out or incr) NVSYNCPT_GRAPHICS_HOST + * syncpt with a matching threshold value of 0, so + * is guaranteed to be popped by the host HW. + */ + dev_dbg(&syncpt_to_dev(sp)->dev->dev, + "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n", + wait->syncpt_id, + syncpt_op().name(sp, wait->syncpt_id), + wait->thresh, + nvhost_syncpt_read_min(sp, wait->syncpt_id)); + + /* patch the wait */ + nvhost_syncpt_patch_wait(sp, + (patch_addr + wait->offset)); + } + + wait->mem = 0; + } + return 0; +} + +int nvhost_job_pin(struct nvhost_job *job, struct nvhost_syncpt *sp) { int err = 0, i = 0; phys_addr_t gather_phys = 0; void *gather_addr = NULL; + unsigned long waitchk_mask = job->waitchk_mask; + + /* get current syncpt values for waitchk */ + for_each_set_bit(i, &waitchk_mask, sizeof(job->waitchk_mask)) + nvhost_syncpt_update_min(sp, i); /* pin gathers */ for (i = 0; i < job->num_gathers; i++) { @@ -233,6 +286,9 @@ int nvhost_job_pin(struct nvhost_job *job) } err = do_relocs(job, g->mem_id, gather_addr); + if (!err) + err = do_waitchks(job, sp, + g->mem_id, gather_addr); nvmap_munmap(g->ref, gather_addr); if (err) diff --git a/drivers/video/tegra/host/nvhost_job.h b/drivers/video/tegra/host/nvhost_job.h index 48555a231412..b30f5faf7e8f 100644 --- a/drivers/video/tegra/host/nvhost_job.h +++ b/drivers/video/tegra/host/nvhost_job.h @@ -27,7 +27,7 @@ struct nvhost_channel; struct nvhost_hwctx; struct nvmap_client; struct nvhost_waitchk; -struct nvmap_handle; +struct nvhost_syncpt; struct nvhost_job_gather { u32 words; @@ -129,8 +129,11 @@ void nvhost_job_put(struct nvhost_job *job); * Pin memory related to job. This handles relocation of addresses to the * host1x address space. Handles both the gather memory and any other memory * referred to from the gather buffers. + * + * Handles also patching out host waits that would wait for an expired sync + * point value. */ -int nvhost_job_pin(struct nvhost_job *job); +int nvhost_job_pin(struct nvhost_job *job, struct nvhost_syncpt *sp); /* * Unpin memory related to job. diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c index 1458a7cb5acc..7550512b0214 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.c +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -74,7 +74,7 @@ u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) BUG_ON(!syncpt_op().update_min); - return syncpt_op().update_min(sp, id); + val = syncpt_op().update_min(sp, id); trace_nvhost_syncpt_update_min(id, val); return val; @@ -330,15 +330,10 @@ void nvhost_mutex_unlock(struct nvhost_syncpt *sp, int idx) atomic_dec(&sp->lock_counts[idx]); } -/* check for old WAITs to be removed (avoiding a wrap) */ -int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 waitchk_mask, - struct nvhost_waitchk *wait, - int num_waitchk) +/* remove a wait pointed to by patch_addr */ +int nvhost_syncpt_patch_wait(struct nvhost_syncpt *sp, void *patch_addr) { - return syncpt_op().wait_check(sp, nvmap, - waitchk_mask, wait, num_waitchk); + return syncpt_op().patch_wait(sp, patch_addr); } /* Displays the current value of the sync point via sysfs */ diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h index b770ed91c76c..b58921bffa9c 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.h +++ b/drivers/video/tegra/host/nvhost_syncpt.h @@ -136,23 +136,7 @@ static inline int nvhost_syncpt_wait(struct nvhost_syncpt *sp, u32 id, u32 thres MAX_SCHEDULE_TIMEOUT, NULL); } -/* - * Check driver supplied waitchk structs for syncpt thresholds - * that have already been satisfied and NULL the comparison (to - * avoid a wrap condition in the HW). - * - * @param: sp - global shadowed syncpt struct - * @param: nvmap - needed to access command buffer - * @param: mask - bit mask of syncpt IDs referenced in WAITs - * @param: wait - start of filled in array of waitchk structs - * @param: waitend - end ptr (one beyond last valid waitchk) - */ -struct nvhost_waitchk; -int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 mask, - struct nvhost_waitchk *wait, - int num_waitchk); +int nvhost_syncpt_patch_wait(struct nvhost_syncpt *sp, void *patch_addr); void nvhost_syncpt_debug(struct nvhost_syncpt *sp); -- cgit v1.2.3 From 73f65f7f6898bd88fe457f96d4dc702f746058bc Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Thu, 24 May 2012 15:06:58 +0300 Subject: video: tegra: host: Replace nvmap structs with own Replace usage of nvmap_pinarray_elem with own nvhost_reloc and nvhost_reloc_shift structs. Bug 965206 Change-Id: I90618d8a34d79156d8880d9925dadbf416353811 Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/104450 Reviewed-by: Juha Tukkinen Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Mayuresh Kulkarni --- drivers/video/tegra/host/bus_client.c | 39 +++++++++++++++---------- drivers/video/tegra/host/nvhost_job.c | 54 +++++++++++++++++++---------------- drivers/video/tegra/host/nvhost_job.h | 3 +- 3 files changed, 56 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index 397cd21f555d..9b71542a48aa 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -238,21 +238,28 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, cmdbuf.mem, cmdbuf.words, cmdbuf.offset); hdr->num_cmdbufs--; } else if (hdr->num_relocs) { - consumed = sizeof(struct nvhost_reloc); - if (remaining < consumed) + int numrelocs = remaining / sizeof(struct nvhost_reloc); + if (!numrelocs) break; - if (copy_from_user(&job->pinarray[job->num_relocs], + numrelocs = min_t(int, numrelocs, priv->hdr.num_relocs); + consumed = numrelocs * sizeof(struct nvhost_reloc); + if (copy_from_user(&job->relocarray[job->num_relocs], buf, consumed)) { err = -EFAULT; break; } - trace_nvhost_channel_write_reloc(chname, - job->pinarray[job->num_relocs].patch_mem, - job->pinarray[job->num_relocs].patch_offset, - job->pinarray[job->num_relocs].pin_mem, - job->pinarray[job->num_relocs].pin_offset); - job->num_relocs++; - hdr->num_relocs--; + while (numrelocs) { + struct nvhost_reloc *reloc = + &job->relocarray[job->num_relocs]; + trace_nvhost_channel_write_reloc(chname, + reloc->cmdbuf_mem, + reloc->cmdbuf_offset, + reloc->target, + reloc->target_offset); + job->num_relocs++; + hdr->num_relocs--; + numrelocs--; + } } else if (hdr->num_waitchks) { int numwaitchks = (remaining / sizeof(struct nvhost_waitchk)); @@ -274,16 +281,18 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, } else if (priv->num_relocshifts) { int next_shift = job->num_relocs - priv->num_relocshifts; - consumed = sizeof(struct nvhost_reloc_shift); - if (remaining < consumed) + int num = + (remaining / sizeof(struct nvhost_reloc_shift)); + if (!num) break; - if (copy_from_user( - &job->pinarray[next_shift].reloc_shift, + num = min_t(int, num, priv->num_relocshifts); + consumed = num * sizeof(struct nvhost_reloc_shift); + if (copy_from_user(&job->relocshiftarray[next_shift], buf, consumed)) { err = -EFAULT; break; } - priv->num_relocshifts--; + priv->num_relocshifts -= num; } else { err = -EFAULT; break; diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index 6d8c3bd2bd50..e029449b6184 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -41,7 +41,8 @@ static int job_size(struct nvhost_submit_hdr_ext *hdr) int num_unpins = num_cmdbufs + num_relocs; return sizeof(struct nvhost_job) - + num_relocs * sizeof(struct nvmap_pinarray_elem) + + num_relocs * sizeof(struct nvhost_reloc) + + num_relocs * sizeof(struct nvhost_reloc_shift) + num_unpins * sizeof(struct nvmap_handle_ref *) + num_waitchks * sizeof(struct nvhost_waitchk) + num_cmdbufs * sizeof(struct nvhost_job_gather); @@ -63,8 +64,10 @@ static void init_fields(struct nvhost_job *job, /* Redistribute memory to the structs */ mem += sizeof(struct nvhost_job); - job->pinarray = num_relocs ? mem : NULL; - mem += num_relocs * sizeof(struct nvmap_pinarray_elem); + job->relocarray = num_relocs ? mem : NULL; + mem += num_relocs * sizeof(struct nvhost_reloc); + job->relocshiftarray = num_relocs ? mem : NULL; + mem += num_relocs * sizeof(struct nvhost_reloc_shift); job->unpins = num_unpins ? mem : NULL; mem += num_unpins * sizeof(struct nvmap_handle_ref *); job->waitchk = num_waitchks ? mem : NULL; @@ -154,44 +157,46 @@ void nvhost_job_add_gather(struct nvhost_job *job, job->num_gathers += 1; } -static int do_relocs(struct nvhost_job *job, u32 patch_mem, void *patch_addr) +static int do_relocs(struct nvhost_job *job, u32 cmdbuf_mem, void *cmdbuf_addr) { - phys_addr_t pin_phys; + phys_addr_t target_phys; int i; u32 mem_id = 0; - struct nvmap_handle_ref *pin_ref = NULL; + struct nvmap_handle_ref *target_ref = NULL; /* pin & patch the relocs for one gather */ for (i = 0; i < job->num_relocs; i++) { - struct nvmap_pinarray_elem *pin = &job->pinarray[i]; + struct nvhost_reloc *reloc = &job->relocarray[i]; + struct nvhost_reloc_shift *shift = &job->relocshiftarray[i]; /* skip all other gathers */ - if (patch_mem != pin->patch_mem) + if (cmdbuf_mem != reloc->cmdbuf_mem) continue; /* check if pin-mem is same as previous */ - if (pin->pin_mem != mem_id) { - pin_ref = nvmap_duplicate_handle_id(job->nvmap, - pin->pin_mem); - if (IS_ERR(pin_ref)) - return PTR_ERR(pin_ref); - - pin_phys = nvmap_pin(job->nvmap, pin_ref); - if (IS_ERR((void *)pin_phys)) { - nvmap_free(job->nvmap, pin_ref); - return pin_phys; + if (reloc->target != mem_id) { + target_ref = nvmap_duplicate_handle_id(job->nvmap, + reloc->target); + if (IS_ERR(target_ref)) + return PTR_ERR(target_ref); + + target_phys = nvmap_pin(job->nvmap, target_ref); + if (IS_ERR((void *)target_phys)) { + nvmap_free(job->nvmap, target_ref); + return target_phys; } - mem_id = pin->pin_mem; - job->unpins[job->num_unpins++] = pin_ref; + mem_id = reloc->target; + job->unpins[job->num_unpins++] = target_ref; } - __raw_writel((pin_phys + pin->pin_offset) >> pin->reloc_shift, - (patch_addr + pin->patch_offset)); + __raw_writel( + (target_phys + reloc->target_offset) >> shift->shift, + (cmdbuf_addr + reloc->cmdbuf_offset)); /* Different gathers might have same mem_id. This ensures we * perform reloc only once per gather memid. */ - pin->patch_mem = 0; + reloc->cmdbuf_mem = 0; } return 0; @@ -216,7 +221,8 @@ static int do_waitchks(struct nvhost_job *job, struct nvhost_syncpt *sp, continue; trace_nvhost_syncpt_wait_check(wait->mem, wait->offset, - wait->syncpt_id, wait->thresh); + wait->syncpt_id, wait->thresh, + nvhost_syncpt_read(sp, wait->syncpt_id)); if (nvhost_syncpt_is_expired(sp, wait->syncpt_id, wait->thresh)) { /* diff --git a/drivers/video/tegra/host/nvhost_job.h b/drivers/video/tegra/host/nvhost_job.h index b30f5faf7e8f..ec1366337279 100644 --- a/drivers/video/tegra/host/nvhost_job.h +++ b/drivers/video/tegra/host/nvhost_job.h @@ -67,7 +67,8 @@ struct nvhost_job { u32 waitchk_mask; /* Array of handles to be pinned & unpinned */ - struct nvmap_pinarray_elem *pinarray; + struct nvhost_reloc *relocarray; + struct nvhost_reloc_shift *relocshiftarray; int num_relocs; struct nvmap_handle_ref **unpins; int num_unpins; -- cgit v1.2.3 From 84a74690b616591b2f178bd9b08953433c66b620 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 24 May 2012 18:22:29 +0530 Subject: spi: tegra: fix fifo_depth to 32. Slink controller have the fifo depth of 32 words in rx and tx side. But some of places it was taken the value as 4. Fixing this to 32 words. Change-Id: I262127c59241ce75d4385464c21ee733a48b1475 Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/104463 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Stephen Warren GVS: Gerrit_Virtual_Submit Reviewed-by: Bitan Biswas --- drivers/spi/spi-tegra.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index 070cc1581efd..67143178c125 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -144,7 +144,6 @@ #define DATA_DIR_TX (1 << 0) #define DATA_DIR_RX (1 << 1) -#define SPI_FIFO_DEPTH 32 #define SLINK_DMA_TIMEOUT (msecs_to_jiffies(1000)) @@ -169,7 +168,7 @@ static const unsigned long spi_tegra_req_sels[] = { RX_FIFO_FULL_COUNT_ZERO << 16) #define MAX_CHIP_SELECT 4 -#define SLINK_FIFO_DEPTH 4 +#define SLINK_FIFO_DEPTH 32 struct spi_tegra_data { struct spi_master *master; @@ -807,7 +806,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi, spi_tegra_writel(tspi, command2, SLINK_COMMAND2); tspi->command2_reg = command2; - if (total_fifo_words > SPI_FIFO_DEPTH) + if (total_fifo_words > SLINK_FIFO_DEPTH) ret = spi_tegra_start_dma_based_transfer(tspi, t); else ret = spi_tegra_start_cpu_based_transfer(tspi, t); @@ -1158,7 +1157,7 @@ static irqreturn_t spi_tegra_isr_thread(int irq, void *context_data) /* Continue transfer in current message */ total_fifo_words = spi_tegra_calculate_curr_xfer_param(tspi->cur_spi, tspi, t); - if (total_fifo_words > SPI_FIFO_DEPTH) + if (total_fifo_words > SLINK_FIFO_DEPTH) err = spi_tegra_start_dma_based_transfer(tspi, t); else err = spi_tegra_start_cpu_based_transfer(tspi, t); -- cgit v1.2.3 From 95c66daaa95d7cb04803d798db36ebbc43927f2a Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 24 May 2012 16:17:29 +0530 Subject: spi: tegra: use devm_* for resource allocation Using of devm_* function for resource allocation does not require to free resource on code and hence it reduces code sizes. Change-Id: Id6f0ba3cde2f351d5668ed28b098e5a829716a30 Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/104464 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bitan Biswas --- drivers/spi/spi-tegra.c | 60 ++++++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index 67143178c125..05c2d2c2607b 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -1220,55 +1220,50 @@ static int __init spi_tegra_probe(struct platform_device *pdev) spin_lock_init(&tspi->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); ret = -ENODEV; - goto fail_no_mem; + goto exit_free_master; } - - if (!request_mem_region(r->start, resource_size(r), - dev_name(&pdev->dev))) { - ret = -EBUSY; - goto fail_no_mem; - } - tspi->phys = r->start; - tspi->base = ioremap(r->start, resource_size(r)); + tspi->base = devm_request_and_ioremap(&pdev->dev, r); if (!tspi->base) { - dev_err(&pdev->dev, "can't ioremap iomem\n"); - ret = -ENOMEM; - goto fail_io_map; + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + ret = -EADDRNOTAVAIL; + goto exit_free_master; } spi_irq = platform_get_irq(pdev, 0); if (unlikely(spi_irq < 0)) { dev_err(&pdev->dev, "can't find irq resource\n"); ret = -ENXIO; - goto fail_irq_req; + goto exit_free_master; } tspi->irq = spi_irq; sprintf(tspi->port_name, "tegra_spi_%d", pdev->id); - ret = request_threaded_irq(tspi->irq, spi_tegra_isr, - spi_tegra_isr_thread, IRQF_ONESHOT, + ret = devm_request_threaded_irq(&pdev->dev, tspi->irq, + spi_tegra_isr, spi_tegra_isr_thread, IRQF_ONESHOT, tspi->port_name, tspi); if (ret < 0) { dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", tspi->irq); - goto fail_irq_req; + goto exit_free_master; } - tspi->clk = clk_get(&pdev->dev, "spi"); + tspi->clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(tspi->clk)) { dev_err(&pdev->dev, "can not get clock\n"); ret = PTR_ERR(tspi->clk); - goto fail_clk_get; + goto exit_free_master; } - tspi->sclk = clk_get(&pdev->dev, "sclk"); + tspi->sclk = devm_clk_get(&pdev->dev, "sclk"); if (IS_ERR(tspi->sclk)) { dev_err(&pdev->dev, "can not get sclock\n"); ret = PTR_ERR(tspi->sclk); - goto fail_sclk_get; + goto exit_free_master; } INIT_LIST_HEAD(&tspi->queue); @@ -1317,7 +1312,7 @@ static int __init spi_tegra_probe(struct platform_device *pdev) if (!tspi->rx_dma) { dev_err(&pdev->dev, "can not allocate rx dma channel\n"); ret = -ENODEV; - goto fail_rx_dma_alloc; + goto exit_free_master; } tspi->rx_buf = dma_alloc_coherent(&pdev->dev, tspi->dma_buf_size, @@ -1433,17 +1428,8 @@ fail_tx_dma_alloc: fail_rx_buf_alloc: if (tspi->rx_dma) tegra_dma_free_channel(tspi->rx_dma); -fail_rx_dma_alloc: - clk_put(tspi->sclk); -fail_sclk_get: - clk_put(tspi->clk); -fail_clk_get: - free_irq(tspi->irq, tspi); -fail_irq_req: - iounmap(tspi->base); -fail_io_map: - release_mem_region(r->start, resource_size(r)); -fail_no_mem: + +exit_free_master: spi_master_put(master); return ret; } @@ -1452,7 +1438,6 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) { struct spi_master *master; struct spi_tegra_data *tspi; - struct resource *r; master = dev_get_drvdata(&pdev->dev); tspi = spi_master_get_devdata(master); @@ -1477,15 +1462,8 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(&pdev->dev)) tegra_spi_runtime_idle(&pdev->dev); - clk_put(tspi->sclk); - clk_put(tspi->clk); - iounmap(tspi->base); - destroy_workqueue(tspi->spi_workqueue); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - return 0; } -- cgit v1.2.3 From 0a9c2618396cdb1fb88a70bae9492c7c7e60c34a Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 24 May 2012 18:26:54 +0530 Subject: spi: tegra: use functions to avoid duplicated code The dma allocation method for receive and transmit is same and so instead of duplicating the same code for rx and tx, making the function to have common code and using the function for dma allocation. This reduces duplicated code. Change-Id: Ibe15eec896bc581bda8c68572eb1425c3bf6a7b2 Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/104465 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bitan Biswas --- drivers/spi/spi-tegra.c | 182 +++++++++++++++++++++++++++--------------------- 1 file changed, 102 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index 05c2d2c2607b..d4b9c3344eee 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -1185,6 +1185,87 @@ static irqreturn_t spi_tegra_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } +static void spi_tegra_deinit_dma_param(struct spi_tegra_data *tspi, + bool dma_to_memory) +{ + struct tegra_dma_channel *tdc; + u32 *dma_buf; + dma_addr_t dma_phys; + + if (dma_to_memory) { + dma_buf = tspi->rx_buf; + tdc = tspi->rx_dma; + dma_phys = tspi->rx_buf_phys; + tspi->rx_dma = NULL; + tspi->rx_buf = NULL; + } else { + dma_buf = tspi->tx_buf; + tdc = tspi->tx_dma; + dma_phys = tspi->tx_buf_phys; + tspi->tx_buf = NULL; + tspi->tx_dma = NULL; + } + + dma_free_coherent(&tspi->pdev->dev, tspi->dma_buf_size, + dma_buf, dma_phys); + tegra_dma_free_channel(tdc); +} + +static int __init spi_tegra_init_dma_param(struct spi_tegra_data *tspi, + bool dma_to_memory) +{ + struct tegra_dma_req *dma_req; + struct tegra_dma_channel *tdc; + u32 *dma_buf; + dma_addr_t dma_phys; + + tdc = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT, "spi_%s_%d", + (dma_to_memory) ? "rx" : "tx", tspi->pdev->id); + if (!tdc) { + dev_err(&tspi->pdev->dev, "can not allocate rx dma channel\n"); + return -ENODEV; + } + + dma_buf = dma_alloc_coherent(&tspi->pdev->dev, tspi->dma_buf_size, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(&tspi->pdev->dev, "can not allocate rx bounce buffer"); + tegra_dma_free_channel(tdc); + return -ENOMEM; + } + + dma_req = (dma_to_memory) ? &tspi->rx_dma_req : &tspi->tx_dma_req; + memset(dma_req, 0, sizeof(*dma_req)); + + dma_req->req_sel = spi_tegra_req_sels[tspi->pdev->id]; + dma_req->dev = tspi; + dma_req->dest_bus_width = 32; + dma_req->source_bus_width = 32; + dma_req->to_memory = (dma_to_memory) ? 1 : 0; + dma_req->virt_addr = dma_buf; + dma_req->dest_wrap = 0; + dma_req->source_wrap = 0; + + if (dma_to_memory) { + dma_req->complete = tegra_spi_rx_dma_complete; + dma_req->dest_addr = dma_phys; + dma_req->source_addr = tspi->phys + SLINK_RX_FIFO; + dma_req->source_wrap = 4; + tspi->rx_buf_phys = dma_phys; + tspi->rx_buf = dma_buf; + tspi->rx_dma = tdc; + } else { + dma_req->complete = tegra_spi_tx_dma_complete; + dma_req->dest_addr = tspi->phys + SLINK_TX_FIFO; + dma_req->source_addr = dma_phys; + dma_req->dest_wrap = 4; + tspi->tx_buf = dma_buf; + tspi->tx_buf_phys = dma_phys; + tspi->tx_dma = tdc; + } + return 0; +} + static int __init spi_tegra_probe(struct platform_device *pdev) { struct spi_master *master; @@ -1306,64 +1387,18 @@ static int __init spi_tegra_probe(struct platform_device *pdev) init_completion(&tspi->tx_dma_complete); init_completion(&tspi->rx_dma_complete); - - tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT, - "spi_rx_%d", pdev->id); - if (!tspi->rx_dma) { - dev_err(&pdev->dev, "can not allocate rx dma channel\n"); - ret = -ENODEV; + ret = spi_tegra_init_dma_param(tspi, true); + if (ret < 0) { + dev_err(&pdev->dev, "Error in rx dma init\n"); goto exit_free_master; } - tspi->rx_buf = dma_alloc_coherent(&pdev->dev, tspi->dma_buf_size, - &tspi->rx_buf_phys, GFP_KERNEL); - if (!tspi->rx_buf) { - dev_err(&pdev->dev, "can not allocate rx bounce buffer\n"); - ret = -ENOMEM; - goto fail_rx_buf_alloc; - } - - memset(&tspi->rx_dma_req, 0, sizeof(struct tegra_dma_req)); - tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete; - tspi->rx_dma_req.to_memory = 1; - tspi->rx_dma_req.dest_addr = tspi->rx_buf_phys; - tspi->rx_dma_req.virt_addr = tspi->rx_buf; - tspi->rx_dma_req.dest_bus_width = 32; - tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO; - tspi->rx_dma_req.source_bus_width = 32; - tspi->rx_dma_req.source_wrap = 4; - tspi->rx_dma_req.dest_wrap = 0; - tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; - tspi->rx_dma_req.dev = tspi; - - tspi->tx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT, - "spi_tx_%d", pdev->id); - if (!tspi->tx_dma) { - dev_err(&pdev->dev, "can not allocate tx dma channel\n"); - ret = -ENODEV; - goto fail_tx_dma_alloc; - } - - tspi->tx_buf = dma_alloc_coherent(&pdev->dev, tspi->dma_buf_size, - &tspi->tx_buf_phys, GFP_KERNEL); - if (!tspi->tx_buf) { - dev_err(&pdev->dev, "can not allocate tx bounce buffer\n"); - ret = -ENOMEM; - goto fail_tx_buf_alloc; + ret = spi_tegra_init_dma_param(tspi, false); + if (ret < 0) { + dev_err(&pdev->dev, "Error in tx dma init\n"); + goto exit_rx_dma_free; } - memset(&tspi->tx_dma_req, 0, sizeof(struct tegra_dma_req)); - tspi->tx_dma_req.complete = tegra_spi_tx_dma_complete; - tspi->tx_dma_req.to_memory = 0; - tspi->tx_dma_req.dest_addr = tspi->phys + SLINK_TX_FIFO; - tspi->tx_dma_req.virt_addr = tspi->tx_buf; - tspi->tx_dma_req.dest_bus_width = 32; - tspi->tx_dma_req.dest_wrap = 4; - tspi->tx_dma_req.source_wrap = 0; - tspi->tx_dma_req.source_addr = tspi->tx_buf_phys; - tspi->tx_dma_req.source_bus_width = 32; - tspi->tx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; - tspi->tx_dma_req.dev = tspi; tspi->max_buf_size = tspi->dma_buf_size; tspi->def_command_reg = SLINK_CS_SW | SLINK_M_S; tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN; @@ -1374,7 +1409,7 @@ skip_dma_alloc: ret = tegra_spi_runtime_resume(&pdev->dev); if (ret) { dev_err(&pdev->dev, "runtime resume failed %d", ret); - goto err_pm_disable; + goto exit_pm_disable; } } @@ -1386,7 +1421,7 @@ skip_dma_alloc: ret = spi_register_master(master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); - goto fail_master_register; + goto exit_pm_suspend; } /* create the workqueue for the kbc path */ @@ -1395,39 +1430,30 @@ skip_dma_alloc: if (!tspi->spi_workqueue) { dev_err(&pdev->dev, "Failed to create work queue\n"); ret = -ENODEV; - goto fail_workqueue; + goto exit_master_unregister; } INIT_WORK(&tspi->spi_transfer_work, tegra_spi_transfer_work); return ret; -fail_workqueue: +exit_master_unregister: spi_unregister_master(master); -fail_master_register: if (tspi->is_clkon_always) pm_runtime_put_sync(&pdev->dev); +exit_pm_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) tegra_spi_runtime_idle(&pdev->dev); -err_pm_disable: +exit_pm_disable: pm_runtime_disable(&pdev->dev); - if (tspi->tx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->tx_buf, tspi->tx_buf_phys); -fail_tx_buf_alloc: - if (tspi->tx_dma) - tegra_dma_free_channel(tspi->tx_dma); -fail_tx_dma_alloc: - if (tspi->rx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->rx_buf, tspi->rx_buf_phys); -fail_rx_buf_alloc: - if (tspi->rx_dma) - tegra_dma_free_channel(tspi->rx_dma); + spi_tegra_deinit_dma_param(tspi, false); + +exit_rx_dma_free: + spi_tegra_deinit_dma_param(tspi, true); exit_free_master: spi_master_put(master); @@ -1443,16 +1469,12 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) tspi = spi_master_get_devdata(master); spi_unregister_master(master); - if (tspi->tx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->tx_buf, tspi->tx_buf_phys); + if (tspi->tx_dma) - tegra_dma_free_channel(tspi->tx_dma); - if (tspi->rx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->rx_buf, tspi->rx_buf_phys); + spi_tegra_deinit_dma_param(tspi, false); + if (tspi->rx_dma) - tegra_dma_free_channel(tspi->rx_dma); + spi_tegra_deinit_dma_param(tspi, true); /* Disable clock if it is always enabled */ if (tspi->is_clkon_always) -- cgit v1.2.3 From 6101daf11fb99307e2b7491eea2b1bf39c8ff5d1 Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Fri, 25 May 2012 18:10:07 +0530 Subject: usb: ehci: tegra: fix remote wakeup issues This change fixes remote wakeup issues when usb line is in suspend state. Bug 989441 Bug 989400 Change-Id: I97982943d5521470b83ed87b83ab8703c4e9c260 Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/104746 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Venkat Moganty --- drivers/usb/host/ehci-tegra.c | 64 +++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 82523bd200a6..c5fa8160bffe 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -23,6 +23,12 @@ #include #include +#if 0 +#define EHCI_DBG(stuff...) pr_info("ehci-tegra: " stuff) +#else +#define EHCI_DBG(stuff...) do {} while (0) +#endif + static const char driver_name[] = "tegra-ehci"; #define TEGRA_USB_DMA_ALIGN 32 @@ -164,6 +170,11 @@ static irqreturn_t tegra_ehci_irq(struct usb_hcd *hcd) } spin_unlock(&ehci->lock); + EHCI_DBG("%s() cmd = 0x%x, int_sts = 0x%x, portsc = 0x%x\n", __func__, + ehci_readl(ehci, &ehci->regs->command), + ehci_readl(ehci, &ehci->regs->status), + ehci_readl(ehci, &ehci->regs->port_status[0])); + irq_status = ehci_irq(hcd); if (pmc_remote_wakeup) { @@ -172,8 +183,6 @@ static irqreturn_t tegra_ehci_irq(struct usb_hcd *hcd) if (ehci->controller_remote_wakeup) { ehci->controller_remote_wakeup = false; - /* disable interrupts */ - ehci_writel(ehci, 0, &ehci->regs->intr_enable); tegra_usb_phy_pre_resume(tegra->phy, true); tegra->port_resuming = 1; } @@ -202,33 +211,30 @@ static int tegra_ehci_hub_control( return retval; } - status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; - - spin_lock_irqsave(&ehci->lock, flags); /* Do tegra phy specific actions based on the type request */ switch (typeReq) { case GetPortStatus: - if (time_after_eq(jiffies, ehci->reset_done[wIndex - 1])) { - if (tegra->port_resuming) { - int delay = ehci->reset_done[wIndex-1] - jiffies; - /* Sometimes it seems we get called too soon... In that case, wait.*/ - if (delay > 0) { - ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); - mdelay(jiffies_to_msecs(delay)); - } - /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ - if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { - pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); - } - tegra_usb_phy_post_resume(tegra->phy); - tegra->port_resuming = 0; - /* If run bit is not set by now enable it */ - if (ehci->command & CMD_RUN) { - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, - &ehci->regs->command); - } + if (tegra->port_resuming) { + int delay = ehci->reset_done[wIndex-1] - jiffies; + /* Sometimes it seems we get called too soon... In that case, wait.*/ + if (delay > 0) { + ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); + mdelay(jiffies_to_msecs(delay)); } + status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; + /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ + if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { + pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); + } + tegra_usb_phy_post_resume(tegra->phy); + tegra->port_resuming = 0; + /* If run bit is not set by now enable it */ + if (ehci->command & CMD_RUN) { + ehci->command |= CMD_RUN; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + } + /* Now we can safely re-enable irqs */ + ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); } break; case ClearPortFeature: @@ -238,14 +244,12 @@ static int tegra_ehci_hub_control( } break; } - spin_unlock_irqrestore(&ehci->lock, flags); /* handle ehci hub control request */ retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); /* do tegra phy specific actions based on the type request */ if (!retval) { - spin_lock_irqsave(&ehci->lock, flags); switch (typeReq) { case SetPortFeature: if (wValue == USB_PORT_FEAT_SUSPEND) { @@ -261,7 +265,6 @@ static int tegra_ehci_hub_control( } break; } - spin_unlock_irqrestore(&ehci->lock, flags); } return retval; @@ -327,7 +330,7 @@ static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); int err = 0; - + EHCI_DBG("%s() BEGIN\n", __func__); mutex_lock(&tegra->sync_lock); tegra->bus_suspended_fail = false; err = ehci_bus_suspend(hcd); @@ -336,6 +339,7 @@ static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) else tegra_usb_phy_suspend(tegra->phy); mutex_unlock(&tegra->sync_lock); + EHCI_DBG("%s() END\n", __func__); return err; } @@ -344,11 +348,13 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); int err = 0; + EHCI_DBG("%s() BEGIN\n", __func__); mutex_lock(&tegra->sync_lock); tegra_usb_phy_resume(tegra->phy); err = ehci_bus_resume(hcd); mutex_unlock(&tegra->sync_lock); + EHCI_DBG("%s() END\n", __func__); return err; } -- cgit v1.2.3 From b6f12a3449234f9960998954382e1a9afea7764d Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Thu, 24 May 2012 14:18:28 +0300 Subject: video: tegra: nvmap: Remove nvhost specific APIs Remove nvmap_pin_array() and nvmap_patch_word() and their utility functions. Bug 965206 Change-Id: I217a427934b0b99b5252b33ab3ac4eaaa8c7e076 Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/104451 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Mayuresh Kulkarni GVS: Gerrit_Virtual_Submit Reviewed-by: Juha Tukkinen --- drivers/video/tegra/nvmap/nvmap.c | 268 -------------------------------------- 1 file changed, 268 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/nvmap/nvmap.c b/drivers/video/tegra/nvmap/nvmap.c index a0c4156668e5..b7fd695d04ee 100644 --- a/drivers/video/tegra/nvmap/nvmap.c +++ b/drivers/video/tegra/nvmap/nvmap.c @@ -352,225 +352,6 @@ static phys_addr_t handle_phys(struct nvmap_handle *h) return addr; } -/* stores the physical address (+offset) of each handle relocation entry - * into its output location. see nvmap_pin_array for more details. - * - * each entry in arr (i.e., each relocation request) specifies two handles: - * the handle to pin (pin), and the handle where the address of pin should be - * written (patch). in pseudocode, this loop basically looks like: - * - * for (i = 0; i < nr; i++) { - * (pin, pin_offset, patch, patch_offset) = arr[i]; - * patch[patch_offset] = address_of(pin) + pin_offset; - * } - */ -static int nvmap_reloc_pin_array(struct nvmap_client *client, - const struct nvmap_pinarray_elem *arr, - int nr, struct nvmap_handle *gather) -{ - struct nvmap_handle *last_patch = NULL; - unsigned int last_pfn = 0; - pte_t **pte; - void *addr; - int i; - - pte = nvmap_alloc_pte(client->dev, &addr); - if (IS_ERR(pte)) - return PTR_ERR(pte); - - for (i = 0; i < nr; i++) { - struct nvmap_handle *patch; - struct nvmap_handle *pin; - phys_addr_t reloc_addr; - phys_addr_t phys; - unsigned int pfn; - - /* all of the handles are validated and get'ted prior to - * calling this function, so casting is safe here */ - pin = (struct nvmap_handle *)arr[i].pin_mem; - - if (arr[i].patch_mem == (unsigned long)last_patch) { - patch = last_patch; - } else if (arr[i].patch_mem == (unsigned long)gather) { - patch = gather; - } else { - if (last_patch) - nvmap_handle_put(last_patch); - - patch = nvmap_get_handle_id(client, arr[i].patch_mem); - if (!patch) { - nvmap_free_pte(client->dev, pte); - return -EPERM; - } - last_patch = patch; - } - - if (patch->heap_pgalloc) { - unsigned int page = arr[i].patch_offset >> PAGE_SHIFT; - phys = page_to_phys(patch->pgalloc.pages[page]); - phys += (arr[i].patch_offset & ~PAGE_MASK); - } else { - phys = patch->carveout->base + arr[i].patch_offset; - } - - pfn = __phys_to_pfn(phys); - if (pfn != last_pfn) { - pgprot_t prot = nvmap_pgprot(patch, pgprot_kernel); - phys_addr_t kaddr = (phys_addr_t)addr; - set_pte_at(&init_mm, kaddr, *pte, pfn_pte(pfn, prot)); - flush_tlb_kernel_page(kaddr); - last_pfn = pfn; - } - - reloc_addr = handle_phys(pin) + arr[i].pin_offset; - reloc_addr >>= arr[i].reloc_shift; - __raw_writel(reloc_addr, addr + (phys & ~PAGE_MASK)); - } - - nvmap_free_pte(client->dev, pte); - - if (last_patch) - nvmap_handle_put(last_patch); - - wmb(); - - return 0; -} - -static int nvmap_validate_get_pin_array(struct nvmap_client *client, - const struct nvmap_pinarray_elem *arr, - int nr, struct nvmap_handle **h) -{ - int i; - int ret = 0; - int count = 0; - - nvmap_ref_lock(client); - - for (i = 0; i < nr; i++) { - struct nvmap_handle_ref *ref; - - if (need_resched()) { - nvmap_ref_unlock(client); - schedule(); - nvmap_ref_lock(client); - } - - ref = _nvmap_validate_id_locked(client, arr[i].pin_mem); - - if (!ref) - nvmap_warn(client, "falied to validate id\n"); - else if (!ref->handle) - nvmap_warn(client, "id had no associated handle\n"); - else if (!ref->handle->alloc) - nvmap_warn(client, "handle had no allocation\n"); - - if (!ref || !ref->handle || !ref->handle->alloc) { - ret = -EPERM; - break; - } - - /* a handle may be referenced multiple times in arr, but - * it will only be pinned once; this ensures that the - * minimum number of sync-queue slots in the host driver - * are dedicated to storing unpin lists, which allows - * for greater parallelism between the CPU and graphics - * processor */ - if (ref->handle->flags & NVMAP_HANDLE_VISITED) - continue; - - ref->handle->flags |= NVMAP_HANDLE_VISITED; - - h[count] = nvmap_handle_get(ref->handle); - BUG_ON(!h[count]); - count++; - } - - nvmap_ref_unlock(client); - - if (ret) { - for (i = 0; i < count; i++) { - h[i]->flags &= ~NVMAP_HANDLE_VISITED; - nvmap_handle_put(h[i]); - } - } - - return ret ?: count; -} - -/* a typical mechanism host1x clients use for using the Tegra graphics - * processor is to build a command buffer which contains relocatable - * memory handle commands, and rely on the kernel to convert these in-place - * to addresses which are understood by the GPU hardware. - * - * this is implemented by having clients provide a sideband array - * of relocatable handles (+ offsets) and the location in the command - * buffer handle to patch with the GPU address when the client submits - * its command buffer to the host1x driver. - * - * the host driver also uses this relocation mechanism internally to - * relocate the client's (unpinned) command buffers into host-addressable - * memory. - * - * @client: nvmap_client which should be used for validation; should be - * owned by the process which is submitting command buffers - * @gather: special handle for relocated command buffer outputs used - * internally by the host driver. if this handle is encountered - * as an output handle in the relocation array, it is assumed - * to be a known-good output and is not validated. - * @arr: array of ((relocatable handle, offset), (output handle, offset)) - * tuples. - * @nr: number of entries in arr - * @unique_arr: list of nvmap_handle objects which were pinned by - * nvmap_pin_array. must be unpinned by the caller after the - * command buffers referenced in gather have completed. - */ -int nvmap_pin_array(struct nvmap_client *client, struct nvmap_handle *gather, - const struct nvmap_pinarray_elem *arr, int nr, - struct nvmap_handle **unique_arr) -{ - int count = 0; - int ret = 0; - int i; - - if (mutex_lock_interruptible(&client->share->pin_lock)) { - nvmap_warn(client, "%s interrupted when acquiring pin lock\n", - current->group_leader->comm); - return -EINTR; - } - - count = nvmap_validate_get_pin_array(client, arr, nr, unique_arr); - if (count < 0) { - mutex_unlock(&client->share->pin_lock); - nvmap_warn(client, "failed to validate pin array\n"); - return count; - } - - for (i = 0; i < count; i++) - unique_arr[i]->flags &= ~NVMAP_HANDLE_VISITED; - - ret = wait_pin_array_locked(client, unique_arr, count); - - mutex_unlock(&client->share->pin_lock); - - if (!ret) - ret = nvmap_reloc_pin_array(client, arr, nr, gather); - - if (WARN_ON(ret)) { - for (i = 0; i < count; i++) - nvmap_handle_put(unique_arr[i]); - return ret; - } else { - for (i = 0; i < count; i++) { - if (unique_arr[i]->heap_pgalloc && - unique_arr[i]->pgalloc.dirty) - map_iovmm_area(unique_arr[i]); - } - } - - return count; -} - phys_addr_t nvmap_pin(struct nvmap_client *client, struct nvmap_handle_ref *ref) { @@ -820,52 +601,3 @@ void nvmap_free(struct nvmap_client *client, struct nvmap_handle_ref *r) nvmap_free_handle_id(client, nvmap_ref_to_id(r)); } - -/* - * create a mapping to the user's buffer and write it - * (uses similar logic from nvmap_reloc_pin_array to map the cmdbuf) - */ -int nvmap_patch_word(struct nvmap_client *client, - struct nvmap_handle *patch, - u32 patch_offset, u32 patch_value) -{ - phys_addr_t phys; - unsigned long kaddr; - unsigned int pfn; - void *addr; - pte_t **pte; - pgprot_t prot; - - if (patch_offset >= patch->size) { - nvmap_warn(client, "read/write outside of handle\n"); - return -EFAULT; - } - - pte = nvmap_alloc_pte(client->dev, &addr); - if (IS_ERR(pte)) - return PTR_ERR(pte); - - /* derive physaddr of cmdbuf WAIT to patch */ - if (patch->heap_pgalloc) { - unsigned int page = patch_offset >> PAGE_SHIFT; - phys = page_to_phys(patch->pgalloc.pages[page]); - phys += (patch_offset & ~PAGE_MASK); - } else { - phys = patch->carveout->base + patch_offset; - } - - pfn = __phys_to_pfn(phys); - prot = nvmap_pgprot(patch, pgprot_kernel); - kaddr = (unsigned long)addr; - - /* write PTE, so addr points to cmdbuf PFN */ - set_pte_at(&init_mm, kaddr, *pte, pfn_pte(pfn, prot)); - flush_tlb_kernel_page(kaddr); - - /* write patch_value to addr + page offset */ - __raw_writel(patch_value, addr + (phys & ~PAGE_MASK)); - - nvmap_free_pte(client->dev, pte); - wmb(); - return 0; -} -- cgit v1.2.3 From bc056c017381a85acf2d41a1e537f45e04ea297a Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Mon, 28 May 2012 11:56:54 +0300 Subject: video: tegra: host: Throttle lower priority jobs Implement per channel counter for jobs in each priority level. If there are jobs active with higher priority than the one being submitted, throttle. Bug 926690 Change-Id: I5fed341e3f248325873b31d1c53bf57bf0a78074 Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/104939 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Juha Tukkinen Reviewed-by: Mayuresh Kulkarni --- drivers/video/tegra/host/bus_client.c | 6 ------ drivers/video/tegra/host/nvhost_cdma.c | 33 +++++++++++++++++++++++++++++++ drivers/video/tegra/host/nvhost_cdma.h | 3 +++ drivers/video/tegra/host/nvhost_channel.c | 24 ++++++++++++++++++---- drivers/video/tegra/host/nvhost_intr.c | 10 +++++++--- 5 files changed, 63 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index 9b71542a48aa..87aa9c64d363 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -141,12 +141,6 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp) priv->clientid = atomic_add_return(1, &nvhost_get_host(ch->dev)->clientid); priv->timeout = MAX_STUCK_CHECK_COUNT * SYNCPT_CHECK_PERIOD; - - priv->job = nvhost_job_alloc(ch, priv->hwctx, &priv->hdr, - NULL, priv->priority, priv->clientid); - if (!priv->job) - goto fail; - return 0; fail: nvhost_channelrelease(inode, filp); diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index b1f138317cc1..c87415bf5ac2 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -53,6 +53,18 @@ static void add_to_sync_queue(struct nvhost_cdma *cdma, job->num_slots = nr_slots; nvhost_job_get(job); list_add_tail(&job->list, &cdma->sync_queue); + + switch (job->priority) { + case NVHOST_PRIORITY_HIGH: + cdma->high_prio_count++; + break; + case NVHOST_PRIORITY_MEDIUM: + cdma->med_prio_count++; + break; + case NVHOST_PRIORITY_LOW: + cdma->low_prio_count++; + break; + } } /** @@ -200,6 +212,19 @@ static void update_cdma_locked(struct nvhost_cdma *cdma) } list_del(&job->list); + + switch (job->priority) { + case NVHOST_PRIORITY_HIGH: + cdma->high_prio_count--; + break; + case NVHOST_PRIORITY_MEDIUM: + cdma->med_prio_count--; + break; + case NVHOST_PRIORITY_LOW: + cdma->low_prio_count--; + break; + } + nvhost_job_put(job); } @@ -466,6 +491,12 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, if (job->timeout && was_idle) cdma_start_timer_locked(cdma, job); + trace_nvhost_cdma_end(job->ch->dev->name, + job->priority, + job->ch->cdma.high_prio_count, + job->ch->cdma.med_prio_count, + job->ch->cdma.low_prio_count); + mutex_unlock(&cdma->lock); } @@ -490,6 +521,8 @@ int nvhost_cdma_flush(struct nvhost_cdma *cdma, int timeout) unsigned int space, err = 0; unsigned long end_jiffies = jiffies + msecs_to_jiffies(timeout); + trace_nvhost_cdma_flush(cdma_to_channel(cdma)->dev->name, timeout); + /* * Wait for at most timeout ms. Recalculate timeout at each iteration * to better keep within given timeout. diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h index 98393f0cc765..2056774a7bc7 100644 --- a/drivers/video/tegra/host/nvhost_cdma.h +++ b/drivers/video/tegra/host/nvhost_cdma.h @@ -99,6 +99,9 @@ struct nvhost_cdma { struct buffer_timeout timeout; /* channel's timeout state/wq */ bool running; bool torndown; + int high_prio_count; + int med_prio_count; + int low_prio_count; }; #define cdma_to_channel(cdma) container_of(cdma, struct nvhost_channel, cdma) diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c index ef8886fe4652..ad303cf0a22d 100644 --- a/drivers/video/tegra/host/nvhost_channel.c +++ b/drivers/video/tegra/host/nvhost_channel.c @@ -51,10 +51,26 @@ int nvhost_channel_init(struct nvhost_channel *ch, int nvhost_channel_submit(struct nvhost_job *job) { - /* Low priority submits wait until sync queue is empty. Ignores result - * from nvhost_cdma_flush, as we submit either when push buffer is - * empty or when we reach the timeout. */ - if (job->priority < NVHOST_PRIORITY_MEDIUM) + /* + * Check if queue has higher priority jobs running. If so, wait until + * queue is empty. Ignores result from nvhost_cdma_flush, as we submit + * either when push buffer is empty or when we reach the timeout. + */ + int higher_count = 0; + + switch (job->priority) { + case NVHOST_PRIORITY_HIGH: + higher_count = 0; + break; + case NVHOST_PRIORITY_MEDIUM: + higher_count = job->ch->cdma.high_prio_count; + break; + case NVHOST_PRIORITY_LOW: + higher_count = job->ch->cdma.high_prio_count + + job->ch->cdma.med_prio_count; + break; + } + if (higher_count > 0) (void)nvhost_cdma_flush(&job->ch->cdma, NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT); diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c index ba821f694cb4..af2e3ad1bdb5 100644 --- a/drivers/video/tegra/host/nvhost_intr.c +++ b/drivers/video/tegra/host/nvhost_intr.c @@ -128,12 +128,16 @@ static void action_submit_complete(struct nvhost_waitlist *waiter) struct nvhost_channel *channel = waiter->data; int nr_completed = waiter->count; + nvhost_cdma_update(&channel->cdma); + nvhost_module_idle_mult(channel->dev, nr_completed); + /* Add nr_completed to trace */ trace_nvhost_channel_submit_complete(channel->dev->name, - nr_completed, waiter->thresh); + nr_completed, waiter->thresh, + channel->cdma.high_prio_count, + channel->cdma.med_prio_count, + channel->cdma.low_prio_count); - nvhost_cdma_update(&channel->cdma); - nvhost_module_idle_mult(channel->dev, nr_completed); } static void action_ctxsave(struct nvhost_waitlist *waiter) -- cgit v1.2.3 From aef1c84834f5228316d1e096434abf98ced90eb6 Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Mon, 28 May 2012 14:06:05 +0300 Subject: video: tegra: host: Dump sync queue in debug dump Dump contents of sync queue when debug dump is requested, either because of a stuck sync point, or when the debugfs entry is accessed. Exclude FIFO from normal debugfs dump. Dumping FIFO is invasive and actually changes how the channel behaves. It's safe to dump only when system is in fault condition. Change-Id: I12c68e8186acd7bd17e4ab52b2589f765396ed17 Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/104970 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Juha Tukkinen --- drivers/video/tegra/host/debug.c | 67 +++++++++++++- drivers/video/tegra/host/host1x/host1x_debug.c | 117 +++++++++++++++---------- 2 files changed, 134 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c index 8a26f92c79f6..820eac85521d 100644 --- a/drivers/video/tegra/host/debug.c +++ b/drivers/video/tegra/host/debug.c @@ -106,13 +106,53 @@ static void show_all(struct nvhost_master *m, struct output *o) nvhost_get_chip_ops()->debug.show_mlocks(m, o); show_syncpts(m, o); nvhost_debug_output(o, "---- channels ----\n"); - bus_for_each_dev(&(nvhost_bus_get())->nvhost_bus_type, NULL, o, show_channels); + bus_for_each_dev(&(nvhost_bus_get())->nvhost_bus_type, NULL, o, + show_channels); nvhost_module_idle(m->dev); } #ifdef CONFIG_DEBUG_FS -static int nvhost_debug_show(struct seq_file *s, void *unused) +static int show_channels_no_fifo(struct device *dev, void *data) +{ + struct nvhost_channel *ch; + struct nvhost_device *nvdev = to_nvhost_device(dev); + struct output *o = data; + struct nvhost_master *m; + + if (nvdev == NULL) + return 0; + + m = nvhost_get_host(nvdev); + ch = nvdev->channel; + if (ch) { + mutex_lock(&ch->reflock); + if (ch->refcount) { + mutex_lock(&ch->cdma.lock); + nvhost_get_chip_ops()->debug.show_channel_cdma(m, + ch, o, nvdev->index); + mutex_unlock(&ch->cdma.lock); + } + mutex_unlock(&ch->reflock); + } + + return 0; +} + +static void show_all_no_fifo(struct nvhost_master *m, struct output *o) +{ + nvhost_module_busy(m->dev); + + nvhost_get_chip_ops()->debug.show_mlocks(m, o); + show_syncpts(m, o); + nvhost_debug_output(o, "---- channels ----\n"); + bus_for_each_dev(&(nvhost_bus_get())->nvhost_bus_type, NULL, o, + show_channels_no_fifo); + + nvhost_module_idle(m->dev); +} + +static int nvhost_debug_show_all(struct seq_file *s, void *unused) { struct output o = { .fn = write_to_seqfile, @@ -121,6 +161,27 @@ static int nvhost_debug_show(struct seq_file *s, void *unused) show_all(s->private, &o); return 0; } +static int nvhost_debug_show(struct seq_file *s, void *unused) +{ + struct output o = { + .fn = write_to_seqfile, + .ctx = s + }; + show_all_no_fifo(s->private, &o); + return 0; +} + +static int nvhost_debug_open_all(struct inode *inode, struct file *file) +{ + return single_open(file, nvhost_debug_show_all, inode->i_private); +} + +static const struct file_operations nvhost_debug_all_fops = { + .open = nvhost_debug_open_all, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; static int nvhost_debug_open(struct inode *inode, struct file *file) { @@ -140,6 +201,8 @@ void nvhost_debug_init(struct nvhost_master *master) debugfs_create_file("status", S_IRUGO, de, master, &nvhost_debug_fops); + debugfs_create_file("status_all", S_IRUGO, de, + master, &nvhost_debug_all_fops); debugfs_create_u32("null_kickoff_pid", S_IRUGO|S_IWUSR, de, &nvhost_debug_null_kickoff_pid); diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c index a5574a0fb60a..7de342298c4d 100644 --- a/drivers/video/tegra/host/host1x/host1x_debug.c +++ b/drivers/video/tegra/host/host1x/host1x_debug.c @@ -28,8 +28,8 @@ #include "host1x_hardware.h" #include "nvhost_cdma.h" #include "nvhost_channel.h" -#include "../../nvmap/nvmap.h" #include "host1x_cdma.h" +#include "nvhost_job.h" #define NVHOST_DEBUG_MAX_PAGE_OFFSET 102400 @@ -160,6 +160,34 @@ static void show_channel_word(struct output *o, int *state, int *count, } } +static void do_show_channel_gather(struct output *o, + phys_addr_t phys_addr, + u32 words, struct nvhost_cdma *cdma, + phys_addr_t pin_addr, u32 *map_addr) +{ + /* Map dmaget cursor to corresponding nvmap_handle */ + u32 offset; + int state, count, i; + + offset = phys_addr - pin_addr; + /* + * Sometimes we're given different hardware address to the same + * page - in these cases the offset will get an invalid number and + * we just have to bail out. + */ + if (offset > NVHOST_DEBUG_MAX_PAGE_OFFSET) { + nvhost_debug_output(o, "[address mismatch]\n"); + } else { + /* GATHER buffer starts always with commands */ + state = NVHOST_DBG_STATE_CMD; + for (i = 0; i < words; i++) + show_channel_word(o, &state, &count, + phys_addr + i * 4, + *(map_addr + offset/4 + i), + cdma); + } +} + static void show_channel_gather(struct output *o, u32 addr, phys_addr_t phys_addr, u32 words, struct nvhost_cdma *cdma) @@ -171,10 +199,8 @@ static void show_channel_gather(struct output *o, u32 addr, struct nvmap_client_handle *nvmap = &pb->nvmap[cur/8]; u32 *map_addr, offset; phys_addr_t pin_addr; - int state, count, i; - if (!nvmap->handle || !nvmap->client - || atomic_read(&nvmap->handle->handle->ref) < 1) { + if (!nvmap || !nvmap->handle || !nvmap->client) { nvhost_debug_output(o, "[already deallocated]\n"); return; } @@ -194,51 +220,13 @@ static void show_channel_gather(struct output *o, u32 addr, } offset = phys_addr - pin_addr; - /* - * Sometimes we're given different hardware address to the same - * page - in these cases the offset will get an invalid number and - * we just have to bail out. - */ - if (offset > NVHOST_DEBUG_MAX_PAGE_OFFSET) { - nvhost_debug_output(o, "[address mismatch]\n"); - } else { - /* GATHER buffer starts always with commands */ - state = NVHOST_DBG_STATE_CMD; - for (i = 0; i < words; i++) - show_channel_word(o, &state, &count, - phys_addr + i * 4, - *(map_addr + offset/4 + i), - cdma); - } + do_show_channel_gather(o, phys_addr, words, cdma, + pin_addr, map_addr); nvmap_unpin(nvmap->client, nvmap->handle); nvmap_munmap(nvmap->handle, map_addr); #endif } -static void show_channel_pair(struct output *o, u32 addr, - u32 w0, u32 w1, struct nvhost_cdma *cdma) -{ - int state = NVHOST_DBG_STATE_CMD; - int count; - - show_channel_word(o, &state, &count, addr, w0, cdma); - show_channel_word(o, &state, &count, addr+4, w1, cdma); -} - -/** - * Retrieve the op pair at a slot offset from a DMA address - */ -static void cdma_peek(struct nvhost_cdma *cdma, - u32 dmaget, int slot, u32 *out) -{ - u32 offset = dmaget - cdma->push_buffer.phys; - u32 *p = cdma->push_buffer.mapped; - - offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; - out[0] = p[offset]; - out[1] = p[offset + 1]; -} - u32 previous_oppair(struct nvhost_cdma *cdma, u32 cur) { u32 pb = cdma->push_buffer.phys; @@ -248,6 +236,42 @@ u32 previous_oppair(struct nvhost_cdma *cdma, u32 cur) return prev; } +void show_channel_gathers(struct output *o, struct nvhost_cdma *cdma) +{ + struct nvhost_job *job; + + list_for_each_entry(job, &cdma->sync_queue, list) { + int i; + nvhost_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d," + " first_get=%08x, timeout=%d, ctx=%p," + " num_slots=%d, num_handles=%d\n", + job, + job->syncpt_id, + job->syncpt_end, + job->first_get, + job->timeout, + job->hwctx, + job->num_slots, + job->num_unpins); + + for (i = 0; i < job->num_gathers; i++) { + struct nvhost_job_gather *g = &job->gathers[i]; + u32 *mapped = nvmap_mmap(g->ref); + if (!mapped) { + nvhost_debug_output(o, "[could not mmap]\n"); + continue; + } + + nvhost_debug_output(o, " GATHER at %08x, %d words\n", + g->mem, g->words); + + do_show_channel_gather(o, g->mem + g->offset, + g->words, cdma, g->mem, mapped); + nvmap_munmap(g->ref, mapped); + } + } +} + static void t20_debug_show_channel_cdma(struct nvhost_master *m, struct nvhost_channel *ch, struct output *o, int chid) { @@ -256,7 +280,6 @@ static void t20_debug_show_channel_cdma(struct nvhost_master *m, u32 dmaput, dmaget, dmactrl; u32 cbstat, cbread; u32 val, base, baseval; - u32 pbw[2]; dmaput = readl(channel->aperture + HOST1X_CHANNEL_DMAPUT); dmaget = readl(channel->aperture + HOST1X_CHANNEL_DMAGET); @@ -305,9 +328,7 @@ static void t20_debug_show_channel_cdma(struct nvhost_master *m, dmaput, dmaget, dmactrl); nvhost_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat); - cdma_peek(cdma, dmaget, -1, pbw); - show_channel_pair(o, previous_oppair(cdma, dmaget), - pbw[0], pbw[1], &channel->cdma); + show_channel_gathers(o, cdma); nvhost_debug_output(o, "\n"); } -- cgit v1.2.3 From 58f7137049f6e7ad1d41fda34f68753de89bee39 Mon Sep 17 00:00:00 2001 From: naveenk Date: Fri, 25 May 2012 15:06:34 +0530 Subject: sdhci: tegra: Do not exceed platform voltage limits check for platform limits before setting the voltage Bug 979504 Change-Id: Iea78be15d6a0eea0f4344c9b78ff9366f4759af8 Signed-off-by: naveen kumar arepalli Reviewed-on: http://git-master/r/104711 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Pavan Kunapuli Reviewed-by: Sachin Nikam Reviewed-by: Bharat Nihalani --- drivers/mmc/host/sdhci-tegra.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 8fec147471a8..609fd6391d1f 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -545,11 +545,6 @@ static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci, u16 clk, ctrl; unsigned int val; - /* Switch OFF the card clock to prevent glitches on the clock line */ - clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL); - clk &= ~SDHCI_CLOCK_CARD_EN; - sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL); - ctrl = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2); if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) { ctrl |= SDHCI_CTRL_VDD_180; @@ -559,6 +554,17 @@ static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci, if (ctrl & SDHCI_CTRL_VDD_180) ctrl &= ~SDHCI_CTRL_VDD_180; } + + /* Check if the slot can support the required voltage */ + if (min_uV > tegra_host->vddio_max_uv) + return 0; + + /* Switch OFF the card clock to prevent glitches on the clock line */ + clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL); + + /* Set/clear the 1.8V signalling */ sdhci_writew(sdhci, ctrl, SDHCI_HOST_CONTROL2); /* Switch the I/O rail voltage */ -- cgit v1.2.3 From ecfc4f641d3bc4491170d6a5dd212ff587683bfa Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 7 Mar 2012 12:11:41 +0530 Subject: i2c: tegra: Fix possible race condition. on tegra3, the i2c communication start immediately after writing the tx fifo. And hence there is possibility to complete the transfer and generates done interrupt before actually sw updates their local pointers/count. This patch will make sure that pointers/count can get updated before data written into the fifo. Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/89510 (cherry picked from commit 999c09f0ed32f271e767a319dd094947e63fdb8c) Change-Id: I8e974b83b5306ec3363d4ca31ce1b539a498ca08 Signed-off-by: Johnny Qiu Reviewed-on: http://git-master/r/99997 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/i2c/busses/i2c-tegra.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 96916bed997f..1358dc70b0e4 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -331,12 +331,19 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) { u32 val; int tx_fifo_avail; - u8 *buf = i2c_dev->msg_buf; - size_t buf_remaining = i2c_dev->msg_buf_remaining; + u8 *buf; + size_t buf_remaining; int words_to_transfer; unsigned long flags; spin_lock_irqsave(&i2c_dev->fifo_lock, flags); + if (!i2c_dev->msg_buf_remaining) { + spin_unlock_irqrestore(&i2c_dev->fifo_lock, flags); + return 0; + } + + buf = i2c_dev->msg_buf; + buf_remaining = i2c_dev->msg_buf_remaining; val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >> @@ -375,7 +382,12 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) * boundary and fault. */ if (tx_fifo_avail > 0 && buf_remaining > 0) { - BUG_ON(buf_remaining > 3); + if (buf_remaining > 3) { + dev_err(i2c_dev->dev, + "Remaining buffer more than 3 %d\n", + buf_remaining); + BUG(); + } memcpy(&val, buf, buf_remaining); /* Again update before writing to FIFO to make sure isr sees. */ -- cgit v1.2.3