diff mbox

[23/24] C6X: Power and Sleep Controller

Message ID 1314826019-22330-24-git-send-email-msalter@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Mark Salter Aug. 31, 2011, 9:26 p.m. UTC
This patch provides support for the Power/Sleep controller found on some SoCs.
The PSC controls two basic things: power domains and peripheral clock gating
and resets. The device tree is parsed at setup_arch tree to find any PSC node.
The number and controllability of the power domains and peripheral reset/gating
varies quite a bit from SoC to SoC. This driver tries to encapsulate the common
bits and provide an API for the SoC specific code to use.

Signed-off-by: Mark Salter <msalter@redhat.com>
---
 arch/c6x/platforms/psc.c |  181 ++++++++++++++++++++++++++++++++++++++++++++++
 arch/c6x/platforms/psc.h |   25 +++++++
 2 files changed, 206 insertions(+), 0 deletions(-)
 create mode 100644 arch/c6x/platforms/psc.c
 create mode 100644 arch/c6x/platforms/psc.h
diff mbox

Patch

diff --git a/arch/c6x/platforms/psc.c b/arch/c6x/platforms/psc.c
new file mode 100644
index 0000000..c3fd52c
--- /dev/null
+++ b/arch/c6x/platforms/psc.c
@@ -0,0 +1,181 @@ 
+/*
+ *  Power/Sleep Controller
+ *
+ *  Copyright (C) 2011 Texas Instruments Incorporated
+ *  Author: Mark Salter <msalter@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <asm/soc.h>
+#include "psc.h"
+
+#define PSC_PTCMD	0x0120
+#define PSC_PTSTAT	0x0128
+
+#define PSC_PDSTAT0	0x0200
+#define PSC_PDCTL0	0x0300
+
+#define PSC_MDSTAT0	0x0800
+#define PSC_MDCTL0	0x0a00
+
+#define PD_NEXT_DISABLE       2
+#define PD_NEXT_ENABLE        3
+
+#define MD_NEXT_SWRST_DISABLE	0
+#define MD_NEXT_SYNCRST		1
+#define MD_NEXT_DISABLE		2
+#define MD_NEXT_ENABLE		3
+#define MD_NEXT_MASK		0x1f
+
+#define LRSTZ	0x100
+
+struct psc_data {
+	u32		phys_base;
+	void __iomem	*base;
+	int		num_domains;
+	int		num_modules;
+	u8		module_domains[MAX_PSC_MODULES];
+};
+
+static struct psc_data psc;
+
+static inline void psc_write(int offset, unsigned int val)
+{
+	if (psc.base)
+		soc_writel(val, psc.base + offset);
+}
+
+static inline unsigned int psc_read(int offset)
+{
+	if (!psc.base)
+		return 0;
+	return soc_readl(psc.base + offset);
+}
+
+static void __wait_for_gostat(int domain_mask)
+{
+	while (psc_read(PSC_PTSTAT) & domain_mask)
+		;
+}
+
+void psc_domain_on(int domain)
+{
+	int reg = PSC_PDCTL0 + (domain * 4);
+
+	if (domain < 0 || domain >= psc.num_domains)
+		return;
+
+	__wait_for_gostat(1 << domain);
+	psc_write(reg, psc_read(reg) | 1);
+}
+
+void psc_domain_off(int domain)
+{
+	int reg = PSC_PDCTL0 + (domain * 4);
+
+	if (domain < 0 || domain >= psc.num_domains)
+		return;
+
+	__wait_for_gostat(1 << domain);
+	psc_write(reg, psc_read(reg) & ~1);
+}
+
+void psc_transition_domains(int domain_mask)
+{
+	domain_mask &= (1 << psc.num_domains) - 1;
+	if (domain_mask == 0)
+		return;
+
+	__wait_for_gostat(domain_mask);
+	psc_write(PSC_PTCMD, domain_mask);
+}
+
+void psc_module_on(int module)
+{
+	u32 reg, val;
+
+	if (module < 0 || module >= psc.num_modules)
+		return;
+
+	__wait_for_gostat(1 << psc.module_domains[module]);
+
+	reg = PSC_MDCTL0 + (module * 4);
+	val = psc_read(reg) & ~MD_NEXT_MASK;
+	psc_write(reg, val | MD_NEXT_ENABLE);
+}
+
+void psc_module_off(int module)
+{
+	u32 reg, val;
+
+	if (module < 0 || module >= psc.num_modules)
+		return;
+
+	__wait_for_gostat(1 << psc.module_domains[module]);
+
+	reg = PSC_MDCTL0 + (module * 4);
+	val = psc_read(reg) & ~MD_NEXT_MASK;
+	psc_write(reg, val | MD_NEXT_DISABLE);
+}
+
+void psc_module_reset(int module)
+{
+	int reg = PSC_MDCTL0 + module * 4;
+
+	if (module < 0 || module >= psc.num_modules)
+		return;
+
+	psc_write(reg, psc_read(reg) & ~LRSTZ);
+}
+
+void psc_module_reset_release(int module)
+{
+	int reg = PSC_MDCTL0 + module * 4;
+
+	if (module < 0 || module >= psc.num_modules)
+		return;
+
+	psc_write(reg, psc_read(reg) | LRSTZ);
+}
+
+void __init psc_init(void)
+{
+	struct device_node *node;
+	const __be32 *p;
+	int i, len;
+
+	node = of_find_compatible_node(NULL, NULL, "ti,c64x+psc");
+	if (!node)
+		return;
+
+	psc.base = of_iomap(node, 0);
+	if (!psc.base) {
+		of_node_put(node);
+		return;
+	}
+
+	p = of_get_property(node, "ti,number-psc-domains", &len);
+	if (p && len == sizeof(u32))
+		psc.num_domains = be32_to_cpup(p);
+
+	p = of_get_property(node, "ti,number-psc-modules", &len);
+	if (p && len == sizeof(u32))
+		psc.num_modules = be32_to_cpup(p);
+
+	p = of_get_property(node, "ti,module-domain-map", &len);
+	if (p) {
+		len /= sizeof(u32);
+		if (len > MAX_PSC_MODULES)
+			len = MAX_PSC_MODULES;
+		for (i = 0; i <= len; i++)
+			psc.module_domains[i] = be32_to_cpup(&p[i]);
+	}
+
+	of_node_put(node);
+}
diff --git a/arch/c6x/platforms/psc.h b/arch/c6x/platforms/psc.h
new file mode 100644
index 0000000..7f4bd1d
--- /dev/null
+++ b/arch/c6x/platforms/psc.h
@@ -0,0 +1,25 @@ 
+/*
+ *  C6X Power/Sleep Controller
+ *
+ *  Copyright (C) 2011 Texas Instruments Incorporated
+ *  Author: Mark Salter <msalter@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+#ifndef _C6X_PSC_H
+#define _C6X_PSC_H
+
+#define MAX_PSC_MODULES 16
+
+extern void psc_init(void);
+extern void psc_domain_on(int domain);
+extern void psc_domain_off(int domain);
+extern void psc_transition_domains(int domain_mask);
+extern void psc_module_on(int module);
+extern void psc_module_off(int module);
+extern void psc_module_reset(int module);
+extern void psc_module_reset_release(int module);
+
+#endif /* _C6X_PSC_H */