From 0ad7c00057dc1640647c1dc81ccbd009de17a767 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 26 Nov 2013 10:04:22 -0700 Subject: dma: add channel request API that supports deferred probe dma_request_slave_channel() simply returns NULL whenever DMA channel lookup fails. Lookup could fail for two distinct reasons: a) No DMA specification exists for the channel name. This includes situations where no DMA specifications exist at all, or other general lookup problems. b) A DMA specification does exist, yet the driver for that channel is not yet registered. Case (b) should trigger deferred probe in client drivers. However, since they have no way to differentiate the two situations, it cannot. Implement new function dma_request_slave_channel_reason(), which performs identically to dma_request_slave_channel(), except that it returns an error-pointer rather than NULL, which allows callers to detect when deferred probe should occur. Eventually, all drivers should be converted to this new API, the old API removed, and the new API renamed to the more desirable name. This patch doesn't convert the existing API and all drivers in one go, since some drivers call dma_request_slave_channel() then dma_request_channel() if that fails. That would require either modifying dma_request_channel() in the same way, or adding extra error-handling code to all affected drivers, and there are close to 100 drivers using the other API, rather than just the 15-20 or so that use dma_request_slave_channel(), which might be tenable in a single patch. acpi_dma_request_slave_chan_by_name() doesn't currently implement deferred probe. It should, but this will be addressed later. Acked-by: Dan Williams Signed-off-by: Stephen Warren Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 41cf0c399288..ed92b30a02fd 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -22,6 +22,7 @@ #define LINUX_DMAENGINE_H #include +#include #include #include #include @@ -1040,6 +1041,8 @@ enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); void dma_issue_pending_all(void); struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param); +struct dma_chan *dma_request_slave_channel_reason(struct device *dev, + const char *name); struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); void dma_release_channel(struct dma_chan *chan); #else @@ -1063,6 +1066,11 @@ static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, { return NULL; } +static inline struct dma_chan *dma_request_slave_channel_reason( + struct device *dev, const char *name) +{ + return ERR_PTR(-ENODEV); +} static inline struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name) { -- cgit v1.2.3-71-gd317 From 8010dad55a0ab0e829f3733854e5235eef4e2734 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 26 Nov 2013 12:40:51 -0700 Subject: dma: add dma_get_any_slave_channel(), for use in of_xlate() mmp_pdma.c implements a custom of_xlate() function that is 95% identical to what Tegra will need. Create a function to implement the common part, so everyone doesn't just cut/paste the implementation. Cc: Dan Williams Cc: Vinod Koul Cc: Lars-Peter Clausen Cc: dmaengine@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 28 ++++++++++++++++++++++++++++ drivers/dma/mmp_pdma.c | 30 +++++++----------------------- include/linux/dmaengine.h | 1 + 3 files changed, 36 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index ea806bdc12ef..4f08ee8c17b4 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -535,6 +535,34 @@ struct dma_chan *dma_get_slave_channel(struct dma_chan *chan) } EXPORT_SYMBOL_GPL(dma_get_slave_channel); +struct dma_chan *dma_get_any_slave_channel(struct dma_device *device) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + int err; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* lock against __dma_request_channel */ + mutex_lock(&dma_list_mutex); + + chan = private_candidate(&mask, device, NULL, NULL); + if (chan) { + err = dma_chan_get(chan); + if (err) { + pr_debug("%s: failed to get %s: (%d)\n", + __func__, dma_chan_name(chan), err); + chan = NULL; + } + } + + mutex_unlock(&dma_list_mutex); + + return chan; +} +EXPORT_SYMBOL_GPL(dma_get_any_slave_channel); + /** * __dma_request_channel - try to allocate an exclusive channel * @mask: capabilities that the channel must satisfy diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index dcb1e05149a7..2998f1bffac1 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -893,33 +893,17 @@ static struct dma_chan *mmp_pdma_dma_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { struct mmp_pdma_device *d = ofdma->of_dma_data; - struct dma_chan *chan, *candidate; + struct dma_chan *chan; + struct mmp_pdma_chan *c; -retry: - candidate = NULL; - - /* walk the list of channels registered with the current instance and - * find one that is currently unused */ - list_for_each_entry(chan, &d->device.channels, device_node) - if (chan->client_count == 0) { - candidate = chan; - break; - } - - if (!candidate) + chan = dma_get_any_slave_channel(&d->device); + if (!chan) return NULL; - /* dma_get_slave_channel will return NULL if we lost a race between - * the lookup and the reservation */ - chan = dma_get_slave_channel(candidate); - - if (chan) { - struct mmp_pdma_chan *c = to_mmp_pdma_chan(chan); - c->drcmr = dma_spec->args[0]; - return chan; - } + c = to_mmp_pdma_chan(chan); + c->drcmr = dma_spec->args[0]; - goto retry; + return chan; } static int mmp_pdma_probe(struct platform_device *op) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 41cf0c399288..09ef23ee8bce 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1079,6 +1079,7 @@ int dma_async_device_register(struct dma_device *device); void dma_async_device_unregister(struct dma_device *device); void dma_run_dependencies(struct dma_async_tx_descriptor *tx); struct dma_chan *dma_get_slave_channel(struct dma_chan *chan); +struct dma_chan *dma_get_any_slave_channel(struct dma_device *device); struct dma_chan *net_dma_find_channel(void); #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y) #define dma_request_slave_channel_compat(mask, x, y, dev, name) \ -- cgit v1.2.3-71-gd317 From 80b28791ff0416a472e5a555a4b6f5f39df2fc24 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 6 Nov 2013 15:45:46 -0700 Subject: ARM: tegra: pass reset to tegra_powergate_sequence_power_up() Tegra's clock driver now provides an implementation of the common reset API (include/linux/reset.h). Use this instead of the old Tegra- specific API; that will soon be removed. Signed-off-by: Stephen Warren Acked-by: Bjorn Helgaas Acked-By: Terje Bergstrom Reviewed-by: Thierry Reding Acked-by: Thierry Reding --- arch/arm/mach-tegra/powergate.c | 8 +++++--- drivers/gpu/drm/tegra/gr3d.c | 6 ++++-- drivers/pci/host/pci-tegra.c | 3 ++- include/linux/tegra-powergate.h | 7 +++++-- 4 files changed, 16 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 85d28e756bb7..f6f5b54ff95e 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -144,11 +145,12 @@ int tegra_powergate_remove_clamping(int id) } /* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk) +int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst) { int ret; - tegra_periph_reset_assert(clk); + reset_control_assert(rst); ret = tegra_powergate_power_on(id); if (ret) @@ -165,7 +167,7 @@ int tegra_powergate_sequence_power_up(int id, struct clk *clk) goto err_clamp; udelay(10); - tegra_periph_reset_deassert(clk); + reset_control_deassert(rst); return 0; diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index f629e38b00e4..0cbb24b1ae04 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -279,7 +279,8 @@ static int gr3d_probe(struct platform_device *pdev) } } - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk); + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk, + gr3d->rst); if (err < 0) { dev_err(&pdev->dev, "failed to power up 3D unit\n"); return err; @@ -287,7 +288,8 @@ static int gr3d_probe(struct platform_device *pdev) if (gr3d->clk_secondary) { err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D1, - gr3d->clk_secondary); + gr3d->clk_secondary, + gr3d->rst_secondary); if (err < 0) { dev_err(&pdev->dev, "failed to power up secondary 3D unit\n"); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 6681c3182c85..0175041ab728 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -955,7 +955,8 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) } err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, - pcie->pex_clk); + pcie->pex_clk, + pcie->pex_rst); if (err) { dev_err(pcie->dev, "powerup sequence failed: %d\n", err); return err; diff --git a/include/linux/tegra-powergate.h b/include/linux/tegra-powergate.h index fd4498329c7c..afe442d2629a 100644 --- a/include/linux/tegra-powergate.h +++ b/include/linux/tegra-powergate.h @@ -19,6 +19,7 @@ #define _MACH_TEGRA_POWERGATE_H_ struct clk; +struct reset_control; #define TEGRA_POWERGATE_CPU 0 #define TEGRA_POWERGATE_3D 1 @@ -52,7 +53,8 @@ int tegra_powergate_power_off(int id); int tegra_powergate_remove_clamping(int id); /* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk); +int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst); #else static inline int tegra_powergate_is_powered(int id) { @@ -74,7 +76,8 @@ static inline int tegra_powergate_remove_clamping(int id) return -ENOSYS; } -static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk) +static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst); { return -ENOSYS; } -- cgit v1.2.3-71-gd317 From 2ae77527bb1a510070d039aaa22d1ae9a5807b6f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 6 Nov 2013 16:58:16 -0700 Subject: clk: tegra: remove legacy reset APIs Now that no code uses the custom Tegra module reset API, we can remove its implementation. Signed-off-by: Stephen Warren Reviewed-by: Thierry Reding Acked-By: Peter De Schrijver --- drivers/clk/tegra/clk-periph-gate.c | 22 -------------------- drivers/clk/tegra/clk-periph.c | 40 ------------------------------------- drivers/clk/tegra/clk.h | 1 - include/linux/clk/tegra.h | 7 ------- 4 files changed, 70 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index f38f33e3c65d..507015314827 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -36,8 +36,6 @@ static DEFINE_SPINLOCK(periph_ref_lock); #define read_rst(gate) \ readl_relaxed(gate->clk_base + (gate->regs->rst_reg)) -#define write_rst_set(val, gate) \ - writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg)) #define write_rst_clr(val, gate) \ writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg)) @@ -123,26 +121,6 @@ static void clk_periph_disable(struct clk_hw *hw) spin_unlock_irqrestore(&periph_ref_lock, flags); } -void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert) -{ - if (gate->flags & TEGRA_PERIPH_NO_RESET) - return; - - if (assert) { - /* - * If peripheral is in the APB bus then read the APB bus to - * flush the write operation in apb bus. This will avoid the - * peripheral access after disabling clock - */ - if (gate->flags & TEGRA_PERIPH_ON_APB) - tegra_read_chipid(); - - write_rst_set(periph_clk_to_bit(gate), gate); - } else { - write_rst_clr(periph_clk_to_bit(gate), gate); - } -} - const struct clk_ops tegra_clk_periph_gate_ops = { .is_enabled = clk_periph_is_enabled, .enable = clk_periph_enable, diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index d62b396863c1..c534043c0481 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -111,46 +111,6 @@ static void clk_periph_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } -void tegra_periph_reset_deassert(struct clk *c) -{ - struct clk_hw *hw = __clk_get_hw(c); - struct tegra_clk_periph *periph = to_clk_periph(hw); - struct tegra_clk_periph_gate *gate; - - if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) { - gate = to_clk_periph_gate(hw); - if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) { - WARN_ON(1); - return; - } - } else { - gate = &periph->gate; - } - - tegra_periph_reset(gate, 0); -} -EXPORT_SYMBOL(tegra_periph_reset_deassert); - -void tegra_periph_reset_assert(struct clk *c) -{ - struct clk_hw *hw = __clk_get_hw(c); - struct tegra_clk_periph *periph = to_clk_periph(hw); - struct tegra_clk_periph_gate *gate; - - if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) { - gate = to_clk_periph_gate(hw); - if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) { - WARN_ON(1); - return; - } - } else { - gate = &periph->gate; - } - - tegra_periph_reset(gate, 1); -} -EXPORT_SYMBOL(tegra_periph_reset_assert); - const struct clk_ops tegra_clk_periph_ops = { .get_parent = clk_periph_get_parent, .set_parent = clk_periph_set_parent, diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 07c62f997371..16ec8d6bb87f 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -393,7 +393,6 @@ struct tegra_clk_periph_gate { #define TEGRA_PERIPH_NO_DIV BIT(4) #define TEGRA_PERIPH_NO_GATE BIT(5) -void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert); extern const struct clk_ops tegra_clk_periph_gate_ops; struct clk *tegra_clk_register_periph_gate(const char *name, const char *parent_name, u8 gate_flags, void __iomem *clk_base, diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h index 23a0ceee831f..3ca9fca827a2 100644 --- a/include/linux/clk/tegra.h +++ b/include/linux/clk/tegra.h @@ -120,13 +120,6 @@ static inline void tegra_cpu_clock_resume(void) } #endif -#ifdef CONFIG_ARCH_TEGRA -void tegra_periph_reset_deassert(struct clk *c); -void tegra_periph_reset_assert(struct clk *c); -#else -static inline void tegra_periph_reset_deassert(struct clk *c) {} -static inline void tegra_periph_reset_assert(struct clk *c) {} -#endif void tegra_clocks_apply_init_table(void); #endif /* __LINUX_CLK_TEGRA_H_ */ -- cgit v1.2.3-71-gd317 From 9a716579660aa87a4ab4b4d1cac0686fba3707ba Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Dec 2013 17:31:03 +0100 Subject: ARM: tegra: Add Tegra124 powergate support Three new gates have been added for Tegra124: SOR, VIC and IRAM. In addition, PCIe and SATA gates are again supported, like on Tegra20 and Tegra30. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 42 +++++++++++++++++++++++++++++++++++++++++ include/linux/tegra-powergate.h | 3 +++ 2 files changed, 45 insertions(+) (limited to 'include/linux') diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index a67c92acb4a6..a8ac634eda77 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -59,6 +59,13 @@ static const u8 tegra114_cpu_domains[] = { TEGRA_POWERGATE_CPU3, }; +static const u8 tegra124_cpu_domains[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + static DEFINE_SPINLOCK(tegra_powergate_lock); static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); @@ -206,6 +213,11 @@ int __init tegra_powergate_init(void) tegra_num_cpu_domains = 4; tegra_cpu_domains = tegra114_cpu_domains; break; + case TEGRA124: + tegra_num_powerdomains = 25; + tegra_num_cpu_domains = 4; + tegra_cpu_domains = tegra124_cpu_domains; + break; default: /* Unknown Tegra variant. Disable powergating */ tegra_num_powerdomains = 0; @@ -267,6 +279,33 @@ static const char * const powergate_name_t114[] = { [TEGRA_POWERGATE_XUSBC] = "xusbc", }; +static const char * const powergate_name_t124[] = { + [TEGRA_POWERGATE_CPU] = "crail", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_SATA] = "sata", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_SOR] = "sor", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", + [TEGRA_POWERGATE_VIC] = "vic", + [TEGRA_POWERGATE_IRAM] = "iram", +}; + static int powergate_show(struct seq_file *s, void *data) { int i; @@ -311,6 +350,9 @@ int __init tegra_powergate_debugfs_init(void) case TEGRA114: powergate_name = powergate_name_t114; break; + case TEGRA124: + powergate_name = powergate_name_t124; + break; } if (powergate_name) { diff --git a/include/linux/tegra-powergate.h b/include/linux/tegra-powergate.h index afe442d2629a..bccad3cfff87 100644 --- a/include/linux/tegra-powergate.h +++ b/include/linux/tegra-powergate.h @@ -38,11 +38,14 @@ struct reset_control; #define TEGRA_POWERGATE_CPU0 14 #define TEGRA_POWERGATE_C0NC 15 #define TEGRA_POWERGATE_C1NC 16 +#define TEGRA_POWERGATE_SOR 17 #define TEGRA_POWERGATE_DIS 18 #define TEGRA_POWERGATE_DISB 19 #define TEGRA_POWERGATE_XUSBA 20 #define TEGRA_POWERGATE_XUSBB 21 #define TEGRA_POWERGATE_XUSBC 22 +#define TEGRA_POWERGATE_VIC 23 +#define TEGRA_POWERGATE_IRAM 24 #define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D -- cgit v1.2.3-71-gd317 From 9d4450ae87398e248e8700e3507748e0d1751e25 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 16 Dec 2013 21:42:28 +0100 Subject: ARM: tegra: Add IO rail support Add tegra_io_rail_power_off() and tegra_io_rail_power_on() functions to put IO rails into or out of deep powerdown mode, respectively. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 132 ++++++++++++++++++++++++++++++++++++++++ include/linux/tegra-powergate.h | 45 ++++++++++++++ 2 files changed, 177 insertions(+) (limited to 'include/linux') diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 0026fb6c984b..3d0c537d9b94 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -34,6 +34,10 @@ #include "fuse.h" #include "iomap.h" +#define DPD_SAMPLE 0x020 +#define DPD_SAMPLE_ENABLE (1 << 0) +#define DPD_SAMPLE_DISABLE (0 << 0) + #define PWRGATE_TOGGLE 0x30 #define PWRGATE_TOGGLE_START (1 << 8) @@ -41,6 +45,17 @@ #define PWRGATE_STATUS 0x38 +#define IO_DPD_REQ 0x1b8 +#define IO_DPD_REQ_CODE_IDLE (0 << 30) +#define IO_DPD_REQ_CODE_OFF (1 << 30) +#define IO_DPD_REQ_CODE_ON (2 << 30) +#define IO_DPD_REQ_CODE_MASK (3 << 30) + +#define IO_DPD_STATUS 0x1bc +#define IO_DPD2_REQ 0x1c0 +#define IO_DPD2_STATUS 0x1c4 +#define SEL_DPD_TIM 0x1c8 + #define GPU_RG_CNTRL 0x2d4 static int tegra_num_powerdomains; @@ -379,3 +394,120 @@ int __init tegra_powergate_debugfs_init(void) } #endif + +static int tegra_io_rail_prepare(int id, unsigned long *request, + unsigned long *status, unsigned int *bit) +{ + unsigned long rate, value; + struct clk *clk; + + *bit = id % 32; + + /* + * There are two sets of 30 bits to select IO rails, but bits 30 and + * 31 are control bits rather than IO rail selection bits. + */ + if (id > 63 || *bit == 30 || *bit == 31) + return -EINVAL; + + if (id < 32) { + *status = IO_DPD_STATUS; + *request = IO_DPD_REQ; + } else { + *status = IO_DPD2_STATUS; + *request = IO_DPD2_REQ; + } + + clk = clk_get_sys(NULL, "pclk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rate = clk_get_rate(clk); + clk_put(clk); + + pmc_write(DPD_SAMPLE_ENABLE, DPD_SAMPLE); + + /* must be at least 200 ns, in APB (PCLK) clock cycles */ + value = DIV_ROUND_UP(1000000000, rate); + value = DIV_ROUND_UP(200, value); + pmc_write(value, SEL_DPD_TIM); + + return 0; +} + +static int tegra_io_rail_poll(unsigned long offset, unsigned long mask, + unsigned long val, unsigned long timeout) +{ + unsigned long value; + + timeout = jiffies + msecs_to_jiffies(timeout); + + while (time_after(timeout, jiffies)) { + value = pmc_read(offset); + if ((value & mask) == val) + return 0; + + usleep_range(250, 1000); + } + + return -ETIMEDOUT; +} + +static void tegra_io_rail_unprepare(void) +{ + pmc_write(DPD_SAMPLE_DISABLE, DPD_SAMPLE); +} + +int tegra_io_rail_power_on(int id) +{ + unsigned long request, status, value; + unsigned int bit, mask; + int err; + + err = tegra_io_rail_prepare(id, &request, &status, &bit); + if (err < 0) + return err; + + mask = 1 << bit; + + value = pmc_read(request); + value |= mask; + value &= ~IO_DPD_REQ_CODE_MASK; + value |= IO_DPD_REQ_CODE_OFF; + pmc_write(value, request); + + err = tegra_io_rail_poll(status, mask, 0, 250); + if (err < 0) + return err; + + tegra_io_rail_unprepare(); + + return 0; +} + +int tegra_io_rail_power_off(int id) +{ + unsigned long request, status, value; + unsigned int bit, mask; + int err; + + err = tegra_io_rail_prepare(id, &request, &status, &bit); + if (err < 0) + return err; + + mask = 1 << bit; + + value = pmc_read(request); + value |= mask; + value &= ~IO_DPD_REQ_CODE_MASK; + value |= IO_DPD_REQ_CODE_ON; + pmc_write(value, request); + + err = tegra_io_rail_poll(status, mask, mask, 250); + if (err < 0) + return err; + + tegra_io_rail_unprepare(); + + return 0; +} diff --git a/include/linux/tegra-powergate.h b/include/linux/tegra-powergate.h index bccad3cfff87..e6f2ab3014a7 100644 --- a/include/linux/tegra-powergate.h +++ b/include/linux/tegra-powergate.h @@ -49,6 +49,38 @@ struct reset_control; #define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D +#define TEGRA_IO_RAIL_CSIA 0 +#define TEGRA_IO_RAIL_CSIB 1 +#define TEGRA_IO_RAIL_DSI 2 +#define TEGRA_IO_RAIL_MIPI_BIAS 3 +#define TEGRA_IO_RAIL_PEX_BIAS 4 +#define TEGRA_IO_RAIL_PEX_CLK1 5 +#define TEGRA_IO_RAIL_PEX_CLK2 6 +#define TEGRA_IO_RAIL_USB0 9 +#define TEGRA_IO_RAIL_USB1 10 +#define TEGRA_IO_RAIL_USB2 11 +#define TEGRA_IO_RAIL_USB_BIAS 12 +#define TEGRA_IO_RAIL_NAND 13 +#define TEGRA_IO_RAIL_UART 14 +#define TEGRA_IO_RAIL_BB 15 +#define TEGRA_IO_RAIL_AUDIO 17 +#define TEGRA_IO_RAIL_HSIC 19 +#define TEGRA_IO_RAIL_COMP 22 +#define TEGRA_IO_RAIL_HDMI 28 +#define TEGRA_IO_RAIL_PEX_CNTRL 32 +#define TEGRA_IO_RAIL_SDMMC1 33 +#define TEGRA_IO_RAIL_SDMMC3 34 +#define TEGRA_IO_RAIL_SDMMC4 35 +#define TEGRA_IO_RAIL_CAM 36 +#define TEGRA_IO_RAIL_RES 37 +#define TEGRA_IO_RAIL_HV 38 +#define TEGRA_IO_RAIL_DSIB 39 +#define TEGRA_IO_RAIL_DSIC 40 +#define TEGRA_IO_RAIL_DSID 41 +#define TEGRA_IO_RAIL_CSIE 44 +#define TEGRA_IO_RAIL_LVDS 57 +#define TEGRA_IO_RAIL_SYS_DDC 58 + #ifdef CONFIG_ARCH_TEGRA int tegra_powergate_is_powered(int id); int tegra_powergate_power_on(int id); @@ -58,6 +90,9 @@ int tegra_powergate_remove_clamping(int id); /* Must be called with clk disabled, and returns with clk enabled */ int tegra_powergate_sequence_power_up(int id, struct clk *clk, struct reset_control *rst); + +int tegra_io_rail_power_on(int id); +int tegra_io_rail_power_off(int id); #else static inline int tegra_powergate_is_powered(int id) { @@ -84,6 +119,16 @@ static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, { return -ENOSYS; } + +static inline int tegra_io_rail_power_on(int id) +{ + return -ENOSYS; +} + +static inline int tegra_io_rail_power_off(int id) +{ + return -ENOSYS; +} #endif #endif /* _MACH_TEGRA_POWERGATE_H_ */ -- cgit v1.2.3-71-gd317 From 4de6a2d6acb0e2a840f07db17def7e674b9d2bb4 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 2 Sep 2013 09:48:53 +0200 Subject: gpu: host1x: Add MIPI pad calibration support This driver adds support to perform calibration of the MIPI pads for CSI and DSI. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/Makefile | 1 + drivers/gpu/host1x/dev.c | 17 ++- drivers/gpu/host1x/dev.h | 2 + drivers/gpu/host1x/mipi.c | 272 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/host1x.h | 6 + 5 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/host1x/mipi.c (limited to 'include/linux') diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index afa1e9e4e512..de305c2d510e 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile @@ -7,6 +7,7 @@ host1x-y = \ channel.o \ job.o \ debug.o \ + mipi.o \ hw/host1x01.o \ hw/host1x02.o diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 80da003d63de..646a333b069a 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -210,17 +210,26 @@ static int __init tegra_host1x_init(void) return err; err = platform_driver_register(&tegra_host1x_driver); - if (err < 0) { - host1x_bus_exit(); - return err; - } + if (err < 0) + goto unregister_bus; + + err = platform_driver_register(&tegra_mipi_driver); + if (err < 0) + goto unregister_host1x; return 0; + +unregister_host1x: + platform_driver_unregister(&tegra_host1x_driver); +unregister_bus: + host1x_bus_exit(); + return err; } module_init(tegra_host1x_init); static void __exit tegra_host1x_exit(void) { + platform_driver_unregister(&tegra_mipi_driver); platform_driver_unregister(&tegra_host1x_driver); host1x_bus_exit(); } diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index a61a976e7a42..0b6e8e9629c5 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h @@ -306,4 +306,6 @@ static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o) host->debug_op->show_mlocks(host, o); } +extern struct platform_driver tegra_mipi_driver; + #endif diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c new file mode 100644 index 000000000000..518b98dd3087 --- /dev/null +++ b/drivers/gpu/host1x/mipi.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2013 NVIDIA Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#define MIPI_CAL_CTRL 0x00 +#define MIPI_CAL_CTRL_START (1 << 0) + +#define MIPI_CAL_AUTOCAL_CTRL 0x01 + +#define MIPI_CAL_STATUS 0x02 +#define MIPI_CAL_STATUS_DONE (1 << 16) +#define MIPI_CAL_STATUS_ACTIVE (1 << 0) + +#define MIPI_CAL_CONFIG_CSIA 0x05 +#define MIPI_CAL_CONFIG_CSIB 0x06 +#define MIPI_CAL_CONFIG_CSIC 0x07 +#define MIPI_CAL_CONFIG_CSID 0x08 +#define MIPI_CAL_CONFIG_CSIE 0x09 +#define MIPI_CAL_CONFIG_DSIA 0x0e +#define MIPI_CAL_CONFIG_DSIB 0x0f +#define MIPI_CAL_CONFIG_DSIC 0x10 +#define MIPI_CAL_CONFIG_DSID 0x11 + +#define MIPI_CAL_CONFIG_SELECT (1 << 21) +#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16) +#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0) + +#define MIPI_CAL_BIAS_PAD_CFG0 0x16 +#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1) +#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0) + +#define MIPI_CAL_BIAS_PAD_CFG1 0x17 + +#define MIPI_CAL_BIAS_PAD_CFG2 0x18 +#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1) + +static const struct module { + unsigned long reg; +} modules[] = { + { .reg = MIPI_CAL_CONFIG_CSIA }, + { .reg = MIPI_CAL_CONFIG_CSIB }, + { .reg = MIPI_CAL_CONFIG_CSIC }, + { .reg = MIPI_CAL_CONFIG_CSID }, + { .reg = MIPI_CAL_CONFIG_CSIE }, + { .reg = MIPI_CAL_CONFIG_DSIA }, + { .reg = MIPI_CAL_CONFIG_DSIB }, + { .reg = MIPI_CAL_CONFIG_DSIC }, + { .reg = MIPI_CAL_CONFIG_DSID }, +}; + +struct tegra_mipi { + void __iomem *regs; + struct mutex lock; + struct clk *clk; +}; + +struct tegra_mipi_device { + struct platform_device *pdev; + struct tegra_mipi *mipi; + struct device *device; + unsigned long pads; +}; + +static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi, + unsigned long reg) +{ + return readl(mipi->regs + (reg << 2)); +} + +static inline void tegra_mipi_writel(struct tegra_mipi *mipi, + unsigned long value, unsigned long reg) +{ + writel(value, mipi->regs + (reg << 2)); +} + +struct tegra_mipi_device *tegra_mipi_request(struct device *device) +{ + struct device_node *np = device->of_node; + struct tegra_mipi_device *dev; + struct of_phandle_args args; + int err; + + err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate", + "#nvidia,mipi-calibrate-cells", 0, + &args); + if (err < 0) + return ERR_PTR(err); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + of_node_put(args.np); + err = -ENOMEM; + goto out; + } + + dev->pdev = of_find_device_by_node(args.np); + if (!dev->pdev) { + of_node_put(args.np); + err = -ENODEV; + goto free; + } + + of_node_put(args.np); + + dev->mipi = platform_get_drvdata(dev->pdev); + if (!dev->mipi) { + err = -EPROBE_DEFER; + goto pdev_put; + } + + dev->pads = args.args[0]; + dev->device = device; + + return dev; + +pdev_put: + platform_device_put(dev->pdev); +free: + kfree(dev); +out: + return ERR_PTR(err); +} +EXPORT_SYMBOL(tegra_mipi_request); + +void tegra_mipi_free(struct tegra_mipi_device *device) +{ + platform_device_put(device->pdev); + kfree(device); +} +EXPORT_SYMBOL(tegra_mipi_free); + +static int tegra_mipi_wait(struct tegra_mipi *mipi) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(250); + unsigned long value; + + while (time_before(jiffies, timeout)) { + value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS); + if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 && + (value & MIPI_CAL_STATUS_DONE) != 0) + return 0; + + usleep_range(10, 50); + } + + return -ETIMEDOUT; +} + +int tegra_mipi_calibrate(struct tegra_mipi_device *device) +{ + unsigned long value; + unsigned int i; + int err; + + err = clk_enable(device->mipi->clk); + if (err < 0) + return err; + + mutex_lock(&device->mipi->lock); + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0); + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0); + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2); + value &= ~MIPI_CAL_BIAS_PAD_PDVREG; + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2); + + for (i = 0; i < ARRAY_SIZE(modules); i++) { + if (device->pads & BIT(i)) + value = MIPI_CAL_CONFIG_SELECT | + MIPI_CAL_CONFIG_HSPDOS(0) | + MIPI_CAL_CONFIG_HSPUOS(4) | + MIPI_CAL_CONFIG_TERMOS(5); + else + value = 0; + + tegra_mipi_writel(device->mipi, value, modules[i].reg); + } + + tegra_mipi_writel(device->mipi, MIPI_CAL_CTRL_START, MIPI_CAL_CTRL); + + err = tegra_mipi_wait(device->mipi); + + mutex_unlock(&device->mipi->lock); + clk_disable(device->mipi->clk); + + return err; +} +EXPORT_SYMBOL(tegra_mipi_calibrate); + +static int tegra_mipi_probe(struct platform_device *pdev) +{ + struct tegra_mipi *mipi; + struct resource *res; + int err; + + mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL); + if (!mipi) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mipi->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mipi->regs)) + return PTR_ERR(mipi->regs); + + mutex_init(&mipi->lock); + + mipi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mipi->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(mipi->clk); + } + + err = clk_prepare(mipi->clk); + if (err < 0) + return err; + + platform_set_drvdata(pdev, mipi); + + return 0; +} + +static int tegra_mipi_remove(struct platform_device *pdev) +{ + struct tegra_mipi *mipi = platform_get_drvdata(pdev); + + clk_unprepare(mipi->clk); + + return 0; +} + +static struct of_device_id tegra_mipi_of_match[] = { + { .compatible = "nvidia,tegra114-mipi", }, + { }, +}; + +struct platform_driver tegra_mipi_driver = { + .driver = { + .name = "tegra-mipi", + .of_match_table = tegra_mipi_of_match, + }, + .probe = tegra_mipi_probe, + .remove = tegra_mipi_remove, +}; diff --git a/include/linux/host1x.h b/include/linux/host1x.h index f5b9b87ac9a9..3af847273277 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -281,4 +281,10 @@ int host1x_device_exit(struct host1x_device *device); int host1x_client_register(struct host1x_client *client); int host1x_client_unregister(struct host1x_client *client); +struct tegra_mipi_device; + +struct tegra_mipi_device *tegra_mipi_request(struct device *device); +void tegra_mipi_free(struct tegra_mipi_device *device); +int tegra_mipi_calibrate(struct tegra_mipi_device *device); + #endif -- cgit v1.2.3-71-gd317