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