1/*
2 * Au1300 media block power gating (VSS)
3 *
4 * This is a stop-gap solution until I have the clock framework integration
5 * ready. This stuff here really must be handled transparently when clocks
6 * for various media blocks are enabled/disabled.
7 */
8
9#include <linux/module.h>
10#include <linux/spinlock.h>
11#include <asm/mach-au1x00/au1000.h>
12
13#define VSS_GATE	0x00	/* gate wait timers */
14#define VSS_CLKRST	0x04	/* clock/block control */
15#define VSS_FTR		0x08	/* footers */
16
17#define VSS_ADDR(blk)	(KSEG1ADDR(AU1300_VSS_PHYS_ADDR) + (blk * 0x0c))
18
19static DEFINE_SPINLOCK(au1300_vss_lock);
20
21/* enable a block as outlined in the databook */
22static inline void __enable_block(int block)
23{
24	void __iomem *base = (void __iomem *)VSS_ADDR(block);
25
26	__raw_writel(3, base + VSS_CLKRST);	/* enable clock, assert reset */
27	wmb();
28
29	__raw_writel(0x01fffffe, base + VSS_GATE); /* maximum setup time */
30	wmb();
31
32	/* enable footers in sequence */
33	__raw_writel(0x01, base + VSS_FTR);
34	wmb();
35	__raw_writel(0x03, base + VSS_FTR);
36	wmb();
37	__raw_writel(0x07, base + VSS_FTR);
38	wmb();
39	__raw_writel(0x0f, base + VSS_FTR);
40	wmb();
41
42	__raw_writel(0x01ffffff, base + VSS_GATE); /* start FSM too */
43	wmb();
44
45	__raw_writel(2, base + VSS_CLKRST);	/* deassert reset */
46	wmb();
47
48	__raw_writel(0x1f, base + VSS_FTR);	/* enable isolation cells */
49	wmb();
50}
51
52/* disable a block as outlined in the databook */
53static inline void __disable_block(int block)
54{
55	void __iomem *base = (void __iomem *)VSS_ADDR(block);
56
57	__raw_writel(0x0f, base + VSS_FTR);	/* disable isolation cells */
58	wmb();
59	__raw_writel(0, base + VSS_GATE);	/* disable FSM */
60	wmb();
61	__raw_writel(3, base + VSS_CLKRST);	/* assert reset */
62	wmb();
63	__raw_writel(1, base + VSS_CLKRST);	/* disable clock */
64	wmb();
65	__raw_writel(0, base + VSS_FTR);	/* disable all footers */
66	wmb();
67}
68
69void au1300_vss_block_control(int block, int enable)
70{
71	unsigned long flags;
72
73	if (alchemy_get_cputype() != ALCHEMY_CPU_AU1300)
74		return;
75
76	/* only one block at a time */
77	spin_lock_irqsave(&au1300_vss_lock, flags);
78	if (enable)
79		__enable_block(block);
80	else
81		__disable_block(block);
82	spin_unlock_irqrestore(&au1300_vss_lock, flags);
83}
84EXPORT_SYMBOL_GPL(au1300_vss_block_control);
85