18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Au1300 media block power gating (VSS)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This is a stop-gap solution until I have the clock framework integration
68c2ecf20Sopenharmony_ci * ready. This stuff here really must be handled transparently when clocks
78c2ecf20Sopenharmony_ci * for various media blocks are enabled/disabled.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/export.h>
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define VSS_GATE	0x00	/* gate wait timers */
158c2ecf20Sopenharmony_ci#define VSS_CLKRST	0x04	/* clock/block control */
168c2ecf20Sopenharmony_ci#define VSS_FTR		0x08	/* footers */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define VSS_ADDR(blk)	(KSEG1ADDR(AU1300_VSS_PHYS_ADDR) + (blk * 0x0c))
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(au1300_vss_lock);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* enable a block as outlined in the databook */
238c2ecf20Sopenharmony_cistatic inline void __enable_block(int block)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	void __iomem *base = (void __iomem *)VSS_ADDR(block);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	__raw_writel(3, base + VSS_CLKRST);	/* enable clock, assert reset */
288c2ecf20Sopenharmony_ci	wmb();
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	__raw_writel(0x01fffffe, base + VSS_GATE); /* maximum setup time */
318c2ecf20Sopenharmony_ci	wmb();
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/* enable footers in sequence */
348c2ecf20Sopenharmony_ci	__raw_writel(0x01, base + VSS_FTR);
358c2ecf20Sopenharmony_ci	wmb();
368c2ecf20Sopenharmony_ci	__raw_writel(0x03, base + VSS_FTR);
378c2ecf20Sopenharmony_ci	wmb();
388c2ecf20Sopenharmony_ci	__raw_writel(0x07, base + VSS_FTR);
398c2ecf20Sopenharmony_ci	wmb();
408c2ecf20Sopenharmony_ci	__raw_writel(0x0f, base + VSS_FTR);
418c2ecf20Sopenharmony_ci	wmb();
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	__raw_writel(0x01ffffff, base + VSS_GATE); /* start FSM too */
448c2ecf20Sopenharmony_ci	wmb();
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	__raw_writel(2, base + VSS_CLKRST);	/* deassert reset */
478c2ecf20Sopenharmony_ci	wmb();
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	__raw_writel(0x1f, base + VSS_FTR);	/* enable isolation cells */
508c2ecf20Sopenharmony_ci	wmb();
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* disable a block as outlined in the databook */
548c2ecf20Sopenharmony_cistatic inline void __disable_block(int block)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	void __iomem *base = (void __iomem *)VSS_ADDR(block);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	__raw_writel(0x0f, base + VSS_FTR);	/* disable isolation cells */
598c2ecf20Sopenharmony_ci	wmb();
608c2ecf20Sopenharmony_ci	__raw_writel(0, base + VSS_GATE);	/* disable FSM */
618c2ecf20Sopenharmony_ci	wmb();
628c2ecf20Sopenharmony_ci	__raw_writel(3, base + VSS_CLKRST);	/* assert reset */
638c2ecf20Sopenharmony_ci	wmb();
648c2ecf20Sopenharmony_ci	__raw_writel(1, base + VSS_CLKRST);	/* disable clock */
658c2ecf20Sopenharmony_ci	wmb();
668c2ecf20Sopenharmony_ci	__raw_writel(0, base + VSS_FTR);	/* disable all footers */
678c2ecf20Sopenharmony_ci	wmb();
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_civoid au1300_vss_block_control(int block, int enable)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	unsigned long flags;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (alchemy_get_cputype() != ALCHEMY_CPU_AU1300)
758c2ecf20Sopenharmony_ci		return;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* only one block at a time */
788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&au1300_vss_lock, flags);
798c2ecf20Sopenharmony_ci	if (enable)
808c2ecf20Sopenharmony_ci		__enable_block(block);
818c2ecf20Sopenharmony_ci	else
828c2ecf20Sopenharmony_ci		__disable_block(block);
838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&au1300_vss_lock, flags);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(au1300_vss_block_control);
86