MIPS: Add CPC probe, access functions

This patch introduces code to probe for a MIPS Cluster Power Controller
& accessor functions to allow for easy register access. This support
code will be used by a subsequent patch.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/6361/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index a3bc014..2a9848e 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -2009,6 +2009,9 @@
 config MIPS_CM
 	bool
 
+config MIPS_CPC
+	bool
+
 config SB1_PASS_1_WORKAROUNDS
 	bool
 	depends on CPU_SB1_PASS_1
diff --git a/arch/mips/include/asm/mips-cpc.h b/arch/mips/include/asm/mips-cpc.h
new file mode 100644
index 0000000..fb78935
--- /dev/null
+++ b/arch/mips/include/asm/mips-cpc.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2013 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __MIPS_ASM_MIPS_CPC_H__
+#define __MIPS_ASM_MIPS_CPC_H__
+
+#include <linux/io.h>
+#include <linux/types.h>
+
+/* The base address of the CPC registers */
+extern void __iomem *mips_cpc_base;
+
+/**
+ * mips_cpc_default_phys_base - retrieve the default physical base address of
+ *                              the CPC
+ *
+ * Returns the default physical base address of the Cluster Power Controller
+ * memory mapped registers. This is platform dependant & must therefore be
+ * implemented per-platform.
+ */
+extern phys_t mips_cpc_default_phys_base(void);
+
+/**
+ * mips_cpc_phys_base - retrieve the physical base address of the CPC
+ *
+ * This function returns the physical base address of the Cluster Power
+ * Controller memory mapped registers, or 0 if no Cluster Power Controller
+ * is present. It may be overriden by individual platforms which determine
+ * this address in a different way.
+ */
+extern phys_t __weak mips_cpc_phys_base(void);
+
+/**
+ * mips_cpc_probe - probe for a Cluster Power Controller
+ *
+ * Attempt to detect the presence of a Cluster Power Controller. Returns 0 if
+ * a CPC is successfully detected, else -errno.
+ */
+#ifdef CONFIG_MIPS_CPC
+extern int mips_cpc_probe(void);
+#else
+static inline int mips_cpc_probe(void)
+{
+	return -ENODEV;
+}
+#endif
+
+/**
+ * mips_cpc_present - determine whether a Cluster Power Controller is present
+ *
+ * Returns true if a CPC is present in the system, else false.
+ */
+static inline bool mips_cpc_present(void)
+{
+#ifdef CONFIG_MIPS_CPC
+	return mips_cpc_base != NULL;
+#else
+	return false;
+#endif
+}
+
+/* Offsets from the CPC base address to various control blocks */
+#define MIPS_CPC_GCB_OFS	0x0000
+#define MIPS_CPC_CLCB_OFS	0x2000
+#define MIPS_CPC_COCB_OFS	0x4000
+
+/* Macros to ease the creation of register access functions */
+#define BUILD_CPC_R_(name, off) \
+static inline u32 read_cpc_##name(void)				\
+{								\
+	return readl(mips_cpc_base + (off));			\
+}
+
+#define BUILD_CPC__W(name, off) \
+static inline void write_cpc_##name(u32 value)			\
+{								\
+	writel(value, mips_cpc_base + (off));			\
+}
+
+#define BUILD_CPC_RW(name, off)					\
+	BUILD_CPC_R_(name, off)					\
+	BUILD_CPC__W(name, off)
+
+#define BUILD_CPC_Cx_R_(name, off)				\
+	BUILD_CPC_R_(cl_##name, MIPS_CPC_CLCB_OFS + (off))	\
+	BUILD_CPC_R_(co_##name, MIPS_CPC_COCB_OFS + (off))
+
+#define BUILD_CPC_Cx__W(name, off)				\
+	BUILD_CPC__W(cl_##name, MIPS_CPC_CLCB_OFS + (off))	\
+	BUILD_CPC__W(co_##name, MIPS_CPC_COCB_OFS + (off))
+
+#define BUILD_CPC_Cx_RW(name, off)				\
+	BUILD_CPC_Cx_R_(name, off)				\
+	BUILD_CPC_Cx__W(name, off)
+
+/* GCB register accessor functions */
+BUILD_CPC_RW(access,		MIPS_CPC_GCB_OFS + 0x00)
+BUILD_CPC_RW(seqdel,		MIPS_CPC_GCB_OFS + 0x08)
+BUILD_CPC_RW(rail,		MIPS_CPC_GCB_OFS + 0x10)
+BUILD_CPC_RW(resetlen,		MIPS_CPC_GCB_OFS + 0x18)
+BUILD_CPC_R_(revision,		MIPS_CPC_GCB_OFS + 0x20)
+
+/* Core Local & Core Other accessor functions */
+BUILD_CPC_Cx_RW(cmd,		0x00)
+BUILD_CPC_Cx_RW(stat_conf,	0x08)
+BUILD_CPC_Cx_RW(other,		0x10)
+
+/* CPC_Cx_CMD register fields */
+#define CPC_Cx_CMD_SHF				0
+#define CPC_Cx_CMD_MSK				(_ULCAST_(0xf) << 0)
+#define  CPC_Cx_CMD_CLOCKOFF			(_ULCAST_(0x1) << 0)
+#define  CPC_Cx_CMD_PWRDOWN			(_ULCAST_(0x2) << 0)
+#define  CPC_Cx_CMD_PWRUP			(_ULCAST_(0x3) << 0)
+#define  CPC_Cx_CMD_RESET			(_ULCAST_(0x4) << 0)
+
+/* CPC_Cx_STAT_CONF register fields */
+#define CPC_Cx_STAT_CONF_PWRUPE_SHF		23
+#define CPC_Cx_STAT_CONF_PWRUPE_MSK		(_ULCAST_(0x1) << 23)
+#define CPC_Cx_STAT_CONF_SEQSTATE_SHF		19
+#define CPC_Cx_STAT_CONF_SEQSTATE_MSK		(_ULCAST_(0xf) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_D0		(_ULCAST_(0x0) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_U0		(_ULCAST_(0x1) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_U1		(_ULCAST_(0x2) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_U2		(_ULCAST_(0x3) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_U3		(_ULCAST_(0x4) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_U4		(_ULCAST_(0x5) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_U5		(_ULCAST_(0x6) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_U6		(_ULCAST_(0x7) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_D1		(_ULCAST_(0x8) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_D3		(_ULCAST_(0x9) << 19)
+#define  CPC_Cx_STAT_CONF_SEQSTATE_D2		(_ULCAST_(0xa) << 19)
+#define CPC_Cx_STAT_CONF_CLKGAT_IMPL_SHF	17
+#define CPC_Cx_STAT_CONF_CLKGAT_IMPL_MSK	(_ULCAST_(0x1) << 17)
+#define CPC_Cx_STAT_CONF_PWRDN_IMPL_SHF		16
+#define CPC_Cx_STAT_CONF_PWRDN_IMPL_MSK		(_ULCAST_(0x1) << 16)
+#define CPC_Cx_STAT_CONF_EJTAG_PROBE_SHF	15
+#define CPC_Cx_STAT_CONF_EJTAG_PROBE_MSK	(_ULCAST_(0x1) << 15)
+
+/* CPC_Cx_OTHER register fields */
+#define CPC_Cx_OTHER_CORENUM_SHF		16
+#define CPC_Cx_OTHER_CORENUM_MSK		(_ULCAST_(0xff) << 16)
+
+#endif /* __MIPS_ASM_MIPS_CPC_H__ */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index be56cd8..a6a8717 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -104,6 +104,7 @@
 obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o
 
 obj-$(CONFIG_MIPS_CM)		+= mips-cm.o
+obj-$(CONFIG_MIPS_CPC)		+= mips-cpc.o
 
 #
 # DSP ASE supported for MIPS32 or MIPS64 Release 2 cores only. It is not
diff --git a/arch/mips/kernel/mips-cpc.c b/arch/mips/kernel/mips-cpc.c
new file mode 100644
index 0000000..c9dc674
--- /dev/null
+++ b/arch/mips/kernel/mips-cpc.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/errno.h>
+
+#include <asm/mips-cm.h>
+#include <asm/mips-cpc.h>
+
+void __iomem *mips_cpc_base;
+
+phys_t __weak mips_cpc_phys_base(void)
+{
+	u32 cpc_base;
+
+	if (!mips_cm_present())
+		return 0;
+
+	if (!(read_gcr_cpc_status() & CM_GCR_CPC_STATUS_EX_MSK))
+		return 0;
+
+	/* If the CPC is already enabled, leave it so */
+	cpc_base = read_gcr_cpc_base();
+	if (cpc_base & CM_GCR_CPC_BASE_CPCEN_MSK)
+		return cpc_base & CM_GCR_CPC_BASE_CPCBASE_MSK;
+
+	/* Otherwise, give it the default address & enable it */
+	cpc_base = mips_cpc_default_phys_base();
+	write_gcr_cpc_base(cpc_base | CM_GCR_CPC_BASE_CPCEN_MSK);
+	return cpc_base;
+}
+
+int mips_cpc_probe(void)
+{
+	phys_t addr;
+
+	addr = mips_cpc_phys_base();
+	if (!addr)
+		return -ENODEV;
+
+	mips_cpc_base = ioremap_nocache(addr, 0x8000);
+	if (!mips_cpc_base)
+		return -ENXIO;
+
+	return 0;
+}