Manuel Lauss | 809f36c | 2011-11-01 20:03:30 +0100 | [diff] [blame] | 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 | |
| 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); |