[1/9] ARM i.MX51: Add ipu clock support
diff mbox series

Message ID 1291902441-24712-2-git-send-email-s.hauer@pengutronix.de
State New, archived
Headers show
Series
  • [1/9] ARM i.MX51: Add ipu clock support
Related show

Commit Message

Sascha Hauer Dec. 9, 2010, 1:47 p.m. UTC
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/mach-mx5/clock-mx51-mx53.c |  140 +++++++++++++++++++++++++++++++++++
 1 files changed, 140 insertions(+), 0 deletions(-)

Comments

Arnd Bergmann Dec. 15, 2010, 3:40 p.m. UTC | #1
On Thursday 09 December 2010, Sascha Hauer wrote:
> +static int clk_ipu_enable(struct clk *clk)
> +{
> +       u32 reg;
> +
> +       _clk_ccgr_enable(clk);
> +
> +       /* Enable handshake with IPU when certain clock rates are changed */
> +       reg = __raw_readl(MXC_CCM_CCDR);
> +       reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
> +       __raw_writel(reg, MXC_CCM_CCDR);
> +
> +       /* Enable handshake with IPU when LPM is entered */
> +       reg = __raw_readl(MXC_CCM_CLPCR);
> +       reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
> +       __raw_writel(reg, MXC_CCM_CLPCR);
> +
> +       return 0;
> +}

Why __raw_readl?

The regular accessor function for I/O registers is readl, which handles
the access correctly with regard to atomicity, I/O ordering and byteorder.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Russell King - ARM Linux Dec. 15, 2010, 4:34 p.m. UTC | #2
On Wed, Dec 15, 2010 at 04:40:03PM +0100, Arnd Bergmann wrote:
> On Thursday 09 December 2010, Sascha Hauer wrote:
> > +static int clk_ipu_enable(struct clk *clk)
> > +{
> > +       u32 reg;
> > +
> > +       _clk_ccgr_enable(clk);
> > +
> > +       /* Enable handshake with IPU when certain clock rates are changed */
> > +       reg = __raw_readl(MXC_CCM_CCDR);
> > +       reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
> > +       __raw_writel(reg, MXC_CCM_CCDR);
> > +
> > +       /* Enable handshake with IPU when LPM is entered */
> > +       reg = __raw_readl(MXC_CCM_CLPCR);
> > +       reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
> > +       __raw_writel(reg, MXC_CCM_CLPCR);
> > +
> > +       return 0;
> > +}
> 
> Why __raw_readl?
> 
> The regular accessor function for I/O registers is readl, which handles
> the access correctly with regard to atomicity, I/O ordering and byteorder.

There's no possibility of those two being mis-ordered - they will be in
program order whatever.

What isn't guaranteed is the ordering between I/O accesses (accesses to
device memory) and SDRAM accesses (normal memory) which can pass each other
without additional barriers.  Memory accesses can pass I/O accesses.

So, (eg), if you're writing to a register which causes the hardware to
begin reading DMA descriptors from an area allocated from dma_alloc_coherent(),
you need a barrier to ensure that writes to the dma_alloc_coherent() are
visible to the hardware before you write the enable register.

If you don't need normal vs device access ordering, using readl_relaxed()/
writel_relaxed() is preferred, and avoids the (apparantly rather high)
performance overhead of having to issue barriers all the way down to the
L2 cache.

Lastly, I don't see where atomicity comes into it - __raw_writel vs writel
have the same atomicity.  Both are single access atomic provided they're
naturally aligned.  Misaligned device accesses are not predictable.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Arnd Bergmann Dec. 15, 2010, 4:49 p.m. UTC | #3
On Wednesday 15 December 2010, Russell King - ARM Linux wrote:
> > The regular accessor function for I/O registers is readl, which handles
> > the access correctly with regard to atomicity, I/O ordering and byteorder.
> 
> There's no possibility of those two being mis-ordered - they will be in
> program order whatever.
> 
> What isn't guaranteed is the ordering between I/O accesses (accesses to
> device memory) and SDRAM accesses (normal memory) which can pass each other
> without additional barriers.  Memory accesses can pass I/O accesses.

Yes, that's what I meant.

> If you don't need normal vs device access ordering, using readl_relaxed()/
> writel_relaxed() is preferred, and avoids the (apparantly rather high)
> performance overhead of having to issue barriers all the way down to the
> L2 cache.

Well, my point was that the authors should choose their I/O accessors
carefully. Using __raw_writel() without any explanations is a rather
bad default, it's not designed for that. Using writel() as a default
is usually a good choice, as we can assume it to do the right thing.

writel_relaxed() is also good where appropriate, because it tells
the reader that the driver author has thought about the I/O (vs. code)
ordering and concluded that it's safe to do.
 
> Lastly, I don't see where atomicity comes into it - __raw_writel vs writel
> have the same atomicity.  Both are single access atomic provided they're
> naturally aligned.  Misaligned device accesses are not predictable.

This is just what gcc turns it into today. In theory, a future gcc or
a future cpu might change that. If you mark a pointer as
'__attribute__((packed))', it probably already does, even for aligned
pointers, while it does not when using writel_{,relaxed}. The point is
that __raw_* means just that -- we don't give any guarantees on what
happens on the bus, so people should not use it.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Russell King - ARM Linux Dec. 15, 2010, 5:12 p.m. UTC | #4
On Wed, Dec 15, 2010 at 05:49:59PM +0100, Arnd Bergmann wrote:
> On Wednesday 15 December 2010, Russell King - ARM Linux wrote:
> > Lastly, I don't see where atomicity comes into it - __raw_writel vs writel
> > have the same atomicity.  Both are single access atomic provided they're
> > naturally aligned.  Misaligned device accesses are not predictable.
> 
> This is just what gcc turns it into today. In theory, a future gcc or
> a future cpu might change that. If you mark a pointer as
> '__attribute__((packed))', it probably already does, even for aligned
> pointers, while it does not when using writel_{,relaxed}. The point is
> that __raw_* means just that -- we don't give any guarantees on what
> happens on the bus, so people should not use it.

No.  It does give guarantees on what happens on the bus.  The kind of
pointer you pass in has no bearing on what happens on the bus because it's
casted away immediately.

__raw_writel(v, ptr) doesn't just do *(ptr) = v - that doesn't work when
ptr is void.  Instead, it has to do a cast to ensure that we have a valid
C dereference (void pointers are illegal to dereference):

#define __raw_writel(v,a)  (__chk_io_ptr(a), \
	*(volatile unsigned int __force   *)(a) = (v))

Doesn't matter if 'a' was marked as packed or not - that's all casted away.
That's not something that should ever change - otherwise we'll all be
screwed as you could never cast away pointer attributes.

It _may_ possible that the compiler decides that accessing an 'unsigned int'
will not be done as a single word load, in which case we have exactly the
same problems with writel() too.

And in any case, if __raw_writel() was as you're suggesting, then it would
serve absolutely no purpose at all.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Patch
diff mbox series

diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c
index 9fc65bb..f550d02 100644
--- a/arch/arm/mach-mx5/clock-mx51-mx53.c
+++ b/arch/arm/mach-mx5/clock-mx51-mx53.c
@@ -39,6 +39,9 @@  static struct clk periph_apm_clk;
 static struct clk ahb_clk;
 static struct clk ipg_clk;
 static struct clk usboh3_clk;
+static struct clk emi_fast_clk;
+static struct clk ipu_clk;
+static struct clk mipi_hsc1_clk;
 
 #define MAX_DPLL_WAIT_TRIES	1000 /* 1000 * udelay(1) = 1ms */
 
@@ -688,6 +691,19 @@  static unsigned long clk_emi_slow_get_rate(struct clk *clk)
 	return clk_get_rate(clk->parent) / div;
 }
 
+static unsigned long _clk_ddr_hf_get_rate(struct clk *clk)
+{
+	unsigned long rate;
+	u32 reg, div;
+
+	reg = __raw_readl(MXC_CCM_CBCDR);
+	div = ((reg & MXC_CCM_CBCDR_DDR_PODF_MASK) >>
+		MXC_CCM_CBCDR_DDR_PODF_OFFSET) + 1;
+	rate = clk_get_rate(clk->parent) / div;
+
+	return rate;
+}
+
 /* External high frequency clock */
 static struct clk ckih_clk = {
 	.get_rate = get_high_reference_clock_rate,
@@ -846,6 +862,109 @@  static struct clk emi_slow_clk = {
 	.get_rate = clk_emi_slow_get_rate,
 };
 
+static int clk_ipu_enable(struct clk *clk)
+{
+	u32 reg;
+
+	_clk_ccgr_enable(clk);
+
+	/* Enable handshake with IPU when certain clock rates are changed */
+	reg = __raw_readl(MXC_CCM_CCDR);
+	reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
+	__raw_writel(reg, MXC_CCM_CCDR);
+
+	/* Enable handshake with IPU when LPM is entered */
+	reg = __raw_readl(MXC_CCM_CLPCR);
+	reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+	__raw_writel(reg, MXC_CCM_CLPCR);
+
+	return 0;
+}
+
+static void clk_ipu_disable(struct clk *clk)
+{
+	u32 reg;
+
+	_clk_ccgr_disable(clk);
+
+	/* Disable handshake with IPU whe dividers are changed */
+	reg = __raw_readl(MXC_CCM_CCDR);
+	reg |= MXC_CCM_CCDR_IPU_HS_MASK;
+	__raw_writel(reg, MXC_CCM_CCDR);
+
+	/* Disable handshake with IPU when LPM is entered */
+	reg = __raw_readl(MXC_CCM_CLPCR);
+	reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+	__raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+static struct clk ahbmux1_clk = {
+	.parent = &ahb_clk,
+	.secondary = &ahb_max_clk,
+	.enable_reg = MXC_CCM_CCGR0,
+	.enable_shift = MXC_CCM_CCGRx_CG8_OFFSET,
+	.enable = _clk_ccgr_enable,
+	.disable = _clk_ccgr_disable_inwait,
+};
+
+static struct clk ipu_sec_clk = {
+	.parent = &emi_fast_clk,
+	.secondary = &ahbmux1_clk,
+};
+
+static struct clk ddr_hf_clk = {
+	.parent = &pll1_sw_clk,
+	.get_rate = _clk_ddr_hf_get_rate,
+};
+
+static struct clk ddr_clk = {
+	.parent = &ddr_hf_clk,
+};
+
+/* clock definitions for MIPI HSC unit which has been removed
+ * from documentation, but not from hardware
+ */
+static int _clk_hsc_enable(struct clk *clk)
+{
+	u32 reg;
+
+	_clk_ccgr_enable(clk);
+	/* Handshake with IPU when certain clock rates are changed. */
+	reg = __raw_readl(MXC_CCM_CCDR);
+	reg &= ~MXC_CCM_CCDR_HSC_HS_MASK;
+	__raw_writel(reg, MXC_CCM_CCDR);
+
+	reg = __raw_readl(MXC_CCM_CLPCR);
+	reg &= ~MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS;
+	__raw_writel(reg, MXC_CCM_CLPCR);
+
+	return 0;
+}
+
+static void _clk_hsc_disable(struct clk *clk)
+{
+	u32 reg;
+
+	_clk_ccgr_disable(clk);
+	/* No handshake with HSC as its not enabled. */
+	reg = __raw_readl(MXC_CCM_CCDR);
+	reg |= MXC_CCM_CCDR_HSC_HS_MASK;
+	__raw_writel(reg, MXC_CCM_CCDR);
+
+	reg = __raw_readl(MXC_CCM_CLPCR);
+	reg |= MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS;
+	__raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+static struct clk mipi_hsp_clk = {
+	.parent = &ipu_clk,
+	.enable_reg = MXC_CCM_CCGR4,
+	.enable_shift = MXC_CCM_CCGRx_CG6_OFFSET,
+	.enable = _clk_hsc_enable,
+	.disable = _clk_hsc_disable,
+	.secondary = &mipi_hsc1_clk,
+};
+
 #define DEFINE_CLOCK_CCGR(name, i, er, es, pfx, p, s)	\
 	static struct clk name = {			\
 		.id		= i,			\
@@ -1077,6 +1196,23 @@  DEFINE_CLOCK_FULL(esdhc2_ipg_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG2_OFFSET,
 DEFINE_CLOCK_MAX(esdhc2_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG3_OFFSET,
 	clk_esdhc2, &pll2_sw_clk, &esdhc2_ipg_clk);
 
+DEFINE_CLOCK(mipi_esc_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG5_OFFSET, NULL, NULL, NULL, &pll2_sw_clk);
+DEFINE_CLOCK(mipi_hsc2_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG4_OFFSET, NULL, NULL, &mipi_esc_clk, &pll2_sw_clk);
+DEFINE_CLOCK(mipi_hsc1_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG3_OFFSET, NULL, NULL, &mipi_hsc2_clk, &pll2_sw_clk);
+
+/* IPU */
+DEFINE_CLOCK_FULL(ipu_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG5_OFFSET,
+	NULL,  NULL, clk_ipu_enable, clk_ipu_disable, &ahb_clk, &ipu_sec_clk);
+
+DEFINE_CLOCK_FULL(emi_fast_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG7_OFFSET,
+		NULL, NULL, _clk_ccgr_enable, _clk_ccgr_disable_inwait,
+		&ddr_clk, NULL);
+
+DEFINE_CLOCK(ipu_di0_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG5_OFFSET,
+		NULL, NULL, &pll3_sw_clk, NULL);
+DEFINE_CLOCK(ipu_di1_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG6_OFFSET,
+		NULL, NULL, &pll3_sw_clk, NULL);
+
 #define _REGISTER_CLOCK(d, n, c) \
        { \
 		.dev_id = d, \
@@ -1117,6 +1253,10 @@  static struct clk_lookup mx51_lookups[] = {
 	_REGISTER_CLOCK(NULL, "iim_clk", iim_clk)
 	_REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk)
 	_REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk)
+	_REGISTER_CLOCK(NULL, "mipi_hsp", mipi_hsp_clk)
+	_REGISTER_CLOCK("imx-ipuv3", NULL, ipu_clk)
+	_REGISTER_CLOCK("imx-ipuv3", "di0", ipu_di0_clk)
+	_REGISTER_CLOCK("imx-ipuv3", "di1", ipu_di1_clk)
 };
 
 static struct clk_lookup mx53_lookups[] = {