xref: /openbmc/linux/arch/mips/alchemy/common/vss.c (revision fcc8487d)
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/export.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 
19 static DEFINE_SPINLOCK(au1300_vss_lock);
20 
21 /* enable a block as outlined in the databook */
22 static 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 */
53 static 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 
69 void 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 }
84 EXPORT_SYMBOL_GPL(au1300_vss_block_control);
85