summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorStephan Broyles <sbroyles@us.ibm.com>2014-11-05 19:09:37 -0600
committerStephan Broyles <sbroyles@us.ibm.com>2014-11-05 19:22:32 -0600
commit9976c207cdb20871880bd2f4cf123cf4cb6a8b0f (patch)
tree1cf9ed8f23085e6fe3e0e6046fc30dcb7e02ccf2 /src/lib
parent2f8ce357b89d361b5091d88aea91416011b73ccb (diff)
downloadtalos-occ-9976c207cdb20871880bd2f4cf123cf4cb6a8b0f.tar.gz
talos-occ-9976c207cdb20871880bd2f4cf123cf4cb6a8b0f.zip
Added remaining occ files.
Change-Id: I91a748d3dcf3161a6a3eedcb376fcaf1e4dfe655
Diffstat (limited to 'src/lib')
-rwxr-xr-xsrc/lib/Makefile79
-rwxr-xr-xsrc/lib/assert.c47
-rwxr-xr-xsrc/lib/ctype.c22
-rwxr-xr-xsrc/lib/ctype.h125
-rw-r--r--src/lib/ctype_table.c278
-rwxr-xr-xsrc/lib/errno.h25
-rwxr-xr-xsrc/lib/fgetc.c88
-rwxr-xr-xsrc/lib/gpe.h78
-rwxr-xr-xsrc/lib/gpe_control.h171
-rwxr-xr-xsrc/lib/gpe_control.pS160
-rwxr-xr-xsrc/lib/gpe_data.h672
-rwxr-xr-xsrc/lib/gpe_data.pS1585
-rwxr-xr-xsrc/lib/gpe_pba.c148
-rw-r--r--src/lib/gpe_pba.h116
-rwxr-xr-xsrc/lib/gpe_pba_pgas.pS110
-rw-r--r--src/lib/gpe_scom.h471
-rw-r--r--src/lib/gpe_scom.pS709
-rwxr-xr-xsrc/lib/gpsm.c600
-rwxr-xr-xsrc/lib/gpsm.h191
-rwxr-xr-xsrc/lib/gpsm_dcm.c753
-rwxr-xr-xsrc/lib/gpsm_dcm.h192
-rwxr-xr-xsrc/lib/gpsm_dcm_fast_handler.S147
-rwxr-xr-xsrc/lib/gpsm_init.c1638
-rwxr-xr-xsrc/lib/heartbeat.c328
-rwxr-xr-xsrc/lib/heartbeat.h46
-rwxr-xr-xsrc/lib/libfiles.mk57
-rwxr-xr-xsrc/lib/libgpefiles.mk30
-rwxr-xr-xsrc/lib/libssx.h20
-rwxr-xr-xsrc/lib/memcpy.c78
-rwxr-xr-xsrc/lib/memset.c118
-rwxr-xr-xsrc/lib/pgas.h1153
-rwxr-xr-xsrc/lib/pgas_ppc.h529
-rwxr-xr-xsrc/lib/pgp_config.h108
-rwxr-xr-xsrc/lib/pmc_dcm.c425
-rwxr-xr-xsrc/lib/pmc_dcm.h102
-rw-r--r--src/lib/polling.c73
-rw-r--r--src/lib/polling.h94
-rwxr-xr-xsrc/lib/pore_hooks.h171
-rwxr-xr-xsrc/lib/printf.c679
-rwxr-xr-xsrc/lib/pstates.c410
-rwxr-xr-xsrc/lib/pstates.h601
-rwxr-xr-xsrc/lib/puts.c95
-rwxr-xr-xsrc/lib/simics_stdio.c150
-rwxr-xr-xsrc/lib/simics_stdio.h69
-rwxr-xr-xsrc/lib/simics_stdio_addresses.h67
-rw-r--r--src/lib/special_wakeup.c149
-rw-r--r--src/lib/special_wakeup.h32
-rwxr-xr-xsrc/lib/sprintf.c112
-rw-r--r--src/lib/ssx_dump.c209
-rw-r--r--src/lib/ssx_dump.h58
-rwxr-xr-xsrc/lib/ssx_io.c311
-rwxr-xr-xsrc/lib/ssx_io.h204
-rwxr-xr-xsrc/lib/stdlib.c91
-rw-r--r--src/lib/strcasecmp.c66
-rwxr-xr-xsrc/lib/strdup.c39
-rwxr-xr-xsrc/lib/string.c168
-rwxr-xr-xsrc/lib/string.h64
-rwxr-xr-xsrc/lib/string_stream.c386
-rwxr-xr-xsrc/lib/string_stream.h253
-rwxr-xr-xsrc/lib/strtox.c593
-rwxr-xr-xsrc/lib/strtox.h127
-rwxr-xr-xsrc/lib/time.c58
-rwxr-xr-xsrc/lib/vrm.c394
-rwxr-xr-xsrc/lib/vrm.h59
64 files changed, 17181 insertions, 0 deletions
diff --git a/src/lib/Makefile b/src/lib/Makefile
new file mode 100755
index 0000000..c9e4640
--- /dev/null
+++ b/src/lib/Makefile
@@ -0,0 +1,79 @@
+# $Id: Makefile,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+#
+# This Makefile currently builds a single archive, 'libssx.a', from
+# various library source files.
+#
+# Beware that although this library can be built standalone, parts of
+# its behavior are controlled by options that may be configured in
+# the optional "ssx_app_cfg.h", so it is best to build the library as
+# part of the complete application build.
+#
+# The object files stored in the archive are loaded 'on demand'.
+# It may be possible to save a little code space therefore by splitting up
+# the implementation of a header file into multiple files whose
+# functions are not necessarily called together.
+
+SSX = ../ssx
+PGP = $(SSX)/pgp
+LIB = .
+
+include ./libfiles.mk
+include ./libgpefiles.mk
+INCLUDES =
+
+include $(PGP)/ssx.mk
+
+# >> gitprep
+# Disabled for GNU builds
+#If it's for OCC_FIRMWARE and non-PGAS_PPC build, the library gpefiles needs to be taken out of libssx.a
+#LIBSSX_OBJECTS += $(if $(filter -DOCC_FIRMWARE=1,$(DEFS)),\
+# $(if $(PGAS_PPC),$(LIB_PSOBJECTS),),\
+# $(LIB_PSOBJECTS))
+# << gitprep
+
+libssx.a: $(LIBSSX_OBJECTS)
+ $(AR) crs libssx.a $(LIBSSX_OBJECTS)
+
+clean:
+ rm -f *.o *.a *.d *.d.* *.lst *.hooks.cc
+ rm -f $(TEST_EXECUTABLES)
+
+disassemble: libssx.a
+ $(OBJDUMP) -d libssx.a
+
+
+# The targets 'push_updates_to_lab' and 'diff_with_lab' require that an
+# environment variable P8PROCS be defined. You can also define this on the
+# 'make' command line, for example
+#
+# make P8PROCS=<... path to your P8 procedures sandbox ...> \
+# push_updates_to_lab
+
+# LIB-TO-PROCS-FILES are files kept here that need to go there, where 'there'
+# is the top-level procedures directory.
+
+LIB-TO-PROCS-FILES = pgas.h pgas_ppc.h
+
+push_updates_to_lab:
+ if [ -z "$(P8PROCS)" ]; then echo "P8PROCS is not defined"; exit 1; fi
+ cp -u $(LIB-TO-PROCS-FILES) $(P8PROCS)
+
+diff_with_lab:
+ if [ -z "$(P8PROCS)" ]; then echo "P8PROCS is not defined"; exit 1; fi
+
+ for file in $(LIB-TO-PROCS-FILES); do \
+ echo "@@@ $$file"; \
+ diff $$file $(P8PROCS)/$$file; \
+ done; exit 0
+
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),push_updates_to_lab)
+ifneq ($(MAKECMDGOALS),diff_with_lab)
+include $(C-SOURCES:.c=.d)
+include $(S-SOURCES:.S=.d)
+include $(if $(filter -DOCC_FIRMWARE=1,$(DEFS)),$(if $(PGAS_PPC),$(pS-SOURCES:.pS=.d),),$(pS-SOURCES:.pS=.d))
+endif
+endif
+endif
+
diff --git a/src/lib/assert.c b/src/lib/assert.c
new file mode 100755
index 0000000..36e3caf
--- /dev/null
+++ b/src/lib/assert.c
@@ -0,0 +1,47 @@
+// $Id: assert.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/assert.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file assert.c
+/// \brief Implementation of library routines implied by <assert.h>
+
+#include "ssx.h"
+#include "ssx_io.h"
+#include "libssx.h"
+
+/// The __assert_fail() function is used to implement the assert() interface
+/// of ISO POSIX (2003). The __assert_fail() function prints the given \a
+/// file filename, \a line line number, \a function function name and a
+/// message on the standard error stream then causes a kernel panic. If there
+/// is no standard error stream then the error message is printed on the \a
+/// ssxout (printk()) stream.
+///
+/// If function is NULL, __assert_fail() omits information about the
+/// function. The aguments \a assertion, \a file, and \a line must be
+/// non-NULL.
+
+void
+__assert_fail(const char *assertion,
+ const char *file,
+ unsigned line,
+ const char *function)
+{
+ FILE *stream;
+
+ stream = stderr;
+ if (stream == 0) {
+ stream = ssxout;
+ }
+
+ fprintf(stream, "%s:%u:%s%s Assertion '%s' failed\n",
+ file, line,
+ function ? function : "", function ? ":" : "",
+ assertion);
+
+ SSX_PANIC(ASSERTION_FAILURE);
+}
+
diff --git a/src/lib/ctype.c b/src/lib/ctype.c
new file mode 100755
index 0000000..5b10635
--- /dev/null
+++ b/src/lib/ctype.c
@@ -0,0 +1,22 @@
+// $Id: ctype.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ctype.c
+/// \brief Replacement for <ctype.h> functions
+///
+/// This file contains entry point equivalents for the "ctype.h" macros.
+/// These would only ever be used by assembler programs, therefore it's likely
+/// that the object file will never be linked into an image.
+
+#define __CTYPE_C__
+#include "ctype.h"
+#undef __CTYPE_C__
+
+
+
+
diff --git a/src/lib/ctype.h b/src/lib/ctype.h
new file mode 100755
index 0000000..9072c41
--- /dev/null
+++ b/src/lib/ctype.h
@@ -0,0 +1,125 @@
+#ifndef __CTYPE_H__
+#define __CTYPE_H__
+
+// $Id: ctype.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ctype.h
+/// \brief Replacement for <ctype.h>
+///
+/// The Gnu <ctype.h> requires some locale information to be defined. We
+/// avoid this overhead and implement the simple functions, simply assuming
+/// standard 8-bit ASCII. The standard requires that these be defined as entry
+/// points, but may be defined as macros, therefore all macros defined by
+/// ctype.h are also be replicated in \c ctype.c
+
+// The reference for which characters are included in each set was:
+//
+// http://www.cplusplus.com/reference/clibrary/cctype/
+//
+// Note that no code was copied from the above or any other published
+// description of the ctype.h functionality.
+
+// To keep space to a minimum we encode the 8 common types directly into
+// an 8-bit mask. Other types take a little longer to compute.
+
+#define _CTYPE_CNTRL 0x01
+#define _CTYPE_SPACE 0x02
+#define _CTYPE_PRINT 0x04
+#define _CTYPE_PUNCT 0x08
+#define _CTYPE_UPPER 0x10
+#define _CTYPE_LOWER 0x20
+#define _CTYPE_DIGIT 0x40
+#define _CTYPE_XDIGIT 0x80
+
+#ifndef __ASSEMBLER__
+
+#include "stdint.h"
+
+/// \bug <ctype.h> can not include <stdio.h> to get the definition of
+/// EOF. This is because it causes conflicts withe the pore_inline* code which
+/// is portable (and ported) to Linux and PHYP. We need to go back through the
+/// way the includes are done in SSX and this library and fix this. We should
+/// have redefined <stdio.h> rather than creating a new "ssx_io.h"
+
+#define _CTYPE_EOF -1
+
+// Note that in all of the type macros, 'c' is an unsigned char.
+
+extern const uint8_t _ctype[256];
+
+#define _CTYPE_ISLOWER(c) (_ctype[c] & _CTYPE_LOWER)
+
+#define _CTYPE_ISUPPER(c) (_ctype[c] & _CTYPE_UPPER)
+
+#define _CTYPE_ISALPHA(c) (_CTYPE_ISUPPER(c) || _CTYPE_ISLOWER(c))
+
+#define _CTYPE_ISDIGIT(c) (_ctype[c] & _CTYPE_DIGIT)
+
+#define _CTYPE_ISALNUM(c) (_CTYPE_ISALPHA(c) || _CTYPE_ISDIGIT(c))
+
+#define _CTYPE_ISXDIGIT(c) (_ctype[c] & _CTYPE_XDIGIT)
+
+#define _CTYPE_ISCNTRL(c) (_ctype[c] & _CTYPE_CNTRL)
+
+#define _CTYPE_ISSPACE(c) (_ctype[c] & _CTYPE_SPACE)
+
+#define _CTYPE_ISPRINT(c) (_ctype[c] & _CTYPE_PRINT)
+
+#define _CTYPE_ISGRAPH(c) (_CTYPE_ISPRINT(c) && ((c) != 0x20))
+
+#define _CTYPE_ISPUNCT(c) (_ctype[c] & _CTYPE_PUNCT)
+
+#define _CTYPE_TOUPPER(c) (islower(c) ? ((c) + ('A' - 'a')) : (c))
+
+#define _CTYPE_TOLOWER(c) (isupper(c) ? ((c) - ('A' - 'a')) : (c))
+
+// When #include'ed into ctype.c, the non-inline forms of the functions are
+// created.
+//
+// Note that the specification requires that 'c' "must have the value of an
+// unsigned char or EOF". The specification also stipulates that "The values
+// returned are non-zero if the character c falls into the tested class, and a
+// zero value if not."
+
+#ifdef __CTYPE_C__
+#define _CTYPE_EXTERN_INLINE
+#else
+#define _CTYPE_EXTERN_INLINE extern inline
+#endif
+
+#define _CTYPE_PREDICATE(predicate, def) \
+ _CTYPE_EXTERN_INLINE int predicate(int c) { \
+ return ((c == _CTYPE_EOF) ? \
+ 0 : _CTYPE_##def((unsigned char)c)); \
+ }
+
+#define _CTYPE_FUNCTION(function, def) \
+ _CTYPE_EXTERN_INLINE int function(int c) { \
+ return ((c == _CTYPE_EOF) ? \
+ _CTYPE_EOF : _CTYPE_##def((unsigned char)c)); \
+ }
+
+_CTYPE_PREDICATE(islower, ISLOWER)
+_CTYPE_PREDICATE(isupper, ISUPPER)
+_CTYPE_PREDICATE(isalpha, ISALPHA)
+_CTYPE_PREDICATE(isdigit, ISDIGIT)
+_CTYPE_PREDICATE(isalnum, ISALNUM)
+_CTYPE_PREDICATE(isxdigit, ISXDIGIT)
+_CTYPE_PREDICATE(iscntrl, ISCNTRL)
+_CTYPE_PREDICATE(isspace, ISSPACE)
+_CTYPE_PREDICATE(isprint, ISPRINT)
+_CTYPE_PREDICATE(isgraph, ISGRAPH)
+_CTYPE_PREDICATE(ispunct, ISPUNCT)
+
+_CTYPE_FUNCTION(tolower, TOLOWER)
+_CTYPE_FUNCTION(toupper, TOUPPER)
+
+#endif // __ASSEMBLER__
+
+#endif /* __CTYPE_H__ */
diff --git a/src/lib/ctype_table.c b/src/lib/ctype_table.c
new file mode 100644
index 0000000..6f3e576
--- /dev/null
+++ b/src/lib/ctype_table.c
@@ -0,0 +1,278 @@
+// $Id: ctype_table.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype_table.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ctype_table.c
+/// \brief Character table for <ctype.h> functions.
+///
+/// This table is used by the <ctype.h> functions for a quick lookup of
+/// character type information. Because the true functional forms of <ctype.h>
+/// functions are likely never required, but this file is always required, it
+/// is stored separately from ctype.c to (slightly) reduce code/data space
+/// requirements.
+
+#include <stdint.h>
+#include <ctype.h>
+
+const uint8_t _ctype[256] = {
+ _CTYPE_CNTRL, /* 0 00 NUL Null char */
+ _CTYPE_CNTRL, /* 1 01 SOH Start of Heading */
+ _CTYPE_CNTRL, /* 2 02 STX Start of Text */
+ _CTYPE_CNTRL, /* 3 03 ETX End of Text */
+ _CTYPE_CNTRL, /* 4 04 EOT End of Transmission */
+ _CTYPE_CNTRL, /* 5 05 ENQ Enquiry */
+ _CTYPE_CNTRL, /* 6 06 ACK Acknowledgment */
+ _CTYPE_CNTRL, /* 7 07 BEL Bell */
+ _CTYPE_CNTRL, /* 8 08 BS Back Space */
+ _CTYPE_CNTRL | _CTYPE_SPACE, /* 9 09 HT Horizontal Tab */
+ _CTYPE_CNTRL | _CTYPE_SPACE, /* 10 0A LF Line Feed */
+ _CTYPE_CNTRL | _CTYPE_SPACE, /* 11 0B VT Vertical Tab */
+ _CTYPE_CNTRL | _CTYPE_SPACE, /* 12 0C FF Form Feed */
+ _CTYPE_CNTRL | _CTYPE_SPACE, /* 13 0D CR Carriage Return */
+ _CTYPE_CNTRL, /* 14 0E SO Shift Out / X-On */
+ _CTYPE_CNTRL, /* 15 0F SI Shift In / X-Off */
+ 0, /* 16 10 DLE Data Line Escape */
+ 0, /* 17 11 DC1 Device Control 1 (oft. XON) */
+ 0, /* 18 12 DC2 Device Control 2 */
+ 0, /* 19 13 DC3 Device Control 3 (oft. XOFF) */
+ 0, /* 20 14 DC4 Device Control 4 */
+ 0, /* 21 15 NAK Negative Acknowledgement */
+ 0, /* 22 16 SYN Synchronous Idle */
+ 0, /* 23 17 ETB End of Transmit Block */
+ 0, /* 24 18 CAN Cancel */
+ 0, /* 25 19 EM End of Medium */
+ 0, /* 26 1A SUB Substitute */
+ 0, /* 27 1B ESC Escape */
+ 0, /* 28 1C FS File Separator */
+ 0, /* 29 1D GS Group Separator */
+ 0, /* 30 1E RS Record Separator */
+ 0, /* 31 1F US Unit Separator */
+ _CTYPE_PRINT | _CTYPE_SPACE, /* 32 20 Space */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 33 21 ! Exclamation mark */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 34 22 " Double quotes (or speech marks) */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 35 23 # Number */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 36 24 $ Dollar */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 37 25 % Procenttecken */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 38 26 & Ampersand */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 39 27 ' Single quote */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 40 28 ( Open parenthesis (or open bracket) */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 41 29 ) Close parenthesis (or close bracket) */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 42 2A * Asterisk */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 43 2B + Plus */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 44 2C , Comma */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 45 2D - Hyphen */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 46 2E . Period, dot or full stop */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 47 2F / Slash or divide */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 48 30 0 Zero */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 49 31 1 One */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 50 32 2 Two */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 51 33 3 Three */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 52 34 4 Four */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 53 35 5 Five */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 54 36 6 Six */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 55 37 7 Seven */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 56 38 8 Eight */
+ _CTYPE_PRINT | _CTYPE_DIGIT, /* 57 39 9 Nine */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 58 3A : Colon */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 59 3B ; Semicolon */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 60 3C < Less than (or open angled bracket) */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 61 3D = Equals */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 62 3E > Greater than (or close angled bracket) */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 63 3F ? Question mark */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 64 40 @ At symbol */
+ _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 65 41 A Uppercase A */
+ _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 66 42 B Uppercase B */
+ _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 67 43 C Uppercase C */
+ _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 68 44 D Uppercase D */
+ _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 69 45 E Uppercase E */
+ _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 70 46 F Uppercase F */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 71 47 G Uppercase G */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 72 48 H Uppercase H */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 73 49 I Uppercase I */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 74 4A J Uppercase J */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 75 4B K Uppercase K */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 76 4C L Uppercase L */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 77 4D M Uppercase M */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 78 4E N Uppercase N */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 79 4F O Uppercase O */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 80 50 P Uppercase P */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 81 51 Q Uppercase Q */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 82 52 R Uppercase R */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 83 53 S Uppercase S */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 84 54 T Uppercase T */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 85 55 U Uppercase U */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 86 56 V Uppercase V */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 87 57 W Uppercase W */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 88 58 X Uppercase X */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 89 59 Y Uppercase Y */
+ _CTYPE_PRINT | _CTYPE_UPPER, /* 90 5A Z Uppercase Z */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 91 5B [ Opening bracket */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 92 5C \ Backslash */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 93 5D ] Closing bracket */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 94 5E ^ Caret - circumflex */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 95 5F _ Underscore */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 96 60 ` Grave accent */
+ _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 97 61 a Lowercase a */
+ _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 98 62 b Lowercase b */
+ _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 99 63 c Lowercase c */
+ _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 100 64 d Lowercase d */
+ _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 101 65 e Lowercase e */
+ _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 102 66 f Lowercase f */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 103 67 g Lowercase g */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 104 68 h Lowercase h */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 105 69 i Lowercase i */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 106 6A j Lowercase j */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 107 6B k Lowercase k */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 108 6C l Lowercase l */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 109 6D m Lowercase m */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 110 6E n Lowercase n */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 111 6F o Lowercase o */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 112 70 p Lowercase p */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 113 71 q Lowercase q */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 114 72 r Lowercase r */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 115 73 s Lowercase s */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 116 74 t Lowercase t */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 117 75 u Lowercase u */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 118 76 v Lowercase v */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 119 77 w Lowercase w */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 120 78 x Lowercase x */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 121 79 y Lowercase y */
+ _CTYPE_PRINT | _CTYPE_LOWER, /* 122 7A z Lowercase z */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 123 7B { Opening brace */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 124 7C | Vertical bar */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 125 7D } Closing brace */
+ _CTYPE_PRINT | _CTYPE_PUNCT, /* 126 7E ~ Equivalency sign - tilde */
+ 0, /* 127 7F Delete */
+ 0, /* 128 80 Euro sign */
+ 0, /* 129 81 */
+ 0, /* 130 82 ' Single low-9 quotation mark */
+ 0, /* 131 83 Latin small letter f with hook */
+ 0, /* 132 84 " Double low-9 quotation mark */
+ 0, /* 133 85 Horizontal ellipsis */
+ 0, /* 134 86 Dagger */
+ 0, /* 135 87 Double dagger */
+ 0, /* 136 88 Modifier letter circumflex accent */
+ 0, /* 137 89 Per mille sign */
+ 0, /* 138 8A Latin capital letter S with caron */
+ 0, /* 139 8B Single left-pointing angle quotation */
+ 0, /* 140 8C Latin capital ligature OE */
+ 0, /* 141 8D */
+ 0, /* 142 8E Latin captial letter Z with caron */
+ 0, /* 143 8F */
+ 0, /* 144 90 */
+ 0, /* 145 91 ' Left single quotation mark */
+ 0, /* 146 92 ' Right single quotation mark */
+ 0, /* 147 93 " Left double quotation mark */
+ 0, /* 148 94 " Right double quotation mark */
+ 0, /* 149 95 Bullet */
+ 0, /* 150 96 En dash */
+ 0, /* 151 97 Em dash */
+ 0, /* 152 98 Small tilde */
+ 0, /* 153 99 Trade mark sign */
+ 0, /* 154 9A Latin small letter S with caron */
+ 0, /* 155 9B Single right-pointing angle quotation mark */
+ 0, /* 156 9C Latin small ligature oe */
+ 0, /* 157 9D */
+ 0, /* 158 9E Latin small letter z with caron */
+ 0, /* 159 9F Latin capital letter Y with diaeresis */
+ 0, /* 160 A0 Non-breaking space */
+ 0, /* 161 A1 Inverted exclamation mark */
+ 0, /* 162 A2 Cent sign */
+ 0, /* 163 A3 Pound sign */
+ 0, /* 164 A4 Currency sign */
+ 0, /* 165 A5 Yen sign */
+ 0, /* 166 A6 Pipe, Broken vertical bar */
+ 0, /* 167 A7 Section sign */
+ 0, /* 168 A8 Spacing diaeresis - umlaut */
+ 0, /* 169 A9 Copyright sign */
+ 0, /* 170 AA Feminine ordinal indicator */
+ 0, /* 171 AB Left double angle quotes */
+ 0, /* 172 AC Not sign */
+ 0, /* 173 AD Soft hyphen */
+ 0, /* 174 AE Registered trade mark sign */
+ 0, /* 175 AF Spacing macron - overline */
+ 0, /* 176 B0 Degree sign */
+ 0, /* 177 B1 Plus-or-minus sign */
+ 0, /* 178 B2 Superscript two - squared */
+ 0, /* 179 B3 Superscript three - cubed */
+ 0, /* 180 B4 Acute accent - spacing acute */
+ 0, /* 181 B5 Micro sign */
+ 0, /* 182 B6 Pilcrow sign - paragraph sign */
+ 0, /* 183 B7 Middle dot - Georgian comma */
+ 0, /* 184 B8 Spacing cedilla */
+ 0, /* 185 B9 Superscript one */
+ 0, /* 186 BA Masculine ordinal indicator */
+ 0, /* 187 BB Right double angle quotes */
+ 0, /* 188 BC one quarter */
+ 0, /* 189 BD Fraction one half */
+ 0, /* 190 BE Fraction three quarters */
+ 0, /* 191 BF Inverted question mark */
+ 0, /* 192 C0 Latin capital letter A with grave */
+ 0, /* 193 C1 Latin capital letter A with acute */
+ 0, /* 194 C2 Latin capital letter A with circumflex */
+ 0, /* 195 C3 Latin capital letter A with tilde */
+ 0, /* 196 C4 Latin capital letter A with diaeresis */
+ 0, /* 197 C5 Latin capital letter A with ring above */
+ 0, /* 198 C6 Latin capital letter AE */
+ 0, /* 199 C7 Latin capital letter C with cedilla */
+ 0, /* 200 C8 Latin capital letter E with grave */
+ 0, /* 201 C9 Latin capital letter E with acute */
+ 0, /* 202 CA Latin capital letter E with circumflex */
+ 0, /* 203 CB Latin capital letter E with diaeresis */
+ 0, /* 204 CC Latin capital letter I with grave */
+ 0, /* 205 CD Latin capital letter I with acute */
+ 0, /* 206 CE Latin capital letter I with circumflex */
+ 0, /* 207 CF Latin capital letter I with diaeresis */
+ 0, /* 208 D0 Latin capital letter ETH */
+ 0, /* 209 D1 Latin capital letter N with tilde */
+ 0, /* 210 D2 Latin capital letter O with grave */
+ 0, /* 211 D3 Latin capital letter O with acute */
+ 0, /* 212 D4 Latin capital letter O with circumflex */
+ 0, /* 213 D5 Latin capital letter O with tilde */
+ 0, /* 214 D6 Latin capital letter O with diaeresis */
+ 0, /* 215 D7 Multiplication sign */
+ 0, /* 216 D8 Latin capital letter O with slash */
+ 0, /* 217 D9 Latin capital letter U with grave */
+ 0, /* 218 DA Latin capital letter U with acute */
+ 0, /* 219 DB Latin capital letter U with circumflex */
+ 0, /* 220 DC Latin capital letter U with diaeresis */
+ 0, /* 221 DD Latin capital letter Y with acute */
+ 0, /* 222 DE Latin capital letter THORN */
+ 0, /* 223 DF Latin small letter sharp s - ess-zed */
+ 0, /* 224 E0 Latin small letter a with grave */
+ 0, /* 225 E1 Latin small letter a with acute */
+ 0, /* 226 E2 Latin small letter a with circumflex */
+ 0, /* 227 E3 Latin small letter a with tilde */
+ 0, /* 228 E4 Latin small letter a with diaeresis */
+ 0, /* 229 E5 Latin small letter a with ring above */
+ 0, /* 230 E6 Latin small letter ae */
+ 0, /* 231 E7 Latin small letter c with cedilla */
+ 0, /* 232 E8 Latin small letter e with grave */
+ 0, /* 233 E9 Latin small letter e with acute */
+ 0, /* 234 EA Latin small letter e with circumflex */
+ 0, /* 235 EB Latin small letter e with diaeresis */
+ 0, /* 236 EC Latin small letter i with grave */
+ 0, /* 237 ED Latin small letter i with acute */
+ 0, /* 238 EE Latin small letter i with circumflex */
+ 0, /* 239 EF Latin small letter i with diaeresis */
+ 0, /* 240 F0 Latin small letter eth */
+ 0, /* 241 F1 Latin small letter n with tilde */
+ 0, /* 242 F2 Latin small letter o with grave */
+ 0, /* 243 F3 Latin small letter o with acute */
+ 0, /* 244 F4 Latin small letter o with circumflex */
+ 0, /* 245 F5 Latin small letter o with tilde */
+ 0, /* 246 F6 Latin small letter o with diaeresis */
+ 0, /* 247 F7 Division sign */
+ 0, /* 248 F8 Latin small letter o with slash */
+ 0, /* 249 F9 Latin small letter u with grave */
+ 0, /* 250 FA Latin small letter u with acute */
+ 0, /* 251 FB Latin small letter u with circumflex */
+ 0, /* 252 FC Latin small letter u with diaeresis */
+ 0, /* 253 FD Latin small letter y with acute */
+ 0, /* 254 FE Latin small letter thorn */
+ 0, /* 255 FF Latin small letter y with diaeresis */
+};
diff --git a/src/lib/errno.h b/src/lib/errno.h
new file mode 100755
index 0000000..a45bb84
--- /dev/null
+++ b/src/lib/errno.h
@@ -0,0 +1,25 @@
+#ifndef __ERRNO_H__
+#define __ERRNO_H__
+
+// $Id: errno.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/errno.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file errno.h
+/// \brief Replacement for <errno.h>
+///
+/// SSX does not support a per-thread or global 'errno'. The standard Unix
+/// errno values returned by library functions are defined here. The prefix
+/// code is the 'telephone code' for "errn".
+
+#define EINVAL 0x00377601
+#define EBADF 0x00377602
+#define EAGAIN 0x00377603
+#define ENXIO 0x00377604
+#define ENOMEM 0x00377605
+
+#endif /* __ERRNO_H__ */
diff --git a/src/lib/fgetc.c b/src/lib/fgetc.c
new file mode 100755
index 0000000..e4e4a49
--- /dev/null
+++ b/src/lib/fgetc.c
@@ -0,0 +1,88 @@
+// $Id: fgetc.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/fgetc.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file fgetc.c
+/// \brief Implementation of fgetc() and ungetc()
+///
+/// The implementations of these APIs are split out to save code space for
+/// applications that do not require them.
+
+#include "ssx_io.h"
+
+
+/// Read a character from a stream
+///
+/// fgetc() reads the next character from \a stream and returns it as an
+/// unsigned char cast to an int, or EOF on end of file or error.
+
+int
+fgetc(FILE* stream)
+{
+ unsigned char c;
+ size_t read;
+ int rc;
+
+ if (stream->flags & SSX_FILE_HAS_CHARACTER) {
+ stream->flags &= ~SSX_FILE_HAS_CHARACTER;
+ rc = stream->character;
+ } else {
+ rc = sread(stream, &c, 1, &read);
+ if (rc || (read != 1)) {
+ rc = EOF;
+ } else {
+ rc = c;
+ if (c == '\n') {
+ stream->lines++;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/// Push a character back onto a stream
+///
+/// ungetc() pushes \a c back to \a stream, cast to unsigned char, where it is
+/// available for subsequent fgetc() operations. Only one pushback is
+/// implemented. A call of ungetc() on a stream that already has a character
+/// pushed back will drop the new push-back and return EOF. Otherwise
+/// ungetc() returns \a c.
+
+int
+ungetc(int c, FILE* stream)
+{
+ int rc;
+
+ if (stream->flags & SSX_FILE_HAS_CHARACTER) {
+ rc = EOF;
+ } else {
+ stream->flags |= SSX_FILE_HAS_CHARACTER;
+ stream->character = c;
+ rc = c;
+ }
+ return rc;
+}
+
+/// Return the number of newline characters read from a stream
+///
+/// This API is an SSX entension to the \<stdio\> APIs. It returns the number
+/// of newline characters read from the stream using fgetc(). Newline
+/// characters read via direct calls to sread() in the stream are not counted.
+///
+/// An application that sees an error while reading from a stream can print
+/// flines() or flines() + 1 (depending on the application) to help users
+/// track down errors in their input.
+size_t
+flines(FILE* stream)
+{
+ return stream->lines;
+}
+
+
+
+
diff --git a/src/lib/gpe.h b/src/lib/gpe.h
new file mode 100755
index 0000000..e759c7d
--- /dev/null
+++ b/src/lib/gpe.h
@@ -0,0 +1,78 @@
+#ifndef __GPE_H__
+#define __GPE_H__
+
+// $Id: gpe.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe.h
+/// \brief Useful PGAS macros for PORE-GPE procedures
+
+#include "pgas.h"
+
+#ifdef __ASSEMBLER__
+#ifdef __PGAS__
+
+// Required to guarantee that the .purgem below always works. .purgem on
+// undefined macros causes an error; There appears to be no way to determine
+// if a macro is defined.
+#include "ppc32_asm.h"
+
+
+ // All GPE code should be assembled in the .text.pore section, and
+ // all GPE data should be assembled in the .data.pore section.
+
+ .macro .text.pore
+ .section .text.pore, "ax", @progbits
+ .balign 4
+ .endm
+
+
+ .macro .data.pore
+ .section .data.pore, "a", @progbits
+ .balign 8
+ .endm
+
+ .purgem .function
+ .macro .function symbol
+ .text.pore
+ .align 2
+ .endm
+
+ .purgem .global_function
+ .macro .global_function symbol
+ .text.pore
+ .align 2
+ .global \symbol
+ .endm
+
+
+ // Get the CFAM Id right-justified in a Dx register, scratching a Px
+ // register in the process.
+
+ .macro cfam_id, Dx:req, Px:req
+ ..data (\Dx)
+ ..pervasive_chiplet_id (\Px)
+ lpcs (\Px), 0x000f000f
+ ldandi (\Dx), 0x000f000f, (\Px), 0xffffffff00000000
+ rols (\Dx), (\Dx), 32
+ .endm
+
+
+ // This macro defines structure offsets for PORE assembler-versions of
+ // structures.
+
+ .macro .gpeStructField, field:req, size=8
+\field\():
+ .struct \field + (\size)
+ .endm
+
+
+#endif // __PGAS__
+#endif // __ASSEMBLER__
+
+#endif // __GPE_H__
diff --git a/src/lib/gpe_control.h b/src/lib/gpe_control.h
new file mode 100755
index 0000000..355330f
--- /dev/null
+++ b/src/lib/gpe_control.h
@@ -0,0 +1,171 @@
+#ifndef __GPE_CONTROL_H__
+#define __GPE_CONTROL_H__
+
+// $Id: gpe_control.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe_control.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_control.h
+/// \brief GPE procedures for control
+
+#include "pstates.h"
+#include "pgp_config.h"
+
+////////////////////////////////////////////////////////////////////////////
+// PcbsPstateRegs
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef __ASSEMBLER__
+
+/// Per-core Pstate control registers
+///
+/// Firmware maintains a vector of PcbsPstateRegs structures - one for
+/// each core on the chip - and updates the register fields in place. The GPE
+/// procedure gpe_set_pstates() is run periodically to update the core
+/// chiplets from this data structure. The array can (should) be cleared
+/// initially.
+
+typedef struct {
+
+ /// The clipping register
+ pcbs_power_management_bounds_reg_t pmbr;
+
+ /// The idle control register
+ pcbs_power_management_idle_control_reg_t pmicr;
+
+ /// The Pstate control register
+ pcbs_power_management_control_reg_t pmcr;
+
+} PcbsPstateRegs;
+
+#endif /* __ASSEMBLER__ */
+
+// Offsets into PcbsPstateRegs
+
+#define PCBSPSTATEREGS_PMBR 0x00
+#define PCBSPSTATEREGS_PMICR 0x08
+#define PCBSPSTATEREGS_PMCR 0x10
+
+#define SIZEOF_PCBSPSTATEREGS 0x18
+
+
+#ifndef __ASSEMBLER__
+
+/// Set a chiplet Pmax clipping Pstate
+static inline void
+set_chiplet_pmax(PcbsPstateRegs *regs, int chiplet, Pstate pmax)
+{
+ regs[chiplet].pmbr.fields.pmax_clip = pmax;
+}
+
+/// Set a chiplet Pmin clipping Pstate
+static inline void
+set_chiplet_pmin(PcbsPstateRegs *regs, int chiplet, Pstate pmin)
+{
+ regs[chiplet].pmbr.fields.pmin_clip = pmin;
+}
+
+/// Set chiplet Global and Local Pstate requests
+static inline void
+set_chiplet_pstate(PcbsPstateRegs *regs, int chiplet,
+ Pstate global, Pstate local)
+{
+ regs[chiplet].pmcr.fields.global_pstate_req = global;
+ regs[chiplet].pmcr.fields.local_pstate_req = local;
+}
+
+/// Enable/Disable/Configure chiplet Nap Pstates
+static inline void
+set_chiplet_nap_pstate(PcbsPstateRegs *regs, int chiplet,
+ Pstate pstate, int enable, int global, int latency)
+{
+ regs[chiplet].pmicr.fields.nap_pstate_req = pstate;
+ regs[chiplet].pmicr.fields.nap_pstate_en = (enable != 0);
+ regs[chiplet].pmicr.fields.nap_global_en = (global != 0);
+ regs[chiplet].pmicr.fields.nap_latency = latency;
+}
+
+/// Enable/Disable/Configure chiplet Sleep Pstates
+static inline void
+set_chiplet_sleep_pstate(PcbsPstateRegs *regs, int chiplet,
+ Pstate pstate, int enable, int global, int latency)
+{
+ regs[chiplet].pmicr.fields.sleep_pstate_req = pstate;
+ regs[chiplet].pmicr.fields.sleep_pstate_en = (enable != 0);
+ regs[chiplet].pmicr.fields.sleep_global_en = (global != 0);
+ regs[chiplet].pmicr.fields.sleep_latency = latency;
+}
+
+/// Enable/Disable/Configure chiplet Winkle Pstates
+static inline void
+set_chiplet_winkle_pstate(PcbsPstateRegs *regs, int chiplet,
+ Pstate pstate, int enable, int global, int latency)
+{
+ regs[chiplet].pmicr.fields.winkle_pstate_req = pstate;
+ regs[chiplet].pmicr.fields.winkle_pstate_en = (enable != 0);
+ regs[chiplet].pmicr.fields.winkle_global_en = (global != 0);
+ regs[chiplet].pmicr.fields.winkle_latency = latency;
+}
+
+#endif /* __ASSEMBLER__ */
+
+/// \bug These need to be defined and documented
+
+#define SLEEP_LATENCY_DISABLED 0
+#define SLEEP_LATENCY_CLOCKS_OFF 1
+#define SLEEP_LATENCY_FAST 2
+#define SLEEP_LATENCY_DEEP 3
+
+#define WINKLE_LATENCY_DISABLED 0
+#define WINKLE_LATENCY_CLOCKS_OFF 1
+#define WINKLE_LATENCY_FAST 2
+#define WINKLE_LATENCY_DEEP 3
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_set_pstates()
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef __ASSEMBLER__
+
+/// Parameters for the GPE procedure gpe_set_pstates()
+
+typedef struct {
+
+ // The chip configuration (for actuation purposes). Only those core
+ // chiplets with bits set in the mask will be actuated.
+ ChipConfig config;
+
+ /// This mask, comprised of a logical OR of the GPE_SET_PSTATE_*
+ /// macros, controls which register(s) is(are) actuated for each core.
+ uint64_t select;
+
+ /// The 32-bit pointer to the array of PcbsPstateRegs holding the register
+ /// data, coerced to a 64-bit unsigned. The real 32-bit pointer must be
+ /// the low-order 32 bits of this value.
+ uint64_t regs;
+
+} GpeSetPstatesParms;
+
+PoreEntryPoint gpe_set_pstates;
+
+#endif /* __ASSEMBLER__ */
+
+// Parameter offsets for gpe_set_pstates()
+
+#define GPESETPSTATESPARMS_CONFIG 0x00
+#define GPESETPSTATESPARMS_SELECT 0x08
+#define GPESETPSTATESPARMS_REGS 0x10
+
+// Register/Function select masks for gpe_set_pstates()
+
+#define GPE_SET_PSTATES_PMBR 0x01
+#define GPE_SET_PSTATES_PMICR 0x02
+#define GPE_SET_PSTATES_PMCR 0x04
+#define GPE_SET_PSTATES_SYNC 0x08
+
+#endif /* __GPE_CONTROL_H__ */
diff --git a/src/lib/gpe_control.pS b/src/lib/gpe_control.pS
new file mode 100755
index 0000000..3f3c790
--- /dev/null
+++ b/src/lib/gpe_control.pS
@@ -0,0 +1,160 @@
+// $Id: gpe_control.pS,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe_control.pS,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_control.S
+/// \brief GPE procedures for control
+
+ .nolist
+
+#include "ssx.h"
+#include "pgas.h"
+#include "gpe.h"
+#include "gpe_control.h"
+
+ .list
+
+ .oci
+ .text.pore
+
+/// \fn gpe_set_pstates(GpeSetPstatesParms *parms)
+/// \brief Set core chiplet Pstate registers
+///
+/// This routine loops through an array of PcbsPstateRegs structures
+/// holding the register images to be actuated. A pointer to the array is
+/// provided as the \a registers parameter. For every core chiplet
+/// appearing in the \a cfg parameter, those registers marked in the \a select
+/// parameter are updated.
+///
+/// When the PMBR is being updated, an option is provided to
+/// set PCBS_PCBSPM_MODE_REG[enable_pmax_sync_interrupt] around the
+/// update of PMBR. This will cause a SYNC interrupt from each
+/// core. This mode currently does not set up the sync protocol in the PMC -
+/// the caller must do that.
+///
+/// Note that actuating the PMCR and PMICR using this method requires that the
+/// PCB Slave bit PMGP0_REG.pm_spr_override_en is set.
+#ifdef DOXYGEN_ONLY
+void gpe_set_pstates(GpeSetPstatesParms *parms);
+#endif
+/// \cond
+
+ // Register usage:
+ //
+ // A1 : Holds the (constant) pointer to the paramaters
+ // A0 : Holds the (varying) pointer to the next register block
+ // D1 : Holds the (varying) ChipConfig mask
+ // D0 : Scratch
+ // P0 : Holds the (varying) chiplet id
+
+ .global gpe_set_pstates
+
+gpe_set_pstates:
+
+ // Set up registers. The chiplet part of the ChipConfig is left
+ // justified in D1, which will be rotated on each loop.
+
+ mr A1, ETR
+ ld D0, GPESETPSTATESPARMS_REGS, A1
+ mr A0, D0
+ ld D0, GPESETPSTATESPARMS_CONFIG, A1
+ mr D1, D0
+ left_justify_core_config D1
+ lpcs P0, 0x10000000 # Load P0 with core chiplet 0 address
+ ls CTR, PGP_NCORES
+ bra start_loop
+
+set_pstates_loop:
+ // If the chiplet is not configured, simply continue
+
+ andi D0, D1, 0x8000000000000000
+ braz D0, set_pstates_continue
+
+ // Test/actuate each register in order
+
+ ldandi D0, GPESETPSTATESPARMS_SELECT, A1, GPE_SET_PSTATES_PMBR
+ braz D0, pmicr
+
+ // PMBR.
+
+ // If SYNCing, the register write is wrapped by a read-modify-write of
+ // the PCBS_PCBSPM_MODE_REG which enables the PMAX Sync
+ // acknowledge. Note that the original PCBSPM mode reg is saved and
+ // restored.
+pmbr:
+ ldandi D0, GPESETPSTATESPARMS_SELECT, A1, GPE_SET_PSTATES_SYNC
+ braz D0, nosync
+
+sync:
+ ld D0, PCBS_PCBSPM_MODE_REG, P0
+ la A1, gsp_pcbs_pcbspm_mode_reg
+ std D0, 0, A1
+ ori D0, D0, PCBS_PCBSPM_MODE_REG_ENABLE_PMC_PMAX_SYNC_NOTIFICATION
+ std D0, PCBS_PCBSPM_MODE_REG, P0
+
+ ld D0, PCBSPSTATEREGS_PMBR, A0
+ std D0, PCBS_POWER_MANAGEMENT_BOUNDS_REG, P0
+
+ ld D0, 0, A1
+ std D0, PCBS_PCBSPM_MODE_REG, P0
+ // restore A1
+ mr A1, ETR
+ bra pmicr
+
+nosync:
+ ld D0, PCBSPSTATEREGS_PMBR, A0
+ std D0, PCBS_POWER_MANAGEMENT_BOUNDS_REG, P0
+
+ // PMICR
+pmicr:
+ ldandi D0, GPESETPSTATESPARMS_SELECT, A1, GPE_SET_PSTATES_PMICR
+ braz D0, pmcr
+ ld D0, PCBSPSTATEREGS_PMICR, A0
+ std D0, PCBS_POWER_MANAGEMENT_IDLE_CONTROL_REG, P0
+
+ // PMCR
+pmcr:
+ ldandi D0, GPESETPSTATESPARMS_SELECT, A1, GPE_SET_PSTATES_PMCR
+ braz D0, set_pstates_continue
+ ld D0, PCBSPSTATEREGS_PMCR, A0
+ std D0, PCBS_POWER_MANAGEMENT_CONTROL_REG, P0
+
+set_pstates_continue:
+
+ // Increment the chiplet index and data pointer, then loop or halt.
+
+ adds P0, P0, 1
+ adds A0, A0, SIZEOF_PCBSPSTATEREGS
+ rotldi D1, D1, 1
+start_loop:
+ loop set_pstates_loop
+
+ halt
+
+ .epilogue gpe_set_pstates
+
+/// \endcond
+
+/// Data storage for procedures.
+/// Placing data in the .rodata section to prevent the 405 from stomping them.
+ .section .rodata
+ .balign 8
+/// data for gpe_set_pstates
+
+
+
+
+
+
+
+
+
+/// \cond
+
+gsp_pcbs_pcbspm_mode_reg:
+ .quad 0
+/// \endcond
diff --git a/src/lib/gpe_data.h b/src/lib/gpe_data.h
new file mode 100755
index 0000000..790d82b
--- /dev/null
+++ b/src/lib/gpe_data.h
@@ -0,0 +1,672 @@
+#ifndef __GPE_DATA_H__
+#define __GPE_DATA_H__
+
+// $Id: gpe_data.h,v 820.1 2014/08/22 16:33:56 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/fw820/procedures/lib/gpe_data.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_data.h
+/// \brief Data structures for the GPE programs that collect raw data defined
+/// in gpe_data.S. The data structure layouts are also documented in the
+/// spreadsheet "Pgp Procedures.ods" in lib/doc.
+///
+/// \todo Add SPURR Fraction update as an option
+
+#include "ssx.h"
+#include "gpe.h"
+#include "pgp_config.h"
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_core_data()
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef __ASSEMBLER__
+
+/// Paramaters for gpe_get_core_data() & gpe_get_per_core_data()
+
+typedef struct {
+
+ /// gpe_get_core_data() and gpe_get_per_core_data() only collects data for
+ /// core chiplets configured in this mask.
+ ChipConfig config;
+
+ /// This mask, comprised of a logical OR of the GPE_GET_CORE_DATA_*
+ /// macros, controls which data groups are collected.
+ uint64_t select;
+
+ /// This is the 32-bit pointer (cast to a uint64_t) to the core chiplet
+ /// raw data area to be filled by this invocation of gpe_get_core_data().
+ ///
+ /// For gpe_get_core_data() (used by the lab thread coreData) this is a
+ /// pointer to an array of CoreData structures, with one structure
+ /// allocated for each possible core supported by the architecture.
+ ///
+ /// For gpe_get_per_core_data() (used by OCC FW) this is a pointer to a
+ /// single CoreData structure to be filled in by the routine.
+ uint64_t data;
+
+} GpeGetCoreDataParms;
+
+// Get data for all cores, placing data at an index into
+// GpeGetCoreDataParms->data pointer, depending on the core which got data.
+PoreEntryPoint gpe_get_core_data;
+
+// Get data for first core in GpeGetCoreDataParms->config, placing data
+// directly into GpeGetCoreDataParms->data pointer
+PoreEntryPoint gpe_get_per_core_data;
+
+#endif /* __ASSEMBLER__ */
+
+// Parameter offsets for gpe_get_core_data()
+
+#define GPEGETCOREDATAPARMS_CONFIG 0x00
+#define GPEGETCOREDATAPARMS_SELECT 0x08
+#define GPEGETCOREDATAPARMS_DATA 0x10
+
+// Data group select masks for gpe_get_core_data()
+
+#define GPE_GET_CORE_DATA_EMPATH 0x0001
+#define GPE_GET_CORE_DATA_MEMORY 0x0002
+#define GPE_GET_CORE_DATA_THROTTLE 0x0004
+#define GPE_GET_CORE_DATA_THREAD 0x0008
+#define GPE_GET_CORE_DATA_DTS_CPM 0x0010
+#define GPE_GET_CORE_DATA_PCB_SLAVE 0x0020
+
+#define GPE_GET_CORE_DATA_ALL 0x003f
+
+// Per-core data area offsets
+
+#define CORE_DATA_EMPATH_BASE 0
+#define CORE_DATA_EMPATH_SIZE 40
+
+#define CORE_DATA_MEMORY_BASE \
+ (CORE_DATA_EMPATH_BASE + CORE_DATA_EMPATH_SIZE)
+#define CORE_DATA_MEMORY_SIZE 24
+
+#define CORE_DATA_THROTTLE_BASE \
+ (CORE_DATA_MEMORY_BASE + CORE_DATA_MEMORY_SIZE)
+#define CORE_DATA_THROTTLE_SIZE 24
+
+#define CORE_DATA_THREAD_BASE(t) \
+ (CORE_DATA_THROTTLE_BASE + CORE_DATA_THROTTLE_SIZE + (24 * (t)))
+#define CORE_DATA_THREAD_SIZE (24 * 8)
+
+#define CORE_DATA_DTS_CPM_BASE \
+ (CORE_DATA_THREAD_BASE(0) + CORE_DATA_THREAD_SIZE)
+#define CORE_DATA_DTS_CPM_SIZE 40
+
+#define CORE_DATA_PCB_SLAVE_BASE \
+ (CORE_DATA_DTS_CPM_BASE + CORE_DATA_DTS_CPM_SIZE)
+#define CORE_DATA_PCB_SLAVE_SIZE 32
+
+#define CORE_DATA_OHA_BASE \
+ (CORE_DATA_PCB_SLAVE_BASE + CORE_DATA_PCB_SLAVE_SIZE)
+#define CORE_DATA_OHA_SIZE 8
+
+#define CORE_DATA_SIZE (CORE_DATA_OHA_BASE + CORE_DATA_OHA_SIZE)
+
+// Data area components. Each data group is marked with the TOD captured just
+// before each data group capture. Data groups that may have some relation to
+// frequency are also tagged with the current raw cycles reading. The offsets
+// are _byte_ offsets into a byte array. The user needs to be aware of
+// whether each datum represents a 32- or 64-bit integer.
+
+#define CORE_DATA_EMPATH_UNUSED (CORE_DATA_EMPATH_BASE + 0x00)
+#define CORE_DATA_EMPATH_TOD (CORE_DATA_EMPATH_BASE + 0x04)
+#define CORE_DATA_DISPATCH (CORE_DATA_EMPATH_BASE + 0x08)
+#define CORE_DATA_COMPLETION (CORE_DATA_EMPATH_BASE + 0x0c)
+#define CORE_DATA_FREQ_SENS_BUSY (CORE_DATA_EMPATH_BASE + 0x10)
+#define CORE_DATA_FREQ_SENS_FINISH (CORE_DATA_EMPATH_BASE + 0x14)
+#define CORE_DATA_RUN_CYCLES (CORE_DATA_EMPATH_BASE + 0x18)
+#define CORE_DATA_RAW_CYCLES (CORE_DATA_EMPATH_BASE + 0x1c)
+#define CORE_DATA_MEM_A (CORE_DATA_EMPATH_BASE + 0x20)
+#define CORE_DATA_MEM_B (CORE_DATA_EMPATH_BASE + 0x24)
+
+#define CORE_DATA_MEMORY_RAW_CYCLES (CORE_DATA_MEMORY_BASE + 0x00)
+#define CORE_DATA_MEMORY_TOD (CORE_DATA_MEMORY_BASE + 0x04)
+#define CORE_DATA_MEMORY_COUNT(p) (CORE_DATA_MEMORY_BASE + 0x08 + ((p) * 4))
+
+#define CORE_DATA_THROTTLE_RAW_CYCLES (CORE_DATA_THROTTLE_BASE + 0x00)
+#define CORE_DATA_THROTTLE_TOD (CORE_DATA_THROTTLE_BASE + 0x04)
+#define CORE_DATA_THROTTLE_IFU_THROTTLE (CORE_DATA_THROTTLE_BASE + 0x08)
+#define CORE_DATA_THROTTLE_ISU_THROTTLE (CORE_DATA_THROTTLE_BASE + 0x10)
+#define CORE_DATA_THROTTLE_IFU_ACTIVE (CORE_DATA_THROTTLE_BASE + 0x18)
+
+#define CORE_DATA_THREAD_RAW_CYCLES(t) (CORE_DATA_THREAD_BASE(t) + 0x00)
+#define CORE_DATA_THREAD_TOD(t) (CORE_DATA_THREAD_BASE(t) + 0x04)
+#define CORE_DATA_THREAD_RUN_CYCLES(t) (CORE_DATA_THREAD_BASE(t) + 0x08)
+#define CORE_DATA_THREAD_COMPLETION(t) (CORE_DATA_THREAD_BASE(t) + 0x0c)
+#define CORE_DATA_THREAD_MEM_A(t) (CORE_DATA_THREAD_BASE(t) + 0x10)
+#define CORE_DATA_THREAD_MEM_B(t) (CORE_DATA_THREAD_BASE(t) + 0x14)
+
+#define CORE_DATA_DTS_CPM_UNUSED (CORE_DATA_DTS_CPM_BASE + 0x00)
+#define CORE_DATA_DTS_CPM_TOD (CORE_DATA_DTS_CPM_BASE + 0x04)
+#define CORE_DATA_SENSOR_V0 (CORE_DATA_DTS_CPM_BASE + 0x08)
+#define CORE_DATA_SENSOR_V1 (CORE_DATA_DTS_CPM_BASE + 0x10)
+#define CORE_DATA_SENSOR_V8 (CORE_DATA_DTS_CPM_BASE + 0x18)
+#define CORE_DATA_SENSOR_V9 (CORE_DATA_DTS_CPM_BASE + 0x20)
+
+#define CORE_DATA_PCB_SLAVE_UNUSED (CORE_DATA_PCB_SLAVE_BASE + 0x00)
+#define CORE_DATA_PCB_SLAVE_TOD (CORE_DATA_PCB_SLAVE_BASE + 0x04)
+#define CORE_DATA_PMCR (CORE_DATA_PCB_SLAVE_BASE + 0x08)
+#define CORE_DATA_PMSR (CORE_DATA_PCB_SLAVE_BASE + 0x10)
+#define CORE_DATA_PM_HISTORY (CORE_DATA_PCB_SLAVE_BASE + 0x18)
+
+#define CORE_DATA_OHA_RO_STATUS_REG (CORE_DATA_OHA_BASE + 0x00)
+
+
+#ifndef __ASSEMBLER__
+
+// The GPE routine requires that the structure of core data collected by
+// gpe_get_core_data() be represented as the offsets defined above. This set
+// of structures represents the equivalent C-structure form of the data. Note
+// that the procedure formats the TOD as a 32-bit, 2 MHz timebase.
+
+typedef struct {
+ uint32_t unused;
+ uint32_t tod_2mhz;
+ uint32_t dispatch;
+ uint32_t completion;
+ uint32_t freq_sens_busy;
+ uint32_t freq_sens_finish;
+ uint32_t run_cycles;
+ uint32_t raw_cycles;
+ uint32_t mem_a;
+ uint32_t mem_b;
+} CoreDataEmpath;
+
+typedef struct {
+ uint32_t raw_cycles;
+ uint32_t tod_2mhz;
+ uint32_t count[4];
+} CoreDataPerPartitionMemory;
+
+typedef struct {
+ uint32_t raw_cycles;
+ uint32_t tod_2mhz;
+ uint32_t ifu_throttle;
+ uint32_t isu_throttle;
+ uint32_t ifu_active;
+ uint32_t undefined;
+} CoreDataThrottle;
+
+typedef struct {
+ uint32_t raw_cycles;
+ uint32_t tod_2mhz;
+ uint32_t run_cycles;
+ uint32_t completion;
+ uint32_t mem_a;
+ uint32_t mem_b;
+} CoreDataPerThread;
+
+typedef struct {
+ uint32_t unused;
+ uint32_t tod_2mhz;
+ sensors_v0_t sensors_v0;
+ sensors_v1_t sensors_v1;
+ sensors_v8_t sensors_v8;
+ sensors_v9_t sensors_v9;
+} CoreDataDtsCpm;
+
+typedef struct {
+ uint32_t unused;
+ uint32_t tod_2mhz;
+ pcbs_power_management_control_reg_t pmcr;
+ pcbs_power_management_status_reg_t pmsr;
+ pcbs_pmstatehistocc_reg_t pm_history;
+} CoreDataPcbSlave;
+
+typedef struct {
+ oha_ro_status_reg_t oha_ro_status_reg;
+} CoreDataOha;
+
+typedef struct {
+ CoreDataEmpath empath;
+ CoreDataPerPartitionMemory per_partition_memory;
+ CoreDataThrottle throttle;
+ CoreDataPerThread per_thread[8];
+ CoreDataDtsCpm dts_cpm;
+ CoreDataPcbSlave pcb_slave;
+ CoreDataOha oha;
+} CoreData;
+
+#endif // __ASSEMBLER__
+
+
+/// \defgroup core_data_status_bits Core Data Status Bits
+///
+/// These bits are set (if appropriate) in the low-order reserved area of the
+/// OHA_RO_STATUS_REG image stored in the CoreData structure.
+///
+/// @{
+
+/// This bit is set if SCOM access to the OHA returns a non-0 PIB return code
+/// when trying to write the OHA_CPM_HIST_RESET_REG to set up PC-only special
+/// wakeup.
+#define CORE_DATA_CPM_HIST_RESET_ACCESS_FAILED 0x01
+
+/// This bit is set if access to the OHA returns a non-0 PIB return code when
+/// trying to read the OHA_RO_STATUS_REG to determine core status.
+#define CORE_DATA_OHA_RO_STATUS_ACCESS_FAILED 0x02
+
+/// This bit is set if EMPATH data was requested to be collected and was
+/// collected. If this bit is not set then any EMPATH data requested to be
+/// collected will be 0.
+///
+/// If EMPATH data was requested but was not collected, then one of the bits
+/// CORE_DATA_EXPECTED_EMPATH_ERROR or CORE_DATA_UNEXPECTED_EMPATH_ERROR will
+/// be set, and the error code is stored in the OHA_RO_STATUS register image.
+#define CORE_DATA_EMPATH_COLLECTED 0x04
+
+/// This bit is set if core sensor data (DTS/CPM) was collected. If this bit
+/// is not set then core DTS/CPM data will be 0.
+#define CORE_DATA_CORE_SENSORS_COLLECTED 0x08
+
+/// This bit is set if L3 sensor data (DTS/CPM) was collected. If this bit is
+/// not set then L3 DTS/CPM data will be 0.
+#define CORE_DATA_L3_SENSORS_COLLECTED 0x10
+
+/// If this bit is set, then an "expected" error was encountered while
+/// collecting EMPATH data. Given that the procedure has gone through the
+/// PC-only special wakeup protocol, the only "expected" error is the
+/// intermittant PCB error code #4 due to HW280375.
+#define CORE_DATA_EXPECTED_EMPATH_ERROR 0x20
+
+/// If this bit is set, then an "unexpected" error was encountered while
+/// collecting EMPATH data. Given that the procedure has gone through the
+/// PC-only special wakeup protocol, the only "expected" error is the
+/// intermittant PCB error code #4 due to HW280375. If this bit is set it
+/// indicates a serious problem.
+#define CORE_DATA_UNEXPECTED_EMPATH_ERROR 0x40
+
+/// The first bit of the 4-bit PCB parity + error code, in the event a PCB
+/// error is encountered during EMPATH processing.
+#define CORE_DATA_EMPATH_ERROR_LOCATION 52
+
+#define CORE_DATA_EMPATH_ERROR_BITS 4
+
+/// @}
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_core_data_fast()
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef __ASSEMBLER__
+
+/// Paramaters for gpe_get_chip_data_fast()
+
+typedef struct {
+
+ /// gpe_get_core_data_fast() only collects data for chiplets configured in
+ /// this mask.
+ ChipConfig config;
+
+ /// This mask, comprised of a logical OR of the GPE_GET_CORE_DATA_FAST_*
+ /// macros, controls which data groups are collected.
+ uint64_t select;
+
+ /// This is the 32-bit pointer (cast to a uint64_t) to the chiplet raw
+ /// data area to be filled by this invocation of gpe_get_core_data_fast().
+ uint64_t data;
+
+} GpeGetChipDataFastParms;
+
+PoreEntryPoint gpe_get_core_data_fast;
+
+#endif /* __ASSEMBLER__ */
+
+// Parameter offsets for gpe_get_core_data()
+
+#define GPEGETCOREDATAFASTPARMS_CONFIG 0x00
+#define GPEGETCOREDATAFASTPARMS_SELECT 0x08
+#define GPEGETCOREDATAFASTPARMS_DATA 0x10
+
+// Data group select masks for gpe_get_core_data_fast()
+
+#define GPE_GET_CORE_DATA_FAST_FREQ_TARGET 0x0001
+
+#define CORE_DATA_FAST_FREQ_TARGET_BASE 0x0
+#define CORE_DATA_FAST_FREQ_TARGET_SIZE (8 + (PGP_NCORES * 8))
+
+#define CORE_DATA_FAST_SIZE \
+ (CORE_DATA_FAST_FREQ_TARGET_BASE + CORE_DATA_FAST_FREQ_TARGET_SIZE)
+
+#define CORE_DATA_FAST_FREQ_TARGET_UNUSED (CORE_DATA_FAST_FREQ_TARGET_BASE + 0)
+#define CORE_DATA_FAST_FREQ_TARGET_TOD (CORE_DATA_FAST_FREQ_TARGET_BASE + 4)
+#define CORE_DATA_FAST_FREQ_TARGET_LPFTSR(n) (CORE_DATA_FAST_FREQ_TARGET_BASE + 8 + ((n) * 8))
+
+#ifndef __ASSEMBLER__
+
+typedef struct {
+ uint32_t unused;
+ uint32_t tod_2mhz;
+ pcbs_local_pstate_frequency_target_status_reg_t lpftsr[PGP_NCORES];
+} CoreDataFast;
+
+#endif // __ASSEMBLER__
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_chip_data()
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef __ASSEMBLER__
+
+/// Paramaters for gpe_get_chip_data()
+
+typedef struct {
+
+ /// This mask, comprised of a logical OR of the GPE_GET_CHIP_DATA_*
+ /// macros, controls which data groups are collected.
+ uint64_t select;
+
+ /// This is the 32-bit pointer (cast to a uint64_t) to the chiplet raw
+ /// data area to be filled by this invocation of gpe_get_chip_data().
+ uint64_t data;
+
+} GpeGetChipDataParms;
+
+PoreEntryPoint gpe_get_chip_data;
+
+#endif /* __ASSEMBLER__ */
+
+// Parameter offsets for gpe_get_chip_data()
+
+#define GPEGETCHIPDATAPARMS_SELECT 0x00
+#define GPEGETCHIPDATAPARMS_DATA 0x08
+
+// Data group select masks for gpe_get_chip_data()
+
+#define GPE_GET_CHIP_DATA_OVERCOMMIT 0x0001
+
+#define CHIP_DATA_OVERCOMMIT_BASE 0
+#define CHIP_DATA_OVERCOMMIT_SIZE 56
+
+#define CHIP_DATA_SIZE (CHIP_DATA_OVERCOMMIT_BASE + CHIP_DATA_OVERCOMMIT_SIZE)
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_mem_data()
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef __ASSEMBLER__
+
+/// Paramaters for gpe_get_mem_data()
+
+typedef struct {
+
+ /// The index (0 .. PGP_NCENTAUR - 1) of the Centaur whose sensor cache
+ /// data to collect, or -1 to bypass collection.
+ uint64_t collect;
+
+ /// The index (0 .. PGP_NCENTAUR - 1) of the Centaur to "poke" to cause it
+ /// to begin collecting the next round of data into its sensor cache, or
+ /// -1 to bypass updating.
+ uint64_t update;
+
+ /// This is the 32-bit pointer (cast to a uint64_t) to the chiplet raw
+ /// data area to be filled by this invocation of gpe_get_mem_data(). This
+ /// pointer need not be valid if the \a collect field of the structure is
+ /// -1.
+ uint64_t data;
+
+ /// The return code returned by the last invocation of the procedure; See
+ /// \ref gpe_get_mem_date_rc.
+ uint64_t rc;
+
+ /// The 'update' timestamp
+ ///
+ /// This is the value of the chip TOD at the time the 'update' phase of
+ /// the procedure is run, as close as possible to the "poke" of the
+ /// Centaur. This timestamp indicates the time that the Centaur sensor
+ /// cache line collection was kicked off. The timestamp is collected even
+ /// if the \a update field of the structure is -1. Consistent with
+ /// gpe_get_core_data() the timestamp is reduced to a 32-bit, 2MHz
+ /// timestamp, and stored in the low-order half of a doubleword.
+ uint32_t pad;
+ uint32_t tod_2mhz;
+
+} GpeGetMemDataParms;
+
+PoreEntryPoint gpe_get_mem_data;
+
+#endif /* __ASSEMBLER__ */
+
+// Parameter offsets for gpe_get_mem_data()
+
+#define GPEGETMEMDATAPARMS_COLLECT 0x00
+#define GPEGETMEMDATAPARMS_UPDATE 0x08
+#define GPEGETMEMDATAPARMS_DATA 0x10
+#define GPEGETMEMDATAPARMS_RC 0x18
+#define GPEGETMEMDATAPARMS_PAD_TOD 0x20
+#define SIZEOF_GPEGETMEMDATAPARMS 0x28
+
+
+/// \defgroup gpe_mem_data_rc gpe_get_mem_data() Error Return Codes
+///
+/// The gpe_get_mem_data() procedure deposits a non-0 return code into the \a
+/// rc field of its parameter structure in the event of failure. Note that the
+/// procedure stops on the first failure, and in particular the TOD timestamp
+/// is not valid in the event of failure.
+///
+/// @{
+
+/// The procedure died, but no other information is available. This would have
+/// signalled an error interrupt and the PORE flex request will contain FFDC
+/// about the failure.
+#define GPE_GET_MEM_DATA_DIED 1
+
+/// The \a collect parameter was invalid, i.e. it either was an illegal index
+/// or the index of an unconfigured MCS or Centaur.
+#define GPE_GET_MEM_DATA_COLLECT_INVALID 2
+
+/// The \a update parameter was invalid, i.e. it either was an illegal index
+/// or the index of an unconfigured MCS or Centaur.
+#define GPE_GET_MEM_DATA_UPDATE_INVALID 3
+
+/// The global G_centaurConfiguration is not valid
+#define GPE_GET_MEM_DATA_NOT_CONFIGURED 4
+
+/// The workaround for HW256773 failed. To diagnose the failure look at the
+/// 'rc' field of the global variable G_hw256773.
+#define GPE_GET_MEM_DATA_HW256773_FAILED 5
+
+/// This code is established in the RC field prior to collecting the Centaur
+/// sensor cache data. If this RC is observed on a hard failure it most likely
+/// indicates an error assiciated with the Centaur whose data was being
+/// collected.
+#define GPE_GET_MEM_DATA_SENSOR_CACHE_FAILED 6
+
+/// This code is established in the RC field prior to "poking" the Centaur (if
+/// any) that is being updated this pass. If this RC is observed on a hard
+/// failure it most likely indicates an error associated with the Centaur
+/// being updated.
+#define GPE_GET_MEM_DATA_UPDATE_FAILED 7
+
+/// @}
+
+
+#ifndef __ASSEMBLER__
+
+// The GPE routine requires that the structure of centaur data collected by
+// gpe_get_mem_data() be represented as the offsets defined above. This set
+// of structures represent the equivalent C-structure form of the data. Note
+// that the procedure formats the TOD as a 32-bit, 2 MHz timebase.
+
+/// Layout of data collected from MCS
+///
+/// This is currently empty, however to avoid code rewrites if any data is
+/// ever collected here the structure is declared and placed in the larger
+/// MemData structure. The fact that the structure is empty does not seem to
+/// cause problems.
+
+typedef struct {
+} MemDataMcs;
+
+/// The layout of a Centaur chip thermal sensor
+///
+/// \todo Centaur spec. has no doc. on layout of these bits; Waiting for more
+/// info from Centaur team.
+
+typedef union {
+ uint16_t value;
+ struct {
+ uint16_t value;
+ } fields;
+} centaur_sensor_t;
+
+/// The layout of a Centaur DIMM sensor
+///
+/// Mnemonic macros for the 2-bit status codes (DIMM_SENSOR_STATUS_*) are
+/// currently defined in ssx/pgp/pgp_common.h
+///
+/// \todo Waiting for more info from Centaur team on how to interpret
+
+typedef union {
+ uint16_t value;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint16_t crit_trip : 1;
+ uint16_t alarm_trip : 1;
+ uint16_t below_trip : 1;
+ uint16_t sign_bit : 1;
+ uint16_t temperature : 8;
+ uint16_t temp_fraction : 2;
+ uint16_t status : 2;
+#else
+ uint16_t status : 2;
+ uint16_t temp_fraction : 2;
+ uint16_t temperature : 8;
+ uint16_t sign_bit : 1;
+ uint16_t below_trip : 1;
+ uint16_t alarm_trip : 1;
+ uint16_t crit_trip : 1;
+#endif
+ } fields;
+} centaur_dimm_sensor_t;
+
+/// The layout of the status bits of the sensor cache line
+///
+/// The sensor cache-line aggregator gets each element of the sensor cache
+/// line by an internal SCOM. The individual PCB return codes for each SCOM
+/// are collected here (3 bits each) - note that many of the 32-bit registers
+/// come back in a single 64-bit internal SCOM. Normally this register will
+/// always read as 0 indicating all data was collected successfully. The PCB
+/// error codes (PCB_ERROR_*) are currently defined in ssx/pgp/pgp_common.h.
+
+typedef union {
+ uint64_t value;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint64_t mba01_rw : 3; /// mba01_rd[+ wr]
+ uint64_t mba01_ap : 3; /// mba01_act[+ powerups]
+ uint64_t mba23_rw : 3; /// mba23_rd[+ wr]
+ uint64_t mba23_ap : 3; /// mba23_act[+ powerups]
+ uint64_t mba_sc : 3; /// mba01[+ 23]_spec_cancels
+ uint64_t lp2_exits : 3; /// lp2_exits
+ uint64_t frame_count : 3; /// frame_count
+ uint64_t mba01_chrw : 3; /// mba01_cache_hits_rd[+ wr]
+ uint64_t mba23_chrw : 3; /// mba23_cache_hits_rd[+ wr]
+ uint64_t mba01_iac_bl : 3; /// mba01_intreq_arr_cnt_base[+ low]
+ uint64_t mba01_iac_mh : 3; /// mba01_intreq_arr_cnt_med[+ high]
+ uint64_t mba23_iac_bl : 3; /// mba23_intreq_arr_cnt_base[+ low]
+ uint64_t mba23_iac_mh : 3; /// mba23_intreq_arr_cnt_med[+ high]
+ uint64_t iac_high_latency : 3; /// intereq_arr_cnt_high_latency
+ uint64_t centaur01 : 3; /// centaur_thermal_sensor[0 - 1]
+ uint64_t dimm03 : 3; /// dimm_thermal_sensor[0 - 3]
+ uint64_t dimm47 : 3; /// dimm_thermal_sensor[4 - 7]
+ uint64_t reserved : 13;
+#else
+ uint64_t reserved : 13;
+ uint64_t dimm47 : 3; /// dimm_thermal_sensor[4 - 7]
+ uint64_t dimm03 : 3; /// dimm_thermal_sensor[0 - 3]
+ uint64_t centaur01 : 3; /// centaur_thermal_sensor[0 - 1]
+ uint64_t iac_high_latency : 3; /// intereq_arr_cnt_high_latency
+ uint64_t mba23_iac_mh : 3; /// mba23_intreq_arr_cnt_med[+ high]
+ uint64_t mba23_iac_bl : 3; /// mba23_intreq_arr_cnt_base[+ low]
+ uint64_t mba01_iac_mh : 3; /// mba01_intreq_arr_cnt_med[+ high]
+ uint64_t mba01_iac_bl : 3; /// mba01_intreq_arr_cnt_base[+ low]
+ uint64_t mba23_chrw : 3; /// mba23_cache_hits_rd[+ wr]
+ uint64_t mba01_chrw : 3; /// mba01_cache_hits_rd[+ wr]
+ uint64_t frame_count : 3; /// frame_count
+ uint64_t lp2_exits : 3; /// lp2_exits
+ uint64_t mba_sc : 3; /// mba01[+ 23]_spec_cancels
+ uint64_t mba23_ap : 3; /// mba23_act[+ powerups]
+ uint64_t mba23_rw : 3; /// mba23_rd[+ wr]
+ uint64_t mba01_ap : 3; /// mba01_act[+ powerups]
+ uint64_t mba01_rw : 3; /// mba01_rd[+ wr]
+#endif
+ } fields;
+} centaur_scom_status_t;
+
+/// The layout of the Centaur sensor cache line
+
+typedef struct {
+ uint32_t mba01_rd; // PP1/MBA01 Reads
+ uint32_t mba01_wr; // PP1/MBA01 Writes
+ uint32_t mba01_act; // PP1/MBA01 Activations
+ uint32_t mba01_powerups; // PP1/MBA01 PowerUps
+
+ uint32_t mba23_rd; // PP2/MBA23 Reads
+ uint32_t mba23_wr; // PP2/MBA23 Writes
+ uint32_t mba23_act; // PP2/MBA23 Activations
+ uint32_t mba23_powerups; // PP2/MBA23 PowerUps
+
+ uint32_t mba01_spec_cancels; // PP1/MBA01 Speculative Cancels
+ uint32_t mba23_spec_cancels; // PP2/MBA23 Speculative Cancels
+#ifdef _BIG_ENDIAN
+ uint32_t eventn :4; // EVENTN
+ uint32_t reserved_0 :20; // Reserved
+ uint32_t lp2_exits :8; // LP2 Exits
+#else
+ uint32_t lp2_exits :8; // LP2 Exits
+ uint32_t reserved_0 :20; // Reserved
+ uint32_t eventn :4; // EVENTN
+#endif
+ uint32_t frame_count; // Frame Count (timestamp)
+
+ uint32_t mba01_cache_hits_rd; // PP1/MBA01 Cache Hits Reads
+ uint32_t mba01_cache_hits_wr; // PP1/MBA01 Cache Hits Writes
+ uint32_t mba23_cache_hits_rd; // PP2/MBA23 Cache Hits Reads
+ uint32_t mba23_cache_hits_wr; // PP2/MBA23 Cache Hits Writes
+
+ uint32_t mba01_intreq_arr_cnt_base; // PP1/MBA01 Inter-Req Arrival Count Base
+ uint32_t mba01_intreq_arr_cnt_low; // PP1/MBA01 Inter-Req Arrival Count Low
+ uint32_t mba01_intreq_arr_cnt_med; // PP1/MBA01 Inter-Req Arrival Count Med
+ uint32_t mba01_intreq_arr_cnt_high; // PP1/MBA01 Inter-Req Arrival Count High
+
+ uint32_t mba23_intreq_arr_cnt_base; // PP2/MBA23 Inter-Req Arrival Count Base
+ uint32_t mba23_intreq_arr_cnt_low; // PP2/MBA23 Inter-Req Arrival Count Low
+ uint32_t mba23_intreq_arr_cnt_med; // PP2/MBA23 Inter-Req Arrival Count Med
+ uint32_t mba23_intreq_arr_cnt_high; // PP2/MBA23 Inter-Req Arrival Count High
+
+ uint32_t intreq_arr_cnt_high_latency; // Inter-Req Arrival Count High Latency
+ centaur_sensor_t centaur_thermal_sensor[2]; // Centaur Thermal Sensors 0-1
+ centaur_dimm_sensor_t dimm_thermal_sensor[8]; // DIMM Thermal Sensors 0-7
+ centaur_scom_status_t status; // Aggregated internal SCOM status
+} MemDataSensorCache;
+
+typedef struct {
+ MemDataMcs mcs; // TODO: Not collected yet
+ MemDataSensorCache scache; // OCC Centaur Sensor Cache Line (128 bytes)
+} MemData;
+
+#endif // __ASSEMBLER__
+
+
+// Data offsets for gpe_get_mem_data()
+
+#define MEM_DATA_MCS_BASE 0
+#define MEM_DATA_MCS_SIZE 0
+
+#define MEM_DATA_CENTAUR_BASE (MEM_DATA_MCS_BASE + MEM_DATA_MCS_SIZE)
+#define MEM_DATA_CENTAUR_SIZE 128
+
+#define MEM_DATA_SIZE (MEM_DATA_MCS_SIZE + MEM_DATA_CENTAUR_SIZE)
+
+#endif /* __GPE_DATA_H__ */
diff --git a/src/lib/gpe_data.pS b/src/lib/gpe_data.pS
new file mode 100755
index 0000000..2338276
--- /dev/null
+++ b/src/lib/gpe_data.pS
@@ -0,0 +1,1585 @@
+// $Id: gpe_data.pS,v 820.1 2014/08/22 16:33:56 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/fw820/procedures/lib/gpe_data.pS,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_data.S
+/// \brief GPE procedures for raw data collection
+
+ .nolist
+
+#include "ssx.h"
+#include "pgas.h"
+#include "pgp_config.h"
+#include "gpe.h"
+#include "gpe_pba.h"
+#include "gpe_data.h"
+#include "gpe_scom.h"
+
+ .list
+
+ .oci
+ .text.pore
+
+ .revision_string G_gpe_data_pS_revision, "$Revision: 820.1 $"
+
+/// \cond
+
+////////////////////////////////////////////////////////////////////////////
+// Common Macros
+////////////////////////////////////////////////////////////////////////////
+
+ // Get a full 64-bit SCOM and write to OCI space. Clobbers a Data
+ // register.
+
+ .macro get_scom, dx, scom, chiplet_base, oci_offset, oci_base
+
+ ld (\dx), (\scom), (\chiplet_base)
+ std (\dx), (\oci_offset), (\oci_base)
+
+ .endm
+
+
+ // Tag a data group with TOD[24..56]. This macro clobbers a data
+ // register.
+
+ .macro tag_data_group, base, dx, oci_base, tod_chiplet
+
+ ld (\dx), TOD_VALUE_REG, (\tod_chiplet)
+ extrdi (\dx), (\dx), 32, 24
+ std (\dx), (\base), (\oci_base)
+
+ .endm
+
+
+ // An OCI - OCI copy. Dx gets clobbered
+
+ .macro ocicopy, dx, src_offset, src_base, dst_offset, dst_base
+
+ ld (\dx), (\src_offset), (\src_base)
+ std (\dx), (\dst_offset), (\dst_base)
+
+ .endm
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_core_data()
+////////////////////////////////////////////////////////////////////////////
+
+ // Macros for gpe_get_core_data().
+
+ // Tag a core data group with TOD[24..56], and optionally with the raw
+ // cycle count. Always clobbers D0 and D1. If called with store=0, the
+ // tag ends up in D0.
+
+ .macro tag_core_data_group, base, oci_base, pc_chiplet, tod_chiplet, \
+ raw=1, store=1
+
+ ld D0, TOD_VALUE_REG, (\tod_chiplet)
+ extrdi D0, D0, 32, 24
+ .if (\raw)
+ sti PC_OCC_SPRC, (\pc_chiplet), SPRN_CORE_RAW_CYCLE
+ ld D1, PC_OCC_SPRD, (\pc_chiplet)
+ rotldi D1, D1, 32
+ or D0, D0, D1
+ .endif
+ .if (\store)
+ std D0, (\base), (\oci_base)
+ .endif
+ .endm
+
+
+ // Get a pair of SCOMs from PC, packing them into a single 64-bit value
+ // and writing them to OCI space. Clobbers D0 and D1. Assumes that
+ // PC_OCC_SPRC is set up for autoincrement access as well.
+ //
+ // This macro takes advantage of the fact that PC-unit SCOMs only
+ // define the lower 32 bits, and the high-32 are 0.
+
+ .macro get_pc_pair, offset, oci_base, chiplet_base
+
+ ld D0, PC_OCC_SPRD, (\chiplet_base)
+ ld D1, PC_OCC_SPRD, (\chiplet_base)
+ rotldi D0, D0, 32
+ or D0, D0, D1
+ std D0, (\offset), (\oci_base)
+
+ .endm
+/// \endcond
+
+
+
+
+/// \fn gpe_get_core_data(GpeGetCoreDataParms *parms);
+/// \brief Get core chiplet raw data on performance/thermal timescale
+///
+/// This routine uses get_per_core_raw_data() to collect raw data for one or
+/// more cores. The \a data field of the GpeGetCoreDataParms parameter
+/// contains a pointer to an array of CoreData* pointers. Data for every core
+/// configured in the configuration mask is collected - it is assumed that the
+/// data area for the data exists.
+///
+/// This entry point is used by the lab thread 'coreData'.
+#ifdef DOXYGEN_ONLY
+void gpe_get_core_data(GpeGetCoreDataParms *parms);
+#endif
+/// \cond
+
+ // Register usage:
+ //
+ // ETR : At entry, holds the parameter pointer.
+ // A1 : Holds the pointer to the paramaters
+ // A0 : Holds the (varying) pointer to the data area for the
+ // current core.
+ // P1 : Holds the (constant) chiplet id of the TOD
+ // P0 : Holds the (varying) chiplet id of the current core
+ // SPRG0 : Temporary storage of the chiplet mask as it rotates.
+ // CTR : Loops through core chiplets indices
+ // D1 : Scratch
+ // D0 : Scratch
+
+ .global gpe_get_core_data
+
+gpe_get_core_data:
+
+ // Set up registers. The chiplet part of the ChipConfig is left
+ // justified then stored in SPRG0, where it will be maintained as we
+ // rotate through it. Note that SPRG0 is 32 bits, so it needs to be
+ // manipulated from the low-order portion of a data register.
+
+ mr D0, ETR
+ la A1, core_data_parms
+ std D0, 0, A1
+ mr A1, D0
+
+ ld D0, GPEGETCOREDATAPARMS_DATA, A1
+ mr A0, D0
+
+ ld D0, GPEGETCOREDATAPARMS_CONFIG, A1
+ left_justify_core_config D0
+ rotldi D0, D0, 32
+ mr SPRG0, D0
+
+ lpcs P1, TOD_VALUE_REG
+ ls P0, 0x10
+ ls CTR, (PGP_NCORES - 1) # PORE does test, then decr. and branch
+
+core_data_loop:
+
+ // Load/test the chiplet mask, and store the rotated mask back to
+ // SPRG0. If the chiplet is not configured, simply continue.
+
+ mr D0, SPRG0
+ andi D1, D0, 0x80000000
+ rotldi D0, D0, 1
+ mr SPRG0, D0
+ braz D1, core_data_continue
+
+ // Collect Raw Data for Core specified by P0, stored at A0
+
+ bsr get_per_core_raw_data
+
+core_data_continue:
+
+ // Increment the core chiplet index and data pointer, then loop or
+ // halt.
+
+ adds P0, P0, 1
+ adds A0, A0, CORE_DATA_SIZE
+ loop core_data_loop
+
+ halt
+
+ .epilogue gpe_get_core_data
+
+/// \endcond
+
+
+
+/// \fn gpe_get_per_core_data(GpeGetCoreDataParms *parms);
+/// \brief Get core chiplet raw data for a single core
+///
+/// This routine uses get_per_core_raw_data() to collect raw data for a single
+/// core. Regardless of the configuration mask setting, this routine exits
+/// after collecting data for a single core. The \a data field of the
+/// GpeGetCoreDataParms contains a pointer to a single CoreData object.
+///
+/// This entry point is used by OCC product firmware.
+#ifdef DOXYGEN_ONLY
+void gpe_get_per_core_data(GpeGetCoreDataParms *parms);
+#endif
+/// \cond
+
+ // Register usage:
+ //
+ // A1 : Holds the pointer to the paramaters
+ // A0 : Holds the (varying) pointer to the data area for the
+ // current core, as well as the data pointer-pointer while
+ // searching for a configured core.
+ // P1 : Holds the (constant) chiplet id of the TOD
+ // P0 : Holds the (varying) chiplet id of the current core
+ // SPRG0 : Temporary storage of the chiplet mask as it rotates.
+ // CTR : Loops through core chiplets indices
+ // D1 : Scratch
+ // D0 : Scratch
+
+ .global gpe_get_per_core_data
+
+gpe_get_per_core_data:
+
+ // Set up registers. A1 gets the parameters (which must also be
+ // stored in memory), the the ETR is replaced by the data
+ // pointer-pointer. The chiplet part of the ChipConfig is left
+ // justified then stored in SPRG0, where it will be maintained as we
+ // rotate through it. Note that SPRG0 is 32 bits, so it needs to be
+ // manipulated from the low-order portion of a data register.
+
+ mr D0, ETR
+ la A1, core_data_parms
+ std D0, 0, A1
+ mr A1, D0
+
+ ld D0, GPEGETCOREDATAPARMS_DATA, A1
+ mr A0, D0
+
+ ld D0, GPEGETCOREDATAPARMS_CONFIG, A1
+ left_justify_core_config D0
+ rotldi D0, D0, 32
+ mr SPRG0, D0
+
+ lpcs P1, TOD_VALUE_REG
+ ls P0, 0x10
+ ls CTR, (PGP_NCORES - 1) # PORE does test, then decr. and branch
+
+per_core_data_loop:
+
+ // Load/test the chiplet mask, and store the rotated mask back to
+ // SPRG0. If the chiplet is not configured, simply continue.
+
+ mr D0, SPRG0
+ andi D1, D0, 0x80000000
+ rotldi D0, D0, 1
+ mr SPRG0, D0
+ braz D1, per_core_data_continue
+
+ // Collect Raw Data for Core specified by P0, stored at A0
+
+ bsr get_per_core_raw_data
+
+ // Exit GPE after gathering data for one core
+ bra per_core_data_complete
+
+per_core_data_continue:
+
+ // Increment the core chiplet index and data pointer, then loop or
+ // halt.
+ adds P0, P0, 1
+ loop per_core_data_loop
+
+per_core_data_complete:
+ halt
+
+ .epilogue gpe_get_per_core_data
+
+/// \endcond
+
+/// \fn gpe_get_per_core_raw_data();
+/// \brief Get core chiplet raw data for one core
+///
+/// This routine collects raw data from the core designated by P0. Data is
+/// grouped into logical groups, and the collection of any group is enabled by
+/// a group select mask. All data and thread groups (except the PCB Slave
+/// group) are tagged with the TOD and raw cycle counts sampled immediately
+/// before the group data are sampled.
+///
+/// The final PCB Slave data group should always be selected (but \e is
+/// configurable) as it contains the PCB Slave Power Management history
+/// register. This register value is required to determine how to interpret
+/// the other data items.
+///
+/// The PC counters are collected using the SPRC/SPRD autoincrement
+/// mechanism. Be very cautious about changing this code or the data layout
+/// because the counter order is fixed by hardware and the data layout
+/// reflects the most natural way to collect the data based on the
+/// hardware. Note that SPRC/SPRD autoincrement IS NOT OPTIONAL for the OCC
+/// registers, regardless of how it may be documented in the PC workbook, or
+/// the fact that the procedure redundantly sets up auto-increment. That is,
+/// the hardware always does auto-increment for these SPRC/SPRD reads.
+///
+/// The data structure includes a TOD/Raw cycles word for each set of counters
+/// for each thread. Due to the amount of time it may take to collect
+/// per-thread data for 8 threads, errors of 1% or more could accrue at thread
+/// 7 if each thread group were not individually tagged. To avoid having to
+/// SCOM the TOD plus a SCOMC/SCOMD pair to create each thread group header
+/// however, we instead tag thread0 with actual data, then tag the remaining
+/// thread groups with interpolated TOD/Raw cycle values computed by obtaining
+/// a tag at the end of all threads. This takes only a little more time than
+/// the simpler expedient of copying the Tod/Raw Cycles count from thread0 to
+/// threads 1-7.
+///
+/// At the entry point of the routine, the code must go through the PC-ONLY
+/// special wakeup procedure to ensure that we can SCOM a napping core. This
+/// has to be done carefully as it's possible that SCOM access to the OHA will
+/// result in a 0x1 PIB response if the core is coming out of deep
+/// sleep/winkle. This PIB response would discombobulate the PORE engine so we
+/// have to run these SCOMs with error handling done manually. If a core is
+/// inaccessible due to an idle state we clear all of the configured EMPATH
+/// counts, per-thread counts and DTS and CPM for the core. If the core is
+/// only asleep (not winkled) then we attempt to read the DTS and CPM for the
+/// L3. Note that TOD timestamps are always collected, even if the data is
+/// simply zeroed.
+///
+/// A modified copy of the OHA_RO_STATUS_REG read during the PC-only SPWU
+/// protocol is stored with the data. Several low-order reserved bits of the
+/// register image are programmed with the following masks. See the
+/// documentation for these bits for full details.
+///
+/// - CORE_DATA_CPM_HIST_RESET_ACCESS_FAILED
+/// - CORE_DATA_OHA_RO_STATUS_ACCESS_FAILED
+/// - CORE_DATA_EMPATH_COLLECTED
+/// - CORE_DATA_CORE_SENSORS_COLLECTED
+/// - CORE_DATA_L3_SENSORS_COLLECTED
+/// - CORE_DATA_EXPECTED_EMPATH_ERROR
+/// - CORE_DATA_UNEXPECTED_EMPATH_ERROR
+///
+/// In the event of expected or unexpected errors during EMPATH data
+/// collection the 3-bit PCB error code will also be stored at bit
+/// CORE_DATA_EMPATH_ERROR_LOCATION.
+///
+/// This is the PC-ONLY Special Wakeup + processing Sequence
+///
+/// 1. Switch to manual error handling mode and disable PIB errors.
+///
+/// 2. Write OHA_CPM_HIST_RESET_REG.pconly_special_wakeup = 1. If the write
+/// fails, note the failure and go to the bypass routine.
+///
+/// 3. Read OHA_RO_STATUS_REG. If the SCOM fails, access is impossible and
+/// noted. If the special wakeup complete is not immediately set that error is
+/// also noted. If either test fails then go to the bypass routine. Otherwise
+/// note success and continue.
+///
+/// 4. Attempt to collect sensor (DTS/CPM) data for the core and L3. This must
+/// be done with manual error handling as these SCOMs are not protected by
+/// PC-only SPWU.
+///
+/// 5. Switch to a private error handling table setup that allows the
+/// procedure to catch PCB data errors during EMPATH processing. This is
+/// required as a workaround for HW280375.
+///
+/// 6. Collect EMPATH data.
+///
+/// 7. Restore error handling; Clear the PC-only SPWU bit.
+///
+/// 8. Collect PCB Slave data.
+///
+/// When the core is inaccessible a similar "bypass" sequence to the data
+/// collection sequence is run, however all data other than timestamps and the
+/// PCB Slave data are stored as 0, and the PC-Only SPWU bit is cleared before
+/// error handling is re-enabled. The bypass routine will also take care of
+/// attempting to collect L3 DTS/CPM data for sleeping cores.
+///
+/// Note that the PCB slave data must be collected after the removal of
+/// PC-only special wakeup, otherwise a napping core will always appear to be
+/// in the run state.
+///
+/// Several global variables are required. Thus this procedure and its callers
+/// are not reentrant.
+#ifdef DOXYGEN_ONLY
+ void get_per_core_raw_data();
+#endif
+/// \cond
+
+get_per_core_raw_data:
+
+ // At entry:
+ //
+ // P0 : The chiplet to access (invariant)
+ // A0 : Pointer to the data area for the core (invariant)
+ // SPRG0 : Reserved to the caller (invariant)
+ // CTR : Reserved to the caller (invariant)
+ //
+ // core_data_parms: Holds the pointer to the parameters
+ //
+ // At exit:
+ //
+ // All other registers are scratched by this routine
+
+ // (1) Switch to manual error handling mode and disable PIB errors.
+
+ mr D0, EMR
+ la A1, saved_emr
+ std D0, 0, A1
+
+ andi D0, D0, ~(PORE_ERROR_MASK_ENABLE_ERR_HANDLER0 | \
+ PORE_ERROR_MASK_ENABLE_ERR_OUTPUT0 | \
+ PORE_ERROR_MASK_ENABLE_FATAL_ERR_OUTPUT0 | \
+ PORE_ERROR_MASK_STOP_EXE_ON_ERROR0)
+ mr EMR, D0
+ la A1, manual_emr
+ std D0, 0, A1
+
+
+ // (2) Write OHA_CPM_HIST_RESET_REG.pconly_special_wakeup = 1. If the
+ // write fails, note the failure and go to the bypass routine.
+
+ sti OHA_CPM_HIST_RESET_REG, P0, \
+ OHA_CPM_HIST_RESET_REG_PCONLY_SPECIAL_WAKEUP
+ tprcbz D0, 3f
+
+ sti CORE_DATA_OHA_RO_STATUS_REG, A0, \
+ CORE_DATA_CPM_HIST_RESET_ACCESS_FAILED
+ bra bypass_core_data
+
+
+ // 3. Read OHA_RO_STATUS_REG. If the SCOM fails, access is impossible
+ // and noted. If the special wakeup complete is not immediately set
+ // that error is also noted. If either test fails then go to the
+ // bypass routine. Otherwise note success and continue.
+
+3:
+ ld D0, OHA_RO_STATUS_REG, P0
+ tprcbz D1, 31f
+
+ sti CORE_DATA_OHA_RO_STATUS_REG, A0, \
+ CORE_DATA_OHA_RO_STATUS_ACCESS_FAILED
+ bra bypass_core_data
+
+31:
+ std D0, CORE_DATA_OHA_RO_STATUS_REG, A0
+
+ // If either access is impossible we go to bypass. The bypass code
+ // will read the L3 DTS/CPM data if it is possible.
+
+ andi D1, D0, (OHA_RO_STATUS_REG_CORE_ACCESS_IMPOSSIBLE | \
+ OHA_RO_STATUS_REG_ECO_ACCESS_IMPOSSIBLE)
+ branz D1, bypass_core_data
+
+ andi D1, D0, OHA_RO_STATUS_REG_SPECIAL_WAKEUP_COMPLETED
+ braz D1, bypass_core_data
+
+
+ // 4. Attempt to collect sensor (DTS/CPM) data. This must be done with
+ // manual error handling (in effect here) as these SCOMs are not
+ // protected by a PC-only SPWU.
+
+ la A1, core_data_parms
+ ld D0, 0, A1
+ mr A1, D0
+
+ bsr getSensors
+
+
+ // 5. Switch to a private error handling table setup that allows the
+ // procedure to catch PCB errors during EMPATH processing.
+
+ // NB: We know that this is being run as a PoreFlex job from OCC FW on
+ // either GPE0 or GPE1. We also know that the default error mask does
+ // not handle any errors with a table.
+
+ tebngpe0 D0, 1f
+ la A1, PORE_GPE0_TABLE_BASE_ADDR
+ bra 2f
+1:
+ la A1, PORE_GPE1_TABLE_BASE_ADDR
+2:
+ la D0, empathErrorHandlers
+ std D0, 0, A1
+
+ la A1, saved_emr
+ ld D0, 0, A1
+ ori D0, D0, PORE_ERROR_MASK_ENABLE_ERR_HANDLER0
+ andi D0, D0, ~(PORE_ERROR_MASK_ENABLE_ERR_OUTPUT0 | \
+ PORE_ERROR_MASK_ENABLE_FATAL_ERR_OUTPUT0 | \
+ PORE_ERROR_MASK_STOP_EXE_ON_ERROR0)
+ mr EMR, D0
+
+#if INJECT_HW280375_ERRORS
+
+ // This code is used to test the workaround for HW280375. The
+ // undiagnosed hardware bug causes PCB error 4 to occur intermittantly
+ // when accessing EMPATH registers. The appearance of the defect is
+ // actually quite rare in practice, therefore this code remains in
+ // case future development and testing of this procedure is necessary.
+
+ // The test generates PCB error 4 by reading a non-existant OHA
+ // register of the current core, once every 1024 samples on
+ // average. The LFSR modifies A0 so we need to shuffle A0 <->
+ // A1. (Note the LFSR code is not delivered to OCC FW).
+
+ mr A1, A0
+
+ la A0, testHw280375Lfsr
+ ld D0, 0, A0
+ bsr pore_rand64
+ la A0, testHw280375Lfsr
+ std D0, 0, A0
+
+ mr A0, A1
+
+ andi D0, D0, 0x3ff
+ branz D0, 1f
+ ld D0, 0x200ff, P0 # Force PCB error 4
+1:
+
+#endif
+
+ // 6. Collect EMPATH data
+
+ // Test/collect each data group in order. First reload the parameter
+ // pointer into A1.
+
+ la A1, core_data_parms
+ ld D0, 0, A1
+ mr A1, D0
+
+ // EMPATH
+empath:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_EMPATH
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_EMPATH_BASE
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ sti PC_OCC_SPRC, P0, \
+ (SPRN_CORE_INSTRUCTION_DISPATCH | SPRN_PC_AUTOINCREMENT)
+
+ get_pc_pair (_BASE + 0x08), A0, P0
+ get_pc_pair (_BASE + 0x10), A0, P0
+ get_pc_pair (_BASE + 0x18), A0, P0
+ get_pc_pair (_BASE + 0x20), A0, P0
+
+ // Per-Core (partition) Memory Counters
+per_core_memory:
+1:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_MEMORY
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_MEMORY_BASE
+ tag_core_data_group _BASE, A0, P0, P1
+
+ sti PC_OCC_SPRC, P0, \
+ (SPRN_CORE_MEM_C_LPAR(0) | SPRN_PC_AUTOINCREMENT)
+
+ get_pc_pair (_BASE + 0x08), A0, P0
+ get_pc_pair (_BASE + 0x10), A0, P0
+
+ // Throttling Counters
+throttling:
+1:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_THROTTLE
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_THROTTLE_BASE
+ tag_core_data_group _BASE, A0, P0, P1
+
+ sti PC_OCC_SPRC, P0, \
+ (SPRN_IFU_THROTTLE_COUNTER | SPRN_PC_AUTOINCREMENT)
+
+ get_pc_pair (_BASE + 0x08), A0, P0
+ get_pc_pair (_BASE + 0x10), A0, P0
+
+ // Per-Thread Counters
+per_thread:
+1:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_THREAD
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_THREAD_BASE(0)
+ tag_core_data_group _BASE, A0, P0, P1
+
+ sti PC_OCC_SPRC, P0, \
+ (SPRN_THREAD_RUN_CYCLES(0) | SPRN_PC_AUTOINCREMENT)
+
+ get_pc_pair (_BASE + 0x08), A0, P0 # Run/Completion T0
+ get_pc_pair (_BASE + 0x10), A0, P0 # Mem A/B T0
+ // (_BASE + 0x18), A0, P0 # Tag T1
+ get_pc_pair (_BASE + 0x20), A0, P0 # Run/Completion T1
+ get_pc_pair (_BASE + 0x28), A0, P0 # Mem A/B T1
+ // (_BASE + 0x30), A0, P0 # Tag T2
+ get_pc_pair (_BASE + 0x38), A0, P0 # Run/Completion T2
+ get_pc_pair (_BASE + 0x40), A0, P0 # Mem A/B T2
+ // (_BASE + 0x48), A0, P0 # Tag T3
+ get_pc_pair (_BASE + 0x50), A0, P0 # Run/Completion T3
+ get_pc_pair (_BASE + 0x58), A0, P0 # Mem A/B T3
+ // (_BASE + 0x60), A0, P0 # Tag T4
+ get_pc_pair (_BASE + 0x68), A0, P0 # Run/Completion T4
+ get_pc_pair (_BASE + 0x70), A0, P0 # Mem A/B T4
+ // (_BASE + 0x78), A0, P0 # Tag T5
+ get_pc_pair (_BASE + 0x80), A0, P0 # Run/Completion T5
+ get_pc_pair (_BASE + 0x88), A0, P0 # Mem A/B T5
+ // (_BASE + 0x90), A0, P0 # Tag T6
+ get_pc_pair (_BASE + 0x98), A0, P0 # Run/Completion T6
+ get_pc_pair (_BASE + 0xa0), A0, P0 # Mem A/B T6
+ // (_BASE + 0xa8), A0, P0 # Tag T7
+ get_pc_pair (_BASE + 0xb0), A0, P0 # Run/Completion T7
+ get_pc_pair (_BASE + 0xb8), A0, P0 # Mem A/B T7
+
+
+ // Interpolation of TOD and Raw Cycles over 8 threads. First collect
+ // a new tag, then compute the difference with the thread0 tag. The
+ // differences are then divided by 8 to form the interpolation
+ // increment, and interpolation takes places in an unrolled loop.
+ //
+ // Note that we're doing parallel arithmetic here, and ignoring the
+ // fact that there may be a carry/borrow from the low-order TOD into
+ // the high-order cycle count. A single LSB is noise for the cycle
+ // count, but would be significant for the TOD, which is why the
+ // TOD is placed in the low-order part of the doubleword. Given that
+ // a single LSB is noise for the cycle count there is no reason to
+ // expend the time/code space to do the arithmetic 'correctly'.
+
+interpolate:
+ tag_core_data_group 0, 0, P0, P1, store=0 # D0 contains the _NOW_ tag
+
+ ld D1, CORE_DATA_THREAD_BASE(0), A0 # D1 will be used for interp.
+ sub D0, D0, D1
+ andi D0, D0, 0xfffffff8fffffff8 # Mask off bad bits and div. by 8.
+ rotrdi D0, D0, 3
+
+ .macro interpolate, thread
+ add D1, D0, D1
+ std D1, CORE_DATA_THREAD_BASE(\thread), A0
+ .endm
+
+ interpolate 1
+ interpolate 2
+ interpolate 3
+ interpolate 4
+ interpolate 5
+ interpolate 6
+ interpolate 7
+
+
+ // If we made it here there were no errors - Yippee! If we were asked
+ // to collect any EMPATH data then acknowledge that we did.
+1:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, \
+ (GPE_GET_CORE_DATA_EMPATH | \
+ GPE_GET_CORE_DATA_MEMORY | \
+ GPE_GET_CORE_DATA_THROTTLE | \
+ GPE_GET_CORE_DATA_THREAD)
+ braz D0, 1f
+
+ ld D0, CORE_DATA_OHA_RO_STATUS_REG, A0
+ ori D0, D0, CORE_DATA_EMPATH_COLLECTED
+ std D0, CORE_DATA_OHA_RO_STATUS_REG, A0
+
+
+ // 7. Restore error handling; Clear the PC-Only SPWU bit
+1:
+ la A1, saved_emr
+ ld D0, 0, A1
+ mr EMR, D0
+
+ sti OHA_CPM_HIST_RESET_REG, P0, 0
+
+ la A1, core_data_parms
+ ld D0, 0, A1
+ mr A1, D0
+
+
+ // 8. Collect PCB-Slave data
+pcb_slave:
+
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_PCB_SLAVE
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_PCB_SLAVE_BASE
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ get_scom D0, PCBS_POWER_MANAGEMENT_CONTROL_REG, P0, CORE_DATA_PMCR, A0
+ get_scom D0, PCBS_POWER_MANAGEMENT_STATUS_REG, P0, CORE_DATA_PMSR, A0
+ get_scom D0, PCBS_PMSTATEHISTOCC_REG, P0, CORE_DATA_PM_HISTORY, A0
+
+1:
+ ret
+
+
+ //////////////////////////////////////////////////////////////////////
+ // getSensors
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Try to get core and L3 sensor (DTS/CPM) data
+ //
+ // At Entry:
+ //
+ // We are in manual PIB error handling mode
+ // A0 : Base address of core data area
+ // A1 : Address of the parameter block
+ // P0 : Chiplet
+ //
+ // At exit:
+ //
+ // A0, P0 unchanged
+ // D0, D1 scratched
+ //
+ // Note that due to HW279433, we can not read the CPM sensors without
+ // the possiblity of a FIR bit being set due to a PCB timeout. Since
+ // the CPMs are currently not in plan for P8, these fields of the data
+ // structure are simply zeroed.
+
+getSensors:
+
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_DTS_CPM
+ braz D0, getSensorsDone
+
+ // HW279433, see above
+ ls D0, 0
+ std D0, CORE_DATA_SENSOR_V8, A0
+ std D0, CORE_DATA_SENSOR_V9, A0
+
+ .set _BASE, CORE_DATA_DTS_CPM_BASE
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ // First try the core
+
+ ld D0, SENSORS_CORE_V0, P0
+ tprcbnz D1, coreSensorsFailed
+ std D0, CORE_DATA_SENSOR_V0, A0
+
+ ld D0, CORE_DATA_OHA_RO_STATUS_REG, A0
+ ori D0, D0, CORE_DATA_CORE_SENSORS_COLLECTED
+ std D0, CORE_DATA_OHA_RO_STATUS_REG, A0
+
+ bra tryL3
+
+coreSensorsFailed:
+
+ la A1, G_ggcd_coreSensorFail
+ std D1, 0, A1
+ la A1, core_data_parms
+ ld D0, 0, A1
+ mr A1, D0
+
+ ls D0, 0
+ std D0, CORE_DATA_SENSOR_V0, A0
+
+ // Now try the L3
+tryL3:
+ ld D0, SENSORS_CORE_V1, P0
+ tprcbnz D1, l3SensorsFailed
+ std D0, CORE_DATA_SENSOR_V1, A0
+
+ ld D0, CORE_DATA_OHA_RO_STATUS_REG, A0
+ ori D0, D0, CORE_DATA_L3_SENSORS_COLLECTED
+ std D0, CORE_DATA_OHA_RO_STATUS_REG, A0
+
+ bra getSensorsDone
+
+l3SensorsFailed:
+
+ la A1, G_ggcd_l3SensorFail
+ std D1, 0, A1
+ la A1, core_data_parms
+ ld D0, 0, A1
+ mr A1, D0
+
+ ls D0, 0
+ std D0, CORE_DATA_SENSOR_V1, A0
+
+getSensorsDone:
+ ret
+
+
+ //////////////////////////////////////////////////////////////////////
+ // gpcrdError0
+ //
+ // Trap error 0 during EMPATH processing, and set a bit indicating if
+ // this is an "expected" or "unexpected" error. The only expected
+ // error is a PCB error #4 due to HW280375.
+ //
+ // Note that PORE treats error branches as subroutine calls. We need
+ // to pop the HW stack before continuing. We assume we are running on
+ // either GPE0 or GPE1.
+ ////////////////////////////////////////////////////////////////////////////
+
+ .global empathErrorHandlers
+empathErrorHandlers:
+ bra gpcrdError0
+
+gpcrdError0:
+
+ // Set A1 for current engine
+
+ tebngpe0 D0, 1f
+ la A1, PORE_GPE0_OCI_BASE
+ bra 2f
+1:
+ la A1, PORE_GPE1_OCI_BASE
+2:
+
+ // Extract PCB parity error + 3-bit code and compare. Apparently the
+ // PCB error code is not set in the IFR when we take the error branch,
+ // so we have to get it from the debug register. The error code is
+ // used to decide if the error is "expected" or "unexpected".
+
+ ld D0, PORE_DBG0_OFFSET, A1
+ extrdi D0, D0, 4, 32
+
+ ld D1, CORE_DATA_OHA_RO_STATUS_REG, A0
+ cmpibraeq D0, 1f, 4
+
+ // This error is "unexpected"
+
+ ori D1, D1, CORE_DATA_UNEXPECTED_EMPATH_ERROR
+ bra 2f
+
+ // This error (#4) is "expected"
+1:
+ ori D1, D1, CORE_DATA_EXPECTED_EMPATH_ERROR
+
+ // Insert the error code into the OHA_RO_STATUS image
+2:
+ insrdi D1, D0, \
+ CORE_DATA_EMPATH_ERROR_BITS, CORE_DATA_EMPATH_ERROR_LOCATION
+ std D1, CORE_DATA_OHA_RO_STATUS_REG, A0
+
+
+ // Pop the hardware stack. The easiest way to do this is to modify the
+ // current stack pointer and "return" to a local label.
+
+ la D0, 1f
+ sldi D0, D0, 16
+ std D0, PORE_PC_STACK0_OFFSET, A1
+ ret
+1:
+
+ // Clear the debug registers.
+
+ ls D0, 0
+ std D0, PORE_DBG0_OFFSET, A1
+ std D0, PORE_DBG1_OFFSET, A1
+
+ // Bypass EMPATH data (that routine will restore the default error
+ // handling and re-establish A1)
+
+ bra bypass_core_data
+
+
+ //////////////////////////////////////////////////////////////////////
+ // bypass_core_data
+ //////////////////////////////////////////////////////////////////////
+ //
+ // This entry point is used when the core is inaccessible due to idle
+ // modes or other conditions. At entry we are in manual SCOM error
+ // handling mode. The routine will first attempt to collect the
+ // core and L3 DTS/CPM for Sleeping cores, then restore error
+ // handling and zero out the EMPATH data before collecting PCBS data.
+
+ // HW243646: We never read EMPATH counters here. The
+ // counters are all zeroed and all calls of tag_core_data_group
+ // specify raw=0.
+
+bypass_core_data:
+
+ la A1, core_data_parms
+ ld D0, 0, A1
+ mr A1, D0
+
+ bsr getSensors
+
+ // Clear the PC-Only SPWU bit and restore SCOM error handling. Then
+ // reload the parameter pointer into A1.
+
+ sti OHA_CPM_HIST_RESET_REG, P0, 0
+
+ la A1, saved_emr
+ ld D0, 0, A1
+ mr EMR, D0
+
+ la A1, core_data_parms
+ ld D0, 0, A1
+ mr A1, D0
+
+ // Bypass core data
+
+ // EMPATH
+
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_EMPATH
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_EMPATH_BASE
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ ls D0, 0
+ std D0, (_BASE + 0x08), A0
+ std D0, (_BASE + 0x10), A0
+ std D0, (_BASE + 0x18), A0
+ std D0, (_BASE + 0x20), A0
+
+
+ // Per-Core Memory Counters
+
+1:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_MEMORY
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_MEMORY_BASE
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ ls D0, 0
+ std D0, (_BASE + 0x08), A0
+ std D0, (_BASE + 0x10), A0
+
+
+ // Throttling Counters
+
+1:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_THROTTLE
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_THROTTLE_BASE
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ ls D0, 0
+ std D0, (_BASE + 0x08), A0
+ std D0, (_BASE + 0x10), A0
+
+
+ // Per-Thread Counters
+
+1:
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_THREAD
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_THREAD_BASE(0)
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ ls D0, 0
+ std D0, (_BASE + 0x08), A0 # Run/Completion T0
+ std D0, (_BASE + 0x10), A0 # Mem A/B T0
+ // (_BASE + 0x18), A0 # Tag T1
+ std D0, (_BASE + 0x20), A0 # Run/Completion T1
+ std D0, (_BASE + 0x28), A0 # Mem A/B T1
+ // (_BASE + 0x30), A0 # Tag T2
+ std D0, (_BASE + 0x38), A0 # Run/Completion T2
+ std D0, (_BASE + 0x40), A0 # Mem A/B T2
+ // (_BASE + 0x48), A0 # Tag T3
+ std D0, (_BASE + 0x50), A0 # Run/Completion T3
+ std D0, (_BASE + 0x58), A0 # Mem A/B T3
+ // (_BASE + 0x60), A0 # Tag T4
+ std D0, (_BASE + 0x68), A0 # Run/Completion T4
+ std D0, (_BASE + 0x70), A0 # Mem A/B T4
+ // (_BASE + 0x78), A0 # Tag T5
+ std D0, (_BASE + 0x80), A0 # Run/Completion T5
+ std D0, (_BASE + 0x88), A0 # Mem A/B T5
+ // (_BASE + 0x90), A0 # Tag T6
+ std D0, (_BASE + 0x98), A0 # Run/Completion T6
+ std D0, (_BASE + 0xa0), A0 # Mem A/B T6
+ // (_BASE + 0xa8), A0 # Tag T7
+ std D0, (_BASE + 0xb0), A0 # Run/Completion T7
+ std D0, (_BASE + 0xb8), A0 # Mem A/B T7
+
+
+ // Interpolation of TOD and Raw Cycles over 8 threads. First collect
+ // a new tag, then compute the difference with the thread0 tag. The
+ // differences are then divided by 8 to form the interpolation
+ // increment, and interpolation takes places in an unrolled loop.
+ //
+ // Note that we're doing parallel arithmetic here, and ignoring the
+ // fact that there may be a carry/borrow from the low-order TOD into
+ // the high-order cycle count. A single LSB is noise for the cycle
+ // count, but would be significant for the TOD, which is why the
+ // TOD is placed in the low-order part of the doubleword. Given that
+ // a single LSB is noise for the cycle count there is no reason to
+ // expend the time/code space to do the arithmetic 'correctly'.
+
+ tag_core_data_group 0, 0, P0, P1, raw=0, store=0 # D0 contains _NOW_ tag
+
+ ld D1, CORE_DATA_THREAD_BASE(0), A0 # D1 will be used for interp.
+ sub D0, D0, D1
+ andi D0, D0, 0xfffffff8fffffff8 # Mask off bad bits and div. by 8.
+ rotrdi D0, D0, 3
+
+ interpolate 1
+ interpolate 2
+ interpolate 3
+ interpolate 4
+ interpolate 5
+ interpolate 6
+ interpolate 7
+
+
+ // Per-Core PCB Slave Registers
+get_pcbs_data:
+
+ ldandi D0, GPEGETCOREDATAPARMS_SELECT, A1, GPE_GET_CORE_DATA_PCB_SLAVE
+ braz D0, 1f
+
+ .set _BASE, CORE_DATA_PCB_SLAVE_BASE
+ tag_core_data_group _BASE, A0, P0, P1, raw=0
+
+ get_scom D0, PCBS_POWER_MANAGEMENT_CONTROL_REG, P0, CORE_DATA_PMCR, A0
+ get_scom D0, PCBS_POWER_MANAGEMENT_STATUS_REG, P0, CORE_DATA_PMSR, A0
+ get_scom D0, PCBS_PMSTATEHISTOCC_REG, P0, CORE_DATA_PM_HISTORY, A0
+
+1:
+ ret
+
+/// \endcond
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_core_data_fast()
+////////////////////////////////////////////////////////////////////////////
+
+/// \fn gpe_get_core_data_fast(GpeGetChipDataFastParms *parms);
+/// \brief Get chip raw data on fastest possible timescale
+///
+/// This routine collects raw data for the entire chip on the fastest possible
+/// timescale. Where chiplet data is collected, the configured chiplets are
+/// specified in the configuration mask parameter. Data is grouped
+/// into logical groups, and the collection of any group is enabled by a group
+/// select mask. All data groups are tagged with the TOD.
+#ifdef DOXYGEN_ONLY
+void gpe_get_core_data_fast(GpeGetChipDataFastParms *parms);
+#endif
+/// \cond
+
+ // Register usage:
+ //
+ // A1 : Holds the (constant) pointer to the paramaters
+ // A0 : Holds the (varying) pointer to the data area for the current
+ // data group or datum.
+ // P1 : Holds the (constant) chiplet id of the TOD
+ // P0 : Holds the (varying) chiplet id of interest
+ // CTR : Loops through chiplet indices
+ // D1 : Holds/rotates configuration mask
+ // D0 : Scratch
+
+ .global gpe_get_core_data_fast
+
+gpe_get_core_data_fast:
+
+ // Set up registers. A0 must follow the target OCI address as each core
+ // chiplet is considered. Since we're only doing a single
+ // getscom/putOCI, we can keep the chiplet mask in D1. The data group
+ // is tagged with the TOD.
+
+ mr A1, ETR
+ ld D0, GPEGETCOREDATAFASTPARMS_CONFIG, A1
+ left_justify_core_config D0
+ mr D1, D0
+ lpcs P1, TOD_VALUE_REG
+ ld D0, GPEGETCOREDATAFASTPARMS_DATA, A1
+ mr A0, D0
+
+ tag_data_group CORE_DATA_FAST_FREQ_TARGET_BASE, D0, A0, P1
+ adds A0, A0, 8
+
+ ls P0, 0x10
+ ls CTR, (PGP_NCORES - 1) # PORE does test, then decr. and branch
+
+freq_target_loop:
+
+ // Test the chiplet mask. If the chiplet is not configured, simply
+ // continue.
+
+ andi D0, D1, 0x8000000000000000
+ rotldi D1, D1, 1
+ braz D0, freq_target_continue
+
+ get_scom D0, PCBS_LOCAL_PSTATE_FREQUENCY_TARGET_STATUS_REG, P0, \
+ 0x00, A0
+
+freq_target_continue:
+
+ // Increment the core chiplet index and data pointer, then loop or
+ // carry on.
+
+ adds P0, P0, 1
+ adds A0, A0, 8
+ loop freq_target_loop
+
+1:
+ halt
+
+/// \endcond
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_chip_data()
+////////////////////////////////////////////////////////////////////////////
+
+/// \fn gpe_get_chip_data(GpeGetChipDataParms *parms);
+/// \brief Get chip-level raw data
+///
+/// This routine collects chip-level raw data. Data is grouped into logical
+/// groups, and the collection of any group is enabled by a group select
+/// mask. All data groups are tagged with the TOD.
+#ifdef DOXYGEN_ONLY
+void gpe_get_chip_data(GpeGetChipDataParms *parms);
+#endif
+/// \cond
+
+ // Register usage:
+ //
+ // A0 : Holds the (varying) pointer to the data area for the current
+ // data group or datum.
+ // P1 : Holds the (constant) chiplet id of the TOD
+ // D1 : Holds the (constant) select mask
+
+ .global gpe_get_chip_data
+
+gpe_get_chip_data:
+
+ // Set up registers.
+
+ mr A1, ETR
+ ld D0, GPEGETCHIPDATAPARMS_SELECT, A1
+ mr D1, D0
+ lpcs P1, TOD_VALUE_REG
+ ld D0, GPEGETCHIPDATAPARMS_DATA, A1
+ mr A0, D0
+
+ // Overcommit data.
+
+ andi D0, D1, GPE_GET_CHIP_DATA_OVERCOMMIT
+ braz D0, 1f
+ tag_data_group CHIP_DATA_OVERCOMMIT_BASE, D0, A0, P1
+
+ // Overcommit data consists of PBA_PBOCR(0)...PBA_PBOCR(5), all stored
+ // at 8-byte offsets
+
+ la A1, PBA_PBOCRN(0)
+ ocicopy D0, 0x00, A1, 0x08, A0
+ ocicopy D0, 0x08, A1, 0x10, A0
+ ocicopy D0, 0x10, A1, 0x18, A0
+ ocicopy D0, 0x18, A1, 0x20, A0
+ ocicopy D0, 0x20, A1, 0x28, A0
+ ocicopy D0, 0x28, A1, 0x30, A0
+
+1:
+ halt
+
+ .epilogue gpe_get_chip_data
+
+/// \endcond
+
+
+////////////////////////////////////////////////////////////////////////////
+// gpe_get_mem_data()
+////////////////////////////////////////////////////////////////////////////
+
+/// \fn gpe_get_mem_data(GpeGetMemDataParms *parms);
+/// \brief Get memory (MCS/Centaur) data for a particular MCS/Centaur
+///
+/// This routine collects data for the MCS/Centaur named (by instance ID,
+/// (0...PGP_NCENTAUR -1)) in the \a collect field of the \a parms parameter,
+/// unless \a collect is -1 in which case the data collection is bypassed.
+/// Once data has been collected, if the \a update field of the a \parms is
+/// not -1 then that numbered Centaur will be "poked" to start the sensor
+/// cache update. Once data collection (if any) and "poking" (if any) are
+/// finished the parameter block is timestamped with the TOD (at the standard
+/// 2MHz). This means that the TOD timestamp marks the "poke" time (when data
+/// collection starts), not the data collection time.
+///
+/// This procedure requires that the global G_centaurConfiguration structure
+/// must be present and have been properly initialized by
+/// centaur_configuration_create(). The procedure returns a return code -
+/// Either 0 for success, or a non zero value for failure. The failure codes
+/// are documented here: \ref gpe_get_mem_data_rc. Since the parameter block
+/// is read and written by GPE code it is strongly recommended to allocate
+/// instances of this structure in non-cacheable data sections, with the
+/// caveat that data structures assigned to non-default data sections must
+/// always be initialized. For example:
+///
+/// \code
+///
+/// static GpeGetMemDataParms S_parms SECTION_ATTRIBUTE(".noncacheable") = {0};
+///
+/// \endcode
+///
+/// NB: SW273814 documents a request to be able to differentiate which of the 2
+/// Centaurs is responsible for a hard failure. That's why we take pains to
+/// set up the RC prior to collection/poking to enable recovery code to make
+/// this determination.
+#ifdef DOXYGEN_ONLY
+void gpe_get_mem_data(GpeGetMemDataParms *parms);
+#endif
+/// \cond
+
+ .global gpe_get_mem_data
+gpe_get_mem_data:
+
+ // At entry:
+ //
+ // ETR : parms
+ //
+ // Invariants:
+ //
+ // ETR : parms
+ // A1 : parms (except when scratched by subroutines, always restored)
+
+ // Begin by marking the procedure as having died
+
+ mr A1, ETR
+ sti GPEGETMEMDATAPARMS_RC, A1, GPE_GET_MEM_DATA_DIED
+
+ // Next check to make sure the G_centaurConfiguration is properly
+ // initialized (.configRc == 0).
+ //
+ // A1 : parms
+
+ la A0, G_centaurConfiguration
+ ld D0, CENTAUR_CONFIGURATION_CONFIG_RC, A0
+ braz D0, 1f
+
+ ls D0, GPE_GET_MEM_DATA_NOT_CONFIGURED
+ bra ggmdExit
+
+1:
+ // Set up the PBA for Centaur sensor cache access
+ //
+ // A1 : parms
+ // A0 : &G_centaurConfiguration ==> &G_centaurConfiguration.dataParms;
+
+ adds A0, A0, CENTAUR_CONFIGURATION_DATA_PARMS
+ bsr gpe_pba_reset
+ bsr gpe_pba_setup
+ mr A1, ETR # Re-establish invariant
+
+
+ // See if we're collecting data this pass. If so validate that the
+ // MCS/Centaur index is valid according to G_centaurConfiguration.
+ //
+ // A1 : parms
+
+ ld D0, GPEGETMEMDATAPARMS_COLLECT, A1
+ cmpibraeq D0, ggmdUpdate, -1
+
+ bsr ggmdDataSetup
+ mr A1, ETR # Re-establish invariant
+ braz D0, 1f
+
+ ls D0, GPE_GET_MEM_DATA_COLLECT_INVALID
+ bra ggmdExit
+
+1:
+ // A0 has the base address of the sensor cache as a PowerBus
+ // mapping. Load A1 with the user data pointer and collect the data.
+ //
+ // A1 : parms ==> &MemData
+
+ sti GPEGETMEMDATAPARMS_RC, A1, GPE_GET_MEM_DATA_SENSOR_CACHE_FAILED
+
+ ld D0, GPEGETMEMDATAPARMS_DATA, A1
+ mr A1, D0
+
+ ocicopy D0, 0x00, A0, 0x00, A1
+ ocicopy D0, 0x08, A0, 0x08, A1
+ ocicopy D0, 0x10, A0, 0x10, A1
+ ocicopy D0, 0x18, A0, 0x18, A1
+ ocicopy D0, 0x20, A0, 0x20, A1
+ ocicopy D0, 0x28, A0, 0x28, A1
+ ocicopy D0, 0x30, A0, 0x30, A1
+ ocicopy D0, 0x38, A0, 0x38, A1
+ ocicopy D0, 0x40, A0, 0x40, A1
+ ocicopy D0, 0x48, A0, 0x48, A1
+ ocicopy D0, 0x50, A0, 0x50, A1
+ ocicopy D0, 0x58, A0, 0x58, A1
+ ocicopy D0, 0x60, A0, 0x60, A1
+ ocicopy D0, 0x68, A0, 0x68, A1
+ ocicopy D0, 0x70, A0, 0x70, A1
+ ocicopy D0, 0x78, A0, 0x78, A1
+
+ mr A1, ETR # Re-establish invariant
+
+ sti GPEGETMEMDATAPARMS_RC, A1, GPE_GET_MEM_DATA_DIED
+
+ // See if we're poking Centaur this pass. If so validate that the
+ // MCS/Centaur index is valid according to G_centaurConfiguration.
+ //
+ // A1 : parms
+ggmdUpdate:
+
+ ld D0, GPEGETMEMDATAPARMS_UPDATE, A1
+ cmpibraeq D0, ggmdTimestamp, -1
+
+ bsr ggmdDataSetup
+ mr A1, ETR # Re-establish invariant
+ braz D0, 1f
+
+ ls D0, GPE_GET_MEM_DATA_UPDATE_INVALID
+ bra ggmdExit
+
+1:
+ // Poke it
+
+ sti GPEGETMEMDATAPARMS_RC, A1, GPE_GET_MEM_DATA_UPDATE_FAILED
+
+ ls D0, 0
+ std D0, 0, A0
+
+ sti GPEGETMEMDATAPARMS_RC, A1, GPE_GET_MEM_DATA_DIED
+
+ // Collect the timestamp and reduce the 64-bit 512MHz timestamp to a
+ // 32-bit 2MHz timestamp. Then we're out...
+ //
+ // A1 : parms
+ggmdTimestamp:
+
+ lpcs P0, TOD_VALUE_REG
+ ld D0, TOD_VALUE_REG, P0
+ extrdi D0, D0, 32, 24
+ std D0, GPEGETMEMDATAPARMS_PAD_TOD, A1
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Not so fast... If this is Centaur DD1 then we did not actually
+ // collect the Centaur internal temperatures due to HW256773. So we
+ // will go collect them now "manually" by calling _gpe_scom_centaur
+ // with a hard-coded setup to collect SCOM 0x02050000. We then
+ // splice this result into the accumulated cache-line data.
+ //
+ // A1 : Parms
+ ////////////////////////////////////////////////////////////////////
+
+ // Nothing to do if we're not collecting data. Otherwise pull out the
+ // CFAM ID and compare for Centaur DD1
+
+ ld D0, GPEGETMEMDATAPARMS_COLLECT, A1
+ cmpibraeq D0, ggmdCleanExit, -1
+
+ sldi D0, D0, 3 # Multiply by 8 for a byte offset
+
+ la D1, G_centaurConfiguration
+ adds D1, D1, CENTAUR_CONFIGURATION_DEVICE_ID
+ add D0, D0, D1
+ mr A0, D0
+ ld D0, 0, A0
+ extrdi D0, D0, 32, 0
+
+ cmpibrane D0, ggmdCleanExit, CFAM_CHIP_ID_CENTAUR_10
+
+ // This is DD1. Set up the parameters and call _gpe_scom_centaur.
+ // Since we can only do 8-byte stores we read-modify-write the first
+ // entry of the scomList_t. Then call for the SCOM. If it failed set
+ // the failure code. All registers must be restored after the
+ // subroutine call.
+
+ la A0, G_ggmdHw256773
+ ld D0, SCOM_LIST_COMMAND, A0
+ ld D1, GPEGETMEMDATAPARMS_COLLECT, A1
+ scom_list_set_instance_number D0, D1
+ std D0, SCOM_LIST_COMMAND, A0
+
+ la A0, G_hw256773
+ bsr _gpe_scom_centaur
+
+ la A0, G_hw256773
+ mr A1, ETR
+
+ ld D0, GPE_SCOM_PARMS_RC_ERROR_INDEX, A0
+ gpe_scom_parms_get_rc D0, D0
+ braz D0, 1f
+
+ ls D0, GPE_GET_MEM_DATA_HW256773_FAILED
+ bra ggmdExit
+
+1:
+ // The SCOM succeeded. The data needs to be moved from the
+ // gpe_scom_centaur data into the sensor-cache data area. Since there
+ // are only 32 bits we need to read-modify-write the SRAM. This is
+ // doubleword 12 of the sensor cache. The 32 bits of the SCOM we need
+ // are the high-order bits, copied into the low-order bits of the
+ // sensor-cache doubleword. Finally fall through to the clean exit.
+
+ la A0, G_ggmdHw256773
+ ld D0, SCOM_LIST_DATA, A0
+
+ ld D1, GPEGETMEMDATAPARMS_DATA, A1
+ mr A0, D1
+ ld D1, 0x60, A0
+ rldimi D1, D0, 32, 32, 63
+ std D1, 0x60, A0
+
+
+ggmdCleanExit:
+ ls D0, 0
+ggmdExit:
+ std D0, GPEGETMEMDATAPARMS_RC, A1
+ halt
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // ggmdDataSetup
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // At entry:
+ //
+ // D0 : The Centaur instance number to set up
+ //
+ // At exit:
+ //
+ // A0 : On success, the OCI base address to use to access the
+ // sensor cache.
+ // D0 : 0 = Success; 1 = Failure - the caller will supply the
+ // correct error code back to the user.
+ //
+ // This routine checks the Centaur instance number for validity. If
+ // the instance number is valid then the PBA is programmed to access
+ // the sensor cache address. This requires reprogramming the PBA
+ // because part of the data address, which varies by Centaur, must be
+ // stored as the extended address field of the PBA slave control
+ // register. It is not necessary to reset the PBA slave for each data
+ // operation.
+ggmdDataSetup:
+
+ // Check the Centaur instance number (D0) for validity.
+
+ ls D1, PGP_NCENTAUR
+ sub D1, D0, D1
+ tfbult D1, 1f
+
+ ls D0, 1
+ ret # Centaur instance too big
+
+1:
+ // Check to make sure the Centaur is configured by testing the base
+ // address for 0. The instance number is first multiplied by 8 to
+ // create an array offset.
+
+ sldi D0, D0, 3
+ la D1, G_centaurConfiguration
+ adds D1, D1, CENTAUR_CONFIGURATION_BASE_ADDRESS
+ add D0, D0, D1
+ mr A0, D0
+ ld D0, 0, A0
+ branz D0, 1f
+
+ ls D0, 1
+ ret # Base address is 0
+
+1:
+ // We have the Centaur base address in D0, and convert it to the full
+ // PowerBus address for the inband sensor cache access. Bit 27 is set
+ // to indicate OCC (vs. FSP) access. Bit 28 is set to indicate a
+ // sensor cache access.
+
+ ori D0, D0, 0x0000001800000000
+
+#if 1
+ la A0, G_ggmd_lastDataAddress # Debug
+ std D0, 0, A0
+#endif
+
+ // The OCI address is always 0, decorated with the PBA BAR number.
+
+ la A0, (PBA_BAR_CENTAUR << 28)
+
+ // Bits 23:36 of the address go into the extended address field (35:
+ // 48) of the PBA slave control register by a read-modify-write
+ // operation. Note: We're using rldimi explicitly here - not an
+ // extended mnemonic - to save having to justify the data.
+
+ la A1, G_centaurConfiguration
+ ld D1, \
+ (CENTAUR_CONFIGURATION_DATA_PARMS + \
+ GPEPBAPARMS_SLVCTL_ADDRESS), \
+ A1
+ mr A1, D1
+ ld D1, 0, A1
+ rldimi D1, D0, 64 - (35 - 23), 35, 48
+ std D1, 0, A1
+
+#if 1
+ la A1, G_ggmd_lastSlaveControl # Debug
+ std D1, 0, A1
+ mr D1, A0
+ la A1, G_ggmd_lastOciAddress
+ std D1, 0, A1
+#endif
+
+ // Clear D0 to signal success and we're out
+
+ ls D0, 0
+ ret
+ .epilogue gpe_get_mem_data
+
+/// \endcond
+
+
+////////////////////////////////////////////////////////////////////////////
+// Global Data
+////////////////////////////////////////////////////////////////////////////
+
+
+
+/// \cond
+
+ .data.pore
+
+ // Data storage for gpe_get_core_data()
+
+core_data_parms:
+ .quad 0
+saved_emr:
+ .quad 0
+manual_emr:
+ .quad 0
+hw243646:
+#if 0
+ .quad 0x3 # Determined + Required
+#else
+ .quad 0x2 # Determined + Not Required
+#endif
+
+ // Used to debug the workaround for HW280375
+
+testHw280375Lfsr:
+ .quad 0xdeadbeef # Initial state of LFSR
+
+ // Debug/Info: Failure codes when sensor reads fail
+
+ .global G_ggcd_coreSensorFail
+G_ggcd_coreSensorFail:
+ .quad 0
+
+ .global G_ggcd_l3SensorFail
+G_ggcd_l3SensorFail:
+ .quad 0
+
+
+ // Debug only, the last values computed by ggmdDataSetup.
+
+ .global G_ggmd_lastDataAddress
+G_ggmd_lastDataAddress:
+ .quad 0
+
+ .global G_ggmd_lastSlaveControl
+G_ggmd_lastSlaveControl:
+ .quad 0
+
+ .global G_ggmd_lastOciAddress
+G_ggmd_lastOciAddress:
+ .quad 0
+
+
+ // Required for Centaur DD1. This is an assembler layout of a
+ // GpeScomParms structure pointing to a scomList_t structure to read
+ // Centaur SCOM 0x02050000. See the code comments for more details.
+
+ .global G_ggmdHw25773
+G_ggmdHw256773:
+ .long 0x02050000 # SCOM
+ .byte 0 # Reserved
+ .byte 0 # Error flags (output)
+ .byte 0 # Instance Number (input)
+ .byte GPE_SCOM_READ # Command
+ .quad 0 # Mask (unused)
+ .quad 0 # Data (output)
+
+ .global G_hw256773
+G_hw256773:
+ .long 0 # (32-bit addresses)
+ .long G_ggmdHw256773 # scomList
+ .long 1 # Entries in the scomList
+ .long 0 # Options
+ .long 0 # rc (output)
+ .long 0 # errorIndex (output)
+
+/// \endcond
diff --git a/src/lib/gpe_pba.c b/src/lib/gpe_pba.c
new file mode 100755
index 0000000..c3b0a00
--- /dev/null
+++ b/src/lib/gpe_pba.c
@@ -0,0 +1,148 @@
+// $Id: gpe_pba.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe_pba.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe.c
+/// \brief Generic PORE-GPE support procesures (outside of the kernel
+/// drivers).
+
+#include "ssx.h"
+#include "gpe.h"
+#include "gpe_pba.h"
+
+/// Create/initialize a GpePbaParms structure
+///
+/// \param parms A unused or uninitialized GpePbaParms structure
+///
+/// \param slave The PBA slave port to program. For production code this will
+/// normally be PBA_SLAVE_PORE_GPE, however for test/verification code it
+/// could be any PBA slave.
+///
+/// \param write_ttype One of PBA_WRITE_TTYPE_* (see pgp_pba.h). Use
+/// PBA_WRITE_TTYPE_DC (don't care) if the GPE application does not do writes.
+///
+/// \param write_tsize One of PBA_WRITE_TSIZE_* (see pgp_pba.h). The \a
+/// write_tsize is only relevant for \a write_ttype == PBA_WRITE_TTYPE_LCO_M,
+/// where the macro PBA_WRITE_TSIZE_CHIPLET() is used to specify the target
+/// chiplet, and \a write_ttype == PBA_WRITE_TTYPE_ATOMIC_RMW, where the \a
+/// write_tsize specifies the atomic operation. Otherwise use
+/// PBA_WRITE_TTYPE_DC (don't care).
+///
+/// \param read_ttype One of PBA_READ_TTYPE_* (see pgp_pba.h). Normally this
+/// will be PBA_READ_TTYPE_CL_READ_NC (or PBA_READ_TTYPE_DC if you don't
+/// care.)
+///
+/// \param flags Two flags are provided that override default
+/// behavior. GPE_PBA_PARMS_READ_INVALIDATE specifies read buffer invalidation
+/// after every read. This is always selected when the read Ttype is a
+/// cache-inhibited partial read, but may be optionally specified for test
+/// purposes.
+///
+/// Similarly, GPE_PBA_PARMS_DISABLE_WRITE_GATHER specifies that write
+/// gathering for write Ttype DMA partial write is disabled. Note that
+/// GPE_PBA_PARMS_DISABLE_WRITE_GATHER disables write gathering in the sense
+/// that writes pass through the PBA immediately without being buffered. This
+/// is different from using the PBA_WRITE_GATHER_TIMEOUT_DISABLE option to \a
+/// write_gather_timeout, which specifies that writes are gathered until an
+/// entire line is filled.
+///
+/// This API initializes the GpePbaParms structure used by every GPE program
+/// that accesses mainstore via PBA. It creates an image of a PBA_SLVCTL
+/// register to be applied under a mask.
+///
+/// \note Read buffer invalidation is always enforced for cache-inhibited
+/// partial reads. This also forces prefetching to be disabled for the
+/// slave. Our procedures currently do not support save/restore of prefetch
+/// controls as different tasks reprogram the PBA Slave. Thus any access of a
+/// shared slave that is also used to do CI_PR_RD will have prefetching
+/// disabled.
+///
+/// \retval 0 Success
+///
+/// \retval -GPE_INVALID_OBJECT The \a parms pointer is NULL (0) or othewise
+/// invalid.
+///
+/// \retval -GPE_INVALID_ARGUMENT One of the arguments is invalid in some way.
+
+int
+gpe_pba_parms_create(GpePbaParms *parms,
+ int slave,
+ int write_ttype,
+ int write_tsize,
+ int read_ttype)
+{
+ pba_slvctln_t *slvctl, *mask;
+ pba_slvrst_t* slvrst;
+ pba_slvrst_t* slvrst_in_progress;
+ uint64_t all1 = 0xffffffffffffffffull;
+
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((parms == 0), GPE_INVALID_OBJECT);
+ SSX_ERROR_IF((slave < 0) ||
+ (slave >= PBA_SLAVES),
+ GPE_INVALID_ARGUMENT);
+ }
+
+ parms->slave_id = slave;
+
+ slvctl = &(parms->slvctl);
+ mask = &(parms->mask);
+ slvrst = &(parms->slvrst);
+ slvrst_in_progress = &(parms->slvrst_in_progress);
+
+ parms->slvctl_address = PBA_SLVCTLN(slave);
+
+ slvrst->value = 0;
+ slvrst->fields.set = PBA_SLVRST_SET(slave);
+
+ slvrst_in_progress->value = 0;
+ slvrst_in_progress->fields.in_prog = PBA_SLVRST_IN_PROG(slave);
+
+ slvctl->value = 0;
+ mask->value = 0;
+
+ slvctl->fields.enable = 1;
+ mask->fields.enable = all1;
+
+ slvctl->fields.write_ttype = write_ttype;
+ mask->fields.write_ttype = all1;
+
+ slvctl->fields.write_tsize = write_tsize;
+ mask->fields.write_tsize = all1;
+
+ slvctl->fields.read_ttype = read_ttype;
+ mask->fields.read_ttype = all1;
+
+ if (read_ttype == PBA_READ_TTYPE_CI_PR_RD) {
+
+ slvctl->fields.buf_invalidate_ctl = 1;
+ mask->fields.buf_invalidate_ctl = all1;
+
+ slvctl->fields.read_prefetch_ctl = PBA_READ_PREFETCH_NONE;
+ mask->fields.read_prefetch_ctl = all1;
+
+ } else {
+
+ slvctl->fields.buf_invalidate_ctl = 0;
+ mask->fields.buf_invalidate_ctl = all1;
+ }
+
+ mask->value = ~(mask->value);
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/gpe_pba.h b/src/lib/gpe_pba.h
new file mode 100644
index 0000000..5f8ea2c
--- /dev/null
+++ b/src/lib/gpe_pba.h
@@ -0,0 +1,116 @@
+#ifndef __GPE_PBA_H__
+#define __GPE_PBA_H__
+
+// $Id: gpe_pba.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe_pba.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_pba.h
+/// \brief PBA subroutines for PORE-GPE procedures
+
+// Error/Panic codes
+
+#define GPE_INVALID_OBJECT 0x00473001
+#define GPE_INVALID_ARGUMENT 0x00473002
+
+
+#ifndef __ASSEMBLER__
+
+/// Encapsulated PBA setup for GPE programs
+///
+/// All GPE programs that access mainstore via PBA utilize a common parameter
+/// structure that encapsulates the required setup. This is required due to
+/// the unusual architecture of the PBA which interprets OCI addresses and the
+/// associated PowerBus transaction type based on which OCI \e master issued
+/// the OCI transaction, not based on the OCI \a address (or PowerBus
+/// address). The final complication is that each OCI master is assigned a
+/// unique PBA "slave port", so any reprogramming must be done on the PBA
+/// registers associated with the particular port assigned to the particular
+/// PORE engine.
+///
+/// In product code the PBA slave port assignment for the PORE-GPE engines
+/// will be fixed; however this structure assumes the most general case and
+/// allows for an arbitraray and dynamic assignment, and even allows mainstore
+/// programs to run on the PORE-SLW. However the procedure that creates this
+/// structure (gpe_pba_parms_create()) must know which engine/port will run
+/// the program in order to set up the parameters.
+///
+/// The PBA software interface is not friendly for dynamic programming of the
+/// PBA slave setup, especially from the PORE. The slave setup is modifed by a
+/// read-modify-write under mask. gpa_pba_parms_create() does not allow
+/// specification of which read buffers, prefetch modes or write timeouts to
+/// use as these have global implications. Only the values that affect the
+/// particular mode can be programmed there (but of course can be later
+/// overridden if required).
+///
+/// Note that there is an assumption that PORE engines have exclusive access
+/// to their PBA ports. All GPE procedures that access the PowerBus follow a
+/// protocol that makes no assumptions about how the PBA is set up - they set
+/// up the PBA for their own use, then leave it to subsequent procedures to
+/// re-setup the PBA as necessary for subsequent use. Also note that only one
+/// GPE thread can be designated to run programs that access the PBA,
+/// as they share an OCI master ID, and hence a PBA slave port.
+///
+/// Another - perhaps obvious - complication has to do with PBA slave reset. A
+/// PORE engine executing from main memory can not modify the PBA slave read
+/// parameter setup, as this would corrupt the instruction stream. Currently
+/// GPE procedures that need to modify the PBA read parameter setup execute
+/// from SRAM, and the SLW executing from main memeory never changes its read
+/// setup.
+///
+/// This structure is read-only to the GPE routines that access it.
+
+typedef struct {
+
+ /// The 32-bit OCI address of the PBA_SLVCTLn register to set up
+ uint64_t slvctl_address;
+
+ /// An image of the relevant parts of the PBA_SLVCTLn register in effect
+ /// for this procedure
+ pba_slvctln_t slvctl;
+
+ /// The mask in effect for this update of the PBA_SLVCTL
+ pba_slvctln_t mask;
+
+ /// The value to write to the PBA_SLVRST register to reset the slave
+ pba_slvrst_t slvrst;
+
+ /// The bit to AND-poll to check for slave reset in progress
+ pba_slvrst_t slvrst_in_progress;
+
+ /// The slave id (0 - 3)
+ uint64_t slave_id;
+
+} GpePbaParms;
+
+int
+gpe_pba_parms_create(GpePbaParms *parms,
+ int slave,
+ int write_ttype,
+ int write_tsize,
+ int read_ttype);
+
+#endif /* __ASSEMBLER__ */
+
+// Parameter offset for GpePbaParms
+
+#define GPEPBAPARMS_SLVCTL_ADDRESS 0x00
+#define GPEPBAPARMS_SLVCTL 0x08
+#define GPEPBAPARMS_MASK 0x10
+#define GPEPBAPARMS_SLVRST 0x18
+#define GPEPBAPARMS_SLVRST_IN_PROGRESS 0x20
+#define GPEPBAPARMS_SLAVE_ID 0x28
+
+#define SIZEOF_GPEPBAPARMS 0x30
+
+
+// Flags for gpe_pba_parms_setup()
+
+#define GPE_PBA_PARMS_READ_INVALIDATE 0x01
+#define GPE_PBA_PARMS_DISABLE_WRITE_GATHER 0x02
+
+#endif /* __GPE_H__ */
diff --git a/src/lib/gpe_pba_pgas.pS b/src/lib/gpe_pba_pgas.pS
new file mode 100755
index 0000000..346234d
--- /dev/null
+++ b/src/lib/gpe_pba_pgas.pS
@@ -0,0 +1,110 @@
+// $Id: gpe_pba_pgas.pS,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe_pba_pgas.pS,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_pba.pS
+/// \brief PBA subroutines for PORE-GPE procedures
+
+ .nolist
+#include "ssx.h"
+#include "pgas.h"
+#include "pgp_config.h"
+#include "gpe.h"
+#include "gpe_pba.h"
+ .list
+
+ .oci
+
+ .text.pore
+
+ // Reset a PBA slave from a GpePbaParms structure. A setup sequence
+ // looks like
+ //
+ // gpe_pba_reset
+ // gpe_pba_setup
+ //
+ // The subroutine gpe_pba_reset can also be called by itself to insure
+ // that all write data has been flushed to mainstore.
+ //
+ // Note that any PORE program that reads or writes Centaur will need
+ // to execute its code from SRAM since it is not possible to set up
+ // the slave for reading from Centaur while executing from main memory.
+ //
+ // Slave reset for PBA is a complex issue, especially in cases like
+ // this where the entity requesting the reset may be executing from
+ // main memory - i.e., continuing to read from the slave being
+ // reset. To work around potential issues the code that polls for
+ // reset is PowerBus cache-line aligned, and we re-hit the reset
+ // button each time we get an unsuccessful poll for the reset being
+ // done. This should guarantee that the slave will go to reset
+ // status as soon as any PowerBus blockages (if any) clear. For
+ // details see HW228485.
+ //
+ // At entry :
+ //
+ // A0 : The (constant) address of the GpePbaParms structure
+ //
+ // Clobbered:
+ //
+ // D0 : scratched
+ // D1 : scratched
+ // A1 : Holds PBA_SLVRST
+
+ .global gpe_pba_reset
+
+ .balign 128
+gpe_pba_reset:
+ la A1, PBA_SLVRST
+ ld D0, GPEPBAPARMS_SLVRST, A0
+ std D0, 0, A1
+
+ ld D0, GPEPBAPARMS_SLVRST_IN_PROGRESS, A0
+ ld D1, 0, A1
+ and D0, D0, D1
+ branz D0, gpe_pba_reset
+
+ ret
+
+ .epilogue gpe_pba_reset
+
+
+ // Set up a PBA slave from a GpePbaParms structure. A setup sequence
+ // looks like
+ //
+ // gpe_pba_reset
+ // gpe_pba_setup
+ //
+ // At entry :
+ //
+ // A0 : The (constant) address of the GpePbaParms structure
+ //
+ // Clobbered:
+ //
+ // D0 : scratch
+ // A1 : Holds PBA_SLVCTL address for the indicated slave
+
+
+ .global gpe_pba_setup
+gpe_pba_setup:
+
+ // Write the new SLVCTL value under MASK
+
+ ld D0, GPEPBAPARMS_SLVCTL_ADDRESS, A0
+ mr A1, D0
+
+ ld D0, 0, A1
+ ld D1, GPEPBAPARMS_MASK, A0
+ and D0, D0, D1
+
+ ld D1, GPEPBAPARMS_SLVCTL, A0
+ or D0, D0, D1
+
+ std D0, 0, A1
+
+ ret
+
+ .epilogue gpe_pba_setup
diff --git a/src/lib/gpe_scom.h b/src/lib/gpe_scom.h
new file mode 100644
index 0000000..3f8f26e
--- /dev/null
+++ b/src/lib/gpe_scom.h
@@ -0,0 +1,471 @@
+#ifndef __GPE_SCOM_H__
+#define __GPE_SCOM_H__
+
+// $Id: gpe_scom.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe_scom.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_scom.h
+/// \brief Generic SCOM procedures for PORE-GPE
+///
+/// We provide 2 generic SCOM procedures for PORE-GPE, one for P8 SCOMs and
+/// another for Centaur SCOMS. The setup and control of the procedures is
+/// roughly modeled after the way the simple SCOM-ing programs were described
+/// and implemented by the P7 OCA unit. This facility was written primarily to
+/// support SCOM-ing Centaur from OCC (which requires a complex setup),
+/// however for some core applications it may also be simpler to use a generic
+/// procedure rather than creating a custom GPE program to read/write P8 SCOM
+/// registers.
+///
+/// SCOM programs are set up and controlled through a GpeScomParms
+/// structure. This structure contains overall control and status information,
+/// as well as a pointer to an array of scomList_t structures which decribes
+/// the program. Each entry of the scomList_t describes one of several
+/// operations that can be performed on a SCOM address including read, write,
+/// and read-modify-write. Special control-only entries are also supported
+/// including a NOP, a programmable wait delay, timestamping with the TOD and
+/// special "SYNC" commands for Centaur. For more on the commands and their
+/// actions please see \ref gpe_scom_commands.
+///
+/// Each scomList_t entry also includes a data field and a mask field. The
+/// data field contains the data to write for SCOM writes and
+/// read-modify-writes, holds the data returned for SCOM reads, or contains a
+/// pointer to a data vector for vector commands. The mask is used in the case
+/// of read-modify-write to indicate which bits of the SCOM to modify. Control
+/// commands may also interpret these fields in different ways.
+
+
+#ifndef __ASSEMBLER__
+
+/// A SCOM command descriptor for gpe_scom_centaur amd gpe_scom_p8.
+///
+/// For an introduction to the use of this structure and the procedures that
+/// use please see the commants for the file gpe_scom.h
+///
+/// The \a scom field is the full 32-bit SCOM address of the targeted SCOM,
+/// initialized by the caller. SCOM addresses for P8 core SCOMs can be
+/// created from the chiplet-0 address using the macro
+/// CORE_CHIPLET_ADDRESS(). Multicast addresses for P8 can be generated using
+/// the macro MC_ADDRESS(). The notions of internal "chiplets" and "multicast"
+/// are not supported for Centaur SCOM addreses, so for Centaur this is always
+/// a simple SCOM address. The procedure gpe_scom_centaur() does support an
+/// iterative notion of multicast however. Some special control commands do
+/// not use this field at all; see \ref gpe_scom_commands.
+///
+/// The \a errorFlags field contains error status associated with the
+/// (attempted) SCOM access. This field is set by the procedure. For futher
+/// informaton on error handling please see each individual procedure.
+///
+/// The \a instanceNumber field is currently only used by
+/// gpe_scom_centaur(). For further details please see the documentation for
+/// gpe_scom_centaur().
+///
+/// The \a commandType field is initialized by the caller. For command
+/// documentation see \ref gpe_scom_commands and the individual procedures.
+///
+/// The \a data field is used to hold write data for SCOM write and
+/// read-modify-write commands (including the "all" forms), contains the
+/// returned read data for scalar SCOM read commands, and contains a pointer
+/// to a data vector for vector commands. Other commands may also use the \a
+/// data field for other purposes as documented with each command.
+///
+/// The \a mask field contains a positive bit mask used to identify the fields
+/// to update for SCOM read-modify-write. Other commands may also use the \a
+/// mask field for other purposes as documented with each command.
+///
+/// \note Because this structure is read and written by the GPE engine it is
+/// strongly recommended to allocate instances of this structure in
+/// non-cacheable data sections, with the caveat that data structures assigned
+/// to non-default data sections must always be initialized. For example:
+///
+/// \code
+///
+/// static scomList_t S_scomList[10]
+/// SECTION_ATTRIBUTE(".noncacheable") = {0};
+///
+/// \endcode
+
+typedef struct {
+ union
+ {
+ struct {
+ uint32_t scom;
+ uint8_t reserved;
+ uint8_t errorFlags;
+ uint8_t instanceNumber;
+ uint8_t commandType;
+ };
+ uint64_t command;
+ };
+ uint64_t mask;
+ union
+ {
+ uint64_t data;
+ struct {
+ uint32_t unused;
+ uint64_t* pData;
+ };
+ };
+} scomList_t;
+
+#else // __ASSEMBLER__
+
+ // scomList_t structure offsets
+
+ .set SCOM_LIST_COMMAND, 0x0
+ .set SCOM_LIST_MASK, 0x8
+ .set SCOM_LIST_DATA, 0x10
+ .set SIZEOF_SCOM_LIST_T, 0x18
+
+ // PGAS macros to extract fields of the scomList_t command. The source
+ // and target must be data registers, and they can be the same data
+ // register.
+
+ .macro scom_list_get_scom, target:req, source:req
+ extrdi \target, \source, 32, 0
+ .endm
+
+ .macro scom_list_get_instance_number, target:req, source:req
+ extrdi \target, \source, 8, 48
+ .endm
+
+ .macro scom_list_get_command_type, target:req, source:req
+ extrdi \target, \source, 8, 56
+ .endm
+
+ // PGAS macros to update fields of the scomList_t command. The source
+ // and target must be different data registers. The target is the
+ // current value and is updated with the new field held
+ // right-justified in the source. The source register is effectively
+ // destroyed by these operations.
+
+ .macro scom_list_set_error_flags, target:req, source:req
+ insrdi \target, \source, 8, 40
+ .endm
+
+ .macro scom_list_set_instance_number, target:req, source:req
+ insrdi \target, \source, 8, 48
+ .endm
+
+#endif // __ASSEMBLER__
+
+/// \defgroup gpe_scom_commands GPE SCOM Procedure Commands
+///
+/// \note Command 0 is not defined on purpose to trap errors.
+/// @{
+
+/// No operation
+#define GPE_SCOM_NOP 1
+
+/// Read from SCOM, depositing read data into the \a data field of the
+/// scomList_t.
+#define GPE_SCOM_READ 2
+
+/// Write to SCOM, taking write data from the \a data field of the scomList_t.
+#define GPE_SCOM_WRITE 3
+
+/// Read-Modify-Write.
+///
+/// This operation first reads the SCOM. Bits under the \a mask field of the
+/// scomList_t are then cleared in the read data. The masked read data is then
+/// ORed with the contents of the \a data field of the scomList_t and the
+/// result is written back to the SCOM address.
+///
+/// \note This command \e does \e not apply the mask to the data from the \a
+/// data field of the scomList_t. The caller should do this (if necessary)
+/// when setting up the scomList_t.
+///
+/// \note The procedures do not provide a way to distinguish errors that may
+/// have occurred on the initial read vs. those that may have occurred on the
+/// subsequenct write.
+#define GPE_SCOM_RMW 4
+
+/// For gpe_scom_centaur(), the \a data field of the scomList_t contains a
+/// 32-bit pointer (cast to a uint64_t) to an array of PGP_NCENTAUR uint64_t
+/// values. SCOM read data for each configured Centaur (MCS) is deposited in
+/// this array. Array entries for unconfigured Centaur are zeroed.
+#define GPE_SCOM_READ_VECTOR 5
+
+/// For gpe_scom_centaur(), write the \a data field of the scomList_t to
+/// all configured Centaur. Currently unsupported for gpe_scom_p8().
+#define GPE_SCOM_WRITE_ALL 6
+
+/// For gpe_scom_centaur(), perform read-modify write for all configured
+/// Centaur. Currently unsupported for gpe_scom_p8().
+#define GPE_SCOM_RMW_ALL 7
+
+/// Programmable wait delay
+///
+/// This command simply waits for an interval of time specified by the \a data
+/// field of the scomList_t. Use the macro GPE_SCOM_WAIT_DELAY() to convert
+/// SSX (OCC timebase) ticks into the correct units for this command. For
+/// example use GPE_SCOM_WAIT_DELAY(SSX_MILLISECONDS(10)) to wait 10 ms.
+///
+/// \note This operation blocks the GPE from completing any other work until
+/// the delay is finished.
+///
+/// \note This time delay can not be implemented with extreme precision due to
+/// the lack of a programmable wait delay in the PORE architecture, plus
+/// procedure overhead, bus and bus interface contention, etc. For
+/// applications requiring extremely precise timing it will be best to code
+/// those by hand in PORE assembler and run them in a dedicated lab-only
+/// setting.
+#define GPE_SCOM_WAIT 8
+
+/// Issue a generic Centaur SYNC
+///
+/// This command is only valid for gpe_scom_centaur(). This command creates
+/// and issues a generic SYNC command to Centaur. The caller is completely
+/// responsible for creating the contents of the data packet sent as part of
+/// the Centaur SYNC. The data packet is taken verbatim from the \a data field
+/// of the scomList_t, and sent to the MCS designated as the SYNC MCS in the
+/// global G_centaurConfiguration. For further details see the comments with
+/// the procedure gpe_scom_centaur() and the CentaurConfiguration structure.
+#define GPE_SCOM_CENTAUR_SYNC 9
+
+/// Issue a Centaur SYNC to all configured Centaur
+///
+/// This command is only valid for gpe_scom_centaur(). This command creates
+/// and issues a SYNC command to all configured Centaur. The data packet is
+/// taken from the \a data field of the scomList_t, and sent to the MCS
+/// designated as the SYNC MCS in the global G_centaurConfiguration. The
+/// caller is responsible for setting the SYNC command bits (bits 8:N); The
+/// procedure will fill bits 0:7 with a mask of all configured Centaur. For
+/// further details see the comments with the procedure gpe_scom_centaur() and
+/// the CentaurConfiguration structure.
+#define GPE_SCOM_CENTAUR_SYNC_ALL 10
+
+/// Read the TOD clock
+///
+/// This command reads the TOD clock and deposits the value into the \a data
+/// field of the scomList_t.
+#define GPE_SCOM_TOD 11
+
+/// @}
+
+
+#ifndef __ASSEMBLER__
+
+/// \defgroup centaur_sync_commands Centaur SYNC Command Bits
+///
+/// The Centaur SYNC command is an 8-byte word written to a specific in-band
+/// address. SYNC commands are generated by the gpe_scom_centaur() procedure
+/// in response to the GPE_SCOM_CENTAUR_SYNC and GPE_SCOM_CENTAUR_SYNC_ALL
+/// commands (which see).
+///
+/// \note From the MCS Unit Workbook: Note that only the N/M Throttle sync
+/// command will be used operationally in P-series, although if will be
+/// possible to test all the sync commands in P-series lab testing. Z-series
+/// will use all specified sync command types. ... Valid combinations of bits
+/// (8:15) are: b00000000, bVVVVVV0V, and b00000010, where V = 0 or 1.
+///
+/// @{
+
+#define CENTAUR_GENERATE_REFRESH_COUNTER_SYNC 0x0080000000000000ull
+#define CENTAUR_RESET_CALIBRATION_COUNTER_1_SYNC 0x0040000000000000ull
+#define CENTAUR_RESET_CALIBRATION_COUNTER_2_SYNC 0x0020000000000000ull
+#define CENTAUR_RESET_CALIBRATION_COUNTER_3_SYNC 0x0010000000000000ull
+#define CENTAUR_RESET_N_M_THROTTLE_COUNTER_SYNC 0x0008000000000000ull
+#define CENTAUR_RESET_MB_TIMEBASE_SYNC 0x0004000000000000ull
+#define CENTAUR_SUPER_SYNC 0x0002000000000000ull
+#define CENTAUR_MYSTERY_SYNC 0x0001000000000000ull
+
+/// \todo Figure out what is the "mystery sync"
+
+/// @}
+
+
+/// Convert an SsxInterval to a delay specification for gpe_scom_*()
+
+// Yes, Virginia, the PORE engine takes 20 cycles to decrement and branch :(
+#define GPE_SCOM_WAIT_DELAY(x) ((x) / 20)
+
+
+/// Parameters for gpe_scom_centaur() and gpe_scom_p8().
+///
+/// A pointer to an initialized GpeScomParms structure is passed as the
+/// parameter to the GPE procedures gpe_scom_centaur() and gpe_scom_p8.
+///
+/// \note Because this structure is read and written by the GPE engine it is
+/// strongly recommended to allocate instances of this structure in
+/// non-cacheable data sections, with the caveat that data structures assigned
+/// to non-default data sections must always be initialized. For example:
+///
+/// \code
+///
+/// static GpeScomParms S_scomParms
+/// SECTION_ATTRIBUTE(".noncacheable") = {0};
+///
+/// \endcode
+
+typedef struct {
+
+ /// Input: The SCOM list
+ ///
+ /// This is a right-justfied pointer to an array of scomList_t structures
+ /// describing the sequence of commands to execute.
+ uint64_t scomList;
+
+ /// Input: The number of entries in the scomList.
+ ///
+ /// \note It is considered an error if \a entries is 0, under the
+ /// assumption that the caller must have neglected to initialize the
+ /// structure.
+ uint32_t entries;
+
+ /// Input: Procedure options
+ ///
+ /// An OR-mask of option flags; See \ref gpe_scom_options;
+ uint32_t options;
+
+ /// Output: The procedure return code
+ ///
+ /// This field will contain 0 in the event of a successful return, and a
+ /// non-zero value in the event of an error. See \ref gpe_scom_rc for
+ /// documentation of the possible return codes.
+ uint32_t rc;
+
+ /// Output: The index of the entry that failed
+ ///
+ /// In the event that \a rc != 0, this field will contain the 0-based
+ /// index of the \a scomList entry that was being processed at the time of
+ /// the failure, or -1 for failures associated with the parameters or
+ /// setup of the procedure.
+ int32_t errorIndex;
+
+} GpeScomParms;
+
+#else // __ASSEMBLER__
+
+ // Offsets into the GpeScomParms structure
+
+ .set GPE_SCOM_PARMS_SCOM_LIST, 0x00
+ .set GPE_SCOM_PARMS_ENTRIES_OPTIONS, 0x08
+ .set GPE_SCOM_PARMS_RC_ERROR_INDEX, 0x10
+
+ // PGAS macros to extract fields of the GpeScomParms. The source
+ // and target must be data registers, and they can be the same data
+ // register.
+
+ .macro gpe_scom_parms_get_entries, target:req, source:req
+ extrdi \target, \source, 32, 0
+ .endm
+
+ .macro gpe_scom_parms_get_options, target:req, source:req
+ extrdi \target, \source, 32, 32
+ .endm
+
+ .macro gpe_scom_parms_get_rc, target:req, source:req
+ extrdi \target, \source, 32, 0
+ .endm
+
+ // PGAS macros to update fields of the GpeScomParms. The source
+ // and target must be different data registers. The target is the
+ // current value and is updated with the new field held
+ // right-justified in the source. The source register is effectively
+ // destroyed by these operations.
+
+ .macro gpe_scom_parms_set_rc, target:req, source:req
+ insrdi \target, \source, 32, 0
+ .endm
+
+ .macro gpe_scom_parms_set_error_index, target:req, source:req
+ insrdi \target, \source, 32, 32
+ .endm
+
+#endif // __ASSEMBLER__
+
+
+/// \defgroup gpe_scom_rc Return Codes From GPE SCOM Procedures
+///
+/// @{
+
+/// Successful completion of a GPE SCOM program
+#define GPE_SCOM_SUCCESS 0
+
+/// An error occurred during setup of the PBA for Centaur access. If this
+/// error code is returned then the \a errorIndex field of the GpeScomParms
+/// structure will be set to -1.
+#define GPE_SCOM_SETUP_ERROR 1
+
+/// One of the fields of the GpeScomParms structure is invalid. In the case of
+/// gpe_scom_centaur(), this code may also be returned if there is a problem
+/// with the global structure G_centaurConfiguration. If this error code is
+/// returned then the \a errorIndex field of the GpeScomParms structure will
+/// be set to -1 if the error occurred before command processing begins.
+#define GPE_SCOM_INVALID_ARGUMENT 2
+
+/// The procedure died. Since GPE procedures do not trap errors by default
+/// they will typically die on the first hardware-detected error, and GPE
+/// error recovery procedures will clean up the failed job. If this error code
+/// is returned then the \a errorIndex field of the GpeScomParms structure
+/// will indicate the \a scomList entry being processed at the time of the
+/// failure.
+#define GPE_SCOM_DIED 3
+
+/// The \a commandType field of the scomList_t was not valid for the procedure.
+/// When this error is signalled then the \a errorIndex field of the
+/// GpeScomParms structure contains the index of the failing entry.
+#define GPE_SCOM_INVALID_COMMAND 4
+
+/// Signalled only by gpe_scom_centaur(), the \a instanceNumber field of the
+/// scomList_t did not index a valid (configured) Centaur. This error is only
+/// signalled by the GPE_SCOM_READ, GPE_SCOM_WRITE and GPE_SCOM_RMW commands
+/// that require a valid Centaur to be specified. When this error is signalled
+/// then the \a errorIndex field of the GpeScomParms structure contains the
+/// index of the failing entry.
+#define GPE_SCOM_INVALID_CENTAUR 5
+
+/// @}
+
+
+/// Execute a SCOM program for Centaur SCOMs
+///
+/// \param[in,out] io_parms A pointer to an initialized GpeScomParms
+/// structure. Since this structure is used both for input of parameterization
+/// and output of return codes it is imperitive that this structure is
+/// allocated in non-cacheable memory to avoid cache-related bugs. See the
+/// documentation for the fields of GpeScomParms for more information.
+///
+/// gpe_scom_centaur() is a GPE program that takes a pointer to an initialized
+/// GpeScomParms structure as input and executes the list of SCOMs and other
+/// commands. Return codes are returned in the GpeScomParms.
+///
+/// The following notes relate to the fields of the scomList_t structure when
+/// used by gpe_scom_centaur().
+///
+/// - \a instanceNumber : This field must be set to the index (0 - 7) of the
+/// Centaur (MCS) to access for the commands GPE_SCOM_READ, GPE_SCOM_WRITE and
+/// GPE_SCOM_RMW. This field is ignored by other commands.
+///
+/// - \a commandType : gpe_scom_centaur() supports the special command types
+/// GPE_SCOM_CENTAUR_SYNC and GPE_CENTAUR_SYNC_ALL as documented in \ref
+/// gpe_scom_commands.
+///
+/// - \a data : The commands GPE_SCOM_CENTAUR_SYNC and
+/// GPE_SCOM_CENTAUR_SYNC_ALL require a unique format for the \a data field as
+/// documented with the command.
+#ifdef DOXYGEN_ONLY
+void gpe_scom_centaur(GpeScomParms *io_parms);
+#endif
+
+#ifndef __ASSEMBLER__
+
+// Procedure entry points
+
+PoreEntryPoint gpe_scom_centaur;
+PoreEntryPoint gpe_scom_p8;
+
+// Debugging symbols
+
+extern uint64_t G_gsc_lastSlaveControl SECTION_ATTRIBUTE(".data.pore");
+extern uint64_t G_gsc_lastScomAddress SECTION_ATTRIBUTE(".data.pore");
+extern uint64_t G_gsc_lastOciAddress SECTION_ATTRIBUTE(".data.pore");
+
+#endif // __ASSEMBLER__
+
+#endif // __GPE_SCOM_H__
diff --git a/src/lib/gpe_scom.pS b/src/lib/gpe_scom.pS
new file mode 100644
index 0000000..2df1b78
--- /dev/null
+++ b/src/lib/gpe_scom.pS
@@ -0,0 +1,709 @@
+// $Id: gpe_scom.pS,v 1.2 2013/12/13 23:04:33 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpe_scom.pS,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_scom.pS
+/// \brief Generic SCOM procedures for PORE-GPE
+
+ .nolist
+
+#include "ssx.h"
+#include "pgas.h"
+#include "pgp_config.h"
+#include "gpe.h"
+#include "gpe_pba.h"
+#include "gpe_scom.h"
+
+ .list
+
+ .oci
+ .text.pore
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Common Routines
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gsWait
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // The wait loop is implemented as a decrement and branch guaranteed
+ // to hit in the I-cache. This is used both by gpe_scom_centaur() and
+ // gpe_scom_p8().
+gsWait:
+ ld D0, SCOM_LIST_DATA, A1
+ braz D0, gsWaitDone
+ bra gsWaitLoop
+
+ .set PORE_INSTRUCTION_BUFFER_SIZE, 8 # Should be global?
+ .balign PORE_INSTRUCTION_BUFFER_SIZE
+gsWaitLoop:
+ subs D0, D0, 1
+ branz D0, gsWaitLoop
+
+gsWaitDone:
+ ret
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gsTod
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+gsTod:
+ lpcs P0, TOD_VALUE_REG
+ ld D0, TOD_VALUE_REG, P0
+ std D0, SCOM_LIST_DATA, A1
+ ret
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// gpe_scom_centaur
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+// gpe_scom_centaur() has 2 entry points: The first, gpe_scom_centaur(), is for
+// the "normal" case that the job is kicked off by the async drivers. The
+// second, _gpe_scom_centaur() is for use as a subroutine call.
+
+// Implementation note: Correctness requires that gpe_scom_centaur() maintins
+// strict control over the PBA slave as the command list is processed. At
+// entry the slave is reset, and then set up to do the cache-inibited partial
+// writes used for Centaur inband SCOM. Prior to every access the extended
+// address portion of the slave control register needs to be modified as it
+// contains part of the address that actually goes out on the PowerBus. For
+// reads there is no issue with setting the extended address at any
+// time. Since cache-inhibited partial reads are not prefetched, once a read
+// completes to the GPE the extended address is no longer in play. This is
+// not the case for writes. Just because a write completes on the OCI does not
+// mean that the write has completed at Centaur, and it is possible that
+// modifying the extended address before the write completes at Centaur can
+// cause a write to be corrrupted. The means that in general we need to reset
+// the slave prior to modifying the extended address to guarantee that any
+// outstanding write has made it to Centaur. As a run-time optimization we
+// only reset the slave after doing write operations.
+
+// At entry (gpe_scom_centaur):
+//
+// ETR : Contains a pointer to the GpeScomParms structure to interpret.
+//
+// At entry (_gpe_scom_centaur):
+//
+// A0 : Contains a pointer to the GpeScomParms structure to interpret.
+//
+// The caller should assume that all register state is destroyed by
+// calling _gpe_scom_centaur
+
+ .macro gscExit
+ la A0, gscCalledAsSubroutine
+ ld D0, 0, A0
+ braz D0, 4723948f
+ ret
+4723948:
+ halt
+ .endm
+
+
+ .global gpe_scom_centaur
+ .global _gpe_scom_centaur
+
+gpe_scom_centaur:
+ mr D0, ETR
+ ls D1, 0
+ bra gpe_scom_centaur_begin
+
+_gpe_scom_centaur:
+ mr D0, A0
+ ls D1, 1
+
+gpe_scom_centaur_begin:
+ la A0, gscParameters
+ std D0, 0, A0
+ la A0, gscCalledAsSubroutine
+ std D1, 0, A0
+ mr A1, D0
+
+ // Establish the "invalid argument" return code and check that the
+ // global centaur configuration is valid and the number of entries is
+ // non-zero and the pointer to the scomList is non-zero.
+
+ la A0, G_centaurConfiguration
+
+ ls D0, GPE_SCOM_INVALID_ARGUMENT
+ gpe_scom_parms_set_rc D1, D0
+ ls D0, -1
+ gpe_scom_parms_set_error_index D1, D0
+ std D1, GPE_SCOM_PARMS_RC_ERROR_INDEX, A1
+
+ ld D0, CENTAUR_CONFIGURATION_CONFIG_RC, A0
+ braz D0, 1f
+ gscExit # Configuration RC != 0 (Structure is invalid)
+
+1:
+ ld D0, GPE_SCOM_PARMS_ENTRIES_OPTIONS, A1
+ gpe_scom_parms_get_entries D1, D0
+ branz D1, 1f
+ gscExit # entries == 0
+
+1:
+ ld D0, GPE_SCOM_PARMS_SCOM_LIST, A1
+ branz D1, 1f
+ gscExit # scomList == 0
+
+1:
+ // Establish the "setup error" return code and error index for the PBA
+ // Slave reset, then reset the slave.
+ //
+ // At entry:
+ //
+ // A0 = &G_centaurConfiguration
+ // A1 = &GpeScomParms
+
+ ls D0, GPE_SCOM_SETUP_ERROR
+ gpe_scom_parms_set_rc D1, D0
+ ls D0, -1
+ gpe_scom_parms_set_error_index D1, D0
+ std D1, GPE_SCOM_PARMS_RC_ERROR_INDEX, A1
+
+ adds A0, A0, CENTAUR_CONFIGURATION_SCOM_PARMS
+ bsr gpe_pba_reset
+ bsr gpe_pba_setup
+
+
+ // Establish the "procedure died" return code, establish variables
+ // used during the iteration over the scomList, then begin iteration:
+ //
+ // GpeScomParms.[rc, errorIndex]
+ // CTR : The number of entries left to process.
+ // A1 : The address of the scomList_t being processed
+
+ la A0, gscParameters
+ ld D0, 0, A0
+ mr A0, D0
+
+ ls D0, GPE_SCOM_DIED
+ gpe_scom_parms_set_rc D1, D0
+ ls D0, 0
+ gpe_scom_parms_set_error_index D1, D0
+ std D1, GPE_SCOM_PARMS_RC_ERROR_INDEX, A0
+
+ ld D0, GPE_SCOM_PARMS_SCOM_LIST, A0
+ mr A1, D0
+
+ ld D0, GPE_SCOM_PARMS_ENTRIES_OPTIONS, A0
+ gpe_scom_parms_get_entries D0, D0
+ mr CTR, D0
+ loop gscLoop # We know CTR != 0, so this is a branch to
+ # gscLoop w/side effect of CTR--
+
+ // Loop over the scomList, dispatching the commands.
+ //
+ // Loop invariants:
+ //
+ // GpeScomParms.entries has the number of entries processed so
+ // far.
+ //
+ // A1 : The address of the scomList_t being processed
+ //
+ // CTR : Counting down the entries left to process.
+ //
+ // Command dispatch invariants
+ //
+ // A1 : Holds the pointer to the scomList being processed
+gscLoop:
+ ld D1, SCOM_LIST_COMMAND, A1
+ scom_list_get_command_type D0, D1
+
+ // Commands listed in rough order of expected use
+
+ cmpibraeq D0, gscReadVector, GPE_SCOM_READ_VECTOR
+ cmpibraeq D0, gscWriteAll, GPE_SCOM_WRITE_ALL
+ cmpibraeq D0, gscRMWAll, GPE_SCOM_RMW_ALL
+ cmpibraeq D0, gscRead, GPE_SCOM_READ
+ cmpibraeq D0, gscSyncAll, GPE_SCOM_CENTAUR_SYNC_ALL
+ cmpibraeq D0, gscWrite, GPE_SCOM_WRITE
+ cmpibraeq D0, gscRMW, GPE_SCOM_RMW
+ cmpibraeq D0, gscSync, GPE_SCOM_CENTAUR_SYNC
+ cmpibraeq D0, gscTod, GPE_SCOM_TOD
+ cmpibraeq D0, gscWait, GPE_SCOM_WAIT
+ cmpibraeq D0, gscContinue, GPE_SCOM_NOP
+
+ bra gscInvalidCommand
+
+
+ // Continue the loop. Update the index number and scomList pointer.
+gscContinue:
+ la A0, gscParameters
+ ld D0, 0, A0
+ mr A0, D0
+
+ ld D0, GPE_SCOM_PARMS_RC_ERROR_INDEX, A0
+ adds D0, D0, 1 # errorIndex is in low-order word
+ std D0, GPE_SCOM_PARMS_RC_ERROR_INDEX, A0
+
+ adds A1, A1, SIZEOF_SCOM_LIST_T
+
+ loop gscLoop
+
+ // We completed successfully. Set the final rc to 0 and halt.
+
+ ls D0, 0
+ std D0, GPE_SCOM_PARMS_RC_ERROR_INDEX, A0
+ gscExit
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscWrite
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // Do a single Centaur in-band SCOM write. The actual SCOM write is
+ // coded as a subroutine for use by the write-all as well.
+gscWrite:
+ ld D0, SCOM_LIST_COMMAND, A1
+ scom_list_get_instance_number D0, D0
+ bsr gscScomSetup
+ branz D0, gscInvalidCentaur
+
+ bsr gscWrite1
+ bsr gscResetSlave
+ bra gscContinue
+
+
+gscWrite1:
+ ld D0, SCOM_LIST_DATA, A1
+ std D0, 0, A0
+ ret
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscWriteAll
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // It is simplest here to simply unroll a loop that does the SCOM
+ // write for all Centaur indices that pass as being valid.
+gscWriteAll:
+ .set __CENTAUR__, 0
+ .rept PGP_NCENTAUR
+ ls D0, __CENTAUR__
+ bsr gscScomSetup
+ branz D0, 1f
+ bsr gscWrite1
+ bsr gscResetSlave
+1:
+ .set __CENTAUR__, __CENTAUR__ + 1
+ .endr
+
+ bra gscContinue
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscRMW
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // Do a single Centaur in-band SCOM read-modify-write. SCOM data is
+ // ANDed with the inverted mask, then the new data is OR-ed in and
+ // stored back to SCOM. The actual RMW is coded as a subroutine for
+ // use by the RMW-all as well.
+gscRMW:
+ ld D0, SCOM_LIST_COMMAND, A1
+ scom_list_get_instance_number D0, D0
+ bsr gscScomSetup
+ branz D0, gscInvalidCentaur
+
+ bsr gscRMW1
+ bsr gscResetSlave
+ bra gscContinue
+
+
+gscRMW1:
+ ld D0, 0, A0
+ ld D1, SCOM_LIST_MASK, A1
+ xori D1, D1, 0xffffffffffffffff
+ and D0, D0, D1
+ ld D1, SCOM_LIST_DATA, A1
+ or D0, D0, D1
+ std D0, 0, A0
+
+ ret
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscRMWAll
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // It is simplest here to simply unroll a loop that does the SCOM
+ // RMW for all Centaur indices that pass as being valid.
+gscRMWAll:
+ .set __CENTAUR__, 0
+ .rept PGP_NCENTAUR
+ ls D0, __CENTAUR__
+ bsr gscScomSetup
+ branz D0, 1f
+ bsr gscRMW1
+ bsr gscResetSlave
+1:
+ .set __CENTAUR__, __CENTAUR__ + 1
+ .endr
+
+ bra gscContinue
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscRead
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // Do a single Centaur in-band SCOM read
+gscRead:
+ ld D0, SCOM_LIST_COMMAND, A1
+ scom_list_get_instance_number D0, D0
+ bsr gscScomSetup
+ branz D0, gscInvalidCentaur
+
+ ld D0, 0, A0
+ std D0, SCOM_LIST_DATA, A1
+ bra gscContinue
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscReadVector
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // It is simplest here to simply unroll a loop that does the SCOM
+ // read for all Centaur indices that pass as being valid.
+gscReadVector:
+ .set __CENTAUR__, 0
+ .rept PGP_NCENTAUR
+ ls D0, __CENTAUR__
+ bsr gscScomSetup
+ ls D1, __CENTAUR__ * 8 # Byte offset
+ bsr gscReadVector1
+ .set __CENTAUR__, __CENTAUR__ + 1
+ .endr
+
+ bra gscContinue
+
+
+gscReadVector1:
+ // At entry, A1 points to the scomList_t, and D1 has the index of the
+ // Centaur being processed. If D0 == 0 then the read is indicated and
+ // A0 contains the address to dereference to accomplish the read. If
+ // D0 != 0, then the Centaur is invalid and we'll zero the data.
+
+ braz D0, 1f
+
+ // No read indicated. Load the data pointer, convert to an indexed
+ // address and store a 0. We can scratch A0 here.
+
+ ld D0, SCOM_LIST_DATA, A1
+ add D0, D0, D1
+ mr A0, D0
+ ls D0, 0
+ std D0, 0, A0
+ ret
+
+ // A read is indicated. Load the data pointer and convert to an
+ // indexed address in D1. Then load the SCOM data into D0 and store it
+ // back at the indexed address.
+1:
+ ld D0, SCOM_LIST_DATA, A1
+ add D1, D0, D1
+ ld D0, 0, A0
+ mr A0, D1
+ std D0, 0, A0
+ ret
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscSync
+ // gscSyncAll
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // These commands only differ in whether the user fills in the mask of
+ // valid Centaurs or the procedure fills in the mask of valid Centaurs
+ // from the global configuration. The extended address needed for the
+ // sync is stored in the global data structure, and it is only
+ // necessary to update the slave and store to the base address of the
+ // PBA BAR to accomplish the SYNC.
+gscSync:
+ bsr gscSyncSetup
+ ld D0, SCOM_LIST_DATA, A1
+ bra gscSyncContinue
+
+gscSyncAll:
+ bsr gscSyncSetup
+ la A0, G_centaurConfiguration
+ ld D0, CENTAUR_CONFIGURATION_CONFIG, A0
+ left_justify_centaur_config D0
+ ld D1, SCOM_LIST_DATA, A1
+ or D0, D0, D1
+ bra gscSyncContinue
+
+
+ // To set up the SYNC we only have to update the extended address
+ // field (bits 35:48) of the slave control register from the slave
+ // control register image held in the G_centaurConfiguration. We can't
+ // destroy A1 so it's a little tedious as we have to load the slave
+ // control register address twice.
+gscSyncSetup:
+ la A0, G_centaurConfiguration
+ ld D1, \
+ (CENTAUR_CONFIGURATION_SCOM_PARMS + \
+ GPEPBAPARMS_SLVCTL_ADDRESS), \
+ A0
+ mr A0, D1
+ ld D0, 0, A0
+
+ la A0, G_centaurConfiguration
+ ld D1, CENTAUR_CONFIGURATION_SYNC_SLAVE_CONTROL, A0
+ rldimi D0, D1, 0, 35, 48
+
+ la A0, G_centaurConfiguration
+ ld D1, \
+ (CENTAUR_CONFIGURATION_SCOM_PARMS + \
+ GPEPBAPARMS_SLVCTL_ADDRESS), \
+ A0
+ mr A0, D1
+ std D0, 0, A0
+
+ ret
+
+
+ // Once it's set up, simply issue a store to complete the sync. The
+ // caller has placed the correct data in D0.
+gscSyncContinue:
+ la A0, (PBA_BAR_CENTAUR << 28)
+ std D0, 0, A0
+ bsr gscResetSlave
+ bra gscContinue
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscWait
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+gscWait:
+ bsr gsWait
+ bra gscContinue
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscTod
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+gscTod:
+ bsr gsTod
+ bra gscContinue
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscScomSetup
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // At entry:
+ //
+ // A1 : The address of the scomList_t being processed
+ // D0 : The Centaur instance number to set up
+ //
+ // At exit:
+ //
+ // PBA : Programmed to do the SCOM
+ // A1 : The address of the scomList_t being processed
+ // A0 : On success, the OCI address to load/store to do the SCOM
+ // D0 : 0 = Success, otherwise an error code
+ //
+ // This routine checks the Centaur instance number for validity. If
+ // the instance number is valid then the PBA is programmed to access
+ // the SCOM address held in the scomList_t. This requires
+ // reprogramming the PBA because part of the SCOM address must be
+ // stored as the extended address field of the PBA slave control
+ // register. It is not necessary to reset the PBA slave for each SCOM
+ // operation. The way SCOM operations are set up they "complete
+ // immediately" in the PBA so there is no issue with lingering state.
+ //
+ // Note: this routine is written this way (separating setup from
+ // execution) to support the Centaur "multicast" and read-modify-write
+ // operations. The multicast loop simply tries all Centaur and ignores
+ // the ones that fail.
+gscScomSetup:
+
+ // Check the Centaur instance number (D0) for validity.
+
+ ls D1, PGP_NCENTAUR
+ sub D1, D0, D1
+ tfbult D1, 1f
+
+ ls D0, GPE_SCOM_INVALID_ARGUMENT
+ ret # Centaur instance too big
+
+1:
+ // Check to make sure the Centaur is configured by testing the base
+ // address for 0. The instance number is first multiplied by 8 to
+ // create an array offset.
+
+ rotldi D0, D0, 3
+ la D1, G_centaurConfiguration
+ adds D1, D1, CENTAUR_CONFIGURATION_BASE_ADDRESS
+ add D0, D0, D1
+ mr A0, D0
+ ld D0, 0, A0
+ branz D0, 1f
+
+ ls D0, GPE_SCOM_INVALID_ARGUMENT
+ ret # Base address is 0
+
+1:
+ // We have the Centaur base address in D0, and convert it to the full
+ // PowerBus address for the inband SCOM. Bit 27 is set to indicate OCC
+ // (vs. FSP) access. Bit 28 remains 0 to indicate a SCOM (vs. sensor
+ // cache) access. Bits 29:60 are the SCOM address. (The SCOM address
+ // is shifted up by 3 bit positions). We need to save A1 to SPRG0 to
+ // continue from here.
+
+ ori D0, D0, 0x0000001000000000
+ ld D1, SCOM_LIST_COMMAND, A1
+ scom_list_get_scom D1, D1
+ rotldi D1, D1, 3
+ or D0, D0, D1
+
+ mr SPRG0, A1
+
+#if 1
+ la A1, G_gsc_lastScomAddress # Debug
+ std D0, 0, A1
+#endif
+
+ // The low-order 27 bits of the PowerBus address are OR-ed with the
+ // PBA BAR base address and go into A0 as the returned OCI address.
+
+ andi D1, D0, 0x7ffffff
+ ori D1, D1, (PBA_BAR_CENTAUR << 28)
+ mr A0, D1
+
+ // Bits 23:36 of the address go into the extended address field (35:
+ // 48) of the PBA slave control register by a read-modify-write
+ // operation. Note: We're using rldimi explicitly here - not an
+ // extended mnemonic - to save having to justify the data.
+
+ la A1, G_centaurConfiguration
+ ld D1, \
+ (CENTAUR_CONFIGURATION_SCOM_PARMS + \
+ GPEPBAPARMS_SLVCTL_ADDRESS), \
+ A1
+ mr A1, D1
+ ld D1, 0, A1
+ rldimi D1, D0, 64 - (35 - 23), 35, 48
+ std D1, 0, A1
+
+#if 1
+ la A1, G_gsc_lastSlaveControl # Debug
+ std D1, 0, A1
+ mr D1, A0
+ la A1, G_gsc_lastOciAddress
+ std D1, 0, A1
+#endif
+
+ // Restore A1 to its invariant state, clear D0 to signal success and
+ // we're out
+
+ mr A1, SPRG0
+ ls D0, 0
+ ret
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscResetSlave
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // Reset the PBA slave after a write. This requires saving and
+ // restoring A1. To avoid PORE stack overflow we have to inline
+ // gpe_pba_reset() here. See the file gpe_pba_pgas.pS for comments on
+ // why the slave reset is written like this.
+
+gscResetSlave:
+ la A0, gscSaveA1
+ mr D0, A1
+ std D0, 0, A0
+
+ la A0, G_centaurConfiguration + CENTAUR_CONFIGURATION_SCOM_PARMS
+ bra gscGpePbaReset
+
+ .balign 128
+gscGpePbaReset:
+ la A1, PBA_SLVRST
+ ld D0, GPEPBAPARMS_SLVRST, A0
+ std D0, 0, A1
+
+ ld D0, GPEPBAPARMS_SLVRST_IN_PROGRESS, A0
+ ld D1, 0, A1
+ and D0, D0, D1
+ branz D0, gscGpePbaReset
+
+ la A0, gscSaveA1
+ ld D0, 0, A0
+ mr A1, D0
+
+ ret
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gscInvalidCommand
+ // gscInvalidCentaur
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ //
+ // Set the rc field. The errorIndex has already been set.
+
+gscInvalidCommand:
+ ls D1, GPE_SCOM_INVALID_COMMAND
+ bra 1f
+gscInvalidCentaur:
+ ls D1, GPE_SCOM_INVALID_CENTAUR
+1:
+ la A0, gscParameters
+ ld D0, 0, A0
+ mr A0, D0
+
+ ld D0, GPE_SCOM_PARMS_RC_ERROR_INDEX, A0
+ gpe_scom_parms_set_rc D0, D1
+ std D0, GPE_SCOM_PARMS_RC_ERROR_INDEX, A0
+ gscExit
+
+
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ // gpe_scom_centaur Global Data
+ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ .data.pore
+
+ // Set to 0/1 when gpe_scom_centaur() is called via
+ // gpe_scom_centaur (ASYNC) / _gpe_scom_centaur (Subroutine)
+
+gscCalledAsSubroutine:
+ .quad 0
+
+
+ // Used to store the parameter block pointer
+
+gscParameters:
+ .quad 0
+
+
+ // Used to store A1 during the inner loop when we need to reset the
+ // slave after a write
+
+gscSaveA1:
+ .quad 0
+
+
+ // Debug only, the last values computed by gscScomSetup.
+
+ .global G_gsc_lastSlaveControl
+G_gsc_lastSlaveControl:
+ .quad 0
+
+ .global G_gsc_lastScomAddress
+G_gsc_lastScomAddress:
+ .quad 0
+
+ .global G_gsc_lastOciAddress
+G_gsc_lastOciAddress:
+ .quad 0
diff --git a/src/lib/gpsm.c b/src/lib/gpsm.c
new file mode 100755
index 0000000..e2b71d6
--- /dev/null
+++ b/src/lib/gpsm.c
@@ -0,0 +1,600 @@
+// $Id: gpsm.c,v 1.2 2014/02/03 01:30:24 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpsm.c
+/// \brief Global Pstate Mechanism procedures
+///
+/// \todo : Should we initialize any/all iVRM delays in gpsm_lpsa_install()?
+
+#include "ssx.h"
+#include "pstates.h"
+#include "gpe_control.h"
+#include "gpsm.h"
+#include "vrm.h"
+
+/// The semaphore used to block threads waiting for GPSM protocol actions
+
+SsxSemaphore G_gpsm_protocol_semaphore;
+
+
+////////////////////////////////////////////////////////////////////////////
+// Private Utilities
+////////////////////////////////////////////////////////////////////////////
+
+// The mechanical transition to Firmware Pstate Mode - Not a procedure
+
+static void
+_gpsm_fw_mode(void)
+{
+ pmc_mode_reg_t pmr;
+
+ pmr.value = in32(PMC_MODE_REG);
+ pmr.fields.enable_hw_pstate_mode = 0;
+ pmr.fields.enable_fw_auction_pstate_mode = 0;
+ pmr.fields.enable_fw_pstate_mode = 1;
+ out32(PMC_MODE_REG, pmr.value);
+}
+
+
+// The mechanical transition to Firmware Auction Pstate Mode - Not a procedure
+
+static void
+_gpsm_fw_auction_mode(void)
+{
+ pmc_mode_reg_t pmr;
+
+ pmr.value = in32(PMC_MODE_REG);
+ pmr.fields.enable_hw_pstate_mode = 0;
+ pmr.fields.enable_fw_auction_pstate_mode = 1;
+ pmr.fields.enable_fw_pstate_mode = 0;
+ out32(PMC_MODE_REG, pmr.value);
+}
+
+
+// The mechanical transition to Hardware Pstate Mode - Not a procedure.
+// Disable voltage change via safe_mode_without_spivid
+// before enter hw mode to prevent possible glitch that
+// hw momentarily flush turbo to pstate actual,
+// After enter hw mode, we will enable back the spivid
+
+static void
+_gpsm_hw_mode(void)
+{
+ pmc_mode_reg_t pmr;
+
+ if (!gpsm_dcm_slave_p()) {
+ pmr.value = in32(PMC_MODE_REG);
+ pmr.fields.safe_mode_without_spivid = 1;
+ out32(PMC_MODE_REG, pmr.value);
+ }
+
+ pmr.value = in32(PMC_MODE_REG);
+ pmr.fields.enable_hw_pstate_mode = 1;
+ pmr.fields.enable_fw_auction_pstate_mode = 0;
+ pmr.fields.enable_fw_pstate_mode = 0;
+ out32(PMC_MODE_REG, pmr.value);
+
+ if (!gpsm_dcm_slave_p()) {
+ pmr.value = in32(PMC_MODE_REG);
+ pmr.fields.safe_mode_without_spivid = 0;
+ out32(PMC_MODE_REG, pmr.value);
+ }
+
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Private Sub-Procedures
+////////////////////////////////////////////////////////////////////////////
+
+// By definition, quiescing the GPSM always leaves the system in firmware
+// Pstate mode. This is necessary for a consistent specification due to the
+// fact that in general, Hardware Pstate mode can not be quiesced without
+// leaving that mode.
+
+// To quiesce the GPSM in firmware or firmware auction mode requires waiting
+// for both the voltage and frequency changes to be complete. Note that they
+// will never be ongoing simultaneously. This predicate is used for all
+// firmware-mode quiesce, even though normally only one part or the other
+// (voltage/protocol) is active.
+//
+// Recall that PMC interrupts are level-low, so if ongoing status is clear,
+// the operation is ongoing.
+
+static int
+gpsm_fw_quiesce(void)
+{
+ int rc = 0;
+
+ if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) {
+ ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING);
+ rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER);
+ }
+
+ if ((!rc) && !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING)) {
+ ssx_irq_enable(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING);
+ rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER);
+ }
+
+ if (!rc) {
+ _gpsm_fw_mode();
+ }
+
+ return rc;
+}
+
+
+// To quiesce the GPSM in hardware mode requires waiting for any ongoing
+// Pstate change to be complete. Note that there is no guarantee that this
+// condition will ever be true in general unless something external to PMC
+// ensures that Global bids stop coming in to the GPSM. An alternative used
+// here is to 'lock' the GPSM temporarily by setting the rail bounds min and
+// max to the current Global Pstate Actual. The GPSM will eventually quiesce
+// at the global actual, and we can safely move to Firmware Pstate mode and
+// release the lock.
+//
+// Recall that PMC 'ongoing' interrupts are level-low, so if ongoing status is
+// clear, the operation is ongoing.
+
+static int
+gpsm_hw_quiesce(void)
+{
+ int rc = 0;
+ pmc_rail_bounds_register_t prbr, original_prbr;
+ pmc_pstate_monitor_and_ctrl_reg_t ppmacr;
+
+ ppmacr.value = in32(PMC_PSTATE_MONITOR_AND_CTRL_REG);
+
+ original_prbr.value = prbr.value = in32(PMC_RAIL_BOUNDS_REGISTER);
+ prbr.fields.pmin_rail = ppmacr.fields.gpsa;
+ prbr.fields.pmax_rail = ppmacr.fields.gpsa;
+ out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value);
+
+ rc = _gpsm_hw_quiesce();
+
+ if (!rc) {
+ _gpsm_fw_mode();
+ out32(PMC_RAIL_BOUNDS_REGISTER, original_prbr.value);
+ }
+
+ return rc;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Public Predicates
+////////////////////////////////////////////////////////////////////////////
+
+/// Is the Global Pstate Mechanism quiesced?
+///
+/// This predicate can only truly be answered 'true' if we are not in
+/// hardware Pstate mode.
+///
+/// \retval 0 Either we're in Hardware Pstate Mode, or a Voltage/Frequency
+/// operation is ongoing.
+///
+/// \retval 1 We're not in Hardware Pstate Mode and no Voltage/Frequency
+/// operation is ongoing.
+
+int
+gpsm_quiesced_p(void)
+{
+ return !(gpsm_hw_mode_p() ||
+ !ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING) ||
+ !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING));
+}
+
+/// Predicate: Is the PMC in hardware Pstate mode?
+///
+/// \returns 0/1
+
+int
+gpsm_hw_mode_p(void)
+{
+ pmc_mode_reg_t pmr;
+
+ pmr.value = in32(PMC_MODE_REG);
+ return (pmr.fields.enable_hw_pstate_mode != 0);
+}
+
+
+/// Predicate: Is the PMC in firmware auction Pstate mode?
+///
+/// \returns 0/1
+
+int
+gpsm_fw_auction_mode_p(void)
+{
+ pmc_mode_reg_t pmr;
+
+ pmr.value = in32(PMC_MODE_REG);
+ return (pmr.fields.enable_fw_auction_pstate_mode != 0);
+}
+
+
+/// Predicate: Is the PMC in firmware Pstate mode?
+///
+/// \returns 0/1
+
+int
+gpsm_fw_mode_p(void)
+{
+ pmc_mode_reg_t pmr;
+
+ pmr.value = in32(PMC_MODE_REG);
+ return (pmr.fields.enable_fw_pstate_mode != 0);
+}
+
+
+/// Predicate: Is the chip configured as a DCM?
+///
+/// \returns 0/1
+
+int
+gpsm_dcm_mode_p(void)
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ return pmc_mode_reg.fields.enable_interchip_interface;
+}
+
+
+/// Predicate: Is the chip configured as a DCM Slave?
+///
+/// \returns 0/1
+
+int
+gpsm_dcm_master_p(void)
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ return
+ pmc_mode_reg.fields.enable_interchip_interface &&
+ pmc_mode_reg.fields.interchip_mode;
+}
+
+
+/// Predicate: Is the chip configured as a DCM Slave?
+///
+/// \returns 0/1
+
+int
+gpsm_dcm_slave_p(void)
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ return
+ pmc_mode_reg.fields.enable_interchip_interface &&
+ (pmc_mode_reg.fields.interchip_mode == 0);
+}
+
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// Procedures
+////////////////////////////////////////////////////////////////////////////
+
+/// Recover the GlobalPstateTable object from the PMC
+///
+/// \note It is assumed that the pointer to the Global Pstate table installed
+/// in the PMC is actually a pointer to a complete GlobalPstateTable object
+/// (which contains a Global Pstate table as its first element).
+///
+/// \returns A pointer to the currently active GlobalPstateTable object.
+
+GlobalPstateTable*
+gpsm_gpst(void)
+{
+ pmc_parameter_reg1_t ppr1;
+
+ ppr1.value = in32(PMC_PARAMETER_REG1);
+ return (GlobalPstateTable*)
+ (ppr1.fields.ba_sram_pstate_table << GLOBAL_PSTATE_TABLE_ALIGNMENT);
+}
+
+
+/// Quiesce the GPSM to firmware mode from any other mode
+///
+/// At the exit of this procedure, the PMC will be in Firmware Pstate Mode and
+/// there will be no ongoing voltage or frequency transitions.
+int
+gpsm_quiesce(void)
+{
+ int rc;
+
+ if (gpsm_hw_mode_p()) {
+ rc = gpsm_hw_quiesce();
+ } else {
+ rc = gpsm_fw_quiesce();
+ }
+
+ return rc;
+}
+
+/// Quiesce the GPSM in Hardware Pstate Mode
+///
+/// In general there is no guarantee that the GPSM will ever quiesce, or
+/// remain quiesced in Hardware Pstate Mode unless something like the
+/// procedure in gpsm_hw_quiesce() is used. This procedure is provided for
+/// the benefit of applications that are in complete control of Pstates
+/// (including idle state Pstates) to simply wait for the Pstate protocol to
+/// quiesce, without quiescing and entering Firmware Pstate mode like
+/// gpsm_hw_quiesce().
+
+int
+_gpsm_hw_quiesce(void)
+{
+ int rc;
+
+ do {
+ rc = 0;
+
+ if (!gpsm_hw_mode_p()) {
+ rc = -GPSM_ILLEGAL_MODE_HW_QUIESCE;
+ break;
+ }
+
+ if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) {
+ ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING);
+ rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore,
+ SSX_WAIT_FOREVER);
+ if (rc) break;
+ }
+ } while (0);
+
+ return rc;
+}
+
+
+/// Change to GPSM firmware mode from any mode
+
+int
+gpsm_fw_mode(void)
+{
+ return gpsm_quiesce();
+}
+
+
+/// Change to GPSM firmware auction mode from any mode
+
+int
+gpsm_fw_auction_mode(void)
+{
+ int rc;
+
+ rc = gpsm_quiesce();
+ if (!rc) {
+ _gpsm_fw_auction_mode();
+ }
+ return rc;
+}
+
+
+/// Change to Hardware Pstate Mode
+///
+/// The (unchecked) assumption behind this procedure is that the caller has
+/// run through Pstate intialization and enablement, and the system is in a
+/// state where the entry to Hardware Pstate Mode is safe once the GPSM is
+/// quiesced.
+
+int
+gpsm_hw_mode(void)
+{
+ int rc;
+
+ TRACE_GPSM(TRACE_GPSM_HW_MODE);
+
+ rc = gpsm_quiesce();
+ if (!rc) {
+ _gpsm_hw_mode();
+ }
+ return rc;
+}
+
+
+/// The default GPSM auction procedure
+///
+/// The default auction returns the value of
+/// PMC_HARDWARE_AUCTION_PSTATE_REG.haps.
+
+Pstate
+gpsm_default_auction(void)
+{
+ pmc_hardware_auction_pstate_reg_t phapr;
+
+ phapr.value = in32(PMC_HARDWARE_AUCTION_PSTATE_REG);
+ return phapr.fields.haps;
+}
+
+
+/// Update a user-supplied vector of Pstates with the current Global bid of
+/// each core.
+///
+/// \param[out] o_bids A vector of Pstates; The vector must be large enough to
+/// hold the bid of every possible core.
+///
+/// This routine is provided for use by non-default Global Pstate auction
+/// procedures.
+
+void
+gpsm_get_global_bids(Pstate *o_bids)
+{
+ // This takes advantage of the implicit layout of the
+ // PMC_CORE_PSTATE_REG<n>.
+
+ uint32_t *bids32 = (uint32_t *)o_bids;
+
+ bids32[0] = in32(PMC_CORE_PSTATE_REG0);
+ bids32[1] = in32(PMC_CORE_PSTATE_REG1);
+ bids32[2] = in32(PMC_CORE_PSTATE_REG2);
+ bids32[3] = in32(PMC_CORE_PSTATE_REG3);
+}
+
+
+/// Update a current Global bid of each core from a user supplied vector.
+///
+/// \param[in] i_bids An array of Global Pstate bids.
+///
+/// This routine is provided for use by test procedures; there is likely no
+/// product-level energy management application for this procedure.
+
+void
+gpsm_set_global_bids(const Pstate *i_bids)
+{
+ // This takes advantage of the implicit layout of the
+ // PMC_CORE_PSTATE_REG<n>.
+
+ uint32_t *bids32 = (uint32_t *)i_bids;
+
+ out32(PMC_CORE_PSTATE_REG0, bids32[0]);
+ out32(PMC_CORE_PSTATE_REG1, bids32[1]);
+ out32(PMC_CORE_PSTATE_REG2, bids32[2]);
+ out32(PMC_CORE_PSTATE_REG3, bids32[3]);
+}
+
+
+/// Application-controlled Global Actual Broadcast
+///
+/// \param[in] i_pstate The Global Actual Pstate to broadcast
+///
+/// \param[in] i_entry A gpst_entry_t containing the information to be used by
+/// the iVRM. If iVRM are not enabled then \a entry can be initialized to 0.
+///
+/// This API is provided for advanced applications to have complete control
+/// over a firmware-mode Global Actual broadcast. There is no error
+/// checking. Most applications in Firware Pstate mode will use the
+/// higher-level gpsm_broadcast_global_actual() API.
+
+void
+_gpsm_broadcast_global_actual(const Pstate i_pstate,
+ const gpst_entry_t i_entry)
+{
+ pmc_pstate_monitor_and_ctrl_reg_t ppmacr;
+ pmc_eff_global_actual_voltage_reg_t pegavr;
+
+ ppmacr.value = 0;
+ ppmacr.fields.gpsa = i_pstate;
+ out32(PMC_PSTATE_MONITOR_AND_CTRL_REG, ppmacr.value);
+
+ pegavr.value = 0;
+ pegavr.fields.maxreg_vdd = i_entry.fields.maxreg_vdd;
+ pegavr.fields.maxreg_vcs = i_entry.fields.maxreg_vcs;
+ pegavr.fields.eff_evid_vdd = i_entry.fields.evid_vdd_eff;
+ pegavr.fields.eff_evid_vcs = i_entry.fields.evid_vcs_eff;
+ out32(PMC_EFF_GLOBAL_ACTUAL_VOLTAGE_REG, pegavr.value);
+
+ TRACE_GPSM(TRACE_GPSM_BROADCAST_GLOBAL_ACTUAL);
+}
+
+
+/// Broadcast the Global Actual Pstate in firmware Pstate mode.
+///
+/// \param[in] i_gpst An initialized GlobalPstateTable structure used to
+/// define the legal Pstate range, and to provide the voltage settings
+/// (maxreg_vxx and eff_evid_vxx) for the internal VRM.
+///
+/// \param[in] i_pstate The pstate specfiying the Global Actual Pstate to be
+/// broadast to the core chiplets.
+///
+/// \param[in] i_bias This is a signed bias used to obtain the voltage Pstate,
+/// <em> in addition to the \a undervolting_bias already built into the Pstate
+/// table </em>. The iVRM information sent with the Global Actual Pstate comes
+/// from the \a pstate - \a undervolting_bias + \a bias entry of the Pstate
+/// table.
+///
+/// This API can be used in firware Pstate mode to broadcast a Global Actual
+/// Pstate and iVRM settings to the core chiplets. The API also supports
+/// optional under/over-volting. The requested Pstate will be broadcast along
+/// with the voltage information from the associated Pstate table entry.
+///
+/// Under/over-volting is specified by setting the \a bias to a non-0
+/// (signed) value. For example, to undervfolt by one Pstate (if possible),
+/// call the API with \a bias = -1.
+///
+/// This API always waits for the Global Pstate Machine to quiesce before
+/// proceeding with the Global Actual broadcast. Therefore it can only be
+/// called from thread mode, or from a non-thread mode guaranteed by the
+/// caller to have quiesced.
+///
+/// \note The application can use the _gpsm_broadcast_global_actual() API for
+/// complete control over the information transmitted to the cores.
+///
+/// The following return codes are not considered errors:
+///
+/// \retval 0 Success
+///
+/// \retval -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA The requested Pstate does not
+/// exist in the table. The maximum Pstate entry in the table has been
+/// broadcast as the voltage Pstate.
+///
+/// \retval -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA The requested Pstate does not
+/// exist in the table. The minimum Pstate entry in the table has been
+/// broadcast as the voltage Pstate.
+///
+/// The following return codes are considered errors:
+///
+/// \retval -GPSM_INVALID_OBJECT The Global Pstate Table is either null (0) or
+/// otherwise invalid.
+///
+/// \retval -GPSM_INVALID_ARGUMENT One or more arguments are invalid or
+/// inconsistent in some way.
+///
+/// \retval -GPSM_ILLEGAL_MODE_BGA The PMC is not in firmware pstate mode.
+///
+/// This API may also return errors from the SSX semaphore operations that
+/// implement the wait for quiescence.
+
+int
+gpsm_broadcast_global_actual(const GlobalPstateTable* i_gpst,
+ const Pstate i_pstate,
+ const int i_bias)
+{
+ int rc, bias_rc, entry_rc;
+ gpst_entry_t entry;
+ Pstate voltage_pstate;
+
+ do {
+
+ if (!gpsm_fw_mode_p()) {
+ rc = GPSM_ILLEGAL_MODE_BGA;
+ break;
+ }
+
+ // Bias the pstate, fetch the Pstate entry, quiesce and broadcast.
+ // bias_pstate() only returns saturation warnings. These are turned
+ // into bounds warnings if necessary (indicating that the Pstate
+ // saturated but the PMAX or PMIN was also a legal entry in the
+ // table).
+
+ bias_rc = bias_pstate(i_pstate, i_bias, &voltage_pstate);
+ entry_rc = gpst_entry(i_gpst, voltage_pstate, 0, &entry);
+ if (entry_rc &&
+ (entry_rc != -GPST_PSTATE_CLIPPED_LOW_GPST_ENTRY) &&
+ (entry_rc != -GPST_PSTATE_CLIPPED_HIGH_GPST_ENTRY)) {
+ rc = entry_rc;
+ break;
+ }
+
+ rc = gpsm_quiesce();
+ if (rc) break;
+
+ _gpsm_broadcast_global_actual(i_pstate, entry);
+
+ if (entry_rc != 0) {
+ rc = entry_rc;
+ } else if (bias_rc == -PSTATE_OVERFLOW_BIAS_PS) {
+ rc = -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA;
+ } else if (bias_rc == -PSTATE_UNDERFLOW_BIAS_PS) {
+ rc = -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA;
+ }
+ } while (0);
+
+ return rc;
+}
+
+
diff --git a/src/lib/gpsm.h b/src/lib/gpsm.h
new file mode 100755
index 0000000..5af2e8d
--- /dev/null
+++ b/src/lib/gpsm.h
@@ -0,0 +1,191 @@
+#ifndef __GPSM_H__
+#define __GPSM_H__
+
+// $Id: gpsm.h,v 1.2 2014/02/03 01:30:24 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpsm.h
+/// \brief PgP Global Pstate Machine (Mechanism)
+
+#include "ssx.h"
+#include "gpe_control.h"
+#include "pgp_async.h"
+#include "pstates.h"
+
+// GPSM modes
+
+#define GPSM_MODE_HW 1
+#define GPSM_MODE_FW_AUCTION 2
+#define GPSM_MODE_FW 3
+
+// Misc./Error/Panic codes
+
+#define GPSM_INVALID_OBJECT 0x00477601
+#define GPSM_INVALID_ARGUMENT_GPST_INSTALL 0x00477602
+#define GPSM_INVALID_ARGUMENT_LPST_INSTALL 0x00477603
+#define GPSM_INVALID_ARGUMENT_RCLK_INSTALL 0x00477604
+#define GPSM_INVALID_ARGUMENT_EPSS 0x00477605
+#define GPSM_ILLEGAL_MODE_HW_QUIESCE 0x00477606
+#define GPSM_ILLEGAL_MODE_BGA 0x00477607
+#define GPSM_ILLEGAL_MODE_GPST_INSTALL 0x00477608
+#define GPSM_ILLEGAL_MODE_LPST_INSTALL 0x00477609
+#define GPSM_ILLEGAL_MODE_RCLK_INSTALL 0x0047760a
+#define GPSM_ILLEGAL_MODE_GPSM_INIT 0x0047760b
+#define GPSM_ILLEGAL_MODE_EPSM 0x0047760c
+#define GPSM_ILLEGAL_MODE_EPSS 0x0047760d
+#define GPSM_SYNC_ERROR 0x0047760e
+#define GPSM_PSTATE_CLIPPED 0x0047760f
+#define GPSM_BUG 0x00477610
+#define GPSM_CONFIGURATION_ERROR 0x00477611
+#define GPSM_ERROR_BREAK 0x00477612
+#define GPSM_INVALID_MAGIC 0x00477613
+#define GPSM_IVRM_CALIBRATION_TIMEOUT 0x00477614
+#define GPSM_IVRM_GROSS_OR_FINE 0x00477615
+#define GPSM_PSTATE_ENABLED 0x00477616
+#define GPSM_BABYSTEPPER_SYNC_TIMEOUT 0x00477617
+#ifndef __ASSEMBLER__
+
+// Lab/VBU/VPO debugging
+
+#if 0
+#include "trace.h"
+#define TRACE_GPSM(i_code) trace_tbl_bbbb(1, i_code, 0, 0, 0);
+#define TRACE_GPSM_B(i_code, i_b0) trace_tbl_bbbb(1, i_code, i_b0, 0, 0);
+#define TRACE_GPSM_H(i_code, i_h0) trace_tbl_bbh(1, i_code, 0, i_h0);
+#else
+#define TRACE_GPSM(i_code)
+#define TRACE_GPSM_B(i_code, i_b0)
+#define TRACE_GPSM_H(i_code, i_h0)
+#endif
+
+
+/// Information required by an SCM or a DCM master to be passed from
+/// gpsm_enable_pstates_master() to gpsm_enable_pstates_slave().
+
+typedef struct {
+
+ /// Indicates whether or not gpsm_enable_pstates_slave() should move the
+ /// voltage.
+ ///
+ /// If 0, it means that the master has already moved the voltage and only
+ /// the frequency needs to move. If 1, voltage is moved after frequency
+ /// moves.
+ int move_voltage;
+
+ /// The current and target external voltage settings as VRM11 VID codes.
+ Vid11 currentVdd, currentVcs, targetVdd, targetVcs;
+
+} GpsmEnablePstatesMasterInfo;
+
+
+/// A GpsmAuctionProcedure is any function of no arguments that returns a
+/// Pstate
+
+typedef Pstate (*GpsmAuctionProcedure)();
+
+extern SsxSemaphore G_gpsm_protocol_semaphore;
+
+extern uint8_t G_gpsm_initialized;
+
+// APIs defined in gpsm_init.c
+
+int
+gpsm_gpst_install(GlobalPstateTable* o_gpst,
+ const GlobalPstateTable* i_source);
+
+int
+gpsm_lpsa_install(const LocalPstateArray* i_lpsa,
+ const PstateOptions* i_options);
+
+int
+gpsm_resclk_install(const ResonantClockingSetup* i_resclk,
+ const GlobalPstateTable* i_gpst,
+ const PstateOptions* i_options);
+
+int
+gpsm_initialize(const PstateSuperStructure* i_pss,
+ GlobalPstateTable* o_gpst);
+
+int
+gpsm_enable_pstates_master(GpsmEnablePstatesMasterInfo* o_info,
+ Pstate* o_voltage_pstate,
+ Pstate* o_frequency_pstate);
+
+int
+gpsm_enable_pstates_slave(const GpsmEnablePstatesMasterInfo* i_info,
+ const Pstate i_voltage_pstate,
+ const Pstate i_frequency_pstate);
+
+// APIs defined in gpsm.c
+
+int
+gpsm_quiesced_p(void);
+
+int
+gpsm_hw_mode_p(void);
+
+int
+gpsm_fw_auction_mode_p(void);
+
+int
+gpsm_fw_mode_p(void);
+
+int
+gpsm_dcm_mode_p(void);
+
+int
+gpsm_dcm_master_p(void);
+
+int
+gpsm_dcm_slave_p(void);
+
+GlobalPstateTable*
+gpsm_gpst();
+
+int
+gpsm_quiesce(void);
+
+int
+_gpsm_hw_quiesce(void);
+
+int
+gpsm_fw_mode(void);
+
+int
+gpsm_fw_auction_mode(void);
+
+int
+gpsm_hw_mode(void);
+
+Pstate
+gpsm_default_auction(void);
+
+void
+gpsm_get_global_bids(Pstate* o_bids);
+
+void
+gpsm_set_global_bids(const Pstate* i_bids);
+
+void
+_gpsm_broadcast_global_actual(const Pstate i_pstate,
+ const gpst_entry_t i_entry);
+
+int
+gpsm_broadcast_global_actual(const GlobalPstateTable *i_gpst,
+ const Pstate i_pstate,
+ const int i_bias);
+
+int
+gpsm_set_pstate(const Pstate i_pstate);
+
+int
+gpsm_hold_auction(const GpsmAuctionProcedure i_procedure);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __GPSM_H__ */
diff --git a/src/lib/gpsm_dcm.c b/src/lib/gpsm_dcm.c
new file mode 100755
index 0000000..452f7cf
--- /dev/null
+++ b/src/lib/gpsm_dcm.c
@@ -0,0 +1,753 @@
+// $Id: gpsm_dcm.c,v 1.2 2014/02/03 01:30:24 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm_dcm.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpsm_dcm.h
+/// \brief PgP Global PState Machine (Mechanism) in Dual Chip Model
+
+#include "ssx.h"
+#include "pmc_dcm.h"
+#include "gpsm_dcm.h"
+#include "gpsm.h"
+
+/// Timeout object and methods
+
+typedef struct {
+ SsxTimebase timeout;
+} Timeout;
+
+static void
+timeout_start(Timeout* t, SsxInterval timeout_period)
+{
+ t->timeout = ssx_timebase_get() + timeout_period;
+}
+
+static int
+timeout_timed_out(Timeout* t)
+{
+ return ssx_timebase_get() > t->timeout;
+}
+
+
+/// Internal API : Send packet with timeout
+///
+/// \param hwPacket pointer to the packet to be sent
+///
+/// \param timeout_period The SSX timeout variable
+///
+/// This API sends \a hwPacket within \a timeout_period
+/// if timed out, the function returns an error code
+///
+/// \retval GPSM_DCM_SUCCESS
+///
+/// \retval GPSM_DCM_SEND_PACKET_TIMEOUT
+///
+
+static int
+_gpsm_dcm_send(PmcDcmPacket* hwPacket,
+ SsxInterval timeout_period)
+{
+ int rc = GPSM_DCM_SUCCESS;
+ Timeout timeout;
+
+ //set timeout
+ timeout_start(&timeout, timeout_period);
+
+ //try to send packet within timeout
+ while( (rc=pmc_dcm_send(hwPacket)) == PMC_DCM_OUTSTANDING_TRANSFER ) {
+ if( timeout_timed_out(&timeout)) {
+ rc = GPSM_DCM_SEND_PACKET_TIMEOUT;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/// Internal API : Receive packet with timeout
+///
+/// \param hwPacket pointer to the packet structure to receive
+///
+/// \param timeout_period The SSX timeout variable
+///
+/// This API receives a \a hwPacket within \a timeout
+/// if timed out, the function returns an error code
+///
+/// \retval GPSM_DCM_SUCCESS
+///
+/// \retval GPSM_DCM_RECV_PACKET_TIMEOUT
+///
+
+static int
+_gpsm_dcm_receive(PmcDcmPacket* hwPacket,
+ SsxInterval timeout_period)
+{
+ int rc = GPSM_DCM_SUCCESS;
+ Timeout timeout;
+
+ //set timeout
+ timeout_start(&timeout, timeout_period);
+
+ //try to receive packet within timeout
+ while( (rc=pmc_dcm_receive(hwPacket)) == PMC_DCM_RECEIVE_NOT_DETECTED ) {
+ if( timeout_timed_out(&timeout)) {
+ rc = GPSM_DCM_RECV_PACKET_TIMEOUT;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// High-level GPSM-DCM Interfaces
+////////////////////////////////////////////////////////////////////////////
+
+/// Abstract non-blocking send over the DCM interchip link
+///
+/// The GPSM-DCM abstract interface guarantees that the PMC-DCM link is always
+/// clear for use by the master sender. This API (which should be called from
+/// a thread context) transmits an abstract packet, then polls for the hardware
+/// ACK - which must always indicate that the packet was received. This ACK
+/// will always occur very quickly.
+///
+/// \param fwPacket a GpsmDcmPacket structure to be sent as part of the
+/// PmcDcmPacket/hwPacket of MSG type via PMC interchip link.
+/// The fwPacket includes a firmware command and 16 bits payload.
+/// The firmware command will be filled in the cmd_ext field of the
+/// hwPacket structure, and payload 0,1 will be filled in the
+/// corresponding slots also in hwPacket structure.
+/// This argument is provided by the caller and passed in as reference.
+///
+/// hwPacket:
+///
+/// cmd_code | cmd_ext | payload 0 | payload 1 | ECC
+/// [0:3] | [4:7] | [8:15] | [16:23] | [24:31]
+///
+/// fwPacket:
+///
+/// | command | payload 0 | payload 1 |
+/// | [4:7] | [8:15] | [16:23] |
+///
+/// firmware command:
+///
+/// GPSM_DCM_DATA 0 //0b0000
+/// GPSM_DCM_WRITE 1 //0b0001
+/// GPSM_DCM_ENABLE_PSTATES 2 //0b0010
+/// GPSM_DCM_ENTER_HW_PSTATE_MODE 3 //0b0011
+///
+/// This API implements the lower level pmc_dcm_send API to send a firmware
+/// command as part of the PMC interchip message packet.
+///
+/// This API is working under a default time out, if send cannot be completed
+/// within the default timeout period due to possible busy interchip link,
+/// the function will exit with returning an error code:
+/// \a GPSM_DCM_SEND_PACKET_TIMEOUT
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// and lower level PMC-DCM API is required to use this
+/// high level GPSM-DCM API.
+/// Also, the hardware must be in DCM setup.
+///
+/// Note: This API must be called by the DCM Master, meaning the firmware
+/// command can only be given by the master to the slave.
+/// Also the API will check if the firmware command to be sent is valid.
+///
+/// \retval SUCCESS
+///
+/// \retval GPSM_DCM_ARG_INVALID_OBJ_SND
+///
+/// \retval GPSM_DCM_PKT_INVALID_CMD_SND
+///
+/// \retval GPSM_DCM_CMD_NOT_FROM_MASTER
+///
+
+int
+gpsm_dcm_send(GpsmDcmPacket* fwPacket) {
+
+ int rc = GPSM_DCM_SUCCESS;
+ PmcDcmPacket hwPacket;
+
+ TRACE_GPSM_B(TRACE_GPSM_DCM_SEND, fwPacket->command);
+
+ do {
+
+ //check if argument is NULL
+ SSX_ERROR_IF_CHECK_API(
+ (fwPacket == 0),
+ GPSM_DCM_ARG_INVALID_OBJ_SND);
+
+ //check if firmware command is valid
+ SSX_ERROR_IF_CHECK_API(
+ (fwPacket->command >= GPSM_DCM_NUMBER_OF_COMMANDS ||
+ fwPacket->command == GPSM_IC_DATA),
+ GPSM_DCM_PKT_INVALID_CMD_SND);
+
+ //check if is dcm master, note only master can send command to slave
+ SSX_ERROR_IF_CHECK_API(
+ (!pmc_dcm_if_dcm_master()),
+ GPSM_DCM_CMD_NOT_FROM_MASTER);
+
+ //form hardware packet from given firmware packet
+ hwPacket.value = 0;
+ hwPacket.fields.cmd_code = PMC_IC_MSG_CC;
+ hwPacket.fields.cmd_ext = fwPacket->command;
+ hwPacket.fields.payload[0] = fwPacket->payload.u8[0];
+ hwPacket.fields.payload[1] = fwPacket->payload.u8[1];
+
+ //send hardware packet
+ rc = _gpsm_dcm_send(&hwPacket, GPSM_DCM_DEFAULT_TIMEOUT);
+
+ } while (0);
+
+ TRACE_GPSM(TRACE_GPSM_DCM_SENT);
+
+ return rc;
+}
+
+
+/// Abstract blocking/non-blocking receive over the DCM interchip link
+///
+/// \param fwPacket A GpsmDcmPacket structure as the part of the
+/// PmcDcmPacket/hwPacket received from PMC interchip link.
+/// This argument is passed by the caller as reference. The corresponding
+/// fields of the received packet will filled in this data structure.
+///
+/// \param timeout_period A SsxInterval variable for time out period
+///
+/// This API implements the lower level pmc_dcm_receive API to receive a firmware
+/// command as part of the PMC interchip message packet.
+///
+/// This API is working under a user given time out, if receive cannot be
+/// completed within the given timeout period,
+/// the function will exit with returning an error code:
+/// \a GPSM_DCM_RECV_PACKET_TIMEOUT
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// and lower level PMC-DCM API is required to use this
+/// high level GPSM-DCM API.
+/// Also, the hardware must be in DCM setup.
+///
+/// Note: This API must be called by the DCM Slave, meaning the firmware
+/// command can only be received by the slave.
+/// Also the API will check if the received firmware command is valid.
+//
+/// \retval GPSM_DCM_SUCCESS
+///
+/// \retval GPSM_DCM_ARG_INVALID_OBJ_RCV
+///
+/// \retval GPSM_DCM_CMD_SHOULD_TO_SLAVE
+///
+/// \retval GPSM_DCM_PKT_INVALID_CMD_RCV
+///
+
+int
+gpsm_dcm_receive(GpsmDcmPacket* fwPacket,
+ SsxInterval timeout_period) {
+
+ int rc = GPSM_DCM_SUCCESS;
+ PmcDcmPacket hwPacket;
+
+ TRACE_GPSM(TRACE_GPSM_DCM_RECEIVE);
+
+ do {
+
+ //check if argument is NULL
+ SSX_ERROR_IF_CHECK_API(
+ (fwPacket == 0),
+ GPSM_DCM_ARG_INVALID_OBJ_RCV);
+
+ //check if is dcm slave, note only slave receives command from master
+ SSX_ERROR_IF_CHECK_API(
+ (pmc_dcm_if_dcm_master()),
+ GPSM_DCM_CMD_SHOULD_TO_SLAVE);
+
+ //receive hardware packet
+ hwPacket.value = 0;
+ rc = _gpsm_dcm_receive(&hwPacket, timeout_period);
+ if( rc ) break;
+
+ //check if the received command is valid
+ fwPacket->command = hwPacket.fields.cmd_ext;
+ SSX_ERROR_IF_CHECK_API(
+ (fwPacket->command >= GPSM_DCM_NUMBER_OF_COMMANDS ||
+ fwPacket->command == 0),
+ GPSM_DCM_PKT_INVALID_CMD_RCV);
+
+ //load payload from hardware packet into firmware packet
+ fwPacket->payload.u8[0] = hwPacket.fields.payload[0];
+ fwPacket->payload.u8[1] = hwPacket.fields.payload[1];
+
+ } while (0);
+
+ TRACE_GPSM_B(TRACE_GPSM_DCM_RECEIVED, fwPacket->command);
+
+ return rc;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Generic communication using GPSM-DCM 'write' command
+////////////////////////////////////////////////////////////////////////////
+///
+/// These APIs allow applications to perform generic communication over the
+/// GPSM-DCM link using the GPSM-DCM 'write' command. Like all GPSM-DCM
+/// commands, communication is always controlled/initiated by the master. The
+/// infrastructure assumes that the master and slave applications argee on the
+/// maximum amount of data that may be transmitted generically as a single
+/// abstract transaction. It is legal for the data transmission size to be 0.
+///
+/// In the following APIs, the \a timeout parameter is a timeout for any
+/// single send/receive transaction to complete, not a timeout covering the
+/// entire send/receive. The timeout may be specified as SSX_WAIT_FOREVER to
+/// indicate no timeout.
+///
+/// One typical use of these APIs would be to implement a polled 'ping' of the
+/// slave from the master. This sequence might be implemented as follows:
+///
+/// - Master: Calls gpsm_dcm_write() with 0 size [ping]
+/// - Master: Blocks on gpsm_dcm_read [Wait for ping response]
+/// - Slave: GpsmSlaveControl.write_handler() is activated to handle the ping
+/// - Slave: write_handler() responds by calling gpsm_dcm_write()
+/// - Master: Unblocks and processes ping reqponse
+
+
+/// Send an arbitrary amount of data (max 2^16 - 1 bytes) using GPSM-DCM
+///
+/// This API is typically used by the master to initiate generic
+/// communication. When received by the slave the application-specific
+/// callback is activated on the slave to handle the write.
+/// The slave will only use this API when it is known that the master is
+/// expecting a communication from the slave and has blocked on a call of
+/// gpsm_dcm_read().
+///
+/// The receiver must be prepared to accept \a size bytes of data, otherwise
+/// the call will fail immediately.
+///
+/// \param buf The buffer contains the sending message prepared by the caller
+///
+/// \param size The size of a message, in number of bytes, given by the caller
+///
+/// \param timeout_period The SSX timeout variable
+///
+/// The firmware level API for sending a generic PMC interchip message
+/// This API sends the message by calling lower level API: pmc_dcm_send
+///
+/// The API times out upon \a timeout_period.
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// and lower level PMC-DCM API is required to use this
+/// high level GPSM-DCM API.
+/// Also, the hardware must be in DCM setup.
+///
+/// Note: When this API is called by the DCM Master,
+/// the slave will need to provide its own write_handler to process
+/// the receiving message, The counterpart gpsm_dcm_read API of this
+/// gpsm_dcm_write API is not designed for slave to read the master
+/// sending message.
+///
+/// \retval GPSM_DCM_SUCCESS
+///
+/// \retval GPSM_DCM_ARG_INVALID_OBJ_WRT
+///
+
+int
+gpsm_dcm_write(void* buf,
+ uint16_t size,
+ SsxInterval timeout_period)
+{
+ int rc = GPSM_DCM_SUCCESS;
+ PmcDcmPacket hwPacket;
+ GpsmDcmFastData fastData;
+
+ TRACE_GPSM_H(TRACE_GPSM_DCM_WRITE, size);
+
+ //setup send irq
+ ssx_irq_setup(PGP_IRQ_PMC_INTERCHIP_MSG_SEND_ONGOING,
+ SSX_IRQ_POLARITY_ACTIVE_LOW,
+ SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);
+ do {
+
+ //check if buffer is allocated
+ SSX_ERROR_IF_CHECK_API(
+ (buf == 0 && size != 0),
+ GPSM_DCM_ARG_INVALID_OBJ_WRT);
+
+ //form initial hardware packet as header packet
+ hwPacket.value = 0;
+ hwPacket.fields.cmd_code = PMC_IC_MSG_CC;
+ hwPacket.fields.cmd_ext = GPSM_IC_WRITE;
+ hwPacket.value += SET_PAYLOAD_FIELD(size);
+
+ //setup data structure for fast write handler
+ fastData.buffer_pointer = buf;
+ fastData.remaining_size = size;
+ ssx_semaphore_create(&(fastData.fast_semaphore), 0, 1);
+
+ //setup fast write handler for send irq
+ ssx_irq_handler_set(PGP_IRQ_PMC_INTERCHIP_MSG_SEND_ONGOING,
+ gpsm_dcm_fast_write,
+ (void*)(&fastData),
+ SSX_NONCRITICAL);
+
+ //send header packet
+ rc = _gpsm_dcm_send(&hwPacket, timeout_period);
+ if( rc ) break;
+
+ //enable interrupt and semaphore
+ ssx_irq_enable(PGP_IRQ_PMC_INTERCHIP_MSG_SEND_ONGOING);
+ ssx_semaphore_pend(&(fastData.fast_semaphore), timeout_period);
+
+ } while (0);
+
+ TRACE_GPSM(TRACE_GPSM_DCM_WRITE_COMPLETE);
+
+ return rc;
+}
+
+
+/// Receive a transmission from the slave
+///
+/// This API is only called on the master, as part of an application-specific
+/// protocol where a transmission is expected from the slave.
+///
+/// The call will fail immediately if the slave attempts to send more than \a
+/// buf_size bytes. The actual number of bytes received is returned as \a
+/// data_size.
+///
+/// \param buf The buffer for storing the incoming messages,
+/// return to caller as reference
+///
+/// \param buf_size The maximum size of the buffer, given by caller
+/// fail operation if data_size > buf_size
+///
+/// \param data_size The size of acutal receiving message,
+/// return to caller as refernce
+///
+/// \param timeout_period The SSX timeout variable
+///
+/// The firmware level API for receiving a PMC interchip message
+/// This API recevies the message by calling lower level API: pmc_dcm_receive
+/// and then checks \a buf_size and \a data_size for unexpected message size
+/// if the size overflow, the function will exit with returning an error code:
+/// \a GPSM_DCM_DAT_BIGGER_THAN_BUF
+/// The valid receive message will be placed into \a buf and return with
+/// the actual \a data_size. The API times out upon \a timeout.
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// and lower level PMC-DCM API is required to use this
+/// high level GPSM-DCM API.
+/// Also, the hardware must be in DCM setup.
+///
+/// Note: This API is only designed for master to read/receive a message from
+/// slave (slave will have its own write_handler). Therefore, this API
+/// can only called by DCM master, and the API will check if the received
+/// message is from a write command.
+///
+/// \retval GPSM_DCM_SUCCESS
+///
+/// \retval GPSM_DCM_ARG_INVALID_OBJ_RED
+///
+/// \retval GPSM_DCM_READ_RECV_NOT_WRITE
+///
+/// \retval GPSM_DCM_DAT_BIGGER_THAN_BUF
+///
+
+int
+gpsm_dcm_read(void* buf,
+ uint16_t buf_size,
+ uint16_t* data_size,
+ SsxInterval timeout_period)
+{
+ int rc = GPSM_DCM_SUCCESS;
+ PmcDcmPacket hwPacket;
+ GpsmDcmFastData fastData;
+
+ TRACE_GPSM(TRACE_GPSM_DCM_READ);
+
+ //setup receive irq
+ ssx_irq_setup(PGP_IRQ_PMC_INTERCHIP_MSG_RECV,
+ SSX_IRQ_POLARITY_ACTIVE_HIGH,
+ SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);
+
+ do {
+
+ //check if buffer is allocated
+ SSX_ERROR_IF_CHECK_API(
+ (buf == 0 && buf_size != 0),
+ GPSM_DCM_ARG_INVALID_OBJ_RED);
+
+ //try to receive the header packet
+ hwPacket.value = 0;
+ rc = _gpsm_dcm_receive(&hwPacket, timeout_period);
+ if( rc ) break;
+
+ //check if command code of header packet is valid
+ SSX_ERROR_IF_CHECK_API(
+ (hwPacket.fields.cmd_ext != GPSM_IC_WRITE),
+ GPSM_DCM_READ_RECV_NOT_WRITE);
+
+ //load size from header packet
+ *data_size = GET_PAYLOAD_FIELD(hwPacket.value);
+
+ //check if data size will fit in the buffer
+ if( *data_size > buf_size ) {
+ rc = GPSM_DCM_DAT_BIGGER_THAN_BUF;
+ break;
+ }
+
+ //check if data size is 0
+ if( *data_size == 0 )
+ break;
+
+ //setup data structure for fast read
+ fastData.buffer_pointer = buf;
+ fastData.remaining_size = *data_size;
+ ssx_semaphore_create(&(fastData.fast_semaphore), 0, 1);
+
+ //setup fast read handler for receive irq
+ ssx_irq_handler_set(PGP_IRQ_PMC_INTERCHIP_MSG_RECV,
+ gpsm_dcm_fast_read,
+ (void*)(&fastData),
+ SSX_NONCRITICAL);
+
+ //enable interrupt and semaphore
+ ssx_irq_enable(PGP_IRQ_PMC_INTERCHIP_MSG_RECV);
+ ssx_semaphore_pend(&(fastData.fast_semaphore), timeout_period);
+
+ } while (0);
+
+ TRACE_GPSM_H(TRACE_GPSM_DCM_READ_COMPLETE, *data_size);
+
+ return rc;
+}
+
+/// Method to sync between two chips using write/read API
+///
+/// This API can be called before a piece of application code to sync
+/// between master and slave to enter the same code block together
+/// as well as after a piece of code to sync on exiting the block of code
+///
+/// \param state use 0 for enter and use 1 for exit
+///
+/// \retval GPSM_DCM_SUCCESS
+///
+
+int
+gpsm_dcm_sync(int state)
+{
+ int rc = GPSM_DCM_SUCCESS;
+ int master = pmc_dcm_if_dcm_master();
+ uint16_t buf_size = sizeof(uint16_t);
+ uint16_t wbuf = 0x7962; //SYNC
+ uint16_t rbuf;
+ uint16_t size;
+ SsxMachineContext smc;
+
+ do {
+
+ ssx_critical_section_enter(SSX_NONCRITICAL, &smc);
+
+ if( master ^ state ) {
+ rc = gpsm_dcm_write((void*)&wbuf,buf_size,SSX_WAIT_FOREVER);
+ if(rc) break;
+ rc = gpsm_dcm_read((void*)&rbuf,buf_size,&size,SSX_WAIT_FOREVER);
+ if(rc) break;
+ } else {
+ rc = gpsm_dcm_read((void*)&rbuf,buf_size,&size,SSX_WAIT_FOREVER);
+ if(rc) break;
+ rc = gpsm_dcm_write((void*)&wbuf,buf_size,SSX_WAIT_FOREVER);
+ if(rc) break;
+ }
+
+ ssx_critical_section_exit(&smc);
+
+ } while (0);
+
+ return rc;
+}
+
+
+/// GPSM DCM Slave function
+///
+/// This function must be called from a thread, as if a command is available
+/// it may always be necessary for the thread to block during command
+/// execution. The \a wait argument indicates whether the caller is willing to
+/// wait for a command indefinitely, or prefers for gpsm_dcm_slave() to retun
+/// immediately if no command is available. \a wait would normally be non-0 if
+/// \a gpsm_dcm_slave() were used as a standalone thread body, and 0 if
+/// gpsm_dcm_slave() were called from another thread.
+///
+/// \param control The callback table structure that allows the user to
+/// implement callback function in addition to completion of each command.
+///
+/// \param wait The SsxInterval type timeout variable
+///
+/// This API is the control function running on a slave thread. The function
+/// will first try to receive a message from master, and then take the
+/// corresponding action according to the command in the message. Upon
+/// completion of the default action designed for each command, any user
+/// provided callback function for that command in the control structure will
+/// be called.
+///
+/// Prerequisite: This API must be called from a DCM slave SSX thread
+/// The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// and lower level PMC-DCM API is required to use this
+/// high level GPSM-DCM API.
+/// Also, the hardware must be in DCM setup.
+///
+/// Similar to other SSX drivers, the callback is always called regardless of
+/// whether the slave command succeeds or fails.
+///
+/// \retval 0 Success
+///
+/// \retval GPSM_DCM_SLAVE_TIMEOUT The call timed out before a packet was
+/// received from the master. The application may or may not treat this as an
+/// error.
+///
+/// \retval GPSM_DCM_SLAVE_ERROR In the event of a this return code, the
+/// application will need to query 3 return codes stored in the \a control
+/// structure to understand the source of the error. The \a
+/// control->protocol_rc contains the return code for all GPSM-DCM protocol
+/// actions, other than the simple timeout mentioned above. The \a
+/// control->slave_rc is the return code for the slave action commanded by the
+/// master. This is also the code sent back to the master as an
+/// acknowledgement. The control->callback_rc is the return code from the
+/// callback, if any.
+///
+/// The return value of the function is either 0,
+/// GPSM_DCM_SLAVE_TIMEOUT, or
+/// GPSM_DCM_SLAVE_ERROR.
+///
+
+int
+gpsm_dcm_slave(GpsmSlaveControl *control,
+ SsxInterval wait)
+{
+ GpsmDcmPacket fwPacket;
+ int rc, protocolRc, slaveRc, callbackRc;
+
+ TRACE_GPSM(TRACE_GPSM_DCM_SLAVE);
+
+ protocolRc = 0;
+ slaveRc = 0;
+ callbackRc = 0;
+
+ do {
+ protocolRc = gpsm_dcm_receive(&fwPacket, wait);
+ if(protocolRc) {
+ control->command = GPSM_IC_NO_COMMAND;
+ break;
+ }
+
+ control->command = fwPacket.command;
+
+ switch (fwPacket.command) {
+
+ case GPSM_IC_ENABLE_PSTATES:
+
+ slaveRc = gpsm_enable_pstates_slave(0,
+ fwPacket.payload.pstate[0],
+ fwPacket.payload.pstate[1]);
+
+ if(control->gpsm_dcm_callback_enable_pstates != 0) {
+ callbackRc =
+ control->gpsm_dcm_callback_enable_pstates(&fwPacket);
+ }
+
+ break;
+
+ case GPSM_IC_HW_PSTATE_MODE:
+
+ slaveRc = gpsm_hw_mode();
+
+ if(control->gpsm_dcm_callback_hw_pstate_mode != 0) {
+ callbackRc =
+ control->gpsm_dcm_callback_hw_pstate_mode(&fwPacket);
+ }
+
+ break;
+
+ case GPSM_IC_WRITE:
+ slaveRc = control->write_handler(control->buffer,
+ control->buffer_size,
+ control->write_arg);
+ break;
+ default:
+ slaveRc = GPSM_DCM_PKT_INVALID_CMD_RCV;
+ break;
+ }
+
+ if(fwPacket.command != GPSM_IC_WRITE) {
+ protocolRc = gpsm_dcm_write((void*)(&slaveRc),
+ sizeof(int),
+ SSX_WAIT_FOREVER);
+ }
+ } while(0);
+
+ control->protocol_rc = protocolRc;
+ control->slave_rc = slaveRc;
+ control->callback_rc = callbackRc;
+
+ if (protocolRc == GPSM_DCM_RECV_PACKET_TIMEOUT) {
+ rc = GPSM_DCM_SLAVE_TIMEOUT;
+ } else if (protocolRc || slaveRc || callbackRc) {
+ rc = GPSM_DCM_SLAVE_ERROR;
+ } else {
+ rc = 0;
+ }
+
+ TRACE_GPSM_B(TRACE_GPSM_DCM_SLAVE_COMPLETE, fwPacket.command);
+
+ return rc;
+}
+
+
+/// Master an abstract transaction with a return code response from the slave
+///
+/// \param fwPacket A legal, initalized GpsmDcmPacket. This packet may be of
+/// any type other than GPSM_IC_DATA or GPSM_IC_WRITE. The GPSM_IC_WRITE does
+/// not get a response from the slave.
+///
+/// \param slaveRc The return code from running the protocol action on the
+/// slave. This includes any return code from a callback installed on the
+/// slave. If the return value of the function is non-0 then \a slaveRc is
+/// considered undefined.
+///
+/// This API send \a fwPacket to the slave and blocks waiting for the slave to
+/// respond with the \a slaveRc. The return value of the API indicates any
+/// problems with sending the packet or receiving the response.
+
+int
+gpsm_dcm_master(GpsmDcmPacket* fwPacket, int* slaveRc)
+{
+ int rc;
+ uint16_t size;
+
+ do {
+
+ rc = gpsm_dcm_send(fwPacket);
+ if(rc) break;
+
+ rc = gpsm_dcm_read((void*)slaveRc,
+ sizeof(int),
+ &size,
+ SSX_WAIT_FOREVER);
+ if(rc) break;
+
+ //check if command_reply has correct size
+ SSX_ERROR_IF_CHECK_API((size != sizeof(int)),
+ GPSM_DCM_CMD_REPLY_NOT_INT);
+
+ }while(0);
+
+ return rc;
+}
diff --git a/src/lib/gpsm_dcm.h b/src/lib/gpsm_dcm.h
new file mode 100755
index 0000000..407ddbb
--- /dev/null
+++ b/src/lib/gpsm_dcm.h
@@ -0,0 +1,192 @@
+#ifndef __GPSM_DCM_H__
+#define __GPSM_DCM_H__
+
+// $Id: gpsm_dcm.h,v 1.2 2014/02/03 01:30:24 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm_dcm.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpsm_dcm.h
+/// \brief PgP Global PState Machine (Mechanism) in Dual Chip Model
+
+#include "pmc_dcm.h"
+
+/// GPSM-DCM Return Code
+#define GPSM_DCM_SUCCESS 0
+#define GPSM_DCM_ARG_INVALID_OBJ_SND 0x00326401 //ssx panic
+#define GPSM_DCM_ARG_INVALID_OBJ_RCV 0x00326402 //ssx panic
+#define GPSM_DCM_ARG_INVALID_OBJ_WRT 0x00326403 //ssx panic
+#define GPSM_DCM_ARG_INVALID_OBJ_RED 0x00326404 //ssx panic
+#define GPSM_DCM_PKT_INVALID_CMD_SND 0x00326405 //ssx panic
+#define GPSM_DCM_PKT_INVALID_CMD_RCV 0x00326406 //ssx panic
+#define GPSM_DCM_CMD_NOT_FROM_MASTER 0x00326407 //ssx panic
+#define GPSM_DCM_CMD_SHOULD_TO_SLAVE 0x00326408 //ssx panic
+#define GPSM_DCM_SEND_PACKET_TIMEOUT 0x00326409 //user handle
+#define GPSM_DCM_RECV_PACKET_TIMEOUT 0x0032640a //user handle
+#define GPSM_DCM_READ_RECV_NOT_WRITE 0x0032640b //ssx panic
+#define GPSM_DCM_READ_NOT_WRITE_DATA 0x0032640c //ssx panic
+#define GPSM_DCM_DAT_BIGGER_THAN_BUF 0x0032640d //user handle
+#define GPSM_DCM_CMD_REPLY_NOT_INT 0x0032640e //ssx panic
+#define GPSM_DCM_SLAVE_TIMEOUT 0x0032640f //user handle
+#define GPSM_DCM_SLAVE_ERROR 0x00326410 //user handle
+
+/// GPSM Interchip Command Code
+#define GPSM_IC_DATA 0 //0b0000
+#define GPSM_IC_WRITE 1 //0b0001
+#define GPSM_IC_ENABLE_PSTATES 2 //0b0010
+#define GPSM_IC_HW_PSTATE_MODE 3 //0b0011
+
+#define GPSM_DCM_NUMBER_OF_COMMANDS 4
+
+/// This is a special command return code returned by gpsm_dcm_slave() when it
+/// times out.
+#define GPSM_IC_NO_COMMAND GPSM_DCM_NUMBER_OF_COMMANDS
+
+
+/// Timeout Parameter
+#define GPSM_DCM_DEFAULT_TIMEOUT SSX_MICROSECONDS(15)
+
+#ifndef __ASSEMBLER__
+
+/// GPSM-DCM abstract packet
+
+typedef struct {
+ /// Firmware command
+ uint8_t command : 4;
+ union {
+ /// Used for Pstate-based protocols
+ Pstate pstate[2];
+ /// Generic byte data
+ uint8_t u8[2];
+ } payload;
+} GpsmDcmPacket;
+
+/// Data Structure for Fast Write/Read Handlers
+
+typedef struct {
+ void* buffer_pointer;
+ uint32_t remaining_size;
+ SsxSemaphore fast_semaphore;
+} GpsmDcmFastData;
+
+/// Data Structure for Sync Mehotds
+
+
+/// Abstract type of gpsm_dcm_slave() callbacks
+///
+/// The callback receives the (first) master packet of the exchange. The
+/// return code is passed back to the master.
+
+typedef int (*GpsmDcmSlaveCallback)(GpsmDcmPacket* fwPacket);
+
+
+/// Control structure for gpsm_dcm_slave()
+
+typedef struct {
+ /// Slave timeout when waiting for next packet to arrive in long commands.
+ SsxInterval timeout;
+
+ /// Callback called after "Enable Pstates" command
+ GpsmDcmSlaveCallback gpsm_dcm_callback_enable_pstates;
+
+ /// Callback called after "Enter HW Pstate Mode" command
+ GpsmDcmSlaveCallback gpsm_dcm_callback_hw_pstate_mode;
+
+ /// Callback for GPSM-DCM write command
+ ///
+ /// Will be called with the application-supplied buffer and the actual
+ /// size of the data transmission.
+ int (*write_handler)(void* buffer, uint16_t size, void* arg);
+
+ /// Application-supplied buffer for GPSM-DCM write commands
+ void* buffer;
+
+ /// Size of the application-supplied write buffer
+ uint16_t buffer_size;
+
+ /// Application-supplied generic argument to the write handler
+ void* write_arg;
+
+ /// Callback when slave detects timeout from master
+ GpsmDcmSlaveCallback timeout_handler;
+
+ /// The last command recieved by the slave.
+ int command;
+
+ /// Return code from gpsm_dcm protocol actions
+ int protocol_rc;
+
+ /// Return code from slave action in response to master command
+ int slave_rc;
+
+ /// Return code from the application specific callback, if any.
+ int callback_rc;
+
+} GpsmSlaveControl;
+
+////////////////////////////////////////////////////////////////////////////
+// High-level GPSM-DCM Interchip Communication Methods
+////////////////////////////////////////////////////////////////////////////
+
+int
+gpsm_dcm_send(GpsmDcmPacket* fwPacket);
+
+
+
+int
+gpsm_dcm_receive(GpsmDcmPacket* fwPacket,
+ SsxInterval timeout_period);
+
+
+////////////////////////////////////////////////////////////////////////////
+// Generic Data Communication using GPSM-DCM 'write' command
+////////////////////////////////////////////////////////////////////////////
+
+int
+gpsm_dcm_write(void* buf,
+ uint16_t size,
+ SsxInterval timeout_period);
+
+
+int
+gpsm_dcm_read(void* buf,
+ uint16_t buf_size,
+ uint16_t* data_size,
+ SsxInterval timeout_period);
+
+////////////////////////////////////////////////////////////////////////////
+// Fast Interrrupt Handlers for Data Transfer of GPSM-DCM 'write' command
+////////////////////////////////////////////////////////////////////////////
+
+SSX_IRQ_HANDLER(gpsm_dcm_fast_write);
+SSX_IRQ_HANDLER(gpsm_dcm_fast_read);
+
+////////////////////////////////////////////////////////////////////////////
+// Generic Synchronization Mechanism using GPSM-DCM 'write' command
+////////////////////////////////////////////////////////////////////////////
+
+int
+gpsm_dcm_sync(int state);
+
+////////////////////////////////////////////////////////////////////////////
+// The Control Method for Slave Thread
+////////////////////////////////////////////////////////////////////////////
+
+int
+gpsm_dcm_slave(GpsmSlaveControl *control,
+ SsxInterval wait);
+
+
+////////////////////////////////////////////////////////////////////////////
+// The Command Method for Master Thread
+////////////////////////////////////////////////////////////////////////////
+
+int
+gpsm_dcm_master(GpsmDcmPacket* fwPacket, int* slaveRc);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __GPSM_DCM_H__ */
diff --git a/src/lib/gpsm_dcm_fast_handler.S b/src/lib/gpsm_dcm_fast_handler.S
new file mode 100755
index 0000000..01cbb2d
--- /dev/null
+++ b/src/lib/gpsm_dcm_fast_handler.S
@@ -0,0 +1,147 @@
+// $Id: gpsm_dcm_fast_handler.S,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+
+/// \file gpsm_dcm_fast_handler.S
+/// \brief Assembler support for GPSM_DCM procedures
+/// \cond
+ .nolist
+#include "ssx.h"
+#include "pmc_dcm.h"
+#include "gpsm_dcm.h"
+ .list
+
+#define GPSM_IC_CONTINUOUS_DATA (PMC_IC_MSG_CC << 4 | GPSM_IC_DATA)
+
+ // The gpsm_dcm_write fast handler.
+
+ // Register use:
+ //
+ // R3 = Entry: void *arg; *arg -> GpsmDcmFast
+ // R4 = Entry: SsxIrqId irq; irq : constant
+ // R5 = Entry: int priority; priority -> data
+ // R6 = data_pointer
+ // R7 = remaining_size
+ // CR = <condition>
+ // LR = <return address>
+ // No other registers can be used, other than SPRG
+
+ .global_function gpsm_dcm_fast_write
+
+gpsm_dcm_fast_write:
+
+ _ssx_irq_status_clear %r4, %r6, %r7 //clear status first
+
+ lwz %r6, 0(%r3) //load data pointer
+ lwz %r7, 4(%r3) //load remaining size
+
+ cmpwi %r7, 0 //if size == 0
+ beq exit_write_handler //exit fast write
+ cmpwi %r7, 1 //if size == 1
+ beq load_one_byte //load only one byte
+
+ lhz %r5, 0(%r6) //otherwise load two bytes
+ addi %r6, %r6, 2 //data pointer+2
+ addi %r7, %r7,-2 //remaining size-2
+ b form_and_send_packet //then form and send packet
+
+load_one_byte:
+
+ lbz %r5, 0(%r6) //load one byte from buffer
+ addi %r6, %r6, 1 //data pointer+1
+ addi %r7, %r7,-1 //remaining size-1
+
+form_and_send_packet:
+
+ stw %r6, 0(%r3) //store updated data pointer
+ stw %r7, 4(%r3) //store updated remaining size
+
+ lis %r6, GPSM_IC_CONTINUOUS_DATA //load cmd code to upper bits
+ or %r5, %r5, %r6 //load data into lower bits
+ slwi %r5, %r5, 8 //packet<<8 to give ecc field
+ _stwi %r5, %r7, PMC_INTCHP_MSG_WDATA //send packet
+ blr //return
+
+exit_write_handler:
+
+ bl __ssx_irq_fast2full //convert fast to full irq handler
+ _ssx_irq_disable %r4, %r6, %r7 //disable this irq
+ addi %r3, %r3, 8 //argument pointer to semaphore
+ bl ssx_semaphore_post //post to that semaphore
+ b __ssx_irq_full_mode_exit //exit
+
+ .epilogue gpsm_dcm_fast_write
+
+
+
+ // The gpsm_dcm_read fast handler.
+
+ // Register use:
+ //
+ // R3 = Entry: void *arg; *arg -> GpsmDcmFast(data,size,semaphore)
+ // R4 = Entry: SsxIrqId irq; irq : constant
+ // R5 = Entry: int priority; priority -> data
+ // R6 = data_pointer
+ // R7 = remaining_size
+ // CR = <condition>
+ // LR = <return address>
+ // No other registers can be used, other than SPRG
+
+ .global_function gpsm_dcm_fast_read
+
+gpsm_dcm_fast_read:
+
+ _ssx_irq_status_clear %r4, %r6, %r7 //clear status first
+
+ lwz %r7, 4(%r3) //load remaining size
+ cmpwi %r7, 0 //if size == 0
+ beq exit_read_handler //then exit fast read
+
+ _lwzi %r6, %r7, PMC_INTCHP_MSG_RDATA //receive packet
+
+ extrwi %r5, %r6, 16, 8 //extract data to r5
+ extrwi %r6, %r6, 8, 0 //extract cmd code to r6
+ li %r7, GPSM_IC_CONTINUOUS_DATA //designated cmd code to r7
+
+ cmpw %r6, %r7 //compare and check cmd code
+ bne panic_read_packet //panic on wrong cmd code
+
+ lwz %r6, 0(%r3) //load data pointer
+ lwz %r7, 4(%r3) //load remaining size
+
+ cmpwi %r7, 1 //if only one byte left
+ beq store_one_byte //then store only one byte
+
+ sth %r5, 0(%r6) //otherwise store two bytes to buf
+ addi %r6, %r6, 2 //data pointer+2
+ addi %r7, %r7,-2 //remaining size-2
+ b check_read_status //check read status
+
+store_one_byte:
+
+ stb %r5, 0(%r6) //store one byte to buf
+ addi %r6, %r6, 1 //data pointer+1
+ addi %r7, %r7,-1 //remaining size-1
+
+check_read_status:
+
+ stw %r6, 0(%r3) //store updated data pointer
+ stw %r7, 4(%r3) //store updated remaining size
+
+ cmpwi %r7, 0 //if size == 0
+ beq exit_read_handler //then terminate fast read
+ blr //return if still data left
+
+exit_read_handler:
+
+ bl __ssx_irq_fast2full //convert fast to full irq handler
+ _ssx_irq_disable %r4, %r6, %r7 //disable this irq
+ addi %r3, %r3, 8 //argument pointer to semaphore
+ bl ssx_semaphore_post //post to that semaphore
+ b __ssx_irq_full_mode_exit //exit
+
+panic_read_packet:
+
+ SSX_PANIC(GPSM_DCM_READ_NOT_WRITE_DATA) //PANIC if cmd code is wrong
+
+ .epilogue gpsm_dcm_fast_read
+/// \endcond
+
diff --git a/src/lib/gpsm_init.c b/src/lib/gpsm_init.c
new file mode 100755
index 0000000..02ac786
--- /dev/null
+++ b/src/lib/gpsm_init.c
@@ -0,0 +1,1638 @@
+// $Id: gpsm_init.c,v 1.9 2014/06/10 19:14:57 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm_init.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpsm_init.c
+/// \brief Global Pstate Machine procedures only required to initialize Pstate
+/// and Pstate modes.
+///
+/// In OCC product firmware this code is only needed immediately after IPL to
+/// set up Pstate tables and enable Pstates. This code could be run from an
+/// applet or otherwise removed once Pstates are initialized. Note that this
+/// file cointains code only; some global variables referenced here are
+/// defined in gpsm.c, along with the run-time APIs for GPSM.
+///
+/// The following sequence of procedures is required to initialize the GPSM
+/// mechanism, enable Pstate mode, and further enable Hardware Pstate Mode.
+///
+/// \code
+///
+/// PstateSuperStructure* pss;
+/// GlobalPstateTable *gpst;
+/// GpsmEnablePstatesMasterInfo info;
+/// Pstate voltage_pstate, frequency_pstate;
+///
+/// gpsm_initialize(pss, gpst);
+/// gpsm_enable_pstates_master(&info, &voltage_pstate, &frequency_pstate);
+/// gpsm_enable_pstates_slave(&info, voltage_pstate, frequency_pstate);
+/// gpsm_hw_mode();
+///
+/// \endcode
+///
+/// Executing these procedures enables Pstate control of voltage and
+/// frequency, and leaves the chip quiesced in Firmware Pstate mode. These
+/// procedures initializes both central (PMC) and remote (PCB Slave) Pstate
+/// hardware to fully enable Pstate control of voltage and frequency.
+/// Carefully note the procedure sequence preconditions and postconditions.
+///
+/// In the case of a DCM environment, the DCM master must execute the sequence
+/// as follows:
+///
+/// \code
+///
+/// PstateSuperStructure* pss;
+/// GlobalPstateTable *gpst;
+/// GpsmEnablePstatesMasterInfo info;
+/// Pstate voltage_pstate, frequency_pstate;
+///
+/// gpsm_initialize(pss, gpst);
+/// gpsm_enable_pstates_master(&info, &voltage_pstate, &frequency_pstate);
+///
+/// <em> Send voltage_pstate and frequency_pstate to the slave and wait for
+/// confirmation that the procedure has completed. </em>
+///
+/// gpsm_enable_pstates_slave(&info, voltage_pstate, frequency_pstate);
+/// gpsm_hw_mode();
+///
+/// <em> Send command to the slave to execute gpsm_hw_mode() </em>
+///
+/// \endcode
+///
+/// The DCM slave executes the following sequence
+/// \code
+/// PstateSuperStructure* pss;
+/// GlobalPstateTable *gpst;
+/// Pstate voltage_pstate, frequency_pstate;
+///
+/// gpsm_initialize(pss, gpst);
+///
+/// <em> Receive voltage_pstate and frequency_pstate from the masterand wait for
+/// confirmation that the procedure has completed. </em>
+///
+/// gpsm_enable_pstates_slave(0, voltage_pstate, frequency_pstate);
+///
+/// <em> Wait for a command from the master to execute gpsm_hw_mode(). </em>
+///
+/// \endcode
+///
+/// <b> Preconditions </b>
+///
+/// - This sequence must be called from a thread as it must be able to block
+/// for the completion of GPSM events.
+///
+/// - The fundamental precondition is the assumption that any snapshot of the
+/// voltage/frequency state of the system yields a legal state. As long as
+/// this is true, then this sequence should never take the system to an
+/// illegal V/F state.
+///
+/// - CPM-DPLL mode should be disabled, and all undervolting controls are
+/// assumed to be at their nominal values. Correctness can not guaranteed
+/// otherwise.
+///
+/// - iVRM mode should be disabled prior to calling this procedure.
+/// Correctness can not be guaranteed otherwise.
+///
+///
+/// <b> Standard/Benign Postconditions after executing
+/// gpsm_enable_pstates_slave() </b>
+///
+/// - The system will be in Firmware Pstate Mode, using the Local and Global
+/// Pstate tables installed by gpsm_initialize(). Pstate 0 will be mapped to
+/// the nominal frequency (modulo rounding down) of the
+/// 'nominal_frequency_khz' field of the Global Pstate table.
+///
+/// - All core chiplet frequencies will be under the control of the PMCR Local
+/// Pstate.
+///
+/// - The Global Actual Pstate immediately prior to the final entry of Pstate
+/// mode is the Pstate that most closely matches an arbitrary snapshot of the
+/// system voltage during the execution of this procedure.
+///
+/// - The PMCR and PMICR are not modified, therefore the Global Pstate
+/// subsequent to the release of Hardware Pstate mode is arbitrary.
+///
+/// - The PMC_RAIL_BOUNDS register is set to the maximum legal bounds allowed
+/// by Global Pstate Table
+///
+/// - IVRM is setup and enabled if Local Pstate Table is installed.
+///
+/// - Resonant Clock is setup and enabled if Resonant Clock is installed.
+///
+/// <b> Side-Effects </b>
+///
+/// - The dpll_fmax and dpll_fmin fields of the FREQ_CTRL_REG in each core are
+/// set to an arbitrary value. The dpll_fmax_bias is set to the value of the
+/// 'dpll_fmax_bias' field of the \a gpst.
+///
+/// - The core-level PCBS_POWER_MANAGEMENT_BOUNDS_REG registers are
+/// set to the maximum legal bounds. This is required due to Pstate table
+/// constraints.
+///
+/// \todo - How to handle redundant SPIVID interfaces? Here we get the
+/// current volatge from interface 0.
+///
+/// \bug Check to make sure that the PMC_CORE_DECONFIGURATION_REG matches
+/// multicast group 1.
+///
+/// \bug Code marked with ** VBU ** is necessary for VBU/EPO simulation, needs
+/// to be scrubbed once this is working in VBU.
+
+#include "ssx.h"
+#include "ssx_io.h"
+#include "gpsm.h"
+#include "vrm.h"
+#include "heartbeat.h"
+#include "special_wakeup.h"
+
+// Debugging support
+
+#if 0
+#define _BREAK \
+ { \
+ fprintf(stderr, "%s:%d: _BREAK trapped error rc = 0x%08x (-0x%08x)\n", \
+ __FILE__, __LINE__, rc, -rc); \
+ SSX_PANIC(GPSM_ERROR_BREAK); \
+ }
+#else
+#define _BREAK break;
+#endif
+
+
+/// Flag set once gpsm_initialize() has been successfully completed.
+uint8_t G_gpsm_initialized = 0;
+
+
+/// Install a (new) Global Pstate Table
+///
+/// \param[out] o_gpst A pointer to a properly-aligned GlobalPstateTable
+///
+/// \param[in] i_source A pointer to an initialized source GlobalPstateTable
+/// that will be copied to \a gpst (if not in fact the same as \a gpst).
+///
+/// This procedure will likely only be called once, at initialization, and
+/// then only as part of the gpsm_initialize() procedure. The procedure:
+///
+/// - Copies the \a source to the \a gpst if required.
+///
+/// - Installs a pointer to the Global Pstate Table in the PMC hardware.
+///
+/// - Sets up the PMC Pstate clipping bounds.
+///
+/// - Sets up the Pstate stepping parameters from the GlobalPstateTable
+///
+/// - Clears the local undervolting register (via multicast) and sets the
+/// default undervolting bounds to the entire Local Pstate Table.
+///
+/// - Broadcasts the safe mode Pstate Psafe to the cores and clears the other
+/// fields of the PCBS_OCC_HEARTBEAT_REG, disabling the heartbeat timer.
+/// However the heartbeat timer must not have been active anyway.
+///
+/// \note This procedure does modify the rail bounds.
+///
+/// \note The caller is responsible for the mode-correctness of this
+/// procedure. This procedure must only be called when the PMC is in Firmware
+/// Pstate Mode.
+///
+/// \retval 0 Success
+///
+/// \retval -GPSM_INVALID_ARGUMENT_GPST_INSTALL The Global Pstate table argument
+/// was either NULL (0) or improperly aligned, or the \a source was NULL (0).
+///
+/// \retval -GPSM_ILLEGAL_MODE_GPST_INSTALL The PMC does not indicate that the
+// system is in Firmware Pstate Mode, or the heartbeat is enabled.
+
+int
+gpsm_gpst_install(GlobalPstateTable* o_gpst,
+ const GlobalPstateTable* i_source)
+{
+ pmc_occ_heartbeat_reg_t pohr;
+ pmc_parameter_reg0_t ppr0;
+ pmc_parameter_reg1_t ppr1;
+ pmc_global_pstate_bounds_reg_t pgpbr;
+ pmc_rail_bounds_register_t prbr;
+ pmc_undervolting_reg_t pur;
+ pcbs_occ_heartbeat_reg_t pcbsohr;
+
+ int rc;
+
+ TRACE_GPSM(TRACE_GPSM_GPST_INSTALL);
+
+ do {
+
+ // Optional bypass of the procedure
+
+ if (i_source->options.options & PSTATE_NO_INSTALL_GPST) {
+
+ rc = 0;
+ break;
+ }
+
+ // Check presence and alignment of the Pstate table, and proper Pstate
+ // and heartbeat modes.
+
+ if ((o_gpst == 0) ||
+ ((unsigned long)o_gpst % POW2_32(GLOBAL_PSTATE_TABLE_ALIGNMENT))) {
+ rc = -GPSM_INVALID_ARGUMENT_GPST_INSTALL;
+ _BREAK;
+ }
+
+ pohr.value = in32(PMC_OCC_HEARTBEAT_REG);
+ rc = getscom(MC_ADDRESS(PCBS_OCC_HEARTBEAT_REG,
+ MC_GROUP_EX, PCB_MULTICAST_OR),
+ &(pcbsohr.value));
+ if (rc) _BREAK;
+
+ if (!gpsm_fw_mode_p() || pohr.fields.pmc_occ_heartbeat_en
+ || pcbsohr.fields.occ_heartbeat_enable) {
+ rc = -GPSM_ILLEGAL_MODE_GPST_INSTALL;
+ _BREAK;
+ }
+
+
+ // Copy \a source to \a gpst if required, then install the Pstate
+ // table, Pvsafe and Pstate stepping parameters, and set the clipping
+ // bounds as well as the rail bounds
+
+ if ((o_gpst != i_source) &&
+ !(i_source->options.options & PSTATE_NO_COPY_GPST)) {
+
+ memcpy(o_gpst, i_source, sizeof(*o_gpst));
+ }
+
+ ppr1.value = in32(PMC_PARAMETER_REG1);
+ ppr1.fields.ba_sram_pstate_table =
+ (unsigned long)o_gpst >> GLOBAL_PSTATE_TABLE_ALIGNMENT;
+ ppr1.fields.pvsafe = i_source->pvsafe;
+
+ // This fix is added per SW260911
+ // Minimum Frequency in the system is given by MRW attribute
+ // PState Datablock procedure will read the attribute then
+ // convert it into pstate _pfloor_ and put it into
+ // Global Pstate Table. GPSM here consumes the value
+ // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS)
+ // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be
+ // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin)
+ // so that we should never run with frequency below the floor
+ // even in safe mode
+ if (ppr1.fields.pvsafe < i_source->pfloor && i_source->pfloor != 0)
+ ppr1.fields.pvsafe = i_source->pfloor;
+
+ out32(PMC_PARAMETER_REG1, ppr1.value);
+
+ pgpbr.value = 0;
+ pgpbr.fields.gpsi_min = gpst_pmin(i_source) - PSTATE_MIN;
+ pgpbr.fields.gpst_number_of_entries_minus_one = i_source->entries - 1;
+ out32(PMC_GLOBAL_PSTATE_BOUNDS_REG, pgpbr.value);
+
+ ppr0.value = in32(PMC_PARAMETER_REG0);
+ ppr0.fields.pstate_stepsize = i_source->pstate_stepsize;
+ ppr0.fields.vrm_stepdelay_range = i_source->vrm_stepdelay_range;
+ ppr0.fields.vrm_stepdelay_value = i_source->vrm_stepdelay_value;
+ ppr0.fields.gpsa_timeout_value_sel = 1;
+ out32(PMC_PARAMETER_REG0, ppr0.value);
+
+ prbr.value = 0;
+ prbr.fields.pmin_rail = gpst_pmin(i_source)+1;
+ prbr.fields.pmax_rail = gpst_pmax(i_source);
+
+ // This fix is added per SW260911
+ // Minimum Frequency in the system is given by MRW attribute
+ // PState Datablock procedure will read the attribute then
+ // convert it into pstate _pfloor_ and put it into
+ // Global Pstate Table. GPSM here consumes the value
+ // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS)
+ // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be
+ // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin)
+ // so that we should never run with frequency below the floor
+ // even in safe mode
+ if (prbr.fields.pmin_rail < i_source->pfloor && i_source->pfloor != 0)
+ prbr.fields.pmin_rail = i_source->pfloor;
+
+ out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value);
+
+ // Clear the undervolting control, and set the undervolting range to
+ // the entire Global Pstate Table range.
+
+ pur.value = 0;
+ pur.fields.puv_min = gpst_pmin(i_source);
+ pur.fields.puv_max = gpst_pmax(i_source);
+ pur.fields.kuv_request = 0;
+ out32(PMC_UNDERVOLTING_REG, pur.value);
+
+
+ // Broadcast the safe mode Pstate Psafe to the cores, disabling the
+ // heartbeat (which must have been disabled anyway) and clearing any
+ // other heartbeat setup.
+
+ pcbsohr.value = 0;
+ pcbsohr.fields.psafe = i_source->psafe;
+
+ // This fix is added per SW260911
+ // Minimum Frequency in the system is given by MRW attribute
+ // PState Datablock procedure will read the attribute then
+ // convert it into pstate _pfloor_ and put it into
+ // Global Pstate Table. GPSM here consumes the value
+ // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS)
+ // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be
+ // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin)
+ // so that we should never run with frequency below the floor
+ // even in safe mode
+ if (pcbsohr.fields.psafe < i_source->pfloor && i_source->pfloor != 0)
+ pcbsohr.fields.psafe = i_source->pfloor;
+
+ rc = putscom(MC_ADDRESS(PCBS_OCC_HEARTBEAT_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ pcbsohr.value);
+ if (rc) _BREAK;
+
+ rc = 0;
+
+ } while(0);
+
+ return rc;
+}
+
+
+/// Install a (new) Local Pstate Array
+///
+/// \param[in] i_lpsa A pointer to a LocalPstateArray to install in every
+/// configured core.
+///
+/// \param[in] i_options Options controlling the installation, or a NULL (0)
+/// pointer to indicate fully default behavior.
+///
+/// This procedure will likely only be called once, at initialization, and
+/// then only as part of the gpsm_initialize() procedure. The procedure:
+///
+/// - Power on PFET Voltage Reference Circuit
+///
+/// - Perform the binary search for IVRM Calibration
+///
+/// - Uploads the LocalPstateArray to every core using multicast.
+///
+/// - Sets up the Local Pstate table bounds in every core using multicast.
+///
+/// - Sets the step delay parameters for every core using multicast.
+///
+/// - Clears the local undervolting register (via multicast) and sets the
+/// default undervolting bounds to the entire Local Pstate Table.
+///
+/// - Setup IVRM delay parameters
+///
+/// - Enable IVRM
+///
+/// \note This procedure \e does \e not modify the rail bounds.
+///
+/// \note The caller is responsible for the mode-correctness of this
+/// procedure. This procedure must only be called when the iVRM are
+/// disabled, the DPLL is in "normal" (not CPM-DPLL) mode, and core heartbeats
+/// are disabled.
+///
+/// \retval 0 Success
+///
+/// \retval -GPSM_INVALID_ARGUMENT_LPST_INSTALL The Local Pstate array argument
+/// was NULL (0).
+///
+/// \retval -GPSM_ILLEGAL_MODE_LPST_INSTALL iVRM mode, CPM-DPLL mode, or the
+/// local heartbeat appears to be enabled in at least one core.
+///
+/// \retval -GPSM_IVRM_CALIBRATION_TIMEOUT, if IVRM Calibration does not
+/// complete in time.
+///
+/// \retval -GPSM_IVRM_GROSS_OR_FINE, if ivrm_gross_or_fine_err is set
+///
+/// \retval -GPSM_PSTATE_ENABLED, if pstate is enabled before enabling IVRM
+///
+/// \retval others This API may also return non-0 codes from
+/// getscom()/putscom()
+
+int
+gpsm_lpsa_install(const LocalPstateArray* i_lpsa,
+ const PstateOptions* i_options)
+{
+ pcbs_ivrm_control_status_reg_t picsr;
+ pcbs_dpll_cpm_parm_reg_t pdcpr;
+ pcbs_pstate_index_bound_reg_t ppibr;
+ pcbs_ivrm_vid_control_reg0_t pivcr0;
+ pcbs_ivrm_vid_control_reg1_t pivcr1;
+ pcbs_undervolting_reg_t pur;
+ pcbs_pmerr_reg_t ppr;
+ pcbs_pcbspm_mode_reg_t ppmr;
+ pmc_core_deconfiguration_reg_t pcdr;
+ ChipConfigCores cores, wakedup;
+ SsxTimebase timeout;
+ int i, rc, timeout_rc = 0;
+ uint32_t configured_cores;
+ int flag, core;
+
+ TRACE_GPSM(TRACE_GPSM_LPSA_INSTALL);
+
+ do {
+
+ // Optional bypass of this procedure
+
+ if ((i_options != 0) &&
+ (i_options->options & PSTATE_NO_INSTALL_LPSA)) {
+
+ rc = 0;
+ break;
+ }
+
+ // No LPST Install and IVRM Enable if there is no configued cores
+
+ configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG);
+ flag = 1;
+ for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) {
+ if (!(configured_cores & 0x80000000)) continue;
+ flag = 0;
+ }
+ if (flag == 1) {
+ rc = 0;
+ break;
+ }
+
+ // Check the array for existence. Do an OR-combining multicast read
+ // to see if any of the cores have iVRM enabled, have the heartbeat
+ // enabled, or any cores are running in CPM-DPLL mode.
+
+ if (i_lpsa == 0) {
+ rc = -GPSM_INVALID_ARGUMENT_LPST_INSTALL;
+ _BREAK;
+ }
+ rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_OR),
+ &(picsr.value));
+ if (rc) _BREAK;
+ rc = getscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG,
+ MC_GROUP_EX, PCB_MULTICAST_OR),
+ &(pdcpr.value));
+ if (rc) _BREAK;
+
+ if (picsr.fields.ivrm_fsm_enable ||
+ pdcpr.fields.cpm_filter_enable) {
+ rc = -GPSM_ILLEGAL_MODE_LPST_INSTALL;
+ _BREAK;
+ }
+
+ // In case cores are in deep winkle so that ivrm caliburation
+ // will fail, insert special wakeup first
+ pcdr.value = in32(PMC_CORE_DECONFIGURATION_REG);
+ cores = ~pcdr.fields.core_chiplet_deconf_vector;
+ rc = occ_special_wakeup(1, cores, 25, &wakedup);
+ if (rc) _BREAK;
+
+ // Power on PFET Voltage Reference Circuit
+ picsr.fields.pvref_en = 1;
+ rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ picsr.value);
+ if (rc) _BREAK;
+
+ // Wait 10us for circuit to power on
+ timeout = ssx_timebase_get() + SSX_MICROSECONDS(10);
+ while (ssx_timebase_get() < timeout) {;}
+
+ // Perform the binary search
+ picsr.fields.binsearch_cal_ena = 1;
+ rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ picsr.value);
+ if (rc) _BREAK;
+
+ // Check IVRM Calibration is completed
+ // Poll for up to 100us for done before erroring out
+ timeout_rc = -GPSM_IVRM_CALIBRATION_TIMEOUT;
+ timeout = ssx_timebase_get() + SSX_MICROSECONDS(100);
+ while (ssx_timebase_get() < timeout) {
+ rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_AND),
+ &(picsr.value));
+ if (rc) _BREAK;
+ if (picsr.fields.binsearch_cal_done) {
+ timeout_rc=0;
+ break;
+ }
+ }
+ if (timeout_rc||rc) _BREAK;
+
+ // IVRM Calibration complete, Clear binary search enable
+ picsr.fields.binsearch_cal_ena = 0;
+ rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ picsr.value);
+ if (rc) _BREAK;
+
+ // Check if IVRM Gross or Fine Error is set after calibration!
+ rc = getscom(MC_ADDRESS(PCBS_PMERR_REG,
+ MC_GROUP_EX, PCB_MULTICAST_OR),
+ &(ppr.value));
+ if (rc) _BREAK;
+ if (ppr.fields.pcbs_ivrm_gross_or_fine_err) {
+ rc = -GPSM_IVRM_GROSS_OR_FINE;
+ _BREAK;
+ }
+
+ // Deassert Special Wakeup
+ rc = occ_special_wakeup(0, cores, 25, &wakedup);
+ if (rc) _BREAK;
+
+ // Upload the Local Pstate Array. The array is loaded via multicast,
+ // using the built-in auto-increment mechanism. Then upload the
+ // Pstate bounds register via multicast. Pstate clipping is not
+ // modified.
+
+ rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_CTRL_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ 0);
+ if (rc) _BREAK;
+
+ for (i = 0; i < LOCAL_PSTATE_ARRAY_ENTRIES+VDSVIN_ARRAY_ENTRIES; i++) {
+
+ if (i < LOCAL_PSTATE_ARRAY_ENTRIES) {
+ rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ i_lpsa->pstate[i].value);
+ if (rc) _BREAK;
+ } else {
+ rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ i_lpsa->vdsvin[i-LOCAL_PSTATE_ARRAY_ENTRIES].value);
+ if (rc) _BREAK;
+ }
+
+ }
+
+ ppibr.value = 0;
+ ppibr.fields.lpsi_min = lpst_pmin(i_lpsa) - PSTATE_MIN;
+ ppibr.fields.lpsi_entries_minus_1 = i_lpsa->entries - 1;
+ rc = putscom(MC_ADDRESS(PCBS_PSTATE_INDEX_BOUND_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ ppibr.value);
+ if (rc) _BREAK;
+
+
+ // Install the step delay parameters, then clear the undervolting
+ // control (applicable to the entire range) via multicast.
+
+ pivcr0.value = 0;
+ if (i_lpsa->stepdelay_rising)
+ pivcr0.fields.ivrm_req_pstate_stepdelay_rising =
+ i_lpsa->stepdelay_rising;
+ else
+ pivcr0.fields.ivrm_req_pstate_stepdelay_rising = 0xFF;
+ if (i_lpsa->stepdelay_lowering)
+ pivcr0.fields.ivrm_req_pstate_stepdelay_lowering =
+ i_lpsa->stepdelay_lowering;
+ else
+ pivcr0.fields.ivrm_req_pstate_stepdelay_lowering = 0XFF;
+ rc = putscom(MC_ADDRESS(PCBS_IVRM_VID_CONTROL_REG0,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ pivcr0.value);
+ if (rc) _BREAK;
+
+ ///bug need to determine where these values come from
+ pivcr1.value = 0;
+ pivcr1.fields.ivrm_stabilize_delay_run = 0x40;
+ pivcr1.fields.ivrm_stabilize_delay_idle = 0x40;
+ pivcr1.fields.ivrm_pfstr_prop_delay = 0x1E;
+ pivcr1.fields.ivrm_pfstrvalid_prop_delay = 0xFF;
+ pivcr1.fields.ivrm_vpump_poweron_time = 0xFF;
+ pivcr1.fields.ivrm_bypass_delay = 0x1E;
+ pivcr1.fields.pfet_vpump_enable_delay = 0x4E;
+ pivcr1.fields.ivrm_vid_vout_threshold = 0x00;
+ rc = putscom(MC_ADDRESS(PCBS_IVRM_VID_CONTROL_REG1,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ pivcr1.value);
+ if (rc) _BREAK;
+
+ pur.value = 0;
+ pur.fields.puv_min = lpst_pmin(i_lpsa);
+ pur.fields.puv_max = lpst_pmax(i_lpsa);
+ pur.fields.kuv = 0;
+ rc = putscom(MC_ADDRESS(PCBS_UNDERVOLTING_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ pur.value);
+ if (rc) _BREAK;
+
+ // Set pre_vret_pstate to Non-Functional Pstate
+ // \bug currently set to pmin, is it always same as psafe?
+ rc = getscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG,
+ MC_GROUP_EX, PCB_MULTICAST_OR),
+ &(pdcpr.value));
+ if (rc) _BREAK;
+ pdcpr.fields.pre_vret_pstate = lpst_pmin(i_lpsa);
+ rc = putscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ pdcpr.value);
+ if (rc) _BREAK;
+
+ // Checking that PStates are NOT enabled
+ rc = getscom(MC_ADDRESS(PCBS_PCBSPM_MODE_REG,
+ MC_GROUP_EX, PCB_MULTICAST_OR),
+ &(ppmr.value));
+ if (rc) _BREAK;
+ if (ppmr.fields.enable_pstate_mode) {
+ rc = -GPSM_PSTATE_ENABLED;
+ _BREAK;
+ }
+
+ // Enable I-VRM FSM
+ rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_OR),
+ &(picsr.value));
+ if (rc) _BREAK;
+ picsr.fields.ivrm_fsm_enable = 1;
+ rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ picsr.value);
+ if (rc) _BREAK;
+
+ } while (0);
+
+ if (timeout_rc && !rc)
+ return timeout_rc;
+ else
+ return rc;
+}
+
+
+/// Install (new) Resonant Clocking Setup
+///
+/// \param[in] i_resclk A pointer to a ResonantClockingSetup to install in
+/// every configured core.
+///
+/// \param[in] i_options Options controlling the installation, or a NULL (0)
+/// pointer to indicate fully default behavior.
+///
+/// This procedure will likely only be called once, at initialization, and
+/// then only as part of the gpsm_initialize() procedure. The procedure:
+///
+/// - Initializes the Pstate resonance range limits in the register
+/// PCBS_RESONANT_CLOCK_CONTROL_REG1 by multicast.
+///
+/// - Setup parameters in PCBS_RESONANT_CLOCK_CONTROL_REG0 and turn on
+/// Resonant Clock in Hardware Pstate Mode.
+///
+/// \note The caller is responsible for the mode-correctness of this
+/// procedure. This procedure must only be called when resonant clocking is
+/// disabled and the controls are set for manual mode. Because of the way the
+/// resonant clocking controls are designed we must enable resonant clocking
+/// to update the Pstate bounds! After the bounds are updated resonant
+/// clocking is disabled again.
+///
+/// \retval 0 Success
+///
+/// \retval -GPSM_INVALID_ARGUMENT_RCLK_INSTALL The ResonantClockingSetup
+/// argument was NULL (0).
+///
+/// \retval -GPSM_ILLEGAL_MODE_RCLK_INSTALL Resonant clocking appears to be
+/// enabled or not in manual mode in at least one configured core.
+///
+/// \retval others This API may also return non-0 codes from
+/// getscom()/putscom()
+
+int
+gpsm_resclk_install(const ResonantClockingSetup* i_resclk,
+ const GlobalPstateTable* i_gpst,
+ const PstateOptions* i_options)
+{
+ ResonantClockingSetup d_resclk;
+ pcbs_resonant_clock_control_reg0_t prccr0;
+ pcbs_resonant_clock_control_reg1_t prccr1;
+ int rc;
+ uint32_t configured_cores;
+ int flag, core;
+
+ TRACE_GPSM(TRACE_GPSM_RESCLK_INSTALL);
+
+ do {
+
+ // Optional bypass of this procedure
+
+ if ((i_options != 0) &&
+ (i_options->options & PSTATE_NO_INSTALL_RESCLK)) {
+
+ rc = 0;
+ break;
+ }
+
+ // No Resonant Clock Install and Enable if there is no configued cores
+
+ configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG);
+ flag = 1;
+ for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) {
+ if (!(configured_cores & 0x80000000)) continue;
+ flag = 0;
+ }
+ if (flag == 1) {
+ rc = 0;
+ break;
+ }
+
+ // Check the setup for existence. Do an AND-combining multicast read
+ // to see if any of the cores have resonant clocking enabled, or are
+ // not in manual mode.
+
+ if (i_resclk == 0) {
+ rc = -GPSM_INVALID_ARGUMENT_RCLK_INSTALL;
+ _BREAK;
+ }
+ rc = getscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG0,
+ MC_GROUP_EX, PCB_MULTICAST_AND),
+ &(prccr0.value));
+ if (rc) _BREAK;
+
+ if (!prccr0.fields.resclk_dis || !prccr0.fields.resclk_control_mode) {
+ rc = -GPSM_ILLEGAL_MODE_RCLK_INSTALL;
+ _BREAK;
+ }
+
+
+ // Resonant clocking is specified such that it must be enabled (in a
+ // benign manual mode) in order to be set up.
+
+ // Enable resonant clocking in the GP3 register (AND), bit 22. Our
+ // Simics environment does not model the GP3->PRCCR0 connection
+ // currently, and does not enforce the register locks.
+
+ if (!SIMICS_ENVIRONMENT) {
+
+ rc = putscom(MC_ADDRESS(0x100f0013, MC_GROUP_EX,
+ PCB_MULTICAST_WRITE),
+ ~0x0000020000000000ull);
+ if (rc) _BREAK;
+ }
+
+ // Write the PCBS_RESONANT_CLOCK_CONTROL_REG1 with the
+ // Pstate setup, clearing all manual fields.
+
+ // If at least one resonant clocking parm was 0 in PstateSuperStructure
+ // write the register with default values
+ // Low Band : 2 GHZ to 3.2 GHz
+ // High Band : 3.2 GHZ - Up
+
+ gpst_frequency2pstate(i_gpst, 0, &(d_resclk.full_csb_ps));
+ gpst_frequency2pstate(i_gpst, 2000000, &(d_resclk.res_low_lower_ps));
+ gpst_frequency2pstate(i_gpst, 3200000, &(d_resclk.res_low_upper_ps));
+ gpst_frequency2pstate(i_gpst, 3200000, &(d_resclk.res_high_lower_ps));
+ gpst_frequency2pstate(i_gpst, 9999999, &(d_resclk.res_high_upper_ps));
+
+ prccr1.value = 0;
+ if (!(i_resclk->full_csb_ps &&
+ i_resclk->res_low_lower_ps &&
+ i_resclk->res_low_upper_ps &&
+ i_resclk->res_high_lower_ps &&
+ i_resclk->res_high_upper_ps)) {
+ prccr1.fields.full_csb_ps = d_resclk.full_csb_ps;
+ prccr1.fields.res_low_lower_ps = d_resclk.res_low_lower_ps;
+ prccr1.fields.res_low_upper_ps = d_resclk.res_low_upper_ps;
+ prccr1.fields.res_high_lower_ps = d_resclk.res_high_lower_ps;
+ prccr1.fields.res_high_upper_ps = d_resclk.res_high_upper_ps;
+ } else {
+ prccr1.fields.full_csb_ps = i_resclk->full_csb_ps;
+ prccr1.fields.res_low_lower_ps = i_resclk->res_low_lower_ps;
+ prccr1.fields.res_low_upper_ps = i_resclk->res_low_upper_ps;
+ prccr1.fields.res_high_lower_ps = i_resclk->res_high_lower_ps;
+ prccr1.fields.res_high_upper_ps = i_resclk->res_high_upper_ps;
+ }
+
+ ///bug need to determine where these values come from
+ prccr1.fields.nonres_csb_value_ti = 0xC;
+ prccr1.fields.full_csb_value_ti = 0xF;
+
+ rc = putscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG1,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ prccr1.value);
+ if (rc) _BREAK;
+
+
+ // Disable resonant clocking in the GP3 register (OR), bit 22.
+
+ if (!SIMICS_ENVIRONMENT) {
+
+ rc = putscom(MC_ADDRESS(0x100f0014, MC_GROUP_EX,
+ PCB_MULTICAST_WRITE),
+ 0x0000020000000000ull);
+ if (rc) _BREAK;
+ }
+
+ // Enable resonant clock pstate hardware mode(control_mode = 0)
+ // Sync Pulse Width maximum value : 0x7 of nest/4 cycles
+ // Sync Delay maximum value : 0x7F of nest/4 cycles
+ // Sector Buffer Strength Instruction . Low Band : 0xAAA
+ // Sector Buffer Strength Instruction . High Band : 0xAAA
+
+ prccr0.fields.resclk_control_mode = 0;
+ prccr0.fields.resclk_sync_pw = 0x7;
+ prccr0.fields.res_sync_delay_cnt = 0x7F;
+ prccr0.fields.res_csb_str_instr_lo = 0xAAA;
+ prccr0.fields.res_csb_str_instr_hi = 0x1FF;
+
+ rc = putscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG0,
+ MC_GROUP_EX, PCB_MULTICAST_WRITE),
+ prccr0.value);
+ if (rc) break;
+
+ // Enable resonant clocking in the GP3 register (AND), bit 22.
+ if (!SIMICS_ENVIRONMENT) {
+
+ rc = putscom(MC_ADDRESS(0x100f0013, MC_GROUP_EX,
+ PCB_MULTICAST_WRITE),
+ ~0x0000020000000000ull);
+ if (rc) break;
+ }
+
+ } while (0);
+
+ return rc;
+}
+
+
+/// Initialize the GPSM procedure mechanism
+///
+/// \param[in] i_pss A pointer to the PstateSuperStructure containing the
+/// Global and Local Pstate tables, plus resonant clocking setup and other
+/// options.
+///
+/// \param[out] o_gpst A pointer to a 1-KB aligned GlobalPstateTable which
+/// will be updated with a copy of the GlobalPstateTable from the
+/// PstateSuperStructure.
+///
+/// This API is designed to be called once at system initialization, to set up
+/// GPSM mechanisms, install the Global and Local Pstate tables, and set up
+/// resonant clocking from the PstateSuperStructure. At the entry of this
+/// procedure it is assumed that the system firmware has initialized the PMC
+/// mode register to either no mode, or to indicate Firmware Pstate Mode. It
+/// is further assumed that the core chiplets are in a state which will allow
+/// the Local Pstate tables and resonant clocking setup to be installed
+/// without affecting system stability. Such a state must be guaranteed at
+/// system initialization and after any OCC reset. If called from any other
+/// context the caller is responsible for ensuring that the system is in a
+/// state that will allow the procedure to run correctly.
+///
+/// This procedure does not enable Pstates or enter any Pstate mode, and does
+/// not alter any voltage or frequency settings. After the Pstate tables have
+/// been installed, Pstate mode is enabled by calls of
+/// gpsm_enable_pstates_master() and gpsm_enable_pstates_slave() as described
+/// in the commenst for gpsm_init.c.
+///
+/// The initialization of Pstates was split up into these three steps to best
+/// handle the initialization of the slave chip in a DCM. This procedure
+/// (gpsm_initialize()) can be called by the DCM slave whenever a
+/// PstateSuperStructure is available. By requirememt and convention this
+/// Pstate SuperStructure will be identical with the one installed by the DCM
+/// master.
+///
+/// The GPSM driver makes few assumptions about how the system firmware has
+/// set up the PMC, but does require some critical setup.
+///
+/// - It is assumed that for DCM configurations the system firmware will have
+/// set the PMC_MODE_REG.enable_interchip_interface (to indicate a DCM
+/// configuration), and set the PMC_MODE_REG.interchip_mode appropriately for
+/// the master and the slave.
+///
+/// - It is assumed that the PMC Core Deconfiguration register implies the
+/// same set of configured cores as the set included in the PCB multicast
+/// group covering all cores.
+///
+/// All GPSM procedures use the same semaphore, which is set by the interrupt
+/// handler for all GPSM interrupts. The GPSM driver claims the PMC protocol
+/// ongoing, voltage change ongoing, and PMC Sync interrupts.
+///
+/// Once the interrupts are set up, the GlobalPstateTable is copied from the
+/// PstateSuperStructure to its proper location and installed. Next, the
+/// Local Pstate tables and resonant clocking setup are installed into all
+/// cores by multicast SCOM.
+///
+/// \retval 0 Success
+///
+/// \retval -GPSM_INVALID_OBJECT Either the \a i_pss or \a o_gpst are NULL (0).
+///
+/// \retval -GPSM_INVALID_MAGIC The 'magic number' of the PstateSuperStructure
+/// is different from that expected.
+///
+/// \retval -GPSM_ILLEGAL_MODE_GPSM_INIT Either the PMC indicates a Pstate mode
+/// is active, one or more cores appear to have iVRM enabled, or one or more
+/// cores appear to have resonant clocking enabled.
+///
+/// \retval others This API may also return codes from gpsm_gpst_install(),
+/// gpsm_lpsa_install() and gpsm_resclk_install().
+
+int
+gpsm_initialize(const PstateSuperStructure* i_pss,
+ GlobalPstateTable* o_gpst)
+{
+ pmc_mode_reg_t pmr;
+ int rc;
+
+ TRACE_GPSM(TRACE_GPSM_INITIALIZE);
+
+ do {
+
+ // Check for a valid PstateSuperStructure and GlobalPstateTable
+
+ if ((i_pss == 0) || (o_gpst == 0)) {
+
+ rc = -GPSM_INVALID_OBJECT;
+ _BREAK;
+ }
+
+ if (i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD1 &&
+ i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD2 &&
+ i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD3 ) {
+
+ rc = -GPSM_INVALID_MAGIC;
+ _BREAK;
+ }
+
+
+ // Check/set up the PMC mode register
+
+ pmr.value = in32(PMC_MODE_REG);
+ if (pmr.fields.enable_hw_pstate_mode ||
+ pmr.fields.enable_fw_auction_pstate_mode) {
+ rc = -GPSM_ILLEGAL_MODE_GPSM_INIT;
+ _BREAK;
+ }
+ if (!pmr.fields.enable_fw_pstate_mode) {
+
+ pmr.fields.enable_fw_pstate_mode = 1;
+ out32(PMC_MODE_REG, pmr.value);
+ }
+
+ // ** VBU **
+ pmr.fields.halt_pstate_master_fsm = 0;
+ out32(PMC_MODE_REG, pmr.value);
+
+
+ // Initialize interrupt handling
+
+ ssx_semaphore_create(&G_gpsm_protocol_semaphore, 0, 1);
+
+ ssx_irq_disable(PGP_IRQ_PMC_PROTOCOL_ONGOING);
+ ssx_irq_disable(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING);
+ ssx_irq_disable(PGP_IRQ_PMC_SYNC);
+
+ ssx_irq_setup(PGP_IRQ_PMC_PROTOCOL_ONGOING,
+ SSX_IRQ_POLARITY_ACTIVE_LOW,
+ SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);
+
+ ssx_irq_setup(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING,
+ SSX_IRQ_POLARITY_ACTIVE_LOW,
+ SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);
+
+ ssx_irq_setup(PGP_IRQ_PMC_SYNC,
+ SSX_IRQ_POLARITY_ACTIVE_HIGH,
+ SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);
+
+
+ ssx_irq_handler_set(PGP_IRQ_PMC_PROTOCOL_ONGOING,
+ ssx_semaphore_post_handler,
+ (void *)(&G_gpsm_protocol_semaphore),
+ SSX_NONCRITICAL);
+
+ ssx_irq_handler_set(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING,
+ ssx_semaphore_post_handler,
+ (void *)(&G_gpsm_protocol_semaphore),
+ SSX_NONCRITICAL);
+
+ ssx_irq_handler_set(PGP_IRQ_PMC_SYNC,
+ ssx_semaphore_post_handler,
+ (void *)(&G_gpsm_protocol_semaphore),
+ SSX_NONCRITICAL);
+
+ // Install the Global Pstate table, Local Pstate Array and resonant
+ // clocking setup, using options contained in the PstateSuperStructure.
+
+ rc = gpsm_gpst_install(o_gpst, &(i_pss->gpst));
+ if (rc) _BREAK;
+
+ rc = gpsm_lpsa_install(&i_pss->lpsa,
+ &(i_pss->gpst.options));
+ if (rc) _BREAK;
+
+ rc = gpsm_resclk_install(&i_pss->resclk,
+ &(i_pss->gpst),
+ &(i_pss->gpst.options));
+ if (rc) _BREAK;
+
+ G_gpsm_initialized = 1;
+
+ } while (0);
+
+ return rc;
+}
+
+
+// Step voltage manually
+//
+// This API is only (?) used by gpsm_enable_pstates_master[slave](). It is
+// used to make a (hopefully) minor adjustment between the current voltage and
+// the target voltage associated with the initial Global Actual Pstate. In
+// cases where the current voltage is not represented in the new Pstate table,
+// this routine may take a long time as it will do many single-VID-code steps
+// as it gradually moves between the current and target voltages.
+//
+// Voltage change direction is determined by the difference in the Vdd VIDs,
+// and the alogorithm mimics the P7 PVID stepping protocol. If voltage is
+// going up, Vdd and Vcs slew together. If voltage is going down, Vdd slews
+// twice for every change in Vcs. Note that given a Vdd differential we can't
+// assume which way Vcs is moving.
+//
+// The use of Vcs offsets instead of straight-up VID codes in the hardware is
+// extremely confusing, especially since the offsets are defined in normal
+// order as opposed to VID codes which decrease as voltage increases.
+
+// Racall that the inputs are VID codes (lower VID --> higher voltage)
+
+static int
+_manual_step_voltage(const uint8_t i_currentVdd,
+ const uint8_t i_currentVcs,
+ const uint8_t i_targetVdd,
+ const uint8_t i_targetVcs)
+{
+ int rc, parity;
+ pmc_global_actual_voltage_reg_t pgavr;
+ uint8_t currentVdd, currentVcs;
+
+ TRACE_GPSM(TRACE_MANUAL_STEP_VOLTAGE);
+
+ do {
+
+ rc = 0;
+ currentVdd = i_currentVdd;
+ currentVcs = i_currentVcs;
+ parity = 1;
+
+ while ((currentVdd != i_targetVdd) &&
+ (currentVcs != i_targetVcs)) {
+
+ if (currentVdd > i_targetVdd) {
+
+ // Voltage going up, slew Vdd and Vcs together. Parity remains
+ // 1.
+
+ currentVdd--;
+
+ } else if (currentVdd > i_targetVdd) {
+
+ // Voltage going down, only slew Vcs every other time. Parity
+ // is inverted.
+
+ currentVdd++;
+ parity = 1 - parity;
+
+ } else {
+
+ // Vdd not moving, set parity so Vcs will move every time.
+
+ parity = 1;
+ }
+
+ if (parity) {
+ if (currentVcs < i_targetVcs) {
+ currentVcs++;
+ } else if (currentVcs > i_targetVcs) {
+ currentVcs--;
+ }
+ }
+
+ rc = gpsm_quiesce();
+ if (rc) _BREAK;
+
+ pgavr.value = 0;
+ pgavr.fields.evid_vdd = currentVdd;
+ pgavr.fields.evid_vcs = -((int)currentVcs - (int)currentVdd);
+ out32(PMC_GLOBAL_ACTUAL_VOLTAGE_REG, pgavr.value);
+ }
+ if (rc) _BREAK;
+
+ rc = gpsm_quiesce();
+ if (rc) _BREAK;
+
+ } while (0);
+
+ return rc;
+}
+
+
+// This is a 'prologue' sequence executed in each core chiplet during the
+// initialization of Pstates. The set of cores to operate on is taken from
+// the current value of the PMC_CORE_DECONFIGURATION_REG.
+//
+// At entry it assumed that iVRM and CPM-DPLL are disabled. It also clears
+// possible safe mode dails before enable pstate.
+//
+// At exit, the following will be true for all configured cores:
+//
+// - The core will be in DPLL frequency override mode with Fmin and Fmax set
+// to the frequency implied by the given Pstate in the given Pstate table with
+// 0 undervolting.
+//
+// - The Fmax bias of the core is set from the Pstate table.
+//
+// - The Fnom of the core is set from the Pstate table.
+//
+// - Pstate mode is enabled in the core and global requests are enabled.
+//
+// - The Local Actual Pstate is being controlled by the Pstate mechanism.
+//
+// - The PMCR will have been updated to the \a frequencyPstate (both global
+// and local) and the global bids (should be) consistent. Auto-override modes
+// in the PMCR are not modified.
+
+static int
+_enable_pstates_core_prologue(const GlobalPstateTable* i_gpst,
+ const Pstate i_frequency_pstate,
+ const gpst_entry_t i_entry)
+{
+ int rc, core;
+ unsigned int bogus;
+ uint32_t configured_cores;
+ DpllCode fNom, fPstate;
+ pcbs_pmgp1_reg_t pmgp1;
+ pcbs_freq_ctrl_reg_t pfcr;
+ pcbs_pcbspm_mode_reg_t ppmr;
+ pcbs_power_management_control_reg_t pmcr;
+ pcbs_power_management_bounds_reg_t ppmbr;
+ pcbs_pmc_vf_ctrl_reg_t ppvcr;
+
+ TRACE_GPSM(TRACE_ENABLE_PSTATES_CORE_PROLOGUE);
+
+ do {
+
+ /* In the event of no configured cores, FW requested to not error out */
+ //rc = -GPSM_CONFIGURATION_ERROR;
+ rc = 0;
+
+ // Do for each core chiplet...
+ configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG);
+
+ // Turn off possible safe mode so we can move pstate
+ pcbs_hb_config(0, configured_cores, 0, 0, 0, &bogus);
+ pmc_hb_config(0, 0, 0, &bogus);
+
+ for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) {
+
+ if (!(configured_cores & 0x80000000)) continue;
+
+ // The 'nominal' frequency code may be biased per core. This
+ // should not under/over-flow.
+
+ fNom = i_gpst->pstate0_frequency_code[core];
+ rc = bias_frequency(fNom, i_frequency_pstate, &fPstate);
+ if (rc) _BREAK;
+
+
+ /// \bug HW Bug: Chicken-and-egg problem with frequency override
+ /// mode. We need a different HW control structure here. This
+ /// may glitch frequency.
+
+ // Initial PMGP1_REG setup
+ //
+ // - Force OCC control of the PM SPRS. This may have to be
+ // rethought if PHYP ever controls Pstates.
+ //
+ // - Enable DPLL frequency overrides
+
+ pmgp1.value = 0;
+ pmgp1.fields.pm_spr_override_en = 1;
+ pmgp1.fields.dpll_freq_override_enable = 1;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG_OR, core),
+ pmgp1.value);
+ if (rc) _BREAK;
+
+
+ // Update Fmin, Fmax, Fmax bias and Pstate0 frequency.
+
+ pfcr.value = 0;
+ pfcr.fields.dpll_fmin = fPstate;
+ pfcr.fields.dpll_fmax = fPstate;
+ pfcr.fields.dpll_fmax_bias = i_gpst->dpll_fmax_bias[core];
+ pfcr.fields.frequ_at_pstate0 = fNom;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_FREQ_CTRL_REG, core),
+ pfcr.value);
+ if (rc) _BREAK;
+
+
+ /// \bug HW BUG : PCBS_Power_Management_Bounds_reg hardware reset
+ /// is whack, violating the Pstate constraints. (HW216565).
+ /// Deferred to Venice. Not a problem for us, we always set up
+ /// this register.
+
+ // The PCBS clipping is initialized to the limits present in the
+ // _global_ Pstate table. This is necessary for correctness of
+ // the PCBS state machines. If fast-idle modes with retention are
+ // enabled this is also necessary to protect against trying to
+ // drop into non-functional Pstates required to be present in the
+ // _local_ pstate table.
+
+ // \bug Workaround, since pre_vret_pstate is set to pmin currently
+ // until pstate super structure and pstate data block procedure
+ // support an entry as non-functional pstate, need to set lower
+ // clip bound to be the pstate one above pmin to make pmin
+ // essentially a non-functional pstate for now
+
+ ppmbr.value = 0;
+ ppmbr.fields.pmin_clip = gpst_pmin(i_gpst)+1;
+ ppmbr.fields.pmax_clip = gpst_pmax(i_gpst);
+
+ // This fix is added per SW260911
+ // Minimum Frequency in the system is given by MRW attribute
+ // PState Datablock procedure will read the attribute then
+ // convert it into pstate _pfloor_ and put it into
+ // Global Pstate Table. GPSM here consumes the value
+ // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS)
+ // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be
+ // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin)
+ // so that we should never run with frequency below the floor
+ // even in safe mode
+ if (ppmbr.fields.pmin_clip < i_gpst->pfloor && i_gpst->pfloor != 0)
+ ppmbr.fields.pmin_clip = i_gpst->pfloor;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_POWER_MANAGEMENT_BOUNDS_REG,
+ core),
+ ppmbr.value);
+ if (rc) _BREAK;
+
+
+ // Now that we've locked the frequency and set valid clipping
+ // bounds, disable the local Pstate override and allow Global Acks
+ // and Pmax-Sync to propogate.
+
+ rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG, core),
+ &(pmgp1.value));
+ if (rc) _BREAK;
+
+ pmgp1.value = 0;
+ pmgp1.fields.enable_occ_ctrl_for_local_pstate_eff_req = 1;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG_AND, core),
+ ~pmgp1.value);
+ if (rc) _BREAK;
+
+
+ // Setup PCBS_PMC_VF_CTRL_REG before enable Pstate
+
+ rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_PMC_VF_CTRL_REG, core),
+ &(ppvcr.value));
+ if (rc) _BREAK;
+
+ ppvcr.fields.pglobal_actual = i_frequency_pstate;
+ ppvcr.fields.maxregvcs = i_entry.fields.maxreg_vdd;
+ ppvcr.fields.maxregvdd = i_entry.fields.maxreg_vcs;
+ ppvcr.fields.evidvcs_eff = i_entry.fields.evid_vdd_eff;
+ ppvcr.fields.evidvdd_eff = i_entry.fields.evid_vcs_eff;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMC_VF_CTRL_REG, core),
+ ppvcr.value);
+ if (rc) _BREAK;
+
+
+ // Enable Pstate in PCB Slave
+
+ rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_PCBSPM_MODE_REG, core),
+ &(ppmr.value));
+ if (rc) _BREAK;
+
+ ppmr.fields.enable_pstate_mode = 1;
+ ppmr.fields.enable_global_pstate_req = 1;
+ ppmr.fields.enable_pmc_pmax_sync_notification = 1;
+
+ // ** VBU **
+ ppmr.fields.dpll_lock_replacement_timer_mode_en = 1;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PCBSPM_MODE_REG, core),
+ ppmr.value);
+ if (rc) _BREAK;
+
+ // Update the PMCR to propagate the global bids
+
+ rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_POWER_MANAGEMENT_CONTROL_REG,
+ core),
+ &(pmcr.value));
+ if (rc) _BREAK;
+
+ pmcr.fields.global_pstate_req = i_frequency_pstate;
+ pmcr.fields.local_pstate_req = i_frequency_pstate;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_POWER_MANAGEMENT_CONTROL_REG,
+ core),
+ pmcr.value);
+ if (rc) _BREAK;
+ }
+ if (rc) _BREAK;
+
+ } while (0);
+
+ return rc;
+}
+
+
+// This is an 'epilogue' sequence executed in each core chiplet during the
+// enablement of Pstate mode. When this code is executed, the core is in
+// frequency override mode at (or below) the frequency of the Global Pstate
+// Actual. This procedure releases frequency override mode and core-level
+// Pstate operations commence.
+//
+// retval -GPSM_BABYSTEPPER_SYNC_TIMEOUT, if baby stepper sync
+// local_pstate_actual times out
+//
+static int
+_enable_pstates_core_epilogue(void)
+{
+ int rc = 0, timeout_rc = 0, core;
+ uint32_t configured_cores;
+ pcbs_pmgp1_reg_t pmgp1;
+ pcbs_power_management_status_reg_t ppmsr;
+ SsxTimebase timeout;
+
+ TRACE_GPSM(TRACE_ENABLE_PSTATES_CORE_EPILOGUE);
+
+ do {
+
+ configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG);
+ for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) {
+
+ if (!(configured_cores & 0x80000000)) continue;
+
+ pmgp1.value = 0;
+ pmgp1.fields.dpll_freq_override_enable = 1;
+
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG_AND, core),
+ ~pmgp1.value);
+ if (rc) _BREAK;
+ }
+ if (rc) _BREAK;
+
+ // For Babystepper to catch up sync the local_pstate_actual
+ // Poll for up to 300us for done before erroring out
+
+ timeout_rc = -GPSM_BABYSTEPPER_SYNC_TIMEOUT;
+ timeout = ssx_timebase_get() + SSX_MICROSECONDS(300);
+ while (ssx_timebase_get() < timeout) {
+ rc = getscom(MC_ADDRESS(PCBS_POWER_MANAGEMENT_STATUS_REG,
+ MC_GROUP_EX, PCB_MULTICAST_AND),
+ &(ppmsr.value));
+ if (rc) _BREAK;
+ if (ppmsr.fields.local_pstate_actual ==
+ ppmsr.fields.global_pstate_actual) {
+ timeout_rc = 0;
+ break;
+ }
+ }
+ if (timeout_rc||rc) _BREAK;
+
+ } while(0);
+
+ if (timeout_rc && !rc)
+ return timeout_rc;
+ else
+ return rc;
+
+}
+
+
+/// Enable Pstates in Firmware Mode, initial Master-only phase
+///
+/// \param[out] o_info This structure is populated by this API for use
+/// by a DCM master in gpsm_enable_pstates_slave. The DCM slave does not
+/// require this information.
+///
+/// \param[out] o_voltage_pstate This parameter returns the Pstate
+/// corresponding to the current system voltage (or the closest safe
+/// approximation). This parameter must be communicated to the slave before
+/// the slave can call gpsm_enable_pstates_slave().
+///
+/// \param[out] o_frequency_pstate *DEPRECATED* This parameter returns the
+/// Pstate corresponding to the current system voltage (or the closest safe
+/// approximation). This parameter must be communicated to the slave before
+/// the slave can call gpsm_enable_pstates_slave().
+///
+/// \note This procedure is only called on an SCM or a DCM master. It will
+/// fail if called on a DCM slave.
+///
+/// \retval 0 Success
+///
+/// \returns All other return codes indicate an error.
+
+int
+gpsm_enable_pstates_master(GpsmEnablePstatesMasterInfo* o_info,
+ Pstate* o_voltage_pstate,
+ Pstate* o_frequency_pstate)
+{
+ int rc, search_rc;
+ GlobalPstateTable* gpst;
+ gpst_entry_t voltage_entry;
+
+ TRACE_GPSM(TRACE_GPSM_ENABLE_PSTATES_MASTER);
+
+ do {
+
+ if (gpsm_dcm_slave_p()) {
+ rc = -GPSM_ILLEGAL_MODE_EPSM;
+ _BREAK;
+ }
+
+ // Enter Firmware Pstate Mode. The gpsm_fw_mode() procedure
+ // guarantees that the GPSM is quiesced at this point. Recover a
+ // pointer to the Pstate table from PMC.
+
+ rc = gpsm_fw_mode();
+ if (rc) _BREAK;
+
+ gpst = gpsm_gpst();
+
+ // Map the current Vdd VID to a pstate in the new Pstate table.
+ //
+ // As an option (workaround, simulation hack), force the assumption
+ // that the current voltage corresponds to PMIN. This will not move
+ // the external voltage, however it will force the frequency down to
+ // the PMIN frequency prior to starting Pstate operations. It is
+ // always safe to change the Pstate from "PMIN", regardless of the
+ // actual external voltage, since the PMIN frequency is safe at any
+ // voltage.
+
+ if (!(gpst->options.options & PSTATE_FORCE_INITIAL_PMIN)) {
+
+ rc = vrm_voltage_read(SPIVRM_PORT(0),
+ VRM_RD_VDD_RAIL,
+ &(o_info->currentVdd));
+ if (rc) _BREAK;
+ rc = vrm_voltage_read(SPIVRM_PORT(0),
+ VRM_RD_VCS_RAIL,
+ &(o_info->currentVcs));
+ if (rc) _BREAK;
+
+ } else {
+
+ rc = gpst_entry(gpst, gpst_pmin(gpst), 0, &voltage_entry);
+ if (rc) {
+ SSX_PANIC(GPSM_BUG); /* This can't happen */
+ }
+
+ o_info->currentVdd = voltage_entry.fields.evid_vdd;
+ o_info->currentVcs = voltage_entry.fields.evid_vcs;
+ }
+
+ search_rc = gpst_vdd2pstate(gpst, o_info->currentVdd,
+ o_voltage_pstate, &voltage_entry);
+ if (search_rc &&
+ (search_rc != -GPST_PSTATE_CLIPPED_LOW_GPST_V2P) &&
+ (search_rc != -GPST_PSTATE_CLIPPED_HIGH_GPST_V2P)) {
+ rc = search_rc;
+ break;
+ }
+
+ o_info->targetVdd = voltage_entry.fields.evid_vdd;
+ o_info->targetVcs = voltage_entry.fields.evid_vcs;
+
+
+ // If the Pstate was 'clipped low', it indicates that the current
+ // voltage is lower than the lowest new Pstate. Therefore we need to
+ // manually step voltage up before locking in the Pmin frequency. If
+ // the Pstate was 'clipped high' it means that the current voltage is
+ // higher than the highest Pstate, and we need to lock frequency at
+ // the Pmax frequency prior to stepping voltage down. The unclipped
+ // case is lumped with the 'clipped low' case as this case might
+ // entail a slight rise of voltage. V/F stepping must be split across
+ // the calls of gpsm_enable_pstates_master[slave].
+
+ if ((search_rc == 0)||(search_rc = -GPST_PSTATE_CLIPPED_LOW_GPST_V2P)) {
+
+ rc = _manual_step_voltage(o_info->currentVdd, o_info->currentVcs,
+ o_info->targetVdd, o_info->targetVcs);
+ if (rc) _BREAK;
+
+ o_info->move_voltage = 0;
+
+ } else {
+
+ o_info->move_voltage = 1;
+
+ }
+ } while (0);
+
+ /// \todo The o_frequency_pstate parameter is no longer needed. It was
+ /// originally needed when the Pstate table had an undervolting bias.
+ *o_frequency_pstate = *o_voltage_pstate;
+
+ return rc;
+}
+
+
+/// Enable Pstates in Firmware Pstate Mode, final Master/Slave phase
+///
+/// \param[in] i_info This structure is populated by
+/// gpsm_enable_pstates_master(), and only required in an SCM or DCM master.
+/// When this API is called on a DCM slave the parameter may be passed as NULL
+/// (0).
+///
+/// \param[in] i_voltage_pstate This parameter is computed by
+/// gpsm_enable_pstates_master(), and is required in every case.
+///
+/// \param[in] i_frequency_pstate This parameter is computed by
+/// gpsm_enable_pstates_master(), and is required in every case.
+///
+/// \note This procedure is called in all cases as the final step in enabling
+/// Pstate mode: SCM, DCM master, DCM slave.
+///
+/// \retval 0 Success
+///
+/// \returns All other return codes indicate an error.
+
+int
+gpsm_enable_pstates_slave(const GpsmEnablePstatesMasterInfo* i_info,
+ const Pstate i_voltage_pstate,
+ const Pstate i_frequency_pstate)
+{
+ int rc;
+ GlobalPstateTable* gpst;
+ pmc_mode_reg_t pmr;
+ gpst_entry_t voltage_entry;
+
+ TRACE_GPSM(TRACE_GPSM_ENABLE_PSTATES_SLAVE);
+
+ do {
+
+ // Enter Firmware Pstate Mode. The gpsm_fw_mode() procedure
+ // guarantees that the GPSM is quiesced at this point for the slave;
+ // the master must already be quiesced. Recover a pointer to the
+ // Pstate table from PMC.
+
+ if (gpsm_dcm_slave_p()) {
+
+ rc = gpsm_fw_mode();
+ if (rc) _BREAK;
+
+ } else {
+
+ if (!i_info) {
+ rc = -GPSM_INVALID_ARGUMENT_EPSS;
+ _BREAK;
+
+ } else if (!gpsm_fw_mode_p() || !gpsm_quiesced_p()) {
+
+ rc = -GPSM_ILLEGAL_MODE_EPSS;
+ _BREAK;
+
+ }
+ }
+
+ gpst = gpsm_gpst();
+ gpst_entry(gpst, i_voltage_pstate, 0, &voltage_entry);
+
+ // Execute the core prologue. An SCM or DCM master may need to move
+ // voltage after the frequency move. Since this is guaranteed to be a
+ // safe downward move (otherwise we would have moved voltage already),
+ // it is safe for the DCM slave to go ahead and finish its Pstate
+ // setup before the master has moved the voltage.
+
+ rc = _enable_pstates_core_prologue(gpst, i_frequency_pstate,
+ voltage_entry);
+ if (rc) _BREAK;
+
+ if (!gpsm_dcm_slave_p()) {
+
+ rc = _manual_step_voltage(i_info->currentVdd, i_info->currentVcs,
+ i_info->targetVdd, i_info->targetVcs);
+ if (rc) _BREAK;
+ }
+
+
+ // The Voltage and Frequency state is now consistent in the cores and
+ // in PMC. Make sure that PMC modes are set correctly for Hardware
+ // Pstate Mode.
+
+ pmr.value = in32(PMC_MODE_REG);
+ pmr.fields.enable_pstate_voltage_changes = 1;
+ pmr.fields.enable_global_actual_pstate_forwarding = 1;
+ //pmr.fields.enable_pstate_stepping = 1;
+ out32(PMC_MODE_REG, pmr.value);
+
+
+ // Since we're in Firmware Pstate mode and all cores are
+ // frequency-locked, we can set the Global Actual without stepping -
+ // under the assumption that the caller has disabled iVRM prior to the
+ // call. The master has already computed the volatge_pstate. We
+ // allow the GPSM to quiesce before unlocking the core frequencies.
+
+ _gpsm_broadcast_global_actual(i_frequency_pstate, voltage_entry);
+
+ rc = gpsm_quiesce();
+ if (rc) _BREAK;
+
+ rc = _enable_pstates_core_epilogue();
+ if (rc) _BREAK;
+
+ } while (0);
+
+ return rc;
+}
+
diff --git a/src/lib/heartbeat.c b/src/lib/heartbeat.c
new file mode 100755
index 0000000..51be390
--- /dev/null
+++ b/src/lib/heartbeat.c
@@ -0,0 +1,328 @@
+// $Id: heartbeat.c,v 1.5 2014/07/16 18:07:35 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/heartbeat.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file heartbeat.c
+/// \brief PgP PMC/PCBS heartbeat configuration procedures
+
+#include "ssx.h"
+#include "heartbeat.h"
+
+/// Configure/Enable/Disable the pmc heartbeat register.
+///
+/// \param enable 1 = enable, 0 = disable, all other values will cause error.
+///
+/// \param req_time_us heartbeat interval time request (in microseconds).
+/// If the pmc does not detect a heartbeat within this time the pmc will
+/// set the corresponding fir bit and enter safe mode. This interval
+/// is the requested value. The return value will be the actual setting.
+/// The procedure well attempt to get as close to the requested time as possible
+/// without choosing a setting lower then requested.
+/// Legal values: 1-4194240 (us). Ignored if force = 1 or enable = 0
+///
+/// \param force 1 = force safe mode (debug), 0 = do not force, all other values
+/// will cause an error. enable = 0 and force = 1 will return an error
+///
+/// \param[out] o_time_us Actual configured time rounded down to the nearest us.
+/// This will be as close as the procedure could get to the requested time given
+/// the frequency and pulse time settings. Returns 0 if hearbeat was disabled or
+/// if safe mode was forced.
+///
+/// \retval 0 Success
+///
+/// \retval -HB_INVALID_ARGUMENT_PMC One of the arguments was invalid in
+/// some way
+
+int
+pmc_hb_config(unsigned int enable,
+ unsigned int req_time_us,
+ unsigned int force,
+ unsigned int *o_time_us
+ )
+
+{
+ pmc_parameter_reg0_t ppr0;
+ pmc_occ_heartbeat_reg_t pohr;
+ tpc_hpr2_t l_hpr2;
+ uint64_t divider, pulses, total_pulses, hp_freq;
+ int rc = 0;
+
+ // @dyd SW238882 fix
+ // remove req_time_us overflow check since the upper boundary of
+ // the req_time_us doesnt depand on certain static value but based on
+ // the value set in hang_pulse_2_reg at runtime.
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((enable > 1) ||
+ (force > 1) ||
+ ((req_time_us < 1) && enable && (! force)) ||
+ ((force == 1 && enable == 0)),
+ HB_INVALID_ARGUMENT_PMC);
+ }
+
+ do {
+
+ // in case firmware does not call ocb_timer_setup
+ // before calling this procedure to setup g_ocb_timer_divider
+ rc = getscom(TPC_HPR2, &l_hpr2.value);
+ if(rc) break;
+ g_ocb_timer_divider = 1 << l_hpr2.fields.hang_pulse_reg;
+
+ // calculation based on pmc_occ_heartbeat_reg defination
+ hp_freq = (__ssx_timebase_frequency_mhz/g_ocb_timer_divider);
+ if(hp_freq < 1)
+ hp_freq = 1;
+ total_pulses = (req_time_us * hp_freq);
+
+ // this may be an overkill for safety but no one should notice
+ if ((req_time_us*__ssx_timebase_frequency_mhz) % g_ocb_timer_divider) {
+ total_pulses++;
+ }
+
+ divider = 0;
+ // determine values for predivider and number of pulses.
+ if (force || (! enable)) { // predivider a don't care in this case
+ pulses = 0;
+ *o_time_us = 0;
+ } else {
+ // can count up to 2^16 pulses with no pre-divide, first determine
+ // minimum pre-divider needed
+ do {
+ divider++;
+ } while ((((divider << 16) - 1) / total_pulses) < 1);
+
+ // @dyd SW238882 fix
+ // underflow case
+ // since pmc heartbeat counter counts with nest_nclk/4
+ // instead of hang pulse when hangpulse_predivider==0,
+ // this procedure doesnt allow predivider to be set to
+ // zero as it is a special case which doesnt work with
+ // occ heartbeat time value calculated by this procedure.
+ // Given hangpulse_predivider = divider - 1,
+ // set divider to 2 if it is 1, zero not possible.
+ if (divider < 2) {
+ divider = 2;
+ //rc = HB_UNDERFLOW_DIVIDER_PMC;
+ //break;
+ }
+ // overflow case
+ // since hangpulse_predivider field is only 6 bit long,
+ // check the overflow first, set to maximum if larger.
+ if (divider > 64) {
+ divider = 64;
+ //rc = HB_OVERFLOW_DIVIDER_PMC;
+ //break;
+ }
+
+ // divider is determined, now setup number of pulses
+ pulses = total_pulses / divider;
+ if (total_pulses % divider) {
+ pulses++;
+ }
+
+ // @dyd SW238882 fix
+ // there is no underflow case for pulses, because pulses=0 as
+ // intended immediate timeout is allowed, plus no mathematical
+ // substraction from pulses is done; however there is an overflow
+ // case: the value of pulses doesnt fit into 16 bits HW field.
+ // Here we set pulses to the maximum value that HW allows,
+ // and use the o_time_us to feedback the caller this is done.
+ if (pulses > 0xFFFF) {
+ pulses = 0xFFFF;
+ //rc = HB_OVERFLOW_PULSES_PMC;
+ // break;
+ }
+
+ // calculating real timeout duration
+ // that this procedure is going to set
+ *o_time_us = (divider*pulses)/hp_freq;
+
+ // @dyd SW238882 fix
+ // in force == 0 && enable == 1 case
+ // disable heartbeat first before reset hang pulse predivider
+ // and new heartbeat time value to prevent immediate timeout.
+ // if force == 1 then it is intended to be immediate timeout anyway
+ // if enable == 0 then it is going to set this bit to zero anyway
+ pohr.value = 0;
+ pohr.fields.pmc_occ_heartbeat_en = 0;
+ if (cfam_id() == CFAM_CHIP_ID_MURANO_10) {
+ out32(PMC_OCC_HEARTBEAT_REG, pohr.value);
+ }
+ out32(PMC_OCC_HEARTBEAT_REG, pohr.value);
+ }
+
+ // Note through experiments, the divider=predivider+1 isnt always
+ // in effect in hardware due to missing last pulse in tight timing,
+ // some setup will end up with just divider=predivider; therefore,
+ // in order to not result unexpected heartbeat timeout, always
+ // set divider to predivider to be safe.
+ if (enable && (! force)) {
+ ppr0.value = in32(PMC_PARAMETER_REG0);
+ ppr0.fields.hangpulse_predivider = divider;
+ out32(PMC_PARAMETER_REG0, ppr0.value);
+ }
+
+ pohr.value = 0;
+ pohr.fields.pmc_occ_heartbeat_en = enable;
+ pohr.fields.pmc_occ_heartbeat_time = pulses;
+ // Due to Issue HW219480, the heartbeat register needs to be written
+ // Twice in order for the heartbeat count value to take correctly.
+ // Technically it would not be harmful to just double-write in
+ // every case, but this is currently written to only double-write
+ // if a Murano dd1.0 part is detected
+ if (cfam_id() == CFAM_CHIP_ID_MURANO_10) {
+ out32(PMC_OCC_HEARTBEAT_REG, pohr.value);
+ }
+ out32(PMC_OCC_HEARTBEAT_REG, pohr.value);
+
+ }while(0);
+ return rc;
+
+}
+
+
+
+/// Configure/Enable/Disable the pcbs heartbeat registers.
+///
+/// \param enable 1 = enable, 0 = disable, all other values will cause error.
+///
+/// \param cores Use this mask to select which cores to update. This routine
+/// will cross reference the current pmc deconfig vector and only update
+/// those cores that are both selected here and configured.
+///
+/// \param hb_reg 32-bit unsigned address of register to setup as the
+/// PCBS heartbeat register. This must be a PCBS address.
+/// Ignored unless enable = 1
+///
+/// \param req_time_us heartbeat interval time request (in microseconds).
+/// If the pcbs does not detect a heartbeat within this time the pcbs will
+/// set the corresponding fir bit and enter safe mode. This interval
+/// is the requested value. The return value will be the actual setting and
+/// the procedure will attempt go get as close to possible to this without
+/// choosing a setting lower then requested.
+/// Legal values: 1 - 16320 (ignored unless enable = 1)
+///
+/// \param force 1 = force safe mode (debug), 0 = do not force, all other values
+/// will cause an error. In PCBS, the force safe mode is not related to
+/// the heartbeat so forcing safe mode while also enabling the heartbeat
+/// is allowed.
+///
+/// \param[out] o_time_us Actual configured time in us. This represents the
+/// actual setting rounded down to the nearest us. 0 if heartbeat was disabled.
+///
+/// \retval 0 Success
+
+/// \retval -HB_INVALID_ARGUMENT_PCBS One of the arguments was invalid in
+/// some way
+///
+/// \retval others This API may also return non-0 codes from
+/// getscom()/putscom()
+
+
+int
+pcbs_hb_config(unsigned int enable,
+ ChipConfigCores cores,
+ uint32_t hb_reg,
+ unsigned int req_time_us,
+ unsigned int force,
+ unsigned int *o_time_us)
+{
+ pcbs_occ_heartbeat_reg_t pohr;
+ pcbs_pmgp1_reg_t pp1r;
+ pmc_core_deconfiguration_reg_t pcdr;
+ uint32_t reg_offset;
+ uint32_t pp1r_addr;
+ uint64_t pp1r_data;
+ ChipConfigCores core_list;
+ ChipConfigCores deconfig;
+ int core;
+ int rc = 0;
+ unsigned int pulses;
+
+ reg_offset = hb_reg - PCBS_PIB_BASE;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((enable > 1) ||
+ ((reg_offset > 0xFF) && enable) ||
+ ((req_time_us < 64) && enable) ||
+ ((req_time_us > 16320) && enable) ||
+ (force > 1),
+ HB_INVALID_ARGUMENT_PCBS);
+ }
+
+
+ do {
+
+ // calculation based on pcbs_occ_heartbeat_reg defination
+ pulses = req_time_us/64;
+ if (req_time_us % 64) {
+ pulses++;
+ }
+
+ // @dyd SW238882 fix
+ // overflow handling: HW only allows 8 bits in the field.
+ // set pulses to maximum allowed value in HW if it overflows,
+ // and o_time_us will feedback to caller this is done.
+ if (pulses > 0xFF) {
+ pulses = 0xFF;
+ //rc = HB_PULSES_OVERFLOW_PCBS;
+ //break;
+ }
+ // underflow case, pulses cannot be zero due to undefined HW behavior
+ if (pulses < 1) {
+ pulses = 1;
+ //rc = HB_PULSES_UNDERFLOW_PCBS;
+ //break;
+ }
+
+ pp1r.value = 0;
+ pp1r.fields.force_safe_mode = 1;
+ if (force) {
+ pp1r_addr = PCBS_PMGP1_REG_OR;
+ pp1r_data = pp1r.value;
+ } else {
+ pp1r_addr = PCBS_PMGP1_REG_AND;
+ pp1r_data = ~(pp1r.value);
+ }
+
+ pcdr.value = in32(PMC_CORE_DECONFIGURATION_REG);
+ deconfig = pcdr.fields.core_chiplet_deconf_vector;
+
+ pohr.value = 0;
+ pohr.fields.occ_heartbeat_enable = enable;
+ pohr.fields.occ_heartbeat_time = pulses;
+ pohr.fields.occ_heartbeat_reg_addr_offset = reg_offset;
+
+ if (enable) {
+ *o_time_us = pulses * 64;
+ } else {
+ *o_time_us = 0;
+ }
+
+ do {
+ core_list = cores & (~deconfig);
+ for (core = 0; core < PGP_NCORES; core++, core_list <<= 1) {
+ if (core_list & 0x8000) {
+ // read modify write to preserve psafe
+ rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_OCC_HEARTBEAT_REG,
+ core), &pohr.value);
+ if (rc) break;
+ pohr.fields.occ_heartbeat_enable = enable;
+ pohr.fields.occ_heartbeat_time = pulses;
+ pohr.fields.occ_heartbeat_reg_addr_offset = reg_offset;
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_OCC_HEARTBEAT_REG,
+ core), pohr.value);
+ if (rc) break;
+ rc = putscom(CORE_CHIPLET_ADDRESS(pp1r_addr, core),
+ pp1r_data);
+ if (rc) break;
+ }
+ }
+ } while (0);
+
+ }while(0);
+ return rc;
+}
diff --git a/src/lib/heartbeat.h b/src/lib/heartbeat.h
new file mode 100755
index 0000000..6c8616b
--- /dev/null
+++ b/src/lib/heartbeat.h
@@ -0,0 +1,46 @@
+#ifndef __HEARTBEAT_H__
+#define __HEARTBEAT_H__
+
+// $Id: heartbeat.h,v 1.3 2014/02/12 05:48:48 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/heartbeat.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file heartbeat.h
+/// \brief PgP PMC/PCBS heartbeat configuration procedures
+
+#ifndef __ASSEMBLER__
+
+#include <stdint.h>
+
+#define HB_INVALID_ARGUMENT_PMC 0x00482801
+#define HB_INVALID_ARGUMENT_PCBS 0x00482802
+#define HB_UNDERFLOW_DIVIDER_PMC 0x00482803
+#define HB_OVERFLOW_DIVIDER_PMC 0x00482804
+#define HB_OVERFLOW_PULSES_PMC 0x00482805
+#define HB_OVERFLOW_PULSES_PCBS 0x00482806
+#define HB_UNDERFLOW_PULSES_PCBS 0x00482807
+
+int
+pmc_hb_config(unsigned int enable,
+ unsigned int req_time_us,
+ unsigned int force,
+ unsigned int *o_time_us);
+
+int
+pcbs_hb_config(unsigned int enable,
+ ChipConfigCores cores,
+ uint32_t hb_reg,
+ unsigned int req_time_us,
+ unsigned int force,
+ unsigned int *o_time_us);
+
+
+
+#endif /* __ASEMBLER__ */
+
+
+#endif /* __HEARTBEAT_H__ */
diff --git a/src/lib/libfiles.mk b/src/lib/libfiles.mk
new file mode 100755
index 0000000..55773a4
--- /dev/null
+++ b/src/lib/libfiles.mk
@@ -0,0 +1,57 @@
+# $Id: libfiles.mk,v 1.5 2014/06/26 12:51:16 cmolsen Exp $
+# $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/libfiles.mk,v $
+# @file libofiles.mk
+#
+# @brief mk for libssx.a object files
+#
+# @page ChangeLogs Change Logs
+# @section ofiles.mk
+# @verbatim
+#
+#
+# Change Log ******************************************************************
+# Flag Defect/Feature User Date Description
+# ------ -------------- ---------- ------------ -----------
+# @pb00E pbavari 03/11/2012 Makefile ODE support
+# @at007 alvinwan 05/25/2012 Use complex method for linking pore and PPC objects
+#
+# @endverbatim
+#
+##########################################################################
+# INCLUDES
+##########################################################################
+
+C-SOURCES = \
+ assert.c \
+ ctype.c \
+ ctype_table.c \
+ fgetc.c \
+ gpe_pba.c \
+ gpsm.c \
+ gpsm_dcm.c \
+ gpsm_init.c \
+ heartbeat.c \
+ memcpy.c \
+ memset.c \
+ pmc_dcm.c \
+ polling.c \
+ printf.c \
+ pstates.c \
+ puts.c \
+ simics_stdio.c \
+ special_wakeup.c \
+ sprintf.c \
+ ssx_dump.c \
+ ssx_io.c \
+ stdlib.c \
+ strcasecmp.c \
+ strdup.c \
+ string.c \
+ string_stream.c \
+ strtox.c \
+ time.c \
+ vrm.c \
+
+S-SOURCES = gpsm_dcm_fast_handler.S
+
+LIBSSX_OBJECTS = $(C-SOURCES:.c=.o) $(S-SOURCES:.S=.o)
diff --git a/src/lib/libgpefiles.mk b/src/lib/libgpefiles.mk
new file mode 100755
index 0000000..3f4efe1
--- /dev/null
+++ b/src/lib/libgpefiles.mk
@@ -0,0 +1,30 @@
+# $Id: libgpefiles.mk,v 1.3 2014/06/26 12:48:31 cmolsen Exp $
+# $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/libgpefiles.mk,v $
+# @file libgpefiles.mk
+#
+# @brief mk for libssx.a gpe object files
+#
+# @page ChangeLogs Change Logs
+# @section libgpefiles.mk
+# @verbatim
+#
+#
+# Change Log ******************************************************************
+# Flag Defect/Feature User Date Description
+# ------ -------------- ---------- ------------ -----------
+# @at007 alvinwan 05/25/2012 Use complex method for linking pore and PPC objects
+#
+# @endverbatim
+#
+##########################################################################
+# INCLUDES
+##########################################################################
+pS-SOURCES = \
+ gpe_control.pS \
+ gpe_data.pS \
+ gpe_scom.pS \
+ gpe_pba_pgas.pS
+
+LIB_PSOBJECTS = ${pS-SOURCES:.pS=.o}
+
+
diff --git a/src/lib/libssx.h b/src/lib/libssx.h
new file mode 100755
index 0000000..2bdffd4
--- /dev/null
+++ b/src/lib/libssx.h
@@ -0,0 +1,20 @@
+#ifndef __LIBSSX_H__
+#define __LIBSSX_H__
+
+// $Id: libssx.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/libssx.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file libssx.h
+/// \brief Header definitions with no other obvious home
+
+// Kernel panics
+
+#define ASSERTION_FAILURE 0x00542701
+#define ERROR_EXIT 0x00542702
+
+#endif // __LIBSSX_H__
diff --git a/src/lib/memcpy.c b/src/lib/memcpy.c
new file mode 100755
index 0000000..ab508ea
--- /dev/null
+++ b/src/lib/memcpy.c
@@ -0,0 +1,78 @@
+// $Id: memcpy.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/memcpy.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file memcpy.c
+/// \brief The memcpy() function
+
+#include "ssx.h"
+
+/// The memcpy() function copies \a n bytes from memory area \a src to memory
+/// area \a dest. The memory areas should not overlap. Use memmove(3) if the
+/// memory areas do overlap. The memcpy() function returns a pointer to dest.
+
+// This implementation should work well for both 32-bit and 64-bit machines,
+// assuming they can handle unaligned accesses. The implementation assumes that
+// it is better to avoid the loop setup overhead by a test and branch for
+// cases where loops can be bypassed.
+
+//void *
+//memcpy(void *dest, const void *src, size_t n)
+//{
+// while(n--) {
+// *dest++ = *src++;
+// }
+//
+// return s;
+//}
+
+void *
+memcpy(void *dest, const void *src, size_t n)
+{
+ uint8_t *d8, *s8;
+ uint64_t *d64, *s64;
+ size_t doublewords, octawords;
+
+ // First copy memory 32 bytes at a time.
+
+ d64 = (uint64_t *)dest;
+ s64 = (uint64_t *)src;
+ octawords = n / 32;
+ if (octawords) {
+ n -= octawords * 32;
+ while(octawords--) {
+ *d64++ = *s64++;
+ *d64++ = *s64++;
+ *d64++ = *s64++;
+ *d64++ = *s64++;
+ }
+ }
+
+ // Now set memory 8 bytes at a time. This might actually be better done
+ // explicitly rather than as a loop because the maximum loop count is 3
+ // here.
+
+ doublewords = n / 8;
+ if (doublewords) {
+ n -= doublewords * 8;
+ while (doublewords--) {
+ *d64++ = *s64++;
+ }
+ }
+
+ // Finally finish any remaining memory bytewise
+
+ if (n) {
+ d8 = (uint8_t *)d64;
+ s8 = (uint8_t *)s64;
+ while (n--) {
+ *d8++ = *s8++;
+ }
+ }
+
+ return dest;
+}
diff --git a/src/lib/memset.c b/src/lib/memset.c
new file mode 100755
index 0000000..98fdda2
--- /dev/null
+++ b/src/lib/memset.c
@@ -0,0 +1,118 @@
+// $Id: memset.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/memset.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file memset.c
+/// \brief The memset() function
+
+#include "ssx.h"
+
+/// The memset() function fills the first \a n bytes of the memory area
+/// pointed to by \a s with the constant byte \a c. The memset() function
+/// returns a pointer to the memory area \a s.
+///
+/// Note that memset() is optimized for setting large memory areas, and
+/// entails quite a bit of overhead to do this efficiently. If a memory area
+/// consists of a small number of basic data types (e.g., integers) it is
+/// probably more time-efficient to set the memory directly with a for loop
+/// (or unrolled loop).
+
+// This implementation should work well for both 32-bit and 64-bit
+// machines. The implementation assumes that it is worthwhile to align memory
+// pointers and do as much as possible using aligned addresses. [This doesn't
+// seem to matter on an X86 server processor, however]. It also assumes that
+// it is better to avoid the loop setup overhead by a test and branch for
+// cases where loops can be bypassed.
+
+//void *
+//memset(void *s, int c, size_t n)
+//{
+// uint8_t byte = (uint8_t)c;
+// uint8_t *p = (uint8_t *)s;
+//
+// while(n--) {
+// *p++ = byte;
+// }
+//
+// return s;
+//}
+
+void *
+memset(void *s, int c, size_t n)
+{
+ uint8_t byte, *p8;
+ uint32_t word;
+ uint64_t doubleword, *p64;
+ size_t bytes, doublewords, octawords;
+
+ // Any initial memory segment not aligned to an 8-byte boundary is set
+ // bytewise.
+
+ byte = (uint8_t)c;
+ p8 = (uint8_t *)s;
+
+ bytes = MIN(n, (unsigned long)s % 8);
+ if (bytes) {
+ n -= bytes;
+ while (bytes--) {
+ *p8++ = byte;
+ }
+ }
+
+ // Short requests are finshed here as well.
+
+ if (n < 8) {
+ while (n--) {
+ *p8++ = byte;
+ }
+ return s;
+ }
+
+ // We have at least 8 bytes of memory aligned on an 8-byte boundary. A
+ // doubleword initializer is created.
+
+ word = (byte << 8) | byte;
+ word = (word << 16) | word;
+ doubleword = ((uint64_t)word << 32) | word;
+
+ // First set memory 32 bytes at a time.
+
+ p64 = (uint64_t *)p8;
+ octawords = n / 32;
+ if (octawords) {
+ n -= octawords * 32;
+ while(octawords--) {
+ *p64++ = doubleword;
+ *p64++ = doubleword;
+ *p64++ = doubleword;
+ *p64++ = doubleword;
+ }
+ }
+
+ // Now set memory 8 bytes at a time. This might actually be better done
+ // explicitly rather than as a loop because the maximum loop count is 3
+ // here.
+
+ doublewords = n / 8;
+ if (doublewords) {
+ n -= doublewords * 8;
+ while (doublewords--) {
+ *p64++ = doubleword;
+ }
+ }
+
+ // Finally finish any remaining memory bytewise
+
+ p8 = (uint8_t *)p64;
+ if (n) {
+ while (n--) {
+ *p8++ = byte;
+ }
+ }
+
+ return s;
+}
diff --git a/src/lib/pgas.h b/src/lib/pgas.h
new file mode 100755
index 0000000..bdd3ba1
--- /dev/null
+++ b/src/lib/pgas.h
@@ -0,0 +1,1153 @@
+#ifndef __PGAS_H__
+#define __PGAS_H__
+
+#define __PGAS__
+
+// $Id: pgas.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pgas.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+// ** WARNING : This file is maintained as part of the OCC firmware. Do **
+// ** not edit this file in the PMX area, the hardware procedure area, **
+// ** or the PoreVe area as any changes will be lost. **
+
+/// \file pgas.h
+/// \brief Pore GAS
+///
+/// PGAS is documented in a seperate standalone document entitled <em> PGAS :
+/// PORE GAS (GNU Assembler) User's and Reference Manual </em>.
+///
+/// This file defines support macros for the GNU PORE assembler, and the PORE
+/// inline assembler and disassebler which follow the PGAS assembly syntax.
+/// If the compile swith PGAS_PPC is defined in the environment then pgas.h
+/// includes pgas_ppc.h which transforms a PowerPC assembler into an assembler
+/// for PORE.
+
+// These are the opcodes and mnemonics as defined by the PORE hardware
+// manual. Many of them will change names slightly in PGAS.
+
+#define PORE_OPCODE_NOP 0x0f
+#define PORE_OPCODE_WAIT 0x01
+#define PORE_OPCODE_TRAP 0x02
+#define PORE_OPCODE_HOOK 0x4f
+
+#define PORE_OPCODE_BRA 0x10
+#define PORE_OPCODE_BRAZ 0x12
+#define PORE_OPCODE_BRANZ 0x13
+#define PORE_OPCODE_BRAI 0x51
+#define PORE_OPCODE_BSR 0x14
+#define PORE_OPCODE_BRAD 0x1c
+#define PORE_OPCODE_BSRD 0x1d
+#define PORE_OPCODE_RET 0x15
+#define PORE_OPCODE_CMPBRA 0x56
+#define PORE_OPCODE_CMPNBRA 0x57
+#define PORE_OPCODE_CMPBSR 0x58
+#define PORE_OPCODE_LOOP 0x1f
+
+#define PORE_OPCODE_ANDI 0x60
+#define PORE_OPCODE_ORI 0x61
+#define PORE_OPCODE_XORI 0x62
+
+#define PORE_OPCODE_AND 0x25
+#define PORE_OPCODE_OR 0x26
+#define PORE_OPCODE_XOR 0x27
+
+#define PORE_OPCODE_ADD 0x23
+#define PORE_OPCODE_ADDI 0x24
+#define PORE_OPCODE_SUB 0x29
+#define PORE_OPCODE_SUBI 0x28
+#define PORE_OPCODE_NEG 0x2a
+
+#define PORE_OPCODE_COPY 0x2c
+#define PORE_OPCODE_ROL 0x2e
+
+#define PORE_OPCODE_LOAD20 0x30
+#define PORE_OPCODE_LOAD64 0x71
+#define PORE_OPCODE_SCR1RD 0x32
+#define PORE_OPCODE_SCR1RDA 0x73
+#define PORE_OPCODE_SCR2RD 0x36
+#define PORE_OPCODE_SCR2RDA 0x77
+#define PORE_OPCODE_WRI 0x78
+#define PORE_OPCODE_BS 0x74
+#define PORE_OPCODE_BC 0x75
+#define PORE_OPCODE_SCR1WR 0x39
+#define PORE_OPCODE_SCR2WR 0x3a
+#define PORE_OPCODE_SCAND 0x7c
+
+
+// These are the PGAS versions of the PORE opcodes used in the legacy PGAS_PPC
+// assembler and the current PORE inline assembler/disassembler.
+
+#define PGAS_OPCODE_NOP PORE_OPCODE_NOP
+#define PGAS_OPCODE_WAITS PORE_OPCODE_WAIT
+#define PGAS_OPCODE_TRAP PORE_OPCODE_TRAP
+#define PGAS_OPCODE_HOOKI PORE_OPCODE_HOOK
+
+#define PGAS_OPCODE_BRA PORE_OPCODE_BRA
+#define PGAS_OPCODE_BRAZ PORE_OPCODE_BRAZ
+#define PGAS_OPCODE_BRANZ PORE_OPCODE_BRANZ
+#define PGAS_OPCODE_BRAI PORE_OPCODE_BRAI
+#define PGAS_OPCODE_BSR PORE_OPCODE_BSR
+#define PGAS_OPCODE_BRAD PORE_OPCODE_BRAD
+#define PGAS_OPCODE_BSRD PORE_OPCODE_BSRD
+#define PGAS_OPCODE_RET PORE_OPCODE_RET
+#define PGAS_OPCODE_CMPIBRAEQ PORE_OPCODE_CMPBRA
+#define PGAS_OPCODE_CMPIBRANE PORE_OPCODE_CMPNBRA
+#define PGAS_OPCODE_CMPIBSREQ PORE_OPCODE_CMPBSR
+#define PGAS_OPCODE_LOOP PORE_OPCODE_LOOP
+
+#define PGAS_OPCODE_ANDI PORE_OPCODE_ANDI
+#define PGAS_OPCODE_ORI PORE_OPCODE_ORI
+#define PGAS_OPCODE_XORI PORE_OPCODE_XORI
+
+#define PGAS_OPCODE_AND PORE_OPCODE_AND
+#define PGAS_OPCODE_OR PORE_OPCODE_OR
+#define PGAS_OPCODE_XOR PORE_OPCODE_XOR
+
+#define PGAS_OPCODE_ADD PORE_OPCODE_ADD
+#define PGAS_OPCODE_ADDS PORE_OPCODE_ADDI
+#define PGAS_OPCODE_SUB PORE_OPCODE_SUB
+#define PGAS_OPCODE_SUBS PORE_OPCODE_SUBI
+#define PGAS_OPCODE_NEG PORE_OPCODE_NEG
+
+#define PGAS_OPCODE_MR PORE_OPCODE_COPY
+#define PGAS_OPCODE_ROLS PORE_OPCODE_ROL
+
+#define PGAS_OPCODE_LS PORE_OPCODE_LOAD20
+#define PGAS_OPCODE_LI PORE_OPCODE_LOAD64
+#define PGAS_OPCODE_LD0 PORE_OPCODE_SCR1RD /* Used by LD */
+#define PGAS_OPCODE_LD0ANDI PORE_OPCODE_SCR1RDA /* Used by LDANDI */
+#define PGAS_OPCODE_LD1 PORE_OPCODE_SCR2RD /* Used by LD */
+#define PGAS_OPCODE_LD1ANDI PORE_OPCODE_SCR2RDA /* Used by LDANDI */
+#define PGAS_OPCODE_STI PORE_OPCODE_WRI
+#define PGAS_OPCODE_STD0 PORE_OPCODE_SCR1WR /* Used by STD */
+#define PGAS_OPCODE_STD1 PORE_OPCODE_SCR2WR /* Used by STD */
+#define PGAS_OPCODE_SCAND PORE_OPCODE_SCAND
+
+#ifdef IGNORE_HW274735
+
+// BSI and BCI are normally redacted due to HW274735. See also pgas.h
+
+#define PGAS_OPCODE_BSI PORE_OPCODE_BS
+#define PGAS_OPCODE_BCI PORE_OPCODE_BC
+
+#endif // IGNORE_HW274735
+
+// These are the programmer-visible register names as defined by the PORE
+// hardware manual. All of these names (except the PC) appear differently in
+// the PGAS syntax, in some cases to reduce confusion, in other cases just to
+// have more traditional short mnemonics.
+
+#define PORE_REGISTER_PRV_BASE_ADDR0 0x0
+#define PORE_REGISTER_PRV_BASE_ADDR1 0x1
+#define PORE_REGISTER_OCI_BASE_ADDR0 0x2
+#define PORE_REGISTER_OCI_BASE_ADDR1 0x3
+#define PORE_REGISTER_SCRATCH0 0x4
+#define PORE_REGISTER_SCRATCH1 0x5
+#define PORE_REGISTER_SCRATCH2 0x6
+#define PORE_REGISTER_ERROR_MASK 0x7
+#define PORE_REGISTER_EXE_TRIGGER 0x9
+#define PORE_REGISTER_DATA0 0xa
+#define PORE_REGISTER_PC 0xe
+#define PORE_REGISTER_IBUF_ID 0xf
+
+
+// PgP IBUF_ID values
+
+#define PORE_ID_GPE0 0x00
+#define PORE_ID_GPE1 0x01
+#define PORE_ID_SLW 0x08
+#define PORE_ID_SBE 0x04
+
+
+// Condition Codes
+
+#define PORE_CC_UGT 0x8000
+#define PORE_CC_ULT 0x4000
+#define PORE_CC_SGT 0x2000
+#define PORE_CC_SLT 0x1000
+#define PORE_CC_C 0x0800
+#define PORE_CC_V 0x0400
+#define PORE_CC_N 0x0200
+#define PORE_CC_Z 0x0100
+
+
+// Memory Spaces
+
+#define PORE_SPACE_UNDEFINED 0xffff
+#define PORE_SPACE_OCI 0x8000
+#define PORE_SPACE_PNOR 0x800b
+#define PORE_SPACE_OTPROM 0x0001
+#define PORE_SPACE_SEEPROM 0x800c
+#define PORE_SPACE_PIBMEM 0x0008
+
+
+#ifdef __ASSEMBLER__
+
+////////////////////////////////////////////////////////////////////////////
+// PGAS Base Assembler Support
+////////////////////////////////////////////////////////////////////////////
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Condition Codes
+ //////////////////////////////////////////////////////////////////////
+
+ .set CC_UGT, PORE_CC_UGT
+ .set CC_ULT, PORE_CC_ULT
+ .set CC_SGT, PORE_CC_SGT
+ .set CC_SLT, PORE_CC_SLT
+ .set CC_C, PORE_CC_C
+ .set CC_V, PORE_CC_V
+ .set CC_N, PORE_CC_N
+ .set CC_Z, PORE_CC_Z
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Utility Macros
+ //////////////////////////////////////////////////////////////////////
+
+ // 'Undefine' PowerPC mnemonics to trap programming errors
+
+ .macro ..undefppc1, i
+ .ifnc \i, ignore
+ .macro \i, args:vararg
+ .error "This is a PowerPC opcode - NOT a PGAS opcode or extended mnemonic"
+ .endm
+ .endif
+ .endm
+
+ .macro .undefppc, i0, i1=ignore, i2=ignore, i3=ignore
+ ..undefppc1 \i0
+ ..undefppc1 \i1
+ ..undefppc1 \i2
+ ..undefppc1 \i3
+ .endm
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Argument Checking Macros
+ //////////////////////////////////////////////////////////////////////
+ //
+ // These macros remain in the final pgas.h file because 1) they are
+ // required for some PGAS pseudo-ops, and 2) to support robust
+ // assembler macro definitions.
+
+ // Check an unsigned immediate for size
+
+ .macro ..checku, x:req, bits:req, err="Unsigned value too large"
+
+ .if (((\bits) <= 0) || ((\bits) > 63))
+ .error "The number of bits must be in the range 0 < bits < 64"
+ .endif
+
+ .iflt (\x)
+ .error "An unsigned value is required here"
+ .endif
+
+ .ifgt ((\x) - (0xffffffffffffffff >> (64 - (\bits))))
+ .error "\err"
+ .endif
+
+ .endm
+
+ // Check unsigned 16/22-bit immediates for size
+ //
+ // In general, PGAS can check immediate values for size restrictions,
+ // but unfortunately is not able to check address offset immediates for
+ // range.
+
+ .macro ..check_u16, u16
+ ..checku (\u16), 16, "Unsigned immediate is larger than 16 bits"
+ .endm
+
+ .macro ..check_u24, u24
+ ..checku (\u24), 24, "Unsigned immediate is larger than 24 bits"
+ .endm
+
+ // Check a 16/20/22-bit signed immediate for size
+
+ .macro ..check_s16, s16
+ .iflt \s16
+ .iflt \s16 + 0x8000
+ .error "Immediate value too small for a signed 16-bit field"
+ .endif
+ .else
+ .ifgt \s16 - 0x7fff
+ .error "Immediate value too large for a signed 16-bit field"
+ .endif
+ .endif
+ .endm
+
+ .macro ..check_s20, s20
+ .iflt \s20
+ .iflt \s20 + 0x80000
+ .error "Immediate value too small for a signed 20-bit field"
+ .endif
+ .else
+ .ifgt \s20 - 0x7ffff
+ .error "Immediate value too large for a signed 20-bit field"
+ .endif
+ .endif
+ .endm
+
+ .macro ..check_s22, s22
+ .iflt \s22
+ .iflt \s22 + 0x200000
+ .error "Immediate value too small for a signed 22-bit field"
+ .endif
+ .else
+ .ifgt \s22 - 0x1fffff
+ .error "Immediate value too large for a signed 22-bit field"
+ .endif
+ .endif
+ .endm
+
+ // Check a putative SCOM address for bits 0 and 8:11 == 0.
+
+ .macro ..check_scom, address
+ .if ((\address) & 0x80f00000)
+ .error "Valid SCOM addresses must have bits 0 and 8:11 equal to 0."
+ .endif
+ .endm
+
+ // A register required to be D0
+
+ .macro ..d0, reg
+ .if (\reg != D0)
+ .error "Data register D0 is required here"
+ .endif
+ .endm
+
+ // A register pair required to be D0, D1 in order
+
+ .macro ..d0d1, reg1, reg2
+ .if (((\reg1) != D0) && ((\reg2) != D1))
+ .error "Register-Register ALU operations are only defined on the source pair D0, D1"
+ .endif
+ .endm
+
+ // A register pair required to be D0, D1 in any order
+ .macro ..dxdy, reg1, reg2, err="Expecting D0, D1 in either order"
+ .if !((((\reg1) == D0) && ((\reg2) == D1)) || \
+ (((\reg1) == D1) && ((\reg2) == D0)))
+ .error "\err"
+ .endif
+ .endm
+
+ // A register pair required to be A0, A1 in any order
+ .macro ..axay, reg1, reg2, err="Expecting A0, A1 in either order"
+ .if !((((\reg1) == A0) && ((\reg2) == A1)) || \
+ (((\reg1) == A1) && ((\reg2) == A0)))
+ .error "\err"
+ .endif
+ .endm
+
+ // A register pair required to be the same register
+
+ .macro ..same, dest, src
+ .if ((\dest) != (\src))
+ .error "PGAS requires the src and dest register of ADDS/SUBS to be explicit and identical"
+ .endif
+ .endm
+
+ // A "Data" register
+
+ .macro ..data, reg:req, err="Expecting a 'Data' register"
+ .if (\reg != D0)
+ .if (\reg != D1)
+ .error "\err"
+ .endif
+ .endif
+ .endm
+
+ // An "Address" register
+
+ .macro ..address, reg:req, err=:"Expecting an 'Address' register"
+ .if (\reg != A0)
+ .if (\reg != A1)
+ .error "\err"
+ .endif
+ .endif
+ .endm
+
+ // A "Pervasive Chiplet ID" register
+
+ .macro ..pervasive_chiplet_id, reg:req, err="Expecting a 'Pervasive Chiplet ID' register"
+ .if (\reg != P0)
+ .if (\reg != P1)
+ .error "\err"
+ .endif
+ .endif
+ .endm
+
+ // A "Branch Compare Data" register
+
+ .macro ..branch_compare_data, reg
+ .if (\reg != D0)
+ .if (\reg != D1)
+ .if (\reg != CTR)
+ .error "Expecting a 'Branch Compare Data' register"
+ .endif
+ .endif
+ .endif
+ .endm
+
+ // An "LS Destination" register; Also the set for ADDS/SUBS
+
+ .macro ..ls_destination, reg
+ .if (\reg != D0)
+ .if (\reg != D1)
+ .if (\reg != A0)
+ .if (\reg != A1)
+ .if (\reg != P0)
+ .if (\reg != P1)
+ .if (\reg != CTR)
+ .error "Expecting an 'LS Destination' register"
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endm
+
+ // An "LI Destination" register
+
+ .macro ..li_destination, reg
+ .if (\reg != D0)
+ .if (\reg != D1)
+ .if (\reg != A0)
+ .if (\reg != A1)
+ .if (\reg != CTR)
+ .error "Expecting an 'LI Destination' register"
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endm
+
+ // An "LIA Destination" register
+
+ .macro ..lia_destination, reg
+ .if (\reg != D0)
+ .if (\reg != D1)
+ .if (\reg != A0)
+ .if (\reg != A1)
+ .if (\reg != TBAR)
+ .error "Expecting an 'LIA Destination' register"
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endm
+
+ // An "MR Source" register
+
+ .macro ..mr_source, reg
+ .if (\reg != D0)
+ .if (\reg != D1)
+ .if (\reg != A0)
+ .if (\reg != A1)
+ .if (\reg != P0)
+ .if (\reg != P1)
+ .if (\reg != CTR)
+ .if (\reg != PC)
+ .if (\reg != ETR)
+ .if (\reg != SPRG0)
+ .if (\reg != IFR)
+ .if (\reg != EMR)
+ .error "Expecting an 'MR Source' register"
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endm
+
+ // An "MR Destination" register
+
+ .macro ..mr_destination, reg
+ .if (\reg != D0)
+ .if (\reg != D1)
+ .if (\reg != A0)
+ .if (\reg != A1)
+ .if (\reg != P0)
+ .if (\reg != P1)
+ .if (\reg != CTR)
+ .if (\reg != PC)
+ .if (\reg != ETR)
+ .if (\reg != SPRG0)
+ .if (\reg != EMR)
+ .error "Expecting an 'MR Destination' register"
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endm
+
+
+ //////////////////////////////////////////////////////////////////////
+ // PORE address spaces
+ //////////////////////////////////////////////////////////////////////
+
+ // The ..set_address_space pseudo-op defines the default address
+ // space. It must be defined in order to use BRAA, BRAIA, BSR and
+ // CMPIBSR. Pseudo-ops are provided to set the default space of the
+ // program. Note that code assembled for PNOR will also work in the
+ // OCI space in the Sleep/Winkle engine.
+
+ .macro ..set_default_space, s
+ ..check_u16 (\s)
+ .set _PGAS_DEFAULT_SPACE, (\s)
+ .endm
+
+ .macro ..check_default_space
+ .if (_PGAS_DEFAULT_SPACE == PORE_SPACE_UNDEFINED)
+ .error "The PGAS default address space has not been defined"
+ .endif
+ .endm
+
+ ..set_default_space PORE_SPACE_UNDEFINED
+
+ .macro .oci
+ ..set_default_space PORE_SPACE_OCI
+ .endm
+
+ .macro .pnor
+ ..set_default_space PORE_SPACE_PNOR
+ .endm
+
+ .macro .seeprom
+ ..set_default_space PORE_SPACE_SEEPROM
+ .endm
+
+ .macro .otprom
+ ..set_default_space PORE_SPACE_OTPROM
+ .endm
+
+ .macro .pibmem
+ ..set_default_space PORE_SPACE_PIBMEM
+#ifndef PGAS_PPC
+ .pibmem_port (PORE_SPACE_PIBMEM & 0xf)
+#else
+ // NB: PGAS_PPC does not support relocatable PIBMEM addressing
+#endif
+ .endm
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Address-Generation Pseudo Ops
+ //////////////////////////////////////////////////////////////////////
+
+ // .QUADA, .QUADIA
+
+ .macro .quada, offset:req
+ ..check_default_space
+ .long _PGAS_DEFAULT_SPACE
+ .long (\offset)
+ .endm
+
+ .macro .quadia, space:req, offset:req
+ ..check_u16 (\space)
+ .long (\space)
+ .long (\offset)
+ .endm
+
+ //////////////////////////////////////////////////////////////////////
+ // Bug workarounds
+ //////////////////////////////////////////////////////////////////////
+
+#ifndef IGNORE_HW274735
+
+ // HW274735 documents that BC and BS are broken for the PORE-GPE0/1
+ // pair. This bug is unfixed in POWER8, and by default we require BSI
+ // and BCI to be implemented as macros on all engines. For
+ // compatability we continue to require that dx == D0.
+
+ .macro bsi, dx:req, offset:req, base:req, imm:req
+ ..d0 (\dx)
+ ld D0, (\offset), (\base)
+ ori D0, D0, (\imm)
+ std D0, (\offset), (\base)
+ .endm
+
+ .macro bci, dx:req, offset:req, base:req, imm:req
+ ..d0 (\dx)
+ ldandi D0, (\offset), (\base), ~(\imm)
+ std D0, (\offset), (\base)
+ .endm
+
+#endif // IGNORE_HW274735
+
+ //////////////////////////////////////////////////////////////////////
+ // "A"- and "IA"-form Instructions
+ //////////////////////////////////////////////////////////////////////
+
+ // BRAA (Branch Address) is a 'long branch' to an address in the
+ // default memory space.
+
+ .macro braa, offset:req
+ braia _PGAS_DEFAULT_SPACE, (\offset)
+ .endm
+
+ // LA (Load Address) loads the full address of an address in the
+ // default memory space.
+
+ .macro la, dest:req, offset:req
+ lia (\dest), _PGAS_DEFAULT_SPACE, (\offset)
+ .endm
+
+ // STA (Store Address) stores the full address of an address in the
+ // default memory space.
+
+ .macro sta, mem_offset:req, base:req, addr_offset:req
+ stia (\mem_offset), (\base), _PGAS_DEFAULT_SPACE, (\addr_offset)
+ .endm
+
+ // BSRIA is a subroutine branch into another memory space. This has to
+ // be emulated by a local subroutine branch and a BRAIA.
+
+ .macro bsria, space:req, offset:req
+ bsr 27742f
+ bra 27743f
+27742:
+ braia (\space), (\offset)
+27743:
+ .endm
+
+
+////////////////////////////////////////////////////////////////////////////
+// Extended Mnemonics, Macros and Special Cases
+////////////////////////////////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////////////////////////
+ // TFB<c> - Test flags and branch conditionally
+ //////////////////////////////////////////////////////////////////////'
+
+ .macro ..tfb, dest, target, flags
+ ..data (\dest)
+ mr (\dest), IFR
+ andi (\dest), (\dest), (\flags)
+ branz (\dest), (\target)
+ .endm
+
+ .macro ..tfbn dest, target, flags
+ ..data (\dest)
+ mr (\dest), IFR
+ andi (\dest), (\dest), (\flags)
+ braz (\dest), (\target)
+ .endm
+
+ .macro tfbcs, dest:req, target:req
+ ..tfb (\dest), (\target), CC_C
+ .endm
+
+ .macro tfbcc, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_C
+ .endm
+
+ .macro tfbvs, dest:req, target:req
+ ..tfb (\dest), (\target), CC_V
+ .endm
+
+ .macro tfbvc, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_V
+ .endm
+
+ .macro tfbns, dest:req, target:req
+ ..tfb (\dest), (\target), CC_N
+ .endm
+
+ .macro tfbnc, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_N
+ .endm
+
+ .macro tfbeq, dest:req, target:req
+ ..tfb (\dest), (\target), CC_Z
+ .endm
+
+ .macro tfbne, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_Z
+ .endm
+
+ .macro tfbult, dest:req, target:req
+ ..tfb (\dest), (\target), CC_ULT
+ .endm
+
+ .macro tfbule, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_UGT
+ .endm
+
+ .macro tfbuge, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_ULT
+ .endm
+
+ .macro tfbugt, dest:req, target:req
+ ..tfb (\dest), (\target), CC_UGT
+ .endm
+
+ .macro tfbslt, dest:req, target:req
+ ..tfb (\dest), (\target), CC_SLT
+ .endm
+
+ .macro tfbsle, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_SGT
+ .endm
+
+ .macro tfbsge, dest:req, target:req
+ ..tfbn (\dest), (\target), CC_SLT
+ .endm
+
+ .macro tfbsgt, dest:req, target:req
+ ..tfb (\dest), (\target), CC_SGT
+ .endm
+
+
+ //////////////////////////////////////////////////////////////////////
+ // TEB[N]<eng> - Test Engine and branch if [not] engine.
+ //////////////////////////////////////////////////////////////////////
+ //
+ // All but GPE0 use a 1-hot code.
+
+ .macro tebgpe0, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), 0xf
+ braz (\dest), (\target)
+ .endm
+
+ .macro tebgpe1, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), PORE_ID_GPE1
+ branz (\dest), (\target)
+ .endm
+
+ .macro tebslw, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), PORE_ID_SLW
+ branz (\dest), (\target)
+ .endm
+
+ .macro tebsbe, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), PORE_ID_SBE
+ branz (\dest), (\target)
+ .endm
+
+
+ .macro tebngpe0, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), 0xf
+ branz (\dest), (\target)
+ .endm
+
+ .macro tebngpe1, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), PORE_ID_GPE1
+ braz (\dest), (\target)
+ .endm
+
+ .macro tebnslw, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), PORE_ID_SLW
+ braz (\dest), (\target)
+ .endm
+
+ .macro tebnsbe, dest:req, target:req
+ mr (\dest), IFR
+ andi (\dest), (\dest), PORE_ID_SBE
+ braz (\dest), (\target)
+ .endm
+
+
+ //////////////////////////////////////////////////////////////////////
+ // EXTRPRC - Extract and right-justify the PIB/PCB return code
+ // TPRCB[N]Z - Test PIB return code and branch if [not] zero
+ // TPRCBGT - Test PIB return code and branch if greater-than
+ // TPRCBLE - Test PIB return code and branch if less-then or equal
+ //////////////////////////////////////////////////////////////////////
+ //
+ // To support cases where PORE code expects or must explicitly handle
+ // non-0 PIB return codes, the PIB return code and parity indication
+ // are stored in bits 32 (parity) and 33-35 (return code) of the IFR.
+ // These macros extract the four PIB/PCB status bits from the IFR and
+ // right-justifies them into the data register provided. For EXTRPRC
+ // that is the total function of the macro. The TPRCB[N]Z macros
+ // provide a simple non-destructive test and branch for zero (success)
+ // and non-zero (potential problem) codes after the extraction.
+ //
+ // In complex error handling scenarios one would typically compare the
+ // PIB return code against an upper-bound, e.g., the offline response
+ // (0x2), and then take further action. If the parity error bit is set
+ // then this would produce an aggregate "return code" higher than any
+ // that one would typically want to ignore. The TPRCBGT/TPRCBLE macros
+ // provide this function; however the test destroys the extracted
+ // return code so that if further analysis is required the code will
+ // need to be a extracted again.
+ //////////////////////////////////////////////////////////////////////
+
+ .macro extrprc, dest:req
+ ..data (\dest)
+ mr (\dest), IFR
+ extrdi (\dest), (\dest), 4, 32
+ .endm
+
+ .macro tprcbz, dest:req, target:req
+ extrprc (\dest)
+ braz (\dest), (\target)
+ .endm
+
+ .macro tprcbnz, dest:req, target:req
+ extrprc (\dest)
+ branz (\dest), (\target)
+ .endm
+
+ .macro tprcbgt, dest:req, target:req, bound:req
+ extrprc (\dest)
+ subs (\dest), (\dest), (\bound)
+ tfbugt (\dest), (\target)
+ .endm
+
+ .macro tprcble, dest:req, target:req, bound:req
+ extrprc (\dest)
+ subs (\dest), (\dest), (\bound)
+ tfbule (\dest), (\target)
+ .endm
+
+ //////////////////////////////////////////////////////////////////////
+ // LPCS - Load Pervasive Chiplet from Scom address
+ //////////////////////////////////////////////////////////////////////
+
+ .macro lpcs, dest:req, scom:req
+ ..pervasive_chiplet_id (\dest)
+ ..check_scom (\scom)
+ ls (\dest), (((\scom) >> 24) & 0x7f)
+ .endm
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Shift/Mask extended mnemonics
+ //////////////////////////////////////////////////////////////////////
+
+ // All of the 'dot-dot' macros assume that error and identity
+ // checking has been done on the arguments already.
+
+ // The initial register-register rotate. If the incoming shift amount
+ // is 0 then the instruction generated is a simple MR.
+
+ .macro ..rotlrr, ra, rs, sh
+
+ .if (\sh) >= 32
+ rols (\ra), (\rs), 32
+ ..rotlr (\ra), ((\sh) - 32)
+ .elseif (\sh) >= 16
+ rols (\ra), (\rs), 16
+ ..rotlr (\ra), ((\sh) - 16)
+ .elseif (\sh) >= 8
+ rols (\ra), (\rs), 8
+ ..rotlr (\ra), ((\sh) - 8)
+ .elseif (\sh) >= 4
+ rols (\ra), (\rs), 4
+ ..rotlr (\ra), ((\sh) - 4)
+ .elseif (\sh) >= 1
+ rols (\ra), (\rs), 1
+ ..rotlr (\ra), ((\sh) - 1)
+ .else
+ mr (\ra), (\rs)
+ .endif
+
+ .endm
+
+
+ // Subsequent rotation of the same register. The SH should never be 0
+ // here.
+
+ .macro ..rotlr, ra, sh
+
+ .if (\sh) >= 32
+ rols (\ra), (\ra), 32
+ ..rotlr (\ra), ((\sh) - 32)
+ .elseif (\sh) >= 16
+ rols (\ra), (\ra), 16
+ ..rotlr (\ra), ((\sh) - 16)
+ .elseif (\sh) >= 8
+ rols (\ra), (\ra), 8
+ ..rotlr (\ra), ((\sh) - 8)
+ .elseif (\sh) >= 4
+ rols (\ra), (\ra), 4
+ ..rotlr (\ra), ((\sh) - 4)
+ .elseif (\sh) >= 1
+ rols (\ra), (\ra), 1
+ ..rotlr (\ra), ((\sh) - 1)
+
+ .endif
+
+ .endm
+
+
+ // RLDINM RA, RS, SH, MB, ME
+ //
+ // Defined as if there were an equivalent PowerPC instruction. The
+ // 'word' forms of the PowerPC instructions and extended mnemonics are
+ // undefined in order to catch programming typos.
+
+ .undefppc rlwinm, extrwi, rotlwi, rotrwi
+ .undefppc slwi, srwi
+
+ .macro rldinm, ra:req, rs:req, sh:req, mb:req, me:req
+
+ .if ((\sh) < 0) || ((\sh) > 63)
+ .error "SH must be in the range 0..63"
+ .endif
+ .if ((\mb) < 0) || ((\mb) > 63)
+ .error "MB must be in the range 0..63"
+ .endif
+ .if ((\me) < 0) || ((\me) > 63)
+ .error "ME must be in the range 0..63"
+ .endif
+
+ .if (((\mb) == 0) && ((\me) == 63) || ((\me) == ((\mb) - 1)))
+
+ // The mask is effectively 0..63, i.e., no mask. This is a
+ // simple rotate.
+
+ ..rotlrr (\ra), (\rs), (\sh)
+
+ .else
+
+ // We need a mask step. However if SH == 0 and RA == RS we can
+ // bypass the rotate step.
+
+ .if ((\sh) != 0) || ((\ra) != (\rs))
+ ..rotlrr (\ra), (\rs), (\sh)
+ .endif
+ .if ((\mb) <= (\me))
+
+ // This is a straightforward masking operation with a
+ // single mask.
+
+ andi (\ra), (\ra), ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - (\me))))
+ .else
+
+ // This is a wrapped mask.
+ // It is created as 2 masks OR-ed together - 0-ME and MB-63
+
+ andi (\ra), (\ra), (((0xffffffffffffffff >> 0) & (0xffffffffffffffff << (63 - (\me)))) | ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - 63))))
+ .endif
+
+ .endif
+
+ .endm
+
+ // RLDINM Extended Mnemonics
+ //
+ // Defined as if they were equivalent to PowerPC 32-bit extended
+ // mnemonics
+
+ .macro extldi, ra:req, rs:req, n:req, b:req
+ .if ((\n) < 0)
+ .error "EXTLDI requires N > 0"
+ .endif
+ rldinm (\ra), (\rs), (\b), 0, ((\n) - 1)
+ .endm
+
+ .macro extrdi, ra:req, rs:req, n:req, b:req
+ .if ((\n) < 0)
+ .error "EXTRDI requires N > 0"
+ .endif
+ rldinm (\ra), (\rs), (((\b) + (\n)) % 64), (64 - (\n)), 63
+ .endm
+
+ .macro rotldi, ra:req, rs:req, n:req
+ rldinm (\ra), (\rs), (\n), 0, 63
+ .endm
+
+
+ .macro rotrdi, ra:req, rs:req, n:req
+ rldinm (\ra), (\rs), (64 - (\n)), 0, 63
+ .endm
+
+
+ .macro sldi, ra:req, rs:req, n:req
+ rldinm (\ra), (\rs), (\n), 0, (63 - (\n))
+ .endm
+
+
+ .macro srdi, ra:req, rs:req, n:req
+ rldinm (\ra), (\rs), (64 - (\n)), (\n), 63
+ .endm
+
+
+ // RLDIMI RA, RS, SH, MB, ME
+ //
+ // Defined as if there were an equivalent PowerPC instruction. The
+ // 'word' forms of the PowerPC instructions and extended mnemonics are
+ // undefined in order to catch programming typos.
+ //
+ // Note that unlike the PowerPC instructions, here RLDIMI must destroy
+ // RS by masking and shifting it, and RA and RS may not be the same
+ // register.
+
+ .undefppc rlwimi, inslwi, insrwi
+
+ .macro rldimi, ra:req, rs:req, sh:req, mb:req, me:req
+
+ ..dxdy (\ra), (\rs)
+
+ // SH error checks are done by rldinm
+
+ .if (((\mb) == 0) && ((\me) == 63) || ((\me) == ((\mb) - 1)))
+
+ // The mask is effectively 0..63, i.e., no mask. This is a
+ // simple rotate of RS into RA
+
+ rotldi (\ra), (\rs), (\sh)
+
+ .else
+
+ // Rotate RS and AND with mask
+
+ rldinm (\rs), (\rs), (\sh), (\mb), (\me)
+
+ // Mask out the significant bits of RS, clear that section of
+ // RA, and logical OR RS into RA
+
+ .if ((\mb) <= (\me))
+
+ // This is a straightforward masking operation with a
+ // single mask.
+
+ andi (\ra), (\ra), \
+ (~((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - (\me)))))
+ .else
+
+ // This is a wrapped mask.
+ // It is created as 2 masks OR-ed together - 0-ME and MB-63
+
+ andi (\ra), (\ra), \
+ (~(((0xffffffffffffffff >> 0) & (0xffffffffffffffff << (63 - (\me)))) | \
+ ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - 63)))))
+ .endif
+
+ or (\ra), D0, D1
+
+ .endif
+
+ .endm
+
+ // RLDIMI Extended Mnemonics
+ //
+ // Defined as if they were equivalent to PowerPC 32-bit extended
+ // mnemonics
+
+ .macro insldi, ra:req, rs:req, n:req, b:req
+ .if ((\n) < 0)
+ .error "INSLDI requires N > 0"
+ .endif
+ rldimi (\ra), (\rs), (64 - (\b)), (\b), ((\b) + (\n) - 1)
+ .endm
+
+ .macro insrdi, ra:req, rs:req, n:req, b:req
+ .if ((\n) < 0)
+ .error "INSRDI requires N > 0"
+ .endif
+ rldimi (\ra), (\rs), (64 - (\b) - (\n)), (\b), ((\b) + (\n) - 1)
+ .endm
+
+
+ //////////////////////////////////////////////////////////////////////
+ // .HOOK
+ //////////////////////////////////////////////////////////////////////
+
+ // The PoreVe (PORE Virtual Environment) is a PORE simulation
+ // environment that allows the programmer to embed C/C++ code into the
+ // PORE assembler source code, and arranges for the C/C++ code to be
+ // executed in-line with the PORE assembly code. Instances of the
+ // .hook macro are inserted into the assembler input by the
+ // hook_extractor script, to mark the locations where hooks are
+ // present. The hook reference is a string that combines the source
+ // file name with an index number to uniquely identify the hook.
+ //
+ // .hook <file name>_<sequence number>
+ //
+ // The .hook macro marks the location of each hook in the relocatable
+ // binaries with special symbols. The symbol name includes the hook
+ // reference, which is used to locate the hook in the HookManager
+ // symbol table. Because hooks can be defined in macros, a hook that
+ // appears once in a source file may appear multiple times in the
+ // final binary. For this reason each hook must also be tagged with a
+ // unique index number to avoid symbol name collisions. The
+ // complexity of the .hook macro is due to the necessity to decode a
+ // dynamic symbol value (_PGAS_HOOK_INDEX) into its binary string form
+ // to create the unique symbol name. The final hook symbol has the
+ // form:
+ //
+ // __hook__<unique>_<reference>
+ //
+ // where <unique> is a binary string. It is then straightforward to
+ // locate these symbols in the 'nm' output of the final link and
+ // create a map of final addresses to the hook routine to call (the
+ // <reference>) before executing the instruction at that address.
+ //
+ // Note: The maximum nesting depth of the recursive ..hook_helper
+ // macro is log2(index), and the assembler supports nesting of at
+ // least 32 which is much more than sufficient.
+
+ .set _PGAS_HOOK_INDEX, 0
+
+ .macro .hook, reference:req
+ .set _PGAS_HOOK_INDEX, (_PGAS_HOOK_INDEX + 1)
+ ..hook_helper _PGAS_HOOK_INDEX, "", \reference
+ .endm
+
+ .macro ..hook_helper, index, unique, reference
+ .ifeq \index
+ __hook__\unique\()_\reference\():
+ .elseif (\index % 2)
+ ..hook_helper (\index / 2), 1\unique, \reference
+ .else
+ ..hook_helper (\index / 2), 0\unique, \reference
+ .endif
+ .endm
+
+
+////////////////////////////////////////////////////////////////////////////
+// Help for Conversion from Old to New PGAS syntax
+////////////////////////////////////////////////////////////////////////////
+
+ .macro loadp, arg:vararg
+ .error "PGAS now implements 'lpcs' rather then 'loadp'"
+ .endm
+
+ .macro loadx, arg:vararg
+ .error "PGAS now implements 'la' rather than 'loadx'"
+ .endm
+
+#endif // __ASSEMBLER__
+
+#ifdef PGAS_PPC
+#include "pgas_ppc.h"
+#endif
+
+#endif // __PGAS_H__
diff --git a/src/lib/pgas_ppc.h b/src/lib/pgas_ppc.h
new file mode 100755
index 0000000..6771e4b
--- /dev/null
+++ b/src/lib/pgas_ppc.h
@@ -0,0 +1,529 @@
+#ifndef __PGAS_PPC_H__
+#define __PGAS_PPC_H__
+
+// $Id: pgas_ppc.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pgas_ppc.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+// ** WARNING : This file is maintained as part of the OCC firmware. Do **
+// ** not edit this file in the PMX area, the hardware procedure area, **
+// ** or the PoreVe area as any changes will be lost. **
+
+/// \file pgas_ppc.h
+/// \brief Legacy PGAS assembler implemented as PowerPC assembler macros.
+///
+/// PGAS is documented in a seperate standalone document entitled <em> PGAS :
+/// PORE GAS (GNU Assembler) User's and Reference Manual </em>.
+///
+/// This file contains the legacy PGAS assembler, which was first implemented
+/// as this set of assembler macros for the PowerPC assembler. This file is
+/// included into pgas.h if the compile switch PGAS_PPC is defined in the
+/// compile environment.
+
+#ifdef __ASSEMBLER__
+
+////////////////////////////////////////////////////////////////////////////
+// PGAS Base Assembler
+////////////////////////////////////////////////////////////////////////////
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Symbolic Register Mnemonics
+ //////////////////////////////////////////////////////////////////////
+ //
+ // PGAS uses gas symbols for register mnemonics so that they will
+ // appear as-is in assembler listings, but we can still do arithmetic
+ // on the mnemonics in the PGAS macros.
+
+ .set P0, PORE_REGISTER_PRV_BASE_ADDR0
+ .set P1, PORE_REGISTER_PRV_BASE_ADDR1
+ .set A0, PORE_REGISTER_OCI_BASE_ADDR0
+ .set A1, PORE_REGISTER_OCI_BASE_ADDR1
+ .set CTR, PORE_REGISTER_SCRATCH0
+ .set D0, PORE_REGISTER_SCRATCH1
+ .set D1, PORE_REGISTER_SCRATCH2
+ .set EMR, PORE_REGISTER_ERROR_MASK
+ .set ETR, PORE_REGISTER_EXE_TRIGGER
+ .set SPRG0, PORE_REGISTER_DATA0
+ .set PC, PORE_REGISTER_PC
+ .set IFR, PORE_REGISTER_IBUF_ID
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Core Instruction Set
+ //////////////////////////////////////////////////////////////////////
+
+ // The final construction of an instruction word. The opcode is a
+ // 7-bit value and the operand is always a 24-bit value. Note that the
+ // parity bit is always 0.
+
+ .macro ..instruction, opcode, operand
+ .long (\opcode << 25) | (\operand)
+ .endm
+
+ // NOP, TRAP, RET
+
+ .macro nop
+ ..instruction PGAS_OPCODE_NOP, 0
+ .endm
+
+ .macro trap
+ ..instruction PGAS_OPCODE_TRAP, 0
+ .endm
+
+ .macro ret
+ ..instruction PGAS_OPCODE_RET, 0
+ .endm
+
+ // WAITS, HALT, HOOKI
+
+ .macro waits, u24:req
+ ..check_u24 (\u24)
+ .if ((\u24) == 0)
+ .error "PGAS does not allow WAITS 0; Use HALT if the intention is to halt"
+ .endif
+ ..instruction PGAS_OPCODE_WAITS, (\u24)
+ .endm
+
+ .macro halt
+ ..instruction PGAS_OPCODE_WAITS, 0
+ .endm
+
+ .macro hooki, u24:req, imm:req
+ ..check_u24 (\u24)
+ ..instruction PGAS_OPCODE_HOOKI, (\u24)
+ .quad (\imm)
+ .endm
+
+ .macro wait, args:vararg
+ .error "PGAS implements the 'waits' mnemonic instead of PORE 'wait'"
+ .endm
+
+ .macro hook, args:vararg
+ .error "PGAS implements the 'hooki' mnemonic instead of PORE 'hook'"
+ .endm
+
+ // BRA, LOOP
+ //
+ // Note that all branch offsets in PORE are WORD offsets, so the byte
+ // offsets computed by the underlying assembler need to be divided by
+ // 4. Unfortunately PGAS is not able to check whether the offsets fit
+ // in the allowed space.
+
+ .macro ..bra, opcode, target
+ ..instruction \opcode, ((((\target) - $) / 4) & 0xffffff)
+ .endm
+
+ .macro bra, target:req
+ ..bra PGAS_OPCODE_BRA, (\target)
+ .endm
+
+ .macro loop, target:req
+ ..bra PGAS_OPCODE_LOOP, (\target)
+ .endm
+
+ // BRAZ, BRANZ
+
+ .macro ..brac, opcode, src, target
+ ..branch_compare_data (\src)
+ ..instruction \opcode, ((\src << 20) | ((((\target) - $) / 4) & 0xfffff))
+ .endm
+
+ .macro braz, src:req, target:req
+ ..brac PGAS_OPCODE_BRAZ, (\src), (\target)
+ .endm
+
+ .macro branz, src:req, target:req
+ ..brac PGAS_OPCODE_BRANZ, (\src), (\target)
+ .endm
+
+ // CMPIBRAEQ, CMPIBRANE
+
+ .macro ..cmpibra, opcode, src, target, imm
+ ..d0 (\src)
+ ..instruction \opcode, ((((\target) - $) / 4) & 0xffffff)
+ .quad (\imm)
+ .endm
+
+ .macro cmpibraeq, src:req, target:req, imm:req
+ ..cmpibra PGAS_OPCODE_CMPIBRAEQ, (\src), (\target), (\imm)
+ .endm
+
+ .macro cmpibrane, src:req, target:req, imm:req
+ ..cmpibra PGAS_OPCODE_CMPIBRANE, (\src), (\target), (\imm)
+ .endm
+
+ .macro cmpbra, args:vararg
+ .error "PGAS implements the 'cmpibraeq' mnemonic instead of PORE 'cmpbra'"
+ .endm
+
+ .macro cmpnbra, args:vararg
+ .error "PGAS implements the 'cmpibrane' mnemonic instead of PORE 'cmpnbra'"
+ .endm
+
+ // BRAD, BSRD
+
+ .macro ..brad, opcode, src
+ ..data (\src)
+ ..instruction \opcode, ((\src) << 20)
+ .endm
+
+ .macro brad, src:req
+ ..brad PGAS_OPCODE_BRAD, (\src)
+ .endm
+
+ .macro bsrd, src:req
+ ..brad PGAS_OPCODE_BSRD, (\src)
+ .endm
+
+ // ANDI, ORI, XORI
+
+ .macro ..ilogic, opcode, dest, src, imm
+ ..data (\dest)
+ ..data (\src)
+ ..instruction \opcode, (((\dest) << 20) | ((\src) << 16))
+ .quad \imm
+ .endm
+
+ .macro andi, dest:req, src:req, imm:req
+ ..ilogic PGAS_OPCODE_ANDI, (\dest), (\src), (\imm)
+ .endm
+
+ .macro ori, dest:req, src:req, imm:req
+ ..ilogic PGAS_OPCODE_ORI, (\dest), (\src), (\imm)
+ .endm
+
+ .macro xori, dest:req, src:req, imm:req
+ ..ilogic PGAS_OPCODE_XORI, (\dest), (\src), (\imm)
+ .endm
+
+ // AND, OR, XOR, ADD, SUB
+
+ .macro ..alurr, opcode, dest, src1, src2
+ ..data (\dest)
+ ..d0d1 (\src1), (\src2)
+ ..instruction \opcode, ((\dest) << 20)
+ .endm
+
+ .macro and, dest:req, src1:req, src2:req
+ ..alurr PGAS_OPCODE_AND, (\dest), (\src1), (\src2)
+ .endm
+
+ .macro or, dest:req, src1:req, src2:req
+ ..alurr PGAS_OPCODE_OR, (\dest), (\src1), (\src2)
+ .endm
+
+ .macro xor, dest:req, src1:req, src2:req
+ ..alurr PGAS_OPCODE_XOR, (\dest), (\src1), (\src2)
+ .endm
+
+ .macro add, dest:req, src1:req, src2:req
+ ..alurr PGAS_OPCODE_ADD, (\dest), (\src1), (\src2)
+ .endm
+
+ .macro sub, dest:req, src1:req, src2:req
+ ..alurr PGAS_OPCODE_SUB, (\dest), (\src1), (\src2)
+ .endm
+
+ // ADDS, SUBS
+
+ .macro ..inc, opcode, dest, src, short
+ ..check_s16 (\short)
+ ..ls_destination (\dest)
+ ..same (\dest), (\src)
+ ..instruction (\opcode), (((\dest) << 20) | ((\short) & 0xffff))
+ .endm
+
+ .macro adds, dest:req, src:req, short:req
+ ..inc PGAS_OPCODE_ADDS, (\dest), (\src), (\short)
+ .endm
+
+ .macro subs, dest:req, src:req, short:req
+ ..inc PGAS_OPCODE_SUBS, (\dest), (\src), (\short)
+ .endm
+
+ .macro addi, args:vararg
+ .error "PGAS implements the 'adds' mnemonic instead of PORE 'addi'"
+ .endm
+
+ .macro subi, args:vararg
+ .error "PGAS implements the 'subs' mnemonic instead of PORE 'subi'"
+ .endm
+
+ // NEG
+
+ .macro neg, dest:req, src:req
+ ..data (\dest)
+ ..data (\src)
+ ..instruction PGAS_OPCODE_NEG, (((\dest) << 20) | ((\src) << 16))
+ .endm
+
+ // MR
+
+ .macro mr, dest:req, src:req
+ ..mr_destination (\dest)
+ ..mr_source (\src)
+ ..instruction PGAS_OPCODE_MR, (((\dest) << 20) | ((\src) << 16))
+ .endm
+
+ .macro copy, args:vararg
+ .error "PGAS implents the 'mr' mnemonic instead of PORE 'copy'"
+ .endm
+
+ // ROLS
+
+ .macro rols, dest:req, src:req, short:req
+ ..data (\dest)
+ ..data (\src)
+ .if ((\short) != 1)
+ .if ((\short) != 4)
+ .if ((\short) != 8)
+ .if ((\short) != 16)
+ .if ((\short) != 32)
+ .error "The legal ROLS shift amounts are 1, 4, 8, 16 and 32"
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ ..instruction PGAS_OPCODE_ROLS, (((\dest) << 20) | ((\src) << 16) | (\short))
+ .endm
+
+ .macro rol, args:vararg
+ .error "PGAS implements the 'rols' mnemonic instead of PORE 'rol'"
+ .endm
+
+ // LS
+
+ .macro ls, dest:req, short:req
+ ..ls_destination (\dest)
+ ..check_s20 (\short)
+ ..instruction PGAS_OPCODE_LS, (((\dest) << 20) | ((\short) & 0xfffff))
+ .endm
+
+ .macro load20, args:vararg
+ .error "PGAS implements the 'ls' mnemonic instead of PORE 'load20'"
+ .endm
+
+ // LI, LIA
+
+ .macro ..li, dest:req
+ ..li_destination (\dest)
+ ..instruction PGAS_OPCODE_LI, ((\dest) << 20)
+ .endm
+
+ .macro li, dest:req, imm:req
+ ..li (\dest)
+ .quad (\imm)
+ .endm
+
+ .macro lia, dest:req, space:req, offset:req
+ ..lia_destination (\dest)
+ ..li (\dest)
+ .quadia (\space), (\offset)
+ .endm
+
+ .macro load64, args:vararg
+ .error "PGAS implements the 'li' mnemonic instead of PORE 'load64'"
+ .endm
+
+ // LD, LDANDI, STD, STI, STIA, BSI, BCI
+ //
+ // For LD, LDANDI, and STD, PGAS does not expose the underlying
+ // register-specific opcodes but only provides the general form.
+ //
+ // The base register is used to determine if this is a load/store from
+ // the pervasive or memory address spaces. For memory space accesses
+ // the offset is a 22-bit unsigned value, and the final ima24 is
+ //
+ // 1<reg 0/1><offset>
+ //
+ // PGAS will not assemble relocatable offsets, and checks that offsets
+ // fit in 24 bits.
+ //
+ // For pervasive accesses, it is assumed that the offset provided is a
+ // 32-bit SCOM address. Here the final ima24 is
+ //
+ // 0<reg 0/1>00<port><local_address>
+ //
+ // PGAS checks that the 32-bit SCOM address looks like a SCOM address
+ // in that SCOM adresses are required to have bits 0 and 8:11 == 0.
+ //
+ // Note that memory and pervasive base registers use a 0/1 encoding
+ // here, not the 4-bit encoding used elsewhere in the ISA. The bit
+ // appearing in the instruction is the low-order bit of the register
+ // encoding.
+
+ .macro ..pervasive_ima24, opcode, offset, base
+ ..check_scom (\offset)
+ ..instruction (\opcode), ((((\base) % 2) << 22) | ((\offset) & 0x3fffff))
+ .endm
+
+ .macro ..memory_ima24, opcode, offset, base
+ ..check_u24 (\offset)
+ .if ((\offset) % 8)
+ .error "The memory space offset is not a multiple of 8 - assumed alignment error"
+ .endif
+ ..instruction (\opcode), (0x800000 | (((\base) % 2) << 22) | ((\offset) & 0x3fffff))
+ .endm
+
+ .macro ..ima24, opcode, offset, base
+ .if ((\base == P0) || ((\base == P1)))
+ ..pervasive_ima24 (\opcode), (\offset), (\base)
+ .elseif ((\base == A0) || ((\base == A1)))
+ ..memory_ima24 (\opcode), (\offset), (\base)
+ .else
+ .error "Expecting either a 'Pervasive Chiplet ID' or an 'Address' register"
+ .endif
+ .endm
+
+ .macro ..ima24_select, opcode0, opcode1, dest, offset, base
+ ..data (\dest)
+ .if ((\dest) == D0)
+ ..ima24 (\opcode0), (\offset), (\base)
+ .else
+ ..ima24 (\opcode1), (\offset), (\base)
+ .endif
+ .endm
+
+ .macro ld, dest:req, offset:req, base:req
+ ..ima24_select PGAS_OPCODE_LD0, PGAS_OPCODE_LD1, (\dest), (\offset), (\base)
+ .endm
+
+ .macro ldandi, dest:req, offset:req, base:req, imm:req
+ ..ima24_select PGAS_OPCODE_LD0ANDI, PGAS_OPCODE_LD1ANDI, (\dest), (\offset), (\base)
+ .quad (\imm)
+ .endm
+
+ .macro std, dest:req, offset:req, base:req
+ ..ima24_select PGAS_OPCODE_STD0, PGAS_OPCODE_STD1, (\dest), (\offset), (\base)
+ .endm
+
+ .macro sti, offset:req, base:req, imm:req
+ ..ima24 PGAS_OPCODE_STI, (\offset), (\base)
+ .quad (\imm)
+ .endm
+
+ .macro stia, offset:req, base:req, space:req, addr:req
+ ..ima24 PGAS_OPCODE_STI, (\offset), (\base)
+ .quadia (\space), (\addr)
+ .endm
+
+ .macro ..bsi, opcode, dest, offset, base, imm
+ ..d0 (\dest)
+ ..ima24 (\opcode), (\offset), (\base)
+ .quad (\imm)
+ .endm
+
+#ifdef IGNORE_HW274735
+
+ // BSI and BCI are normally redacted due to HW274735. See also pgas.h
+
+ .macro bsi, dest:req, offset:req, base:req, imm:req
+ ..bsi PGAS_OPCODE_BSI, (\dest), (\offset), (\base), (\imm)
+ .endm
+
+ .macro bci, dest:req, offset:req, base:req, imm:req
+ ..bsi PGAS_OPCODE_BCI, (\dest), (\offset), (\base), (\imm)
+ .endm
+
+#endif // IGNORE_HW274735
+
+ .macro scr1rd, args:vararg
+ .error "PGAS implements the 'ld' mnemonic instead of the PORE 'scr1rd'"
+ .endm
+
+ .macro scr2rd, args:vararg
+ .error "PGAS implements the 'ld' mnemonic instead of the PORE 'scr2rd'"
+ .endm
+
+ .macro scr1rda, args:vararg
+ .error "PGAS implements the 'ldandi' mnemonic instead of the PORE 'scr1rda'"
+ .endm
+
+ .macro scr2rda, args:vararg
+ .error "PGAS implements the 'ldandi' mnemonic instead of the PORE 'scr2rda'"
+ .endm
+
+ .macro scr1wr, args:vararg
+ .error "PGAS implements the 'std' mnemonic instead of the PORE 'scr1wr'"
+ .endm
+
+ .macro scr2wr, args:vararg
+ .error "PGAS implements the 'std' mnemonic instead of the PORE 'scr2wr'"
+ .endm
+
+ .macro wri, args:vararg
+ .error "PGAS implements the 'sti' mnemonic instead of the PORE 'wri'"
+ .endm
+
+ .macro bs, args:vararg
+ .error "PGAS implements the 'bsi' mnemonic instead of the PORE 'bs'"
+ .endm
+
+ .macro bc, args:vararg
+ .error "PGAS implements the 'bci' mnemonic instead of the PORE 'bc'"
+ .endm
+
+ // SCAND
+ //
+ // The 24-bit operand here is
+ //
+ // <update><capture>000000<length>
+
+ .macro scand, update:req, capture:req, length:req, select:req, offset:req
+ .if (((\update) != 0) && ((\update) != 1))
+ .error "SCAND requires a binary value for 'update'"
+ .endif
+ .if (((\capture) != 0) && ((\capture) != 1))
+ .error "SCAND requires a binary value for 'capture'"
+ .endif
+ ..check_u16 (\length)
+ ..instruction PGAS_OPCODE_SCAND, ((\update << 23) | (\capture << 22) | (\length))
+ .long (\select)
+ .long (\offset)
+ .endm
+
+ // BRAIA, BSR, CMPIBSREQ
+ //
+ // In order to support separate compilation in PGAS programs being
+ // linked with the PowerPC linker it is necessary to implement BSR and
+ // CMPIBSREQ in terms of BRAIA. These instructions require that the
+ // default address space have been defined. The BSR instructions
+ // first take a short local subroutine branch to create a stack frame,
+ // then use BRAIA to branch to the (relocatable) target address. The
+ // return from the subroutine then branches around the BRAIA to
+ // complete the sequence.
+
+ .macro braia, space:req, offset:req
+ ..instruction PGAS_OPCODE_BRAI, 0
+ .quadia (\space), (\offset)
+ .endm
+
+ .macro ..bsr, target
+ ..bra PGAS_OPCODE_BSR, (\target)
+ .endm
+
+ .macro bsr, target:req
+ ..check_default_space
+ ..bsr (. + 8)
+ bra (. + 16)
+ braia _PGAS_DEFAULT_SPACE, (\target)
+ .endm
+
+ .macro cmpibsreq, src:req, target:req, imm:req
+ ..d0 (\src)
+ ..check_default_space
+ cmpibrane (\src), (. + 32), (\imm)
+ ..bsr (. + 8)
+ bra (. + 16)
+ braia _PGAS_DEFAULT_SPACE, (\target)
+ .endm
+
+#endif // __ASSEMBLER__
+
+#endif // __PGAS_PPC_H__
diff --git a/src/lib/pgp_config.h b/src/lib/pgp_config.h
new file mode 100755
index 0000000..f4041d4
--- /dev/null
+++ b/src/lib/pgp_config.h
@@ -0,0 +1,108 @@
+#ifndef __PGP_CONFIG_H__
+#define __PGP_CONFIG_H__
+
+// $Id: pgp_config.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pgp_config.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pgp_config.h
+/// \brief Chip configuration data structures for PgP OCC procedures
+
+#ifndef __ASSEMBLER__
+
+#include <stdint.h>
+
+/// A bitmask defining a chip configuration
+///
+/// Since we are using the conventional big-endian notation, any use of these
+/// bitmasks requires that the data being tested is of this type - otherwise
+/// the masks won't work.
+///
+/// Layout:
+///
+/// Bits 0:15 - Core chiplet 0..15 is configured
+/// Bits 16:23 - MCS 0..7 is configured
+/// Bits 24:31 - Centaur 0..7 is configured
+
+typedef uint64_t ChipConfig;
+typedef uint16_t ChipConfigCores;
+typedef uint8_t ChipConfigMcs;
+typedef uint8_t ChipConfigCentaur;
+
+
+/// Convert a ChipConfig into a mask suitable for use as the 32-bit chiplet
+/// mask argument of a PORE wakeup program.
+
+static inline uint32_t
+pore_exe_mask(ChipConfig config)
+{
+ return (uint32_t)((config >> 32) & 0xffff0000);
+}
+
+/// Left justify and mask core chiplet configuration into a uint32_t
+
+static inline uint32_t
+left_justify_core_config(ChipConfig config)
+{
+ return (uint32_t)((config >> 32) & 0xffff0000);
+}
+
+/// Left justify and mask MCS configuration into a uint32_t
+
+static inline uint32_t
+left_justify_mcs_config(ChipConfig config)
+{
+ return (uint32_t)((config >> 16) & 0xff000000);
+}
+
+/// Left justify and mask Centaur configuration into a uint32_t
+
+static inline uint32_t
+left_justify_centaur_config(ChipConfig config)
+{
+ return (uint32_t)((config >> 8) & 0xff000000);
+}
+
+#endif // __ASSEMBLER__
+
+
+#define CHIP_CONFIG_CORE_BASE 0
+#define CHIP_CONFIG_CORE(n) \
+ ((0x8000000000000000ull >> CHIP_CONFIG_CORE_BASE) >> (n))
+
+#define CHIP_CONFIG_MCS_BASE 16
+#define CHIP_CONFIG_MCS(n) \
+ ((0x8000000000000000ull >> CHIP_CONFIG_MCS_BASE) >> (n))
+
+#define CHIP_CONFIG_CENTAUR_BASE 24
+#define CHIP_CONFIG_CENTAUR(n) \
+ ((0x8000000000000000ull >> CHIP_CONFIG_CENTAUR_BASE) >> (n))
+
+
+// PGAS macros to left justify configuration groups, allowing each member to
+// be tested in a loop that rotates the data (d) register left on each loop,
+// assuming standard big-endian bit assignments. The macros mask off all other
+// configuration bits so the destination register can also be tested for
+// 0/non-0 to determine if any of a configuration class are selected.
+
+#ifdef __PGAS__
+
+ .macro left_justify_core_config, d
+ extldi (\d), (\d), PGP_NCORES, CHIP_CONFIG_CORE_BASE
+ .endm
+
+ .macro left_justify_mcs_config, d
+ extldi (\d), (\d), PGP_NMCS, CHIP_CONFIG_MCS_BASE
+ .endm
+
+ .macro left_justify_centaur_config, d
+ extldi (\d), (\d), PGP_NCENTAUR, CHIP_CONFIG_CENTAUR_BASE
+ .endm
+
+#endif /* __PGAS__ */
+
+#endif /* __PGP_CONFIG_H__ */
diff --git a/src/lib/pmc_dcm.c b/src/lib/pmc_dcm.c
new file mode 100755
index 0000000..a0f7d82
--- /dev/null
+++ b/src/lib/pmc_dcm.c
@@ -0,0 +1,425 @@
+// $Id: pmc_dcm.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pmc_dcm.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pmc_dcm.c
+/// \brief Genertic PMC Interhchip Communication Mechanism
+
+#include "ssx.h"
+#include "pmc_dcm.h"
+
+////////////////////////////////////////////////////////////////////////////
+// Low-level PMC-DCM Interfaces
+////////////////////////////////////////////////////////////////////////////
+
+/// Non-blocking transmission of a packet over the PMC-DCM interface
+///
+/// Note: Locking/synchronization of the PMC-DCM interface is the
+/// responsibility of the application.
+///
+/// \param hwPacket A PmcDcmPacket structure to be sent via PMC interchip link
+/// This argument is provided by the caller and passed in as reference.
+///
+/// \code
+/// hwPacket:
+///
+/// cmd_code | cmd_ext | payload 0 | payload 1 | ECC
+/// [0:3] | [4:7] | [8:15] | [16:23] | [24:31]
+///
+/// cmd_code:
+/// PMC_IC_GPA_CC | 1 | 0b0001 | Global PState Actual | Master to Slave
+/// PMC_IC_GPA_ACK_CC | 2 | 0b0010 | Global PState Ack | Slave to Master
+/// PMC_IC_GAR_CC | 3 | 0b0011 | Global Actual Request | Slave to Master
+/// PMC_IC_PING_CC | 4 | 0b0100 | Ping | Master to Slave
+/// PMC_IC_PING_ACK_CC | 6 | 0b0110 | Ping Acknowledge | Slave to Master
+/// PMC_IC_MSG_CC | 8 | 0b1000 | Message | Bidirectional
+/// PMC_IC_MSG_NACK_CC | 10 | 0b1010 | Message NACK | Bidirectional
+/// PMC_IC_MSG_ACK_CC | 11 | 0b1011 | Message ACK | Bidirectional
+/// PMC_IC_ERROR_CC | 12 | 0b1111 | Error | Slave to Master
+/// \endcode
+///
+/// This API sends command to another chip through the PMC interchip wire
+/// in DCM setup. The message will be sent out by writing the packet value
+/// to the regisiter: PMC_INTCHP_MSG_WDATA
+///
+/// It also checks the interchip_ga_ongoing and interchip_msg_send_ongoing
+/// bits of PMC_INTCHP_STATUS_REG to detect if the channel is free
+/// If the channel is busy, the function will exit with returning
+/// an error code \a PMC_DCM_OUTSTANDING_TRANSFER
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// Also, the hardware must be in DCM setup
+///
+/// Note: This API can be used to send any valid command over the link
+/// however, both command code and corresponding direction of transfer
+/// will be checked to ensure correctness of the protocol. Upon
+/// attempt to send an invalid command or an unexpected direction
+/// of transfering certain command will cause this API to abort
+/// which indicates a HW/FW bug
+///
+/// \retval PMC_DCM_SUCCESS
+///
+/// \retval PMC_DCM_ARG_NULL_OBJECT_SEND
+///
+/// \retval PMC_DCM_INTCHP_DISABLED_SEND
+///
+/// \retval PMC_DCM_OUTSTANDING_TRANSFER
+///
+/// \retval PMC_DCM_INVALID_COMMAND_CODE
+///
+
+int
+pmc_dcm_send(PmcDcmPacket* hwPacket)
+{
+ int rc = PMC_DCM_SUCCESS;
+
+ do {
+
+ // check if reference packet is null
+ SSX_ERROR_IF_CHECK_API(
+ (hwPacket == 0),
+ PMC_DCM_ARG_NULL_OBJECT_SEND);
+
+ // check if interchip transfer is enabled on this chip
+ SSX_ERROR_IF_CHECK_API(
+ (pmc_dcm_if_interchip_interface_enabled() == 0),
+ PMC_DCM_INTCHP_DISABLED_SEND);
+
+ //check if command code is valid and direction of transfer is valid
+ rc = pmc_dcm_check_ic_command((int)hwPacket->fields.cmd_code);
+ if( rc ) break;
+
+ // check if the interchip channel is busy
+ if( pmc_dcm_if_channel_busy() ) {
+ rc = PMC_DCM_OUTSTANDING_TRANSFER;
+ break;
+ }
+
+ // send out the command
+ _pmc_dcm_send(&hwPacket->value);
+
+ } while (0);
+
+ return rc;
+}
+
+/// Non-blocking reception of a packet from the PMC-DCM interface
+///
+/// Note: Locking/synchronization of the PMC-DCM interface is the
+/// responsibility of the application.
+///
+/// \param hwPacket A PmcDcmPacket structure passed by the caller
+/// as reference to receive the message sent from PMC interchip link
+///
+/// This API receives the message from the PMC interchip wire
+/// by reading the register: PMC_INTCHP_MSG_RDATA
+///
+/// It checks the interchip_msg_recv_detected bit of
+/// PMC_INTCHP_STATUS_REG to know if there is a new message
+/// If no new message is detected, the receive function will
+/// exit with returning an error code \a PMC_DCM_RECEIVE_NOT_DETECTRD
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// Also, the hardware must be in DCM setup
+///
+/// Note: only MSG type message or cmd code should be received
+/// otherwise function aborts and indicates a hardware bug
+///
+/// \retval PMC_DCM_SUCCESS
+///
+/// \retval PMC_DCM_ARG_NULL_OBJECT_RECV
+///
+/// \retval PMC_DCM_INTCHP_DISABLED_RECV
+///
+/// \retval PMC_DCM_RECEIVE_NOT_DETECTRD
+///
+/// \retval PMC_DCM_RECEIVE_NOT_MSG_TYPE
+///
+
+int
+pmc_dcm_receive(PmcDcmPacket* hwPacket)
+{
+ int rc = PMC_DCM_SUCCESS;
+
+ do {
+
+ //check if reference packet is null
+ SSX_ERROR_IF_CHECK_API(
+ (hwPacket == 0),
+ PMC_DCM_ARG_NULL_OBJECT_RECV);
+
+ // check if interchip transfer is enabled on this chip
+ SSX_ERROR_IF_CHECK_API(
+ (!pmc_dcm_if_interchip_interface_enabled()),
+ PMC_DCM_INTCHP_DISABLED_RECV);
+
+ // check if there is a new incoming message
+ if( !pmc_dcm_if_new_message() ) {
+ rc = PMC_DCM_RECEIVE_NOT_DETECTED;
+ break;
+ }
+
+ // receive the new message
+ _pmc_dcm_receive(&hwPacket->value);
+
+ // check if the command is MSG type
+ SSX_ERROR_IF_CHECK_API(
+ (hwPacket->fields.cmd_code != PMC_IC_MSG_CC),
+ PMC_DCM_RECEIVE_NOT_MSG_TYPE);
+
+ } while (0);
+
+ return rc;
+}
+
+
+
+/// Internal API : Send data without error checking
+///
+/// \param value 32 bits data to be sent
+///
+/// This API send the interchip data by writing register:
+/// PMC_INTCHP_MSG_WDATA
+///
+/// \retval NONE
+///
+
+void
+_pmc_dcm_send(uint32_t *value)
+{
+ out32(PMC_INTCHP_MSG_WDATA, *value);
+}
+
+
+/// Internal API : Receive data without error checking
+///
+/// \param value 32 bits data to be received
+///
+/// This API receive the interchip data by reading register:
+/// PMC_INTCHP_MSG_RDATA
+///
+/// \retval NONE
+///
+
+void
+_pmc_dcm_receive(uint32_t *value)
+{
+ *value = in32(PMC_INTCHP_MSG_RDATA);
+}
+
+/// This API tells if the given command is a valid pmc interchip command
+/// and if the command is given by the designated source
+///
+/// \param cmd_code the command code
+///
+/// \param rc the return code back to caller
+///
+/// \code
+/// cmd_code:
+///
+/// PMC_IC_GPA_CC | 1 | 0b0001 | Global PState Actual | Master to Slave
+/// PMC_IC_GPA_ACK_CC | 2 | 0b0010 | Global PState Ack | Slave to Master
+/// PMC_IC_GAR_CC | 3 | 0b0011 | Global Actual Request | Slave to Master
+/// PMC_IC_PING_CC | 4 | 0b0100 | Ping | Master to Slave
+/// PMC_IC_PING_ACK_CC | 6 | 0b0110 | Ping Acknowledge | Slave to Master
+/// PMC_IC_MSG_CC | 8 | 0b1000 | Message | Bidirectional
+/// PMC_IC_MSG_NACK_CC | 10 | 0b1010 | Message NACK | Bidirectional
+/// PMC_IC_MSG_ACK_CC | 11 | 0b1011 | Message ACK | Bidirectional
+/// PMC_IC_ERROR_CC | 12 | 0b1111 | Error | Slave to Master
+/// \endcode
+///
+/// \retval PMC_DCM_INTCHP_CMD_ONLY_MTOS
+///
+/// \retval PMC_DCM_INTCHP_CMD_ONLY_STOM
+///
+/// \retval PMC_DCM_INVALID_COMMAND_CODE
+///
+
+int
+pmc_dcm_check_ic_command(int cmd_code)
+{
+ //note:certain command can only be transferred from master to slave
+ // or from slave to master or bidirectional.
+ if( cmd_code == PMC_IC_GPA_CC ||
+ cmd_code == PMC_IC_PING_CC ) {
+ //those commands can only be sent from master to slave
+ SSX_ERROR_IF_CHECK_API(
+ (!pmc_dcm_if_dcm_master()),
+ PMC_DCM_INTCHP_CMD_ONLY_MTOS);
+ } else if( cmd_code == PMC_IC_GPA_ACK_CC ||
+ cmd_code == PMC_IC_GAR_CC ||
+ cmd_code == PMC_IC_PING_ACK_CC ||
+ cmd_code == PMC_IC_ERROR_CC ) {
+ //those commands can only be sent from slave to master
+ SSX_ERROR_IF_CHECK_API(
+ (pmc_dcm_if_dcm_master()),
+ PMC_DCM_INTCHP_CMD_ONLY_STOM);
+ } else if( !(cmd_code == PMC_IC_MSG_CC ||
+ cmd_code == PMC_IC_MSG_NACK_CC ||
+ cmd_code == PMC_IC_MSG_ACK_CC) ) {
+ //those commands are bidirectional
+ //none of above, invalid command
+ SSX_ERROR_IF_CHECK_API(
+ 0,
+ PMC_DCM_INVALID_COMMAND_CODE);
+ }
+ return PMC_DCM_SUCCESS;
+}
+
+
+
+/// This API tells if the current chip is the DCM master or slave
+///
+/// \param NONE
+///
+/// The DCM master/slave is configured as the interchip_mode bit in register:
+/// PMC_MODE_REG
+///
+/// \retval 1 Master
+///
+/// \retval 0 Slave
+///
+
+int
+pmc_dcm_if_dcm_master()
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ return pmc_mode_reg.fields.enable_interchip_interface &&
+ pmc_mode_reg.fields.interchip_mode;
+}
+
+/// This API sets the current chip to be the DCM master or slave
+///
+/// \param master if 1 then set to master otherwise slave
+///
+/// The DCM master/slave is configured as the interchip_mode bit in register:
+/// PMC_MODE_REG
+///
+/// \retval NONE
+///
+
+void
+pmc_dcm_set_interchip_mode(int master)
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ pmc_mode_reg.fields.enable_interchip_interface = 1;
+ if( master == 0 )
+ pmc_mode_reg.fields.interchip_mode = 0;
+ else
+ pmc_mode_reg.fields.interchip_mode = 1;
+ out32(PMC_MODE_REG, pmc_mode_reg.value);
+}
+
+/// This API tells if the current chip is enabled with interchip interface
+///
+/// \param NONE
+///
+/// The DCM master/slave is configured as the enable_interchip_interface bit
+/// in register: PMC_MODE_REG
+///
+/// Note: set this bit is required for any interchip communication
+///
+/// \retval 1 Enabled
+///
+/// \retval 0 Disabled
+///
+
+int
+pmc_dcm_if_interchip_interface_enabled()
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ return pmc_mode_reg.fields.enable_interchip_interface;
+}
+
+/// This API sets the current chip to be enabled with interchip interface
+///
+/// \param enable if 1 then interface enabled otherwise disabled
+///
+/// The DCM master/slave is configured as the enable_interchip_interface bit
+/// in register: PMC_MODE_REG
+///
+/// \retval NONE
+///
+
+void
+pmc_dcm_set_interchip_interface(int enable)
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ if( enable == 0 )
+ pmc_mode_reg.fields.enable_interchip_interface = 0;
+ else
+ pmc_mode_reg.fields.enable_interchip_interface = 1;
+ out32(PMC_MODE_REG, pmc_mode_reg.value);
+}
+
+
+/// This API tells if the interchip channel is busy for outgoing communication
+///
+/// \param NONE
+///
+/// depends on bits: interchip_ga_ongoing and interchip_msg_send_ongoing
+/// in register: PMC_INTCHP_STATUS_REG
+///
+/// \retval 1 Busy
+///
+/// \retval 0 Free
+///
+
+int
+pmc_dcm_if_channel_busy()
+{
+ pmc_intchp_status_reg_t pmc_intchp_status_reg;
+ pmc_intchp_status_reg.value = in32(PMC_INTCHP_STATUS_REG);
+ return pmc_intchp_status_reg.fields.interchip_msg_send_ongoing;
+ //return (pmc_intchp_status_reg.fields.interchip_ga_ongoing ||
+ // pmc_intchp_status_reg.fields.interchip_msg_send_ongoing);
+}
+
+/// This API tells if there is a new message arrived from the interchip wire
+///
+/// \param NONE
+///
+/// if bit interchip_msg_recv_detected in register: PMC_INTCHP_STATUS_REG
+/// is set then new message otherwise none
+///
+/// \retval 1 New Message
+///
+/// \retval 0 NO New Message
+///
+
+int
+pmc_dcm_if_new_message()
+{
+ pmc_intchp_status_reg_t pmc_intchp_status_reg;
+ pmc_intchp_status_reg.value = in32(PMC_INTCHP_STATUS_REG);
+ return pmc_intchp_status_reg.fields.interchip_msg_recv_detected;
+}
+
+/// This API initializes the DCM setup
+///
+/// \param master_or_slave configure the current chip to be master or slave
+///
+/// for current chip
+/// 1) enable interchip interface
+/// 2) configure to be DCM master or slave
+///
+/// Note: one chip has to be master and one chip has to be slave
+///
+/// \retval NONE
+///
+
+void
+pmc_dcm_init(int master_or_slave)
+{
+ pmc_dcm_set_interchip_mode(master_or_slave);
+}
diff --git a/src/lib/pmc_dcm.h b/src/lib/pmc_dcm.h
new file mode 100755
index 0000000..3141aec
--- /dev/null
+++ b/src/lib/pmc_dcm.h
@@ -0,0 +1,102 @@
+#ifndef __PMC_DCM_H__
+#define __PMC_DCM_H__
+
+// $Id: pmc_dcm.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pmc_dcm.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pmc_dcm.h
+/// \brief Generic PMC Interhchip Communication Mechanism
+
+
+/// PMC-DCM Return Code
+#define PMC_DCM_SUCCESS 0
+#define PMC_DCM_ARG_NULL_OBJECT_SEND 0x00326501 //ssx panic
+#define PMC_DCM_ARG_NULL_OBJECT_RECV 0x00326501 //ssx panic
+#define PMC_DCM_INTCHP_DISABLED_SEND 0x00326502 //ssx panic
+#define PMC_DCM_INTCHP_DISABLED_RECV 0x00326502 //ssx panic
+#define PMC_DCM_OUTSTANDING_TRANSFER 0x00326503 //user handle
+#define PMC_DCM_INTCHP_CMD_ONLY_MTOS 0x00326504 //ssx panic
+#define PMC_DCM_INTCHP_CMD_ONLY_STOM 0x00326504 //ssx panic
+#define PMC_DCM_INVALID_COMMAND_CODE 0x00326505 //ssx panic
+#define PMC_DCM_RECEIVE_NOT_DETECTED 0x00326506 //user handle
+#define PMC_DCM_RECEIVE_NOT_MSG_TYPE 0x00326507 //ssx panic
+
+/// PMC Interchip Command Code
+#define PMC_IC_GPA_CC 1 //0b0001
+#define PMC_IC_GPA_ACK_CC 2 //0b0010
+#define PMC_IC_GAR_CC 3 //0b0011
+#define PMC_IC_PING_CC 4 //0b0100
+#define PMC_IC_PING_ACK_CC 6 //0b0110
+#define PMC_IC_MSG_CC 8 //0b1000
+#define PMC_IC_MSG_NACK_CC 10 //0b1010
+#define PMC_IC_MSG_ACK_CC 11 //0b1011
+#define PMC_IC_ERROR_CC 15 //0b1111
+
+#ifndef __ASSEMBLER__
+
+/// PMC-DCM low-level (hardware) packet
+
+typedef union PmcInterchipPacket {
+ uint32_t value;
+ struct {
+ /// Hardware command code
+ uint8_t cmd_code : 4;
+ /// Hardware command extension; GPSM-DCM command code
+ uint8_t cmd_ext : 4;
+ /// Payload. The plan of record is to use payload[2] as HW-generated ECC.
+ uint8_t payload[3];
+ } fields;
+} PmcDcmPacket;
+
+/// Macro to set and get payload field
+#define SET_PAYLOAD_FIELD(value) (value << 8) & 0xFFFF00
+#define GET_PAYLOAD_FIELD(value) (value & 0xFFFF00) >> 8
+
+
+/// low-level PMC-DCM interchip communication methods
+
+int
+pmc_dcm_send(PmcDcmPacket* hwPacket);
+
+int
+pmc_dcm_receive(PmcDcmPacket* hwPacket);
+
+void
+_pmc_dcm_send(uint32_t *value);
+
+void
+_pmc_dcm_receive(uint32_t *value);
+
+int
+pmc_dcm_check_ic_command(int cmd_code);
+
+int
+pmc_dcm_if_dcm_master();
+
+void
+pmc_dcm_set_interchip_mode(int master);
+
+int
+pmc_dcm_if_interchip_interface_enabled();
+
+void
+pmc_dcm_set_interchip_interface(int enable);
+
+int
+pmc_dcm_if_channel_busy();
+
+int
+pmc_dcm_if_new_message();
+
+void
+pmc_dcm_init(int master_or_slace);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PMC_DCM_H__ */
+
diff --git a/src/lib/polling.c b/src/lib/polling.c
new file mode 100644
index 0000000..42e9fef
--- /dev/null
+++ b/src/lib/polling.c
@@ -0,0 +1,73 @@
+// $Id: polling.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/polling.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file polling.c
+/// \brief Library APIs for polling
+
+#include "polling.h"
+
+int
+polling(int* o_rc,
+ int (*i_condition)(void* io_arg, int* o_satisfied),
+ void* io_arg,
+ SsxInterval i_timeout,
+ SsxInterval i_sleep)
+{
+ SsxTimebase start;
+ int rc, pollRc, timed_out, done;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((i_condition == 0), POLLING_ERROR);
+ }
+
+ start = ssx_timebase_get();
+ timed_out = 0;
+
+ do {
+ pollRc = i_condition(io_arg, &done);
+ if (pollRc) {
+ rc = POLLING_CONDITION;
+ break;
+ }
+ if (done) {
+ rc = 0;
+ break;
+ }
+ if (timed_out) {
+ rc = POLLING_TIMEOUT;
+ break;
+ }
+ if (i_sleep != 0) {
+ rc = ssx_sleep(i_sleep);
+ if (rc) {
+ break;
+ }
+ }
+ timed_out =
+ ((i_timeout != SSX_WAIT_FOREVER) &&
+ ((ssx_timebase_get() - start) >= i_timeout));
+
+ } while (1);
+
+ if (o_rc) {
+ *o_rc = pollRc;
+ }
+
+ return rc;
+}
+
+
+void
+busy_wait(SsxInterval i_interval)
+{
+ SsxTimebase start;
+
+ start = ssx_timebase_get();
+ while ((ssx_timebase_get() - start) < i_interval);
+}
+
diff --git a/src/lib/polling.h b/src/lib/polling.h
new file mode 100644
index 0000000..b719ac6
--- /dev/null
+++ b/src/lib/polling.h
@@ -0,0 +1,94 @@
+#ifndef __POLLING_H__
+#define __POLLING_H__
+
+// $Id: polling.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/polling.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file polling.c
+/// \brief Library APIs for polling and busy-waiting
+
+#include "ssx.h"
+
+// Return/Panic codes
+
+#define POLLING_TIMEOUT 0x00765501
+#define POLLING_ERROR 0x00765502
+#define POLLING_CONDITION 0x00765503
+
+
+#ifndef __ASSEMBLER__
+
+/// Poll for a condition or a timeout with optional sleep
+///
+/// \param[out] o_rc The last return code from calling \a i_condition. This
+/// will only be valid if the return code from polling() is
+/// POLLING_CONDITION. This argument may be passed as NULL (0) if the caller
+/// does not require this information.
+///
+/// \param[in] i_condition A function of two arguments, returning an integer
+/// return code - 0 for success, non-0 for failure. The first argument is a
+/// private state or parameter variable. The second argument is used to
+/// return the truth value of the \a i_condition predicate (0 for false, non-0
+/// for true), and is only considered if the return value of \a i_condition is
+/// 0.
+///
+/// \param[in,out] io_arg The private argument of the \a condition function.
+///
+/// \param[in] i_timeout The maximum amount of time to poll the \a condition
+/// before declaring a timeout. The special value SSX_WAIT_FOREVER can be
+/// used to specify polling without timeout.
+///
+/// \param[in] i_sleep If non-0 at entry, then the thread will sleep for this
+/// interval between polls of the condition. Otherwise the polling is
+/// continuous. polling() can only be called with i_sleep non-0 from a
+/// thread context (since interrupt contexts can not block).
+///
+/// polling() implements a generic polling protocol for conditions that can
+/// not be recognized as interrupt events. polling() polls the \a i_condition
+/// until either an error is encountered, the condition is true, or the
+/// polling times out as measured by the SSX timebase. Whenever a timeout is
+/// detected the condition is polled once more to exclude false timeouts that
+/// may have been caused by thread preemption.
+///
+/// The \a i_sleep A non-0 value of \a i_sleep specifies that the thread
+/// should sleep for the given interval between polling tries instead of
+/// polling continuously. A non-0 \a i_sleep argument is only legal in thread
+/// contexts.
+///
+/// \retval 0 Success; The condition was satisfied prior to the timeout.
+///
+/// \retval POLLING_TIMEOUT A timeout was detected before the condition became
+/// valid.
+///
+/// \retval POLLING_ERROR This code is returned if any of the arguments of
+/// polling() are invalid.
+///
+/// \retval POLLING_CONDITION This code is returned if the \a i_condition
+/// function returns a non-0 return code.
+///
+/// If the embedded call of ssx_sleep() fails for some reason then the return
+/// code will be the code returned by ssx_sleep().
+int
+polling(int* o_rc,
+ int (*i_condition)(void* io_arg, int* o_satisfied),
+ void* io_arg,
+ SsxInterval i_timeout,
+ SsxInterval i_sleep);
+
+
+/// A busy-wait loop
+///
+/// \param[in] i_interval The interval of time to busy-wait. The actual
+/// interval may be more than this if the thread is interrupted. If called
+/// from a context with interrupts disabled the timing should be very precise.
+void
+busy_wait(SsxInterval i_interval);
+
+#endif // __ASSEMBLER__
+
+#endif // __POLLING_H__
diff --git a/src/lib/pore_hooks.h b/src/lib/pore_hooks.h
new file mode 100755
index 0000000..f278acb
--- /dev/null
+++ b/src/lib/pore_hooks.h
@@ -0,0 +1,171 @@
+#ifndef __PORE_HOOKS_H__
+#define __PORE_HOOKS_H__
+
+// $Id: pore_hooks.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pore_hooks.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pore_hooks.h
+/// \brief Support for PORE hooks in Simics
+///
+/// Our Simics model of the PORE supports "hooks", that is, special forms of
+/// comments that include C++ code that is extracted and made available at
+/// simulation time in the Simics environment.
+///
+/// Besides hooks that do simple printf() type tracing, logging and tracing
+/// hooks are also provided that make use of the Simics log-level facility.
+/// This allows precise control over which PORE objects are logged/traced, and
+/// at which level.
+///
+/// In the Simics environment, hook routines have the following prototype:
+///
+/// void f(const PoreAddress& i_address,
+/// const HookType i_type,
+/// PoreSimics& io_pore);
+
+// Define the "1-liner" syntax
+
+#define HOOK_MARKER HOOK_INSERT_MARKER(#,#)
+#define HOOK_INSERT_MARKER(x,y) x##y##1@
+
+
+/// \defgroup standard_io_hooks Standard I/O Logging and Tracing Hooks
+///
+/// Standard I/O printing. The *TRACE* forms prefix the output with the file
+/// name and line number.
+///
+/// @{
+
+#define PORE_PRINTF(...) HOOK_MARKER printf(__VA_ARGS__);
+
+#define PORE_FPRINTF(stream, ...) HOOK_MARKER fprintf(stream, __VA_ARGS__);
+
+#define PORE_TRACEF(fmt, ...) \
+ HOOK_MARKER printf("%s:d:" fmt, __FILE__, __LINE__, ##__VA_ARGS__);
+
+#define PORE_FTRACEF(stream, fmt, ...) \
+ HOOK_MARKER printf(stream, "%s:d:" fmt, __FILE__, __LINE__, ##__VA_ARGS__);
+
+/// @}
+
+/// \defgroup quickie_debugging_prints Quickie Debugging Print Hooks
+///
+/// Quickie debugging prints. You provide a register name and string (w/o
+/// newline), the macro formats the data.
+///
+/// @{
+
+#define PORE_PRINT_REG(msg, reg, fmt, fn) \
+ PORE_PRINTF(msg " : " #reg " = " FMT_##fmt "\n", fn)
+
+#define PORE_TRACE_REG(msg, reg, fmt, fn) \
+ PORE_TRACEF(msg " : " #reg " = " FMT_##fmt "\n", fn)
+
+#define PORE_PRINT_D0(msg) PORE_PRINT_REG(msg, D0, DX, d0())
+#define PORE_PRINT_D1(msg) PORE_PRINT_REG(msg, D1, DX, d1())
+#define PORE_PRINT_A0(msg) PORE_PRINT_REG(msg, A0, AX, a0())
+#define PORE_PRINT_A1(msg) PORE_PRINT_REG(msg, A1, AX, a1())
+#define PORE_PRINT_P0(msg) PORE_PRINT_REG(msg, P0, PX, p0())
+#define PORE_PRINT_P1(msg) PORE_PRINT_REG(msg, P1, PX, p1())
+#define PORE_PRINT_CTR(msg) PORE_PRINT_REG(msg, CTR, CTR, ctr())
+#define PORE_PRINT_SPRG0(msg) PORE_PRINT_REG(msg, SPRG0, SPRG0, sprg0())
+#define PORE_PRINT_STATUS(msg) PORE_PRINT_REG(msg, STATUS, STATUS, status())
+#define PORE_PRINT_CONTROL(msg) PORE_PRINT_REG(msg, CONTROL, CONTROL, control())
+
+#define PORE_TRACE_D0(msg) PORE_TRACE_REG(msg, D0, DX, d0())
+#define PORE_TRACE_D1(msg) PORE_TRACE_REG(msg, D1, DX, d1())
+#define PORE_TRACE_A0(msg) PORE_TRACE_REG(msg, A0, AX, a0())
+#define PORE_TRACE_A1(msg) PORE_TRACE_REG(msg, A1, AX, a1())
+#define PORE_TRACE_P0(msg) PORE_TRACE_REG(msg, P0, PX, p0())
+#define PORE_TRACE_P1(msg) PORE_TRACE_REG(msg, P1, PX, p1())
+#define PORE_TRACE_CTR(msg) PORE_TRACE_REG(msg, CTR, CTR, ctr())
+#define PORE_TRACE_SPRG0(msg) PORE_TRACE_REG(msg, SPRG0, SPRG0, sprg0())
+#define PORE_TRACE_STATUS(msg) PORE_TRACE_REG(msg, STATUS, STATUS, status())
+#define PORE_TRACE_CONTROL(msg) PORE_TRACE_REG(msg, CONTROL, CONTROL, control())
+
+/// @}
+
+/// \defgroup simics_style_logging Simics-style Logging Hooks
+///
+/// Simics-style logging. All of these will produce a Simics prefix detailing
+/// the unit that failed. The *_TRACE_* forms add the file name and line number
+/// to the Simics info, print a newline and then format the trace message on
+/// the following line.
+///
+/// @{
+
+#define SIM_LOG_INFO(level, group, ...) HOOK_MARKER \
+ SIM_log_info(level, io_pore.d_log, group, __VA_ARGS__);
+
+#define SIM_LOG_ERROR(group, ...) HOOK_MARKER \
+ SIM_log_error(io_pore.d_log, group, __VA_ARGS__);
+
+#define SIM_TRACE_INFO(level, group, fmt, ...) HOOK_MARKER \
+ SIM_log_info(level, io_pore.d_log, group, \
+ "%s:%d\n" fmt, __FILE__, __LINE__,## __VA_ARGS__);
+
+#define SIM_TRACE_ERROR(group, fmt, ...) HOOK_MARKER \
+ SIM_log_error(io_pore.d_log, group, \
+ "%s:%d\n" fmt, __FILE__, __LINE__, ##__VA_ARGS__);
+
+/// @}
+
+/// \defgroup vcl_style_3_level_printing VCL-style 3-Level Logging Hooks
+///
+/// Define VCL-style 3-level logging and tracing, with programmable Simics
+/// log-level selection. All logs are controlled by (?) group 0. Note that
+/// setting the Simics log-level to 4 produces gobs of output from every part
+/// of the system, however here at the debug level of 3 we only get messages
+/// from hooks.
+///
+/// @{
+
+#ifndef SIMICS_LOG_LEVEL_OUTPUT
+#define SIMICS_LOG_LEVEL_OUTPUT 1
+#endif
+
+#ifndef SIMICS_LOG_LEVEL_INFO
+#define SIMICS_LOG_LEVEL_INFO 2
+#endif
+
+#ifndef SIMICS_LOG_LEVEL_DEBUG
+#define SIMICS_LOG_LEVEL_DEBUG 3
+#endif
+
+#define PORE_LOG_OUTPUT(...) SIM_LOG_INFO(SIMICS_LOG_LEVEL_OUTPUT, 0, __VA_ARGS__)
+#define PORE_LOG_INFO(...) SIM_LOG_INFO(SIMICS_LOG_LEVEL_INFO, 0, __VA_ARGS__)
+#define PORE_LOG_DEBUG(...) SIM_LOG_INFO(SIMICS_LOG_LEVEL_DEBUG, 0, __VA_ARGS__)
+
+#define PORE_LOG_ERROR(...) SIM_LOG_ERROR(0, __VA_ARGS__)
+
+#define PORE_TRACE_OUTPUT(...) SIM_TRACE_INFO(SIMICS_LOG_LEVEL_OUTPUT, 0, __VA_ARGS__)
+#define PORE_TRACE_INFO(...) SIM_TRACE_INFO(SIMICS_LOG_LEVEL_INFO, 0, __VA_ARGS__)
+#define PORE_TRACE_DEBUG(...) SIM_TRACE_INFO(SIMICS_LOG_LEVEL_DEBUG, 0, __VA_ARGS__)
+
+#define PORE_TRACE_ERROR(...) SIM_TRACE_ERROR(0, __VA_ARGS__)
+
+/// @}
+
+/// Break Simics simulation
+#define SIM_BREAK_SIMULATION(msg) \
+ HOOK_MARKER SIM_break_simulation(msg); io_pore.dumpAll();
+
+
+/// A PORE Assertion
+#define PORE_ASSERT(assertion) \
+ HOOK_MARKER \
+ if (!(assertion)) { \
+ SIM_log_error(io_pore.d_log, 0, \
+ "Assertion below failed\n" #assertion); \
+ SIM_break_simulation("Assertion failure"); \
+ }
+
+
+/// Dump the PORE state
+#define PORE_DUMP(...) LOG_OUTPUT(__VA_ARGS__) io_pore.dumpAll();
+
+#endif // __PORE_HOOKS_H__
diff --git a/src/lib/printf.c b/src/lib/printf.c
new file mode 100755
index 0000000..96f08f3
--- /dev/null
+++ b/src/lib/printf.c
@@ -0,0 +1,679 @@
+// $Id: printf.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/printf.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file printf.c
+/// \brief Clean-room implementation of printf() functions for SSX I/O
+///
+/// For licensing reasons we are required to create our own version of the
+/// printf() family of functions. This implementation was created without
+/// reference to or inclusion of any licensed or copyrighted code.
+///
+/// The functions defined in this file have prototypes, behavior and return
+/// values as defined by C language standards. In the event of an error a
+/// negative value is returned, generally corresponding to a standard Unix
+/// 'errno' code. Note that SSX does not support either an application- or
+/// per-thread 'errno', so the only record of any error is the \a error field
+/// of the stream. Also note that SSX may be configured to cause a panic if an
+/// error is detected rather than returning an error code.
+///
+/// This implementation defines a limited but useful subset of the C standard
+/// for format control. This implementation includes the following:
+///
+/// - \b c, \b d, \b i, \b n, \b p, \b s, \b u, \b x, and \b X conversion
+/// specifiers, as well as '%%' to output a single '%'
+///
+/// - \b #, \b 0, \b ' ' and \b + flag characters
+///
+/// - Decimal field width specifiers including * (but indirect field widths
+/// must be positive as left-justification is not supported)
+///
+/// - Decimal precision specifiers (currently only apply to %s formats, may be
+/// indirect using *)
+///
+/// - \b l, \b ll and \b z length modifiers
+///
+/// \b Notes:
+///
+/// \a If a \c p conversion specifier is used without any flags (\c '%p'), the
+/// \c p conversion is interptered as if it were \c 0x%08lx for 32-bit address
+/// machines and \c 0x%016llx for 64-bit address machines. The GCC builtin
+/// format checker gives warnings about '0' flag characters for \c p
+/// conversion specifiers, so there is otherwise no 'un-warned' way to get
+/// this preferred (by some) format of pointer values. If you do include
+/// explicit flags (e.g., \c %30p) they will be processed as expected.
+///
+/// Similar to how printf() behaves on an X86-Linux machine, a null pointer
+/// will print as "(null)" with the %s format (unless the precision specifier
+/// precludes it) and "(nil)" with the %p format.
+///
+/// Note that calling formatted I/O functions on non-blocking streams may fail
+/// with the -EAGAIN error, and there is no clean way to restart these
+/// calls. Calling formatted (or any) I/O functions on blocking streams from
+/// interrupt contexts in SSX is also likely to fail intermittently since
+/// interrupt contexts can not block in SSX.
+///
+/// \todo I'd really like to implement the '-' flag for
+/// left-justification. Implementing the precision specifer for integers
+/// should be done for completeness.
+
+#include "ssx.h"
+#include "ssx_io.h"
+
+// Formatting options
+
+#define OPTION_ALTERNATE 0x0001
+#define OPTION_PAD_ZERO 0x0002
+#define OPTION_PLUS_SIGN 0x0004
+#define OPTION_FIELD_WIDTH 0x0008
+#define OPTION_PRECISION 0x0010
+#define OPTION_LONG 0x0020
+#define OPTION_LONG_LONG 0x0040
+#define OPTION_SIZE_T 0x0080
+#define OPTION_UPPERCASE 0x0100
+#define OPTION_HEX 0x0200
+#define OPTION_SPACE 0x0400
+
+
+// Generate padding if required, returning the total number of pad characters
+// output or a negative error code. The 'nchars' argument is the number of
+// non-pad characters to be output by the caller.
+
+#define PAD_SIZE 8
+static const char zeros[PAD_SIZE] = {'0', '0', '0', '0', '0', '0', '0', '0'};
+static const char blanks[PAD_SIZE] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+
+static ssize_t
+pad(FILE *stream, size_t nchars, int options, size_t width)
+{
+ const char *padchars;
+ size_t chars, written;
+ int rc;
+
+ if (!(options & OPTION_FIELD_WIDTH) || (nchars >= width)) {
+ return 0;
+ }
+ chars = width - nchars;
+ if (options & OPTION_PAD_ZERO) {
+ padchars = zeros;
+ } else {
+ padchars = blanks;
+ }
+ while (chars) {
+ rc = swrite(stream, (void *)padchars, MIN(chars, PAD_SIZE), &written);
+ if (rc < 0) return rc;
+ chars -= written;
+ }
+ return width - nchars;
+}
+
+
+// Format a character
+
+static ssize_t
+format_char(FILE *stream, unsigned char c, int options, size_t width)
+{
+ ssize_t padchars, nchars;
+ int rc;
+
+ padchars = pad(stream, 1, options, width);
+ if (padchars < 0) return padchars;
+ nchars = padchars + 1;
+ rc = swrite(stream, (void *)(&c), 1, 0);
+ if (rc < 0) return rc;
+ return nchars;
+}
+
+
+// Format a string
+//
+// If the string is the NULL pointer then normally "(null)" is printed
+// unless the precision is < 6, in which case the empty string is printed.
+// The specification leaves it as undefined what happens if a string requests
+// 0 padding; Here we always pad with blanks (although GCC/PowerPC catches
+// this as an error).
+
+static ssize_t
+format_string(FILE *stream, const char *s, int options,
+ size_t width, size_t precision)
+{
+ size_t len;
+ ssize_t padchars, nchars;
+ int rc;
+
+ if (s == 0) {
+ if ((options & OPTION_PRECISION) && (precision < 6)) {
+ s = "";
+ } else {
+ s = "(null)";
+ }
+ }
+
+ len = strlen(s);
+ if (options & OPTION_PRECISION) {
+ len = MIN(len, precision);
+ }
+
+ options &= ~OPTION_PAD_ZERO;
+ padchars = pad(stream, len, options, width);
+ if (padchars < 0) return padchars;
+ nchars = padchars + len;
+ rc = swrite(stream, (void *)s, len, 0);
+ if (rc < 0) return rc;
+ return nchars;
+}
+
+
+// Format an integer - signed and unsigned. A 64-bit integer (assumed to be
+// the longest we'll see) has 20 decimal digits. An extra space is reserved
+// for the sign. If zero-padding is specified, the sign will be output
+// separately.
+
+static const char lower[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+static const char upper[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+static ssize_t
+format_int(FILE *stream, long long int lli, int options, size_t width)
+{
+ char digits[21];
+ int rc, k, ndigits, negative, positive, i;
+ ssize_t output;
+
+ negative = (lli < 0);
+ positive = (lli > 0);
+
+ // Unpack the integer to characters. The code is optimized for 32-bit
+ // machines where 64-bit division is not built in. The first part of the
+ // loop handles integers requiring a 64-bit divide, the second loop
+ // handles 32-bit integers.
+
+ if (lli == 0) {
+ digits[20] = '0';
+ k = 20;
+ } else if (negative) {
+ for (k = 21;
+ lli != (int)lli;
+ digits[--k] = lower[-(lli % 10)], lli = lli / 10);
+ for (i = (int)lli;
+ i != 0;
+ digits[--k] = lower[-(i % 10)], i = i / 10);
+ } else {
+ for (k = 21;
+ lli != (int)lli;
+ digits[--k] = lower[lli % 10], lli = lli / 10);
+ for (i = (int)lli;
+ i != 0;
+ digits[--k] = lower[i % 10], i = i / 10);
+ }
+
+ ndigits = 21 - k;
+
+ // Handle other options and output
+
+ output = 0;
+ if (options & OPTION_PAD_ZERO) {
+
+ if (negative) {
+ rc = swrite(stream, "-", 1, 0);
+ if (rc < 0) return rc;
+ output++;
+ } else if (positive) {
+ if (options & OPTION_PLUS_SIGN) {
+ rc = swrite(stream, "+", 1, 0);
+ if (rc < 0) return rc;
+ output++;
+ } else if (options & OPTION_SPACE) {
+ rc = swrite(stream, " ", 1, 0);
+ if (rc < 0) return rc;
+ output++;
+ }
+ }
+ rc = pad(stream, ndigits + output, options, width);
+ if (rc < 0) return rc;
+ output += rc;
+ rc = swrite(stream, &(digits[k]), ndigits, 0);
+ if (rc < 0) return rc;
+ output += ndigits;
+
+ } else {
+
+ if (negative) {
+ digits[--k] = '-';
+ ndigits++;
+ } else if (positive) {
+ if (options & OPTION_PLUS_SIGN) {
+ digits[--k] = '+';
+ ndigits++;
+ } else if (options & OPTION_SPACE) {
+ digits[--k] = ' ';
+ ndigits++;
+ }
+ }
+ rc = pad(stream, ndigits, options, width);
+ if (rc < 0) return rc;
+ output += rc;
+ rc = swrite(stream, &(digits[k]), ndigits, 0);
+ if (rc < 0) return rc;
+ output += ndigits;
+ }
+
+ return output;
+}
+
+
+static ssize_t
+format_unsigned(FILE *stream, unsigned long long ull, int options, size_t width)
+{
+ char digits[21], *alternate;
+ const char *xchars;
+ int rc, k, ndigits, zero;
+ unsigned u;
+ ssize_t output;
+
+ zero = (ull == 0);
+
+ // Determine hex case and alternate string
+
+ alternate = 0;
+ if (options & OPTION_HEX) {
+ if (options & OPTION_UPPERCASE) {
+ xchars = upper;
+ if (options & OPTION_ALTERNATE) {
+ alternate = "0X";
+ }
+ } else {
+ xchars = lower;
+ if (options & OPTION_ALTERNATE) {
+ alternate = "0x";
+ }
+ }
+ } else {
+ xchars = lower;
+ }
+
+ // Unpack the unsigned integer to characters. The Hex conversions are
+ // easier since they can be done with shift and mask rather than
+ // divison. The code is optimized for a 32-bit machine where 64-bit
+ // division is not built-in.
+
+ if (zero) {
+ digits[20] = '0';
+ k = 20;
+ } else if (options & OPTION_HEX) {
+ for (k = 21;
+ ull != (unsigned)ull;
+ digits[--k] = xchars[ull & 0xf], ull = ull >> 4);
+ for (u = (unsigned)ull;
+ u != 0;
+ digits[--k] = xchars[u & 0xf], u = u >> 4);
+ } else {
+ for (k = 21;
+ ull != (unsigned)ull;
+ digits[--k] = xchars[ull % 10], ull = ull / 10);
+ for (u = (unsigned)ull;
+ u != 0;
+ digits[--k] = xchars[u % 10], u = u / 10);
+ }
+
+ ndigits = 21 - k;
+
+ // Handle other options and output
+
+ output = 0;
+ if (options & OPTION_PAD_ZERO) {
+
+ if (!zero && alternate) {
+ rc = swrite(stream, (void *)alternate, 2, 0);
+ if (rc < 0) return rc;
+ output += 2;
+ }
+ rc = pad(stream, ndigits + output, options, width);
+ if (rc < 0) return rc;
+ output += rc;
+ rc = swrite(stream, &(digits[k]), ndigits, 0);
+ if (rc < 0) return rc;
+ output += ndigits;
+
+ } else {
+
+ if (!zero && alternate) {
+ output += 2;
+ }
+ rc = pad(stream, ndigits + output, options, width);
+ if (rc < 0) return rc;
+ output += rc;
+ if (!zero && alternate) {
+ rc = swrite(stream, alternate, 2, 0);
+ if (rc < 0) return rc;
+ output += 2;
+ }
+ rc = swrite(stream, &(digits[k]), ndigits, 0);
+ if (rc < 0) return rc;
+ output += ndigits;
+ }
+
+ return output;
+}
+
+
+int
+vfprintf(FILE *stream, const char *format, va_list argp)
+{
+ const char *fmt, *scan;
+ int rc, total_chars, options, done;
+ size_t width, precision;
+
+ int arg_i, *arg_pi;
+ long int arg_li;
+ long long int arg_lli;
+ ssize_t arg_zi;
+ unsigned arg_u;
+ unsigned long arg_lu;
+ unsigned long long arg_llu;
+ size_t arg_zu;
+ char *arg_s;
+
+ total_chars = 0;
+
+ fmt = format;
+ while (*fmt) {
+
+ // Scan until '%' or the end of the format, then output the text.
+
+ scan = fmt;
+ while (*scan && (*scan != '%')) {
+ scan++;
+ }
+ if (scan != fmt) {
+ rc = swrite(stream, fmt, scan - fmt, 0);
+ if (rc < 0) return rc;
+ total_chars += scan - fmt;
+ }
+ fmt = scan;
+ if (!*fmt) {
+ return total_chars;
+ }
+ fmt++;
+
+ // We got a '%'. Check for %% and %n.
+
+ switch (*fmt) {
+ case '\0':
+ SSX_IO_ERROR(stream, EINVAL);
+ break;
+ case '%':
+ rc = swrite(stream, "%", 1, 0);
+ if (rc < 0) return rc;
+ total_chars++;
+ fmt++;
+ continue;
+ case 'n':
+ arg_pi = va_arg(argp, int *);
+ *arg_pi = total_chars;
+ fmt++;
+ continue;
+ }
+
+ // Collect padding options, if any. Left justification is not
+ // implemeted.
+
+ options = 0;
+ done = 0;
+ do {
+ switch (*fmt) {
+ case '\0':
+ SSX_IO_ERROR(stream, EINVAL);
+ break;
+ case '#':
+ options |= OPTION_ALTERNATE;
+ break;
+ case '0':
+ options |= OPTION_PAD_ZERO;
+ break;
+ case '+':
+ options |= OPTION_PLUS_SIGN;
+ break;
+ case ' ':
+ options |= OPTION_SPACE;
+ break;
+ case '-':
+ SSX_IO_ERROR(stream, EINVAL); // Left just. not impl.
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ if (!done) {
+ fmt++;
+ }
+ } while (!done);
+
+ // Collect the field width, if specified. A negative precision
+ // specified as an argument indicates left justification (not
+ // implemented).
+
+ width = 0;
+ if (isdigit(*fmt)) {
+ options |= OPTION_FIELD_WIDTH;
+ for (; isdigit(*fmt); fmt++) {
+ width = (width * 10) + (*fmt - '0');
+ }
+ } else if (*fmt == '*') {
+ fmt++;
+ options |= OPTION_FIELD_WIDTH;
+ arg_i = va_arg(argp, int);
+ if (arg_i < 0) {
+ SSX_IO_ERROR(stream, EINVAL); // Left just. not impl.
+ }
+ width = arg_i;
+ }
+
+ // Collect the precision, if specified. By standard specification an
+ // empty or negative precision is interpreted as 0.
+
+ precision = 0;
+ if (*fmt == '.') {
+ fmt++;
+ options |= OPTION_PRECISION;
+ if (isdigit(*fmt)) {
+ for(; isdigit(*fmt); fmt++) {
+ precision = (precision * 10) + (*fmt - '0');
+ }
+ } else if (*fmt == '*') {
+ fmt++;
+ arg_i = va_arg(argp, int);
+ if (arg_i < 0) {
+ arg_i = 0;
+ }
+ precision = arg_i;
+ }
+ }
+
+ // Collect length modifiers.
+
+ done = 0;
+ do {
+ switch (*fmt) {
+ case '\0':
+ SSX_IO_ERROR(stream, EINVAL);
+ break;
+ case 'l':
+ if (options & OPTION_LONG) {
+ options &= ~OPTION_LONG;
+ options |= OPTION_LONG_LONG;
+ } else if (options & OPTION_LONG_LONG) {
+ SSX_IO_ERROR(stream, EINVAL);
+ } else {
+ options |= OPTION_LONG;
+ }
+ if (options & OPTION_SIZE_T) {
+ SSX_IO_ERROR(stream, EINVAL);
+ }
+ break;
+ case 'z':
+ if ((options & OPTION_LONG) || (options & OPTION_LONG_LONG)) {
+ SSX_IO_ERROR(stream, EINVAL);
+ }
+ options |= OPTION_SIZE_T;
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ if (!done) {
+ fmt++;
+ }
+ } while (!done);
+
+ // Use the conversion specifier to format the next argument
+
+ switch (*fmt) {
+
+ case 'c':
+ arg_i = va_arg(argp, int);
+ rc = format_char(stream, (unsigned char)arg_i, options, width);
+ if (rc < 0) return rc;
+ total_chars++;
+ break;
+
+ case 'd':
+ case 'i':
+ if (options & OPTION_LONG) {
+ arg_li = va_arg(argp, long int);
+ rc = format_int(stream, (long long int)arg_li, options,
+ width);
+ } else if (options & OPTION_LONG_LONG) {
+ arg_lli = va_arg(argp, long long int);
+ rc = format_int(stream, (long long int)arg_lli, options,
+ width);
+ } else if (options & OPTION_SIZE_T) {
+ arg_zi = va_arg(argp, ssize_t);
+ rc = format_int(stream, (long long int)arg_zi, options,
+ width);
+ } else {
+ arg_i = va_arg(argp, int);
+ rc = format_int(stream, (long long int)arg_i, options,
+ width);
+ }
+ if (rc < 0) return rc;
+ total_chars += rc;
+ break;
+
+ case 'p':
+ arg_lu = va_arg(argp, unsigned long);
+ options |= (OPTION_ALTERNATE | OPTION_HEX);
+ if (!(options & OPTION_PAD_ZERO) &&
+ !(options & OPTION_FIELD_WIDTH)) {
+ options |= (OPTION_PAD_ZERO | OPTION_FIELD_WIDTH);
+ width = (2 * sizeof(unsigned long)) + 2; /* 0x........ */
+ }
+ if (arg_lu == 0) {
+ options &= ~OPTION_PRECISION;
+ rc = format_string(stream, "(nil)", options, width, precision);
+ } else {
+ rc = format_unsigned(stream, (unsigned long long)arg_lu,
+ options, width);
+ }
+ if (rc < 0) return rc;
+ total_chars += rc;
+ break;
+
+ case 's':
+ arg_s = va_arg(argp, char *);
+ rc = format_string(stream, arg_s, options, width, precision);
+ if (rc < 0) return rc;
+ total_chars += rc;
+ break;
+
+ case 'X':
+ options |= OPTION_UPPERCASE;
+ case 'x':
+ options |= OPTION_HEX;
+ case 'u':
+ if (options & OPTION_LONG) {
+ arg_lu = va_arg(argp, unsigned long);
+ rc = format_unsigned(stream, (unsigned long long)arg_lu,
+ options, width);
+ } else if (options & OPTION_LONG_LONG) {
+ arg_llu = va_arg(argp, unsigned long long);
+ rc = format_unsigned(stream, (unsigned long long)arg_llu,
+ options, width);
+ } else if (options & OPTION_SIZE_T) {
+ arg_zu = va_arg(argp, size_t);
+ rc = format_unsigned(stream, (unsigned long long)arg_zu,
+ options, width);
+ } else {
+ arg_u = va_arg(argp, unsigned);
+ rc = format_unsigned(stream, (unsigned long long )arg_u,
+ options, width);
+ }
+ if (rc < 0) return rc;
+ total_chars += rc;
+ break;
+
+ default:
+ SSX_IO_ERROR(stream, EINVAL);
+ break;
+ }
+
+ fmt++;
+ }
+
+ return total_chars;
+}
+
+
+int
+vprintf(const char *format, va_list argp)
+{
+ return vfprintf(stdout, format, argp);
+}
+
+
+int
+fprintf(FILE *stream, const char *format, ...)
+{
+ va_list argp;
+ int rc;
+
+ va_start(argp, format);
+ rc = vfprintf(stream, format, argp);
+ va_end(argp);
+ return rc;
+}
+
+
+int
+printf(const char *format, ...)
+{
+ va_list argp;
+ int rc;
+
+ va_start(argp, format);
+ rc = vfprintf(stdout, format, argp);
+ va_end(argp);
+ return rc;
+}
+
+
+int
+printk(const char *format, ...)
+{
+ va_list argp;
+ int rc;
+
+ va_start(argp, format);
+ rc = vfprintf(ssxout, format, argp);
+ va_end(argp);
+ return rc;
+}
+
diff --git a/src/lib/pstates.c b/src/lib/pstates.c
new file mode 100755
index 0000000..958b9e1
--- /dev/null
+++ b/src/lib/pstates.c
@@ -0,0 +1,410 @@
+// $Id: pstates.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pstates.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pstates.c
+/// \brief Pstate routines required by OCC product firmware
+
+#include "ssx.h"
+#include "pgp_common.h"
+#include "pstates.h"
+
+/// Validate a VRM11 VID code
+///
+/// \param vid A VRM11 VID
+///
+/// \retval 0 The VID is valid
+///
+/// \retval -VID11_UNDERFLOW_VID11_VALIDATE The Vid code is a low 'power off'
+/// VID (0 or 1)
+///
+/// \retval -VID11_OVERFLOW_VID11_VALIDATE The Vid code is a high 'power off'
+/// VID (0xfe or 0xff)
+
+int
+vid11_validate(Vid11 vid)
+{
+ int rc;
+
+ if (vid < VID11_MIN) {
+
+ rc = -VID11_UNDERFLOW_VID11_VALIDATE;
+
+ } else if (vid > VID11_MAX) {
+
+ rc = -VID11_OVERFLOW_VID11_VALIDATE;
+
+ } else {
+
+ rc = 0;
+
+ }
+
+ return rc;
+}
+
+
+/// Bias a Pstate with saturation
+///
+/// \param pstate The initial Pstate to bias
+///
+/// \param bias The signed bias amount
+///
+/// \param biased_pstate The final biased Pstate
+///
+/// This API adds a signed bias to the \a pstate and returns the saturated sum
+/// as \a biased_pstate. Any application that biases Pstates should use this
+/// API rather than simple addition/subtraction.
+///
+/// The following return codes are not considered errors:
+///
+/// \retval 0 Success
+///
+/// \retval -PSTATE_OVERFLOW_BIAS_PS The biased Pstate saturated at PSTATE_MAX.
+///
+/// \retval -PSTATE_UNDERFLOW_BIAS_PS The biased Pstate saturated at PSTATE_MIN.
+
+int
+bias_pstate(Pstate pstate, int bias, Pstate* biased_pstate)
+{
+ int rc, int_pstate;
+
+ int_pstate = (int)pstate + bias;
+ if (int_pstate != (Pstate)int_pstate) {
+ if (bias < 0) {
+ *biased_pstate = PSTATE_MIN;
+ rc = -PSTATE_UNDERFLOW_BIAS_PS;
+ } else {
+ *biased_pstate = PSTATE_MAX;
+ rc = -PSTATE_OVERFLOW_BIAS_PS;
+ }
+ } else {
+ *biased_pstate = int_pstate;
+ rc = 0;
+ }
+
+ return rc;
+}
+
+
+/// Bias a DPLL frequency code with saturation and bounds checking
+///
+/// \param fcode The initial frequency code to bias
+///
+/// \param bias The signed bias amount
+///
+/// \param biased_fcode The final biased frequency code
+///
+/// This API adds a signed bias to the \a fcode and returns the saturated and
+/// bounded sum as \a biased_fcode. Any application that biases frequency
+/// codes should use this API rather than simple addition/subtraction.
+///
+/// The following return codes are not considered errors:
+///
+/// \retval 0 Success
+///
+/// \retval -DPLL_OVERFLOW The biased frequency code saturated at DPLL_MAX.
+///
+/// \retval -DPLL_UNDERFLOW1 The biased frequency code saturated at DPLL_MIN.
+///
+/// \retval -DPLL_UNDERFLOW2 The biased frequency code saturated at DPLL_MIN.
+
+int
+bias_frequency(DpllCode fcode, int bias, DpllCode* biased_fcode)
+{
+ int rc;
+ unsigned uint_fcode;
+
+ uint_fcode = (unsigned)fcode + bias;
+ if (uint_fcode != (DpllCode)uint_fcode) {
+ if (bias < 0) {
+ *biased_fcode = DPLL_MIN;
+ rc = -DPLL_UNDERFLOW1;
+ } else {
+ *biased_fcode = DPLL_MAX;
+ rc = -DPLL_OVERFLOW;
+ }
+ } else if (uint_fcode < DPLL_MIN) {
+ *biased_fcode = DPLL_MIN;
+ rc = -DPLL_UNDERFLOW2;
+ } else {
+ *biased_fcode = uint_fcode;
+ rc = 0;
+ }
+
+ return rc;
+}
+
+
+/// Bias a VRM11 VID code with saturation and bounds checking
+///
+/// \param vid The initial vid code to bias
+///
+/// \param bias The signed bias amount
+///
+/// \param biased_vid The final biased VID code
+///
+/// This API adds a signed bias to the \a vid and returns the saturated and
+/// bounded sum as \a biased_vid. Any application that biases VID codes
+/// should use this API rather than simple addition/subtraction.
+///
+/// The following return codes are not considered errors:
+///
+/// \retval 0 Success
+///
+/// \retval -VID11_OVERFLOW_BIAS_VID11 The biased VID code saturated
+/// at VID11_MAX.
+///
+/// \retval -VID11_UNDERFLOW_BIAS_VID11 The biased VID code saturated
+/// at VID11_MIN.
+
+int
+bias_vid11(Vid11 vid, int bias, Vid11* biased_vid)
+{
+ int rc;
+ unsigned uint_vid;
+
+ uint_vid = (unsigned)vid + bias;
+ if (uint_vid != (DpllCode)uint_vid) {
+ if (bias < 0) {
+ *biased_vid = VID11_MIN;
+ rc = -VID11_UNDERFLOW_BIAS_VID11;
+ } else {
+ *biased_vid = VID11_MAX;
+ rc = -VID11_OVERFLOW_BIAS_VID11;
+ }
+ } else {
+
+ rc = vid11_validate(uint_vid);
+ *biased_vid = uint_vid;
+
+ }
+
+ return rc;
+}
+
+
+/// Retrieve an entry from the Global Pstate Table abstraction
+///
+/// \param gpst An initialized GlobalPstateTable structure.
+///
+/// \param pstate The Pstate index of the entry to fetch
+///
+/// \param bias This is a signed bias. The entry searched is the \a pstate +
+/// \a bias entry.
+///
+/// \param entry A pointer to a gpst_entry_t to hold the returned data.
+///
+/// This routine functions similar to PMC harwdare. When a Pstate is
+/// requested the index is first biased (under/over-volted) and clipped to the
+/// defined bounds, then the Pstate entry is returned.
+///
+/// The following return codes are not considered errors:
+///
+/// \retval 0 Success
+///
+/// \retval -GPST_PSTATE_CLIPPED_HIGH_GPST_ENTRY The requested Pstate does not
+/// exist in the table. The maximum Pstate entry in the table has been returned.
+///
+/// \retval -GPST_PSTATE_CLIPPED_LOW_GPST_ENTRY The requested Pstate does not
+/// exist in the table. The minimum Pstate entry in the table has been returned.
+///
+/// The following return codes are considered errors:
+///
+/// \retval -GPST_INVALID_OBJECT_GPST_ENTRY The Global Pstate Table is
+/// either null (0) or otherwise invalid.
+
+int
+gpst_entry(const GlobalPstateTable *gpst,
+ const Pstate pstate,
+ int bias,
+ gpst_entry_t *entry)
+{
+ int rc, index;
+ Pstate biased_pstate;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(gpst == 0, GPST_INVALID_OBJECT_GPST_ENTRY);
+ }
+
+ rc = bias_pstate(pstate, bias, &biased_pstate);
+
+ if ((rc == -PSTATE_UNDERFLOW_BIAS_PS) || (pstate < gpst_pmin(gpst))) {
+
+ rc = -GPST_PSTATE_CLIPPED_LOW_GPST_ENTRY;
+ index = 0;
+
+ } else if ((rc == -PSTATE_OVERFLOW_BIAS_PS) || (pstate > gpst_pmax(gpst))) {
+
+ rc = -GPST_PSTATE_CLIPPED_HIGH_GPST_ENTRY;
+ index = gpst->entries - 1;
+
+ } else {
+
+ rc = 0;
+ index = pstate - gpst_pmin(gpst);
+
+ }
+
+ *entry = gpst->pstate[index];
+
+ return rc;
+}
+
+
+/// Translate a Vdd VID code to the closest Pstate in a Global Pstate table.
+///
+/// \param gpst The GlobalPstateTable to search
+///
+/// \param vdd A VID code representing an external VDD voltage
+///
+/// \param pstate The Pstate most closely matching the \a vid.
+///
+/// \param entry The GlobalPstateTable entry of the returned \a pstate.
+///
+/// This routine assumes that Pstate voltages increase monotonically from
+/// lower to higher Pstates. The algorithm operates from lowest to highest
+/// voltage, scanning until the Pstate voltage is >= the VID voltage. Thus
+/// the algorithm effectively rounds up voltage (unless clipped at the highest
+/// Pstate).
+///
+/// The following return codes are not considered errors:
+///
+/// \retval 0 Success
+///
+/// \retval -GPST_PSTATE_CLIPPED_HIGH_GPST_V2P The requested voltage does not
+/// exist in the table. The highest legal Pstate is returned.
+///
+/// \retval -GPST_PSTATE_CLIPPED_LOW_GPST_V2P The requested voltage does not
+/// exist in the table. The lowest legal Pstate in the table is returned.
+///
+/// The following return codes are considered errors:
+///
+/// \retval -VRM_INVALID_VOLTAGE The \a vid is invalid.
+///
+/// \retval -GPST_INVALID_OBJECT_GPST_V2P The \a gpst argument is NULL (0).
+
+// Recall that VID codes _decrease_ as voltage _increases_
+
+int
+gpst_vdd2pstate(const GlobalPstateTable* gpst,
+ const Vid11 vdd,
+ Pstate* pstate,
+ gpst_entry_t* entry)
+{
+ size_t i;
+ int rc;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(gpst == 0, GPST_INVALID_OBJECT_GPST_V2P);
+ }
+
+ do {
+ rc =vid11_validate(vdd);
+ if (rc) break;
+
+ // Search for the Pstate that contains (close to) the requested
+ // voltage, then handle special cases.
+
+ for (i = 0; i < gpst->entries; i++) {
+ if (gpst->pstate[i].fields.evid_vdd <= vdd) {
+ break;
+ }
+ }
+
+ if (i == gpst->entries) {
+
+ *pstate = gpst_pmax(gpst);
+ *entry = gpst->pstate[i - 1];
+ rc = -GPST_PSTATE_CLIPPED_HIGH_GPST_V2P;
+
+ } else if ((i == 0) && (gpst->pstate[i].fields.evid_vdd < vdd)) {
+
+ *pstate = gpst_pmin(gpst);
+ *entry = gpst->pstate[0];
+ rc = -GPST_PSTATE_CLIPPED_LOW_GPST_V2P;
+
+ } else {
+
+ rc = bias_pstate(gpst_pmin(gpst), i, pstate);
+ if (rc) break;
+
+ *entry = gpst->pstate[i];
+ }
+ } while (0);
+ return rc;
+}
+
+
+/// Translate a frequency in KHz to the closest Pstate in a Global Pstate
+/// table.
+///
+/// \param gpst The GlobalPstateTable to search
+///
+/// \param frequency_khz The frequency in KHz
+///
+/// \param pstate The Pstate most closely matching the frequency, rounded down
+/// (towards lower Pstates).
+///
+///
+/// Note that the Pstate returned may or may not be represented in the Pstate
+/// table. This means that it may be higher that the highest legal frequency
+/// or lower than the lowest frequency represented in the Pstate table.
+///
+/// The following return codes are not considered errors:
+///
+/// \retval 0 Success
+///
+/// \retval -PSTATE_OVERFLOW_GPST_F2P The requested frequency translates to an
+/// unrepresentable Pstate. PSTATE_MAX (127) is returned.
+///
+/// \retval -PSTATE_UNDERFLOW_GPST_F2P The requested frequency translates to an
+/// unrepresentable Pstate. PSTATE_MIN (-128) is returned.
+int
+gpst_frequency2pstate(const GlobalPstateTable* gpst,
+ const uint32_t frequency_khz,
+ Pstate* pstate)
+{
+ int rc;
+ int32_t intPstate;
+
+ // Compute the Pstate and round down
+
+ intPstate =
+ (int32_t)(frequency_khz - gpst->pstate0_frequency_khz) /
+ (int32_t)gpst->frequency_step_khz;
+
+ if (intPstate < 0) {
+
+ if (((int32_t)(frequency_khz - gpst->pstate0_frequency_khz) %
+ (int32_t)gpst->frequency_step_khz) != 0) {
+
+ intPstate--;
+ }
+ }
+
+
+ // Clip to legal Pstate type values
+
+ if (intPstate < PSTATE_MIN) {
+
+ *pstate = PSTATE_MIN;
+ rc = -PSTATE_UNDERFLOW_GPST_F2P;
+
+ } else if (intPstate > PSTATE_MAX) {
+
+ *pstate = PSTATE_MAX;
+ rc = -PSTATE_OVERFLOW_GPST_F2P;
+
+ } else {
+
+ *pstate = intPstate;
+ rc = 0;
+ }
+
+ return rc;
+}
diff --git a/src/lib/pstates.h b/src/lib/pstates.h
new file mode 100755
index 0000000..3e4b1c9
--- /dev/null
+++ b/src/lib/pstates.h
@@ -0,0 +1,601 @@
+#ifndef __PSTATES_H__
+#define __PSTATES_H__
+
+// $Id: pstates.h,v 1.3 2014/05/27 15:35:05 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pstates.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pstates.h
+/// \brief Pstate structures and support routines for OCC product firmware
+
+#include "pgp_common.h"
+
+////////////////////////////////////////////////////////////////////////////
+// Global and Local Pstate Tables
+////////////////////////////////////////////////////////////////////////////
+
+/// The Global Pstate Table must be 1KB-aligned in SRAM. The alignment is
+/// specified in the traditional log2 form.
+#define GLOBAL_PSTATE_TABLE_ALIGNMENT 10
+
+/// The Global Pstate table has 128 * 8-byte entries
+#define GLOBAL_PSTATE_TABLE_ENTRIES 128
+
+/// The Local Pstate table has 32 x 64-bit entries
+#define LOCAL_PSTATE_ARRAY_ENTRIES 32
+
+/// The VDS/VIN table has 32 x 64-bit entries
+#define VDSVIN_ARRAY_ENTRIES 64
+
+/// The VRM-11 VID base voltage in micro-Volts
+#define VRM11_BASE_UV 1612500
+
+/// The VRM-11 VID step as an unsigned number (micro-Volts)
+#define VRM11_STEP_UV 6250
+
+/// The iVID base voltage in micro-Volts
+#define IVID_BASE_UV 600000
+
+/// The iVID step as an unsigned number (micro-Volts)
+#define IVID_STEP_UV 6250
+
+/// CPM Inflection Points
+#define CPM_RANGES 8
+
+// Error/Panic codes for support routines
+
+#define VRM11_INVALID_VOLTAGE 0x00876101
+
+#define PSTATE_OVERFLOW_BIAS_PS 0x00778a01
+#define PSTATE_UNDERFLOW_BIAS_PS 0x00778a02
+#define PSTATE_OVERFLOW_GPST_F2P 0x00778a03
+#define PSTATE_UNDERFLOW_GPST_F2P 0x00778a04
+
+#define PSTATE_LT_PSTATE_MIN 0x00778a05
+#define PSTATE_GT_PSTATE_MAX 0x00778a06
+
+#define DPLL_OVERFLOW 0x00d75501
+#define DPLL_UNDERFLOW1 0x00d75502
+#define DPLL_UNDERFLOW2 0x00d75503
+
+#define VID11_OVERFLOW_VID11_VALIDATE 0x00843101
+#define VID11_OVERFLOW_BIAS_VID11 0x00843102
+#define VID11_UNDERFLOW_VID11_VALIDATE 0x00843103
+#define VID11_UNDERFLOW_BIAS_VID11 0x00843104
+
+#define GPST_INVALID_OBJECT_GPST_ENTRY 0x00477801
+#define GPST_INVALID_OBJECT_GPST_V2P 0x00477802
+#define GPST_INVALID_ARGUMENT 0x00477803
+#define GPST_INVALID_ENTRY 0x00477804
+#define GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA 0x00477805
+#define GPST_PSTATE_CLIPPED_LOW_GPSM_BGA 0x00477806
+#define GPST_PSTATE_CLIPPED_HIGH_GPST_ENTRY 0x00477807
+#define GPST_PSTATE_CLIPPED_LOW_GPST_ENTRY 0x00477808
+#define GPST_PSTATE_CLIPPED_HIGH_GPST_V2P 0x00477809
+#define GPST_PSTATE_CLIPPED_LOW_GPST_V2P 0x0047780a
+#define GPST_BUG 0x0047780b
+#define GPST_PSTATE_GT_GPST_PMAX 0x0047780c
+
+#define LPST_INVALID_OBJECT 0x00477901
+#define LPST_GPST_WARNING 0x00477902
+#define LPST_INCR_CLIP_ERROR 0x00477903
+
+/// PstateSuperStructure Magic Number
+///
+/// This magic number identifies a particular version of the
+/// PstateSuperStructure and its substructures. The version number should be
+/// kept up to date as changes are made to the layout or contents of the
+/// structure.
+
+#define PSTATE_SUPERSTRUCTURE_MAGIC 0x5053544154453033ull /* PSTATE03 */
+#define PSTATE_SUPERSTRUCTURE_GOOD1 0x5053544154453031ull /* PSTATE01 */
+#define PSTATE_SUPERSTRUCTURE_GOOD2 0x5053544154453032ull /* PSTATE02 */
+#define PSTATE_SUPERSTRUCTURE_GOOD3 0x5053544154453033ull /* PSTATE03 */
+
+
+/// \defgroup pstate_options Pstate Options
+///
+/// These are flag bits for the \a options field of the PstateOptions
+/// structure.
+///
+/// @{
+
+/// gpsm_gpst_install() - Bypass copying the Pstate table from the
+/// PstateSuperStructure into the aligned global location.
+#define PSTATE_NO_COPY_GPST 0x01
+
+/// gpsm_gpst_install() - Bypass Global Pstate Table installation and setup.
+#define PSTATE_NO_INSTALL_GPST 0x02
+
+/// gpsm_lpsa_install() - Bylass Local Pstate Array installation and setup
+#define PSTATE_NO_INSTALL_LPSA 0x04
+
+/// gpsm_resclk_install - Bypass resonant clocking Pstate limit setup
+#define PSTATE_NO_INSTALL_RESCLK 0x08
+
+/// gpsm_enable_pstates() - Force the system to the minimum Pstate at
+/// initialization
+///
+/// This mode is added as a workaround for the case that the SPIVID interface
+/// is not working correctly during initial bringup. This forces Pstate mode
+/// to come up at a low frequency.
+#define PSTATE_FORCE_INITIAL_PMIN 0x10
+
+/// @}
+
+#ifndef __ASSEMBLER__
+
+#include <stdint.h>
+
+/// A Global Pstate Table Entry, in the form of a packed 'firmware register'
+///
+/// Global Pstate table entries are referenced by OCC firmware, for example
+/// in procedures that do 'manual' Pstate manipulation.
+
+typedef union gpst_entry {
+
+ uint64_t value;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint32_t high_order;
+ uint32_t low_order;
+#else
+ uint32_t low_order;
+ uint32_t high_order;
+#endif // _BIG_ENDIAN
+ } words;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint64_t evid_vdd : 8;
+ uint64_t evid_vcs : 8;
+ uint64_t reserved16 : 1;
+ uint64_t evid_vdd_eff : 7;
+ uint64_t reserved24 : 1;
+ uint64_t evid_vcs_eff : 7;
+ uint64_t reserved32 : 1;
+ uint64_t maxreg_vdd : 7;
+ uint64_t reserved40 : 1;
+ uint64_t maxreg_vcs : 7;
+ uint64_t reserved48 : 8;
+ uint64_t ecc : 8;
+#else
+ uint64_t ecc : 8;
+ uint64_t reserved48 : 8;
+ uint64_t maxreg_vcs : 7;
+ uint64_t reserved40 : 1;
+ uint64_t maxreg_vdd : 7;
+ uint64_t reserved32 : 1;
+ uint64_t evid_vcs_eff : 7;
+ uint64_t reserved24 : 1;
+ uint64_t evid_vdd_eff : 7;
+ uint64_t reserved16 : 1;
+ uint64_t evid_vcs : 8;
+ uint64_t evid_vdd : 8;
+#endif // _BIG_ENDIAN
+ } fields;
+
+} gpst_entry_t;
+
+
+/// A Local Pstate Table Entry, in the form of a packed 'firmware register'
+///
+/// This structure is provided for reference only; Currently the OCC firmware
+/// does not manupulate Local Pstate table entries, however it is possible
+/// that future lab applications will require this.
+
+typedef union lpst_entry {
+
+ uint64_t value;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint32_t high_order;
+ uint32_t low_order;
+#else
+ uint32_t low_order;
+ uint32_t high_order;
+#endif // _BIG_ENDIAN
+ } words;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint64_t ivid_vdd : 7;
+ uint64_t ivid_vcs : 7;
+ uint64_t vdd_core_pwrratio : 6;
+ uint64_t vcs_core_pwrratio : 6;
+ uint64_t vdd_eco_pwrratio : 6;
+ uint64_t vcs_eco_pwrratio : 6;
+ uint64_t ps1_vid_incr : 3;
+ uint64_t ps2_vid_incr : 3;
+ uint64_t ps3_vid_incr : 3;
+ uint64_t reserved47 : 7;
+ uint64_t inc_step : 3;
+ uint64_t dec_step : 3;
+ uint64_t reserved60 : 4;
+#else
+ uint64_t reserved60 : 4;
+ uint64_t dec_step : 3;
+ uint64_t inc_step : 3;
+ uint64_t reserved47 : 7;
+ uint64_t ps3_vid_incr : 3;
+ uint64_t ps2_vid_incr : 3;
+ uint64_t ps1_vid_incr : 3;
+ uint64_t vcs_eco_pwrratio : 6;
+ uint64_t vdd_eco_pwrratio : 6;
+ uint64_t vcs_core_pwrratio : 6;
+ uint64_t vdd_core_pwrratio : 6;
+ uint64_t ivid_vcs : 7;
+ uint64_t ivid_vdd : 7;
+#endif // _BIG_ENDIAN
+ } fields;
+
+} lpst_entry_t;
+
+
+/// A VDS/VIN table Entry
+
+typedef union vdsvin_entry {
+ uint64_t value;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint32_t high_order;
+ uint32_t low_order;
+#else
+ uint32_t low_order;
+ uint32_t high_order;
+#endif // _BIG_ENDIAN
+ } words;
+ struct {
+#ifdef _BIG_ENDIAN
+ uint64_t ivid0 : 7;
+ uint64_t ivid1 : 7;
+ uint64_t reserved14 : 2;
+ uint64_t pfet0 : 5;
+ uint64_t pfet1 : 5;
+ uint64_t pfet2 : 5;
+ uint64_t pfet3 : 5;
+ uint64_t pfet4 : 5;
+ uint64_t pfet5 : 5;
+ uint64_t pfet6 : 5;
+ uint64_t pfet7 : 5;
+ uint64_t reserved_56 : 8;
+#else
+ uint64_t reserved_56 : 8;
+ uint64_t pfet7 : 5;
+ uint64_t pfet6 : 5;
+ uint64_t pfet5 : 5;
+ uint64_t pfet4 : 5;
+ uint64_t pfet3 : 5;
+ uint64_t pfet2 : 5;
+ uint64_t pfet1 : 5;
+ uint64_t pfet0 : 5;
+ uint64_t reserved14 : 2;
+ uint64_t ivid1 : 7;
+ uint64_t ivid0 : 7;
+#endif // _BIG_ENDIAN
+ } fields;
+} vdsvin_entry_t;
+
+/// Standard options controlling Pstate setup and GPSM procedures
+
+typedef struct {
+
+ /// Option flags; See \ref pstate_options
+ uint32_t options;
+
+ /// Pad structure to 8 bytes. Could also be used for other options later.
+ uint32_t pad;
+
+} PstateOptions;
+
+
+/// An abstract Global Pstate table
+///
+/// The GlobalPstateTable is an abstraction of a set of voltage/frequency
+/// operating points along with hardware limits. Besides the hardware global
+/// Pstate table, the abstract table contains enough extra information to make
+/// it the self-contained source for setting up and managing voltage and
+/// frequency in either Hardware or Firmware Pstate mode.
+///
+/// When installed in PMC, Global Pstate table indices are adjusted such that
+/// the defined Pstates begin with table entry 0. The table need not be full -
+/// the \a pmin and \a entries fields define the minimum and maximum Pstates
+/// represented in the table. However at least 1 entry must be defined to
+/// create a legal table.
+///
+/// Note that Global Pstate table structures to be mapped into PMC hardware
+/// must be 1KB-aligned. This requirement is fullfilled by ensuring that
+/// instances of this structure are 1KB-aligned.
+
+typedef struct {
+
+ /// The Pstate table
+ gpst_entry_t pstate[GLOBAL_PSTATE_TABLE_ENTRIES];
+
+ /// Pstate options
+ ///
+ /// The options are included as part of the GlobalPstateTable so that they
+ /// are available to all procedures after gpsm_initialize().
+ PstateOptions options;
+
+ /// The frequency associated with Pstate[0] in KHz
+ uint32_t pstate0_frequency_khz;
+
+ /// The frequency step in KHz
+ uint32_t frequency_step_khz;
+
+ /// The DPLL frequency code corresponding to Pstate 0
+ ///
+ /// This frequency code is installed in the PCB Slave as the DPLL Fnom
+ /// when the Pstate table is activated. Normally this frequency code is
+ /// computed as
+ ///
+ /// pstate0_frequency_khz / frequency_step_khz
+ ///
+ /// however it may be replaced by any other code as a way to
+ /// transparently bias frequency on a per-core basis.
+ DpllCode pstate0_frequency_code[PGP_NCORES];
+
+ /// The DPLL Fmax bias
+ ///
+ /// This bias value (default 0, range -8 to +7 frequency ticks) is
+ /// installed when the Pstate table is installed. The value is allowed to
+ /// vary per core. This bias value will usually be set to a small
+ /// positive number to provide a small amount of frequency headroom for
+ /// the CPM-DPLL voltage control algorithm.
+ ///
+ /// \bug Hardware currently specifies this field as unsigned for the
+ /// computation of frequency stability in
+ /// dpll_freqout_mode_en. (HW217404). This issue will be fixed in
+ /// Venice. Since we never plan to use this mode no workaround or
+ /// mitigation is provided by GPSM procedures.
+
+ int8_t dpll_fmax_bias[PGP_NCORES];
+
+ /// The number of entries defined in the table.
+ uint8_t entries;
+
+ /// The minimum Pstate in the table
+ ///
+ /// Note that gpsi_min = pmin - PSTATE_MIN, gpsi_max = pmin + entries - 1.
+ Pstate pmin;
+
+ /// The "Safe" Global Pstate
+ ///
+ /// This Pstate is installed in the PMC and represents the safe-mode
+ /// voltage.
+ Pstate pvsafe;
+
+ /// The "Safe" Local Pstate
+ ///
+ /// This Pstate is installed in the PCB Slaves and represents the
+ /// safe-mode frequency.
+ Pstate psafe;
+
+ /// Step size of Global Pstate steps
+ uint8_t pstate_stepsize;
+
+ /// The exponent of the exponential encoding of Pstate stepping delay
+ uint8_t vrm_stepdelay_range;
+
+ /// The significand of the exponential encoding of Pstate stepping delay
+ uint8_t vrm_stepdelay_value;
+
+ /// The Pstate for minimum core frequency in the system, defined by MRW
+ uint8_t pfloor;
+
+} GlobalPstateTable;
+
+
+/// This macro creates a properly-aligned Global Pstate table structure as a
+/// static initialization.
+
+#define GLOBAL_PSTATE_TABLE(x) \
+ GlobalPstateTable x \
+ ALIGNED_ATTRIBUTE(POW2_32(GLOBAL_PSTATE_TABLE_ALIGNMENT)) \
+ SECTION_ATTRIBUTE(".noncacheable") \
+ = {.entries = 0}
+
+
+/// An opaque Local Pstate Array
+///
+/// An array local to each core contains the Local Pstate Table, Vds table and
+/// Vin table. The array contents are presented to OCC firmware as an opaque
+/// set of 96 x 64-bit entries which are simply installed verbatim into each
+/// core. Every core stores the same table.
+///
+/// When installed in the core, Local Pstate table indices are adjusted such
+/// that the defined Pstates begin with table entry 0. The table need not be
+/// full - the \a pmin and \a entries fields define the minimum and maximum
+/// Pstates represented in the table. However at least 1 entry must be
+/// defined to create a legal table.
+
+typedef struct {
+
+ /// The vdsvin table contents
+ vdsvin_entry_t vdsvin[VDSVIN_ARRAY_ENTRIES];
+
+ /// The local pstate table contents
+ lpst_entry_t pstate[LOCAL_PSTATE_ARRAY_ENTRIES];
+
+ /// The number of entries defined in the Local Pstate Table
+ uint8_t entries;
+
+ /// The minimum Pstate in the Local Pstate table
+ ///
+ /// Note that lpsi_min = pmin - PSTATE_MIN, lpsi_max = pmin + entries - 1.
+ Pstate pmin;
+
+ /// Pstate step delay for rising iVRM voltages
+ uint8_t stepdelay_rising;
+
+ /// Pstate step delay for falling iVRM voltages
+ uint8_t stepdelay_lowering;
+
+ /// Pad structure to 8-byte alignment
+ uint8_t pad[4];
+
+} LocalPstateArray;
+
+
+/// Resonant Clocking setup parameters
+///
+/// All Pstate parameters are specified in terms of Pstates as defined in the
+/// current PstateSuperStructure.
+
+typedef struct {
+
+ /// Full Clock Sector Buffer Pstate
+ Pstate full_csb_ps;
+
+ /// Low-Frequency Resonant Lower Pstate
+ Pstate res_low_lower_ps;
+
+ /// Low-Frequency Resonant Upper Pstate
+ Pstate res_low_upper_ps;
+
+ /// High-Frequency Resonant Lower Pstate
+ Pstate res_high_lower_ps;
+
+ /// High-Frequency Resonant Upper Pstate
+ Pstate res_high_upper_ps;
+
+ /// Pad structure to 8-byte alignment
+ uint8_t pad[3];
+
+} ResonantClockingSetup;
+
+/// CPM Pstate ranges per mode
+///
+/// These Pstate range specifications apply to all chiplets operating in the
+/// same mode.
+
+typedef union {
+
+ /// Forces alignment
+ uint64_t quad[2];
+
+ struct {
+
+ /// Lower limit of each CPM calibration range
+ ///
+ /// The entries in this table are Pstates representing the
+ /// lowest-numbered (lowest-voltage) Pstate of each range. This is the
+ /// inflection point between range N and range N+1.
+ Pstate inflectionPoint[CPM_RANGES];
+
+ /// The number of ranges valid in the \a inflectionPoint table
+ ///
+ /// Validity here is defined by the original characterization
+ /// data. Whether or not OCC will use any particular range is managed
+ /// by OCC.
+ uint8_t validRanges;
+
+ /// The Pstate corresponding to the upper limit of range 0.
+ ///
+ /// This is the "CPmax" for the mode. The "CPmin" for this
+ /// mode is the value of inflectionPoint[valid_ranges - 1].
+ Pstate pMax;
+
+ uint8_t pad[6];
+ };
+
+} CpmPstateModeRanges;
+
+
+/// The layout of the data created by the Pstate table creation firmware
+///
+/// This structure is only used for passing Pstate data from the FSP into OCC,
+/// therefore there is no alignment requirement. The \gpst member is copied
+/// to an aligned location, and the \a lpsa and \a resclk members are directly
+/// installed in hardware.
+///
+/// Both the master and slave OCCs (in DCM-mode) install their Pstate tables
+/// independently via the API gpsm_initialize(). At that point the
+/// PstateSuperStructure can be discarded.
+
+typedef struct {
+
+ /// Magic Number
+ uint64_t magic;
+
+ /// Global Pstate Table
+ GlobalPstateTable gpst;
+
+ /// Local Pstate Array
+ LocalPstateArray lpsa;
+
+ /// Resonant Clocking Setup
+ ResonantClockingSetup resclk;
+
+ /// CPM Pstate ranges
+ CpmPstateModeRanges cpmranges;
+
+} PstateSuperStructure;
+
+
+int
+vid11_validate(Vid11 vid);
+
+int
+bias_pstate(Pstate pstate, int bias, Pstate* biased_pstate);
+
+int
+bias_frequency(DpllCode fcode, int bias, DpllCode* biased_fcode);
+
+int
+bias_vid11(Vid11 vid, int bias, Vid11* biased_fcode);
+
+int
+gpst_entry(const GlobalPstateTable* gpst,
+ const Pstate pstate,
+ const int bias,
+ gpst_entry_t* entry);
+
+int
+gpst_frequency2pstate(const GlobalPstateTable* gpst,
+ const uint32_t frequency_khz,
+ Pstate* pstate);
+
+int
+gpst_vdd2pstate(const GlobalPstateTable* gpst,
+ const uint8_t vdd,
+ Pstate* pstate,
+ gpst_entry_t* entry);
+
+
+/// Return the Pmin value associated with a GlobalPstateTable
+static inline Pstate
+gpst_pmin(const GlobalPstateTable* gpst)
+{
+ return gpst->pmin;
+}
+
+
+/// Return the Pmax value associated with a GlobalPstateTable
+static inline Pstate
+gpst_pmax(const GlobalPstateTable* gpst)
+{
+ return (int)(gpst->pmin) + (int)(gpst->entries) - 1;
+}
+
+/// Return the Pmin value associated with a LocalPstateTable
+static inline Pstate
+lpst_pmin(const LocalPstateArray* lpsa)
+{
+ return lpsa->pmin;
+}
+
+
+/// Return the Pmax value associated with a GlobalPstateTable
+static inline Pstate
+lpst_pmax(const LocalPstateArray* lpsa)
+{
+ return (int)(lpsa->pmin) + (int)(lpsa->entries) - 1;
+}
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PSTATES_H__ */
diff --git a/src/lib/puts.c b/src/lib/puts.c
new file mode 100755
index 0000000..ab24d6b
--- /dev/null
+++ b/src/lib/puts.c
@@ -0,0 +1,95 @@
+// $Id: puts.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/puts.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file puts.c
+/// \brief Implementation of puts(), fputs(), putchar(), fputc()
+///
+/// The implementations of these APIs are split out to save code space for
+/// applications that do not require them.
+
+#include "ssx_io.h"
+
+
+/// Put a character to a stream
+///
+/// The fputc() function writes the byte specified by \a c (converted to an
+/// unsigned char) to the stream pointed to by \a stream. On success, fputc()
+/// returns the value written, i.e., the \c unsigned \c char form of \a c.
+///
+/// The POSIX standard fputc() returns EOF on error; this version returns the
+/// negative error code of the underlying I/O error, which will also be set in
+/// the \a error field of the \a stream. Note that SSX may also be configured
+/// to panic in the event of an I/O error.
+
+int
+fputc(int c, FILE *stream)
+{
+ unsigned char uc = (unsigned char)c;
+ int rc = swrite(stream, (void *)(&uc), 1, 0);
+ if (rc < 0) {
+ return rc;
+ } else {
+ return uc;
+ }
+}
+
+
+/// Put a string to a stream
+///
+/// The fputs() function writes the null-terminated string \a s to the stream
+/// pointed to by \a stream. The terminating null byte is not written. On
+/// success, fputs() returns 0.
+///
+/// The POSIX standard fputc() returns EOF on error; this version returns the
+/// negative error code of the underlying I/O error, which will also be set in
+/// the \a error field of the \a stream. Note that SSX may also be configured
+/// to panic in the event of an I/O error.
+
+int
+fputs(const char *s, FILE *stream)
+{
+ return swrite(stream, s, strlen(s), 0);
+}
+
+/// Put a string to \c stdout
+///
+/// The puts() function writes the null-terminated string \a s followed by a
+/// newline to \a stdout. The terminating null byte is not written. On
+/// success, puts() returns 0.
+///
+/// The POSIX standard fputc() returns EOF on error; this version returns the
+/// negative error code of the underlying I/O error, which will also be set in
+/// the \a error field of the \a stream. Note that SSX may also be configured
+/// to panic in the event of an I/O error.
+
+int
+puts(const char *s)
+{
+ int rc = fputs(s, stdout);
+ if (rc < 0) {
+ return rc;
+ } else {
+ rc = fputc('\n', stdout);
+ if (rc < 0) {
+ return rc;
+ } else {
+ return 0;
+ }
+ }
+}
+
+
+/// Put a character to \a stdout.
+///
+/// \c putchar(c) is equivalent to <c> fputc(c, stdout) </c>.
+
+int
+putchar(int c)
+{
+ return fputc(c, stdout);
+}
diff --git a/src/lib/simics_stdio.c b/src/lib/simics_stdio.c
new file mode 100755
index 0000000..5c276a2
--- /dev/null
+++ b/src/lib/simics_stdio.c
@@ -0,0 +1,150 @@
+// $Id: simics_stdio.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/simics_stdio.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file simics_stdio.c
+/// \brief SSX I/O drivers for Simics stdio streams
+///
+/// The Simics 'stdio' component is a pseudo serial port for I/O to stdio,
+/// stdout and stderr, as well as to other configurable streams. Each virtual
+/// file potentially has a read port, write port and flush port.
+///
+/// The write ports accept 1, 2 and 4-byte transactions on a 32-bit OCI
+/// address and write the data to the associated stream. Writing any value to
+/// the flush port flushes the output stream. The input ports are not yet
+/// implemented.
+
+#include "ssx.h"
+#include "simics_stdio.h"
+
+
+SimicsStdio simics_stdin;
+SimicsStdio simics_stdout;
+SimicsStdio simics_stderr;
+
+int
+simics_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read)
+{
+ SSX_PANIC(ENXIO);
+ return -ENXIO;
+}
+
+
+int
+simics_stdio_swrite(FILE *stream, const void *buf,
+ size_t count, size_t *written)
+{
+ SimicsStdio *simics = (SimicsStdio *)stream;
+ size_t n;
+
+ n = count;
+ while (n) {
+ if (n >= 4) {
+ out32(simics->write_address, *((uint32_t *)buf));
+ buf += 4;
+ n -= 4;
+ } else if (n >= 2) {
+ out16(simics->write_address, *((uint16_t *)buf));
+ buf += 2;
+ n -= 2;
+ } else {
+ out8(simics->write_address, *((uint8_t *)buf));
+ buf++;
+ n--;
+ }
+ }
+
+ *written = count;
+
+ return 0;
+}
+
+
+ssize_t
+simics_stdio_fflush(FILE *stream)
+{
+ SimicsStdio *simics = (SimicsStdio *)stream;
+
+ out8(simics->flush_address, 0);
+ return 0;
+}
+
+
+/// Create a SimicsStdio stream
+///
+/// Any of the \a read_address, \a write_address or \a flush_address which are
+/// non-0 specify that the stream supports the associated method. The Simics
+/// I/O drivers do not require locking.
+
+int
+simics_stdio_create(SimicsStdio* stream,
+ SsxAddress read_address,
+ SsxAddress write_address,
+ SsxAddress flush_address)
+{
+ FILE *base = (FILE *)stream;
+ int rc;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(stream == 0, EBADF);
+ }
+
+ rc = FILE_create((FILE*)stream, 0);
+
+ if (!rc) {
+
+ stream->read_address = read_address;
+ if (read_address != 0) {
+ base->sread = simics_stdio_sread;
+ }
+
+ stream->write_address = write_address;
+ if (write_address != 0) {
+ base->swrite = simics_stdio_swrite;
+ }
+
+ stream->flush_address = flush_address;
+ if (flush_address != 0) {
+ base->fflush = simics_stdio_fflush;
+ }
+ }
+
+ return rc;
+}
+
+
+int
+simics_stdin_create(SimicsStdio *stream)
+{
+ return simics_stdio_create(stream, SIMICS_STDIN, 0, 0);
+}
+
+
+int
+simics_stdout_create(SimicsStdio *stream)
+{
+ return simics_stdio_create(stream, 0, SIMICS_STDOUT, SIMICS_STDOUT_FLUSH);
+}
+
+int
+simics_stderr_create(SimicsStdio *stream)
+{
+ return simics_stdio_create(stream, 0, SIMICS_STDERR, SIMICS_STDERR_FLUSH);
+}
+
+int
+simics_stdfile_create(SimicsStdio *stream, int fn)
+{
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((fn < 0) > (fn >= SIMICS_STDFILE_STREAMS), EBADF);
+ }
+
+ return simics_stdio_create(stream,
+ SIMICS_STDFILE_READ(fn),
+ SIMICS_STDFILE_WRITE(fn),
+ SIMICS_STDFILE_FLUSH(fn));
+}
diff --git a/src/lib/simics_stdio.h b/src/lib/simics_stdio.h
new file mode 100755
index 0000000..abbfe26
--- /dev/null
+++ b/src/lib/simics_stdio.h
@@ -0,0 +1,69 @@
+#ifndef __SIMICS_STDIO_H__
+#define __SIMICS_STDIO_H__
+
+// $Id: simics_stdio.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/simics_stdio.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file simics_stdio.h
+/// \brief SSX I/O implementations for Simics stdio streams
+
+#include "ssx_io.h"
+#include "simics_stdio_addresses.h"
+#include "string.h"
+
+/// A FILE structure for a Simics fake stdio stream
+
+typedef struct {
+
+ /// The base class
+ FILE stream;
+
+ /// The MMIO address of the Simics read port for the stream
+ SsxAddress read_address;
+
+ /// The MMIO address of the Simics write port for the stream
+ SsxAddress write_address;
+
+ /// The MMIO address of the Simics device for flushing the stream;
+ SsxAddress flush_address;
+
+} SimicsStdio;
+
+extern SimicsStdio simics_stdin;
+extern SimicsStdio simics_stdout;
+extern SimicsStdio simics_stderr;
+
+int
+simics_stdio_create(SimicsStdio* stream,
+ SsxAddress read_address,
+ SsxAddress write_address,
+ SsxAddress flush_address);
+
+int
+simics_stdin_create(SimicsStdio *stream);
+
+int
+simics_stdout_create(SimicsStdio *stream);
+
+int
+simics_stderr_create(SimicsStdio *stream);
+
+int
+simics_stdfile_create(SimicsStdio *stream, int fn);
+
+int
+simics_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read);
+
+int
+simics_stdio_swrite(FILE *stream, const void *buf,
+ size_t count, size_t *written);
+
+int
+simics_stdio_sflush(FILE *stream);
+
+#endif /* __SIMICS_STDIO_H__ */
diff --git a/src/lib/simics_stdio_addresses.h b/src/lib/simics_stdio_addresses.h
new file mode 100755
index 0000000..9f7d886
--- /dev/null
+++ b/src/lib/simics_stdio_addresses.h
@@ -0,0 +1,67 @@
+#ifndef __SIMICS_STDIO_ADDRESSES_H__
+#define __SIMICS_STDIO_ADDRESSES_H__
+
+// $Id: simics_stdio_addresses.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/simics_stdio_addresses.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file simics_stdio_adresses.h
+/// \brief MMIO addresses and offsets of the Simics fake stdio model
+///
+/// The Simics 'stdio' component is a pseudo serial port for I/O to stdio,
+/// stdout and stderr, as well as to other configurable streams. Each virtual
+/// file potentially has a read port, write port and flush port.
+///
+/// The write ports accept 1, 2 and 4-byte transactions on a 32-bit OCI
+/// address and write the data to the associated stream. Writing any value to
+/// the flush port flushes the output stream. The input ports are not yet
+/// implemented.
+
+// -*- This header is maintained as part of the PMX Simics model. -*-
+// -*- Do not edit in the SSX library as your edits will be lost. -*-
+
+#define SIMICS_STDIO_BASE 0x40060000
+
+#define SIMICS_STDIN_OFFSET 0x00
+#define SIMICS_STDOUT_OFFSET 0x04
+#define SIMICS_STDOUT_FLUSH_OFFSET 0x08
+#define SIMICS_STDERR_OFFSET 0x0c
+#define SIMICS_STDERR_FLUSH_OFFSET 0x10
+
+#define SIMICS_STDFILE_0_OFFSET 0x14
+#define SIMICS_STDFILE_1_OFFSET 0x20
+#define SIMICS_STDFILE_2_OFFSET 0x2c
+#define SIMICS_STDFILE_3_OFFSET 0x38
+
+#define SIMICS_STDFILE_STREAMS 4
+
+#define SIMICS_STDIN (SIMICS_STDIO_BASE + SIMICS_STDIN_OFFSET)
+#define SIMICS_STDOUT (SIMICS_STDIO_BASE + SIMICS_STDOUT_OFFSET)
+#define SIMICS_STDOUT_FLUSH (SIMICS_STDIO_BASE + SIMICS_STDOUT_FLUSH_OFFSET)
+#define SIMICS_STDERR (SIMICS_STDIO_BASE + SIMICS_STDERR_OFFSET)
+#define SIMICS_STDERR_FLUSH (SIMICS_STDIO_BASE + SIMICS_STDERR_FLUSH_OFFSET)
+
+#define SIMICS_STDFILE_READ_OFFSET(fn) \
+ (SIMICS_STDFILE_0_OFFSET + (12 * fn) + 0x00)
+
+#define SIMICS_STDFILE_WRITE_OFFSET(fn) \
+ (SIMICS_STDFILE_0_OFFSET + (12 * fn) + 0x04)
+
+#define SIMICS_STDFILE_FLUSH_OFFSET(fn) \
+ (SIMICS_STDFILE_0_OFFSET + (12 * fn) + 0x08)
+
+#define SIMICS_STDFILE_READ(fn) \
+ (SIMICS_STDIO_BASE + SIMICS_STDFILE_READ_OFFSET(fn))
+
+#define SIMICS_STDFILE_WRITE(fn) \
+ (SIMICS_STDIO_BASE + SIMICS_STDFILE_WRITE_OFFSET(fn))
+
+#define SIMICS_STDFILE_FLUSH(fn) \
+ (SIMICS_STDIO_BASE + SIMICS_STDFILE_FLUSH_OFFSET(fn))
+
+
+#endif /* __SIMICS_STDIO_ADDRESSES_H__ */
diff --git a/src/lib/special_wakeup.c b/src/lib/special_wakeup.c
new file mode 100644
index 0000000..59e3274
--- /dev/null
+++ b/src/lib/special_wakeup.c
@@ -0,0 +1,149 @@
+// $Id: special_wakeup.c,v 1.4 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/special_wakeup.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file special_wakeup.c
+/// \brief Container for special wakeup related procedures
+
+#include "special_wakeup.h"
+
+uint32_t G_special_wakeup_count[PGP_NCORES] = {0};
+
+/// Enter or clear special wakeup for a core
+///
+/// \param set 1 = set, 0 = clear, all other values will cause an error.
+///
+/// \param cores = mask of cores to set/clear special wakeup.
+///
+/// \param[out] o_timeouts. Mask of cores that timed out before special wakeup
+/// complete was observed.
+///
+/// \retval 0 Success
+///
+/// \retval -SPWU_INVALID_ARGUMENT One of the arguments was invalid in some way
+///
+/// \retval others This API may also return non-0 codes from
+/// getscom()/putscom()
+///
+/// If getscom/putscom rc = 0, the state of the global special_wakeup counts
+/// may no longer be valid.
+///
+
+int
+occ_special_wakeup(int set,
+ ChipConfigCores cores,
+ int timeout_ms,
+ ChipConfigCores *o_timeouts)
+
+{
+
+ pmc_core_deconfiguration_reg_t pcdr;
+ pcbs_pmgp0_reg_t pmgp0;
+ pcbs_pmspcwkupocc_reg_t ppr;
+ ChipConfigCores core_list;
+ ChipConfigCores awake_list = 0;
+ ChipConfigCores success_list = 0;
+ ChipConfigCores poll_list = 0;
+ ChipConfigCores timeout_list = 0;
+ int rc, poll_count, core;
+ int time = 0;
+ int bad_clear;
+
+ // get pmc deconfig vector
+ pcdr.value = in32(PMC_CORE_DECONFIGURATION_REG);
+
+ core_list = cores;
+ bad_clear = 0;
+ if (! set) {
+ for (core = 0; core < PGP_NCORES; core++, core_list <<=1) {
+ if (core_list & (0x1 << (PGP_NCORES - 1))) {
+ if (G_special_wakeup_count[core] == 0) {
+ bad_clear = 1;
+ }
+ }
+ }
+ }
+
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF( (set < 0) ||
+ (set > 1) ||
+ (bad_clear) ||
+ (pcdr.fields.core_chiplet_deconf_vector & cores),
+ SPWU_INVALID_ARGUMENT);
+ }
+
+ do {
+ ppr.value = 0;
+ if (set) {
+ // If count is currently zero, set the bit and increment count.
+ // Otherwise, just increment count
+ core_list = cores;
+ for (core = 0; core < PGP_NCORES; core++, core_list <<=1) {
+ if (core_list & (0x1 << (PGP_NCORES - 1))) {
+ if (! G_special_wakeup_count[core]) {
+ ppr.fields.occ_special_wakeup = 1;
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMSPCWKUPOCC_REG, core), ppr.value);
+ poll_list |= (0x1 << (PGP_NCORES - 1 - core));
+ }
+ if (rc) break; // break for loop
+ ++G_special_wakeup_count[core];
+ success_list |= (0x1 << (PGP_NCORES - 1 - core));
+ }
+ }
+ if (rc) break;
+
+ // poll special_wkup_done bit.
+ poll_count = 0;
+ while ((poll_list != awake_list) && (time < timeout_ms)) {
+ if (! poll_count) {
+ ssx_sleep(SSX_MICROSECONDS(2));
+ ++poll_count;
+ } else {
+ ssx_sleep(SSX_MILLISECONDS(5));
+ time += 5;
+ }
+ core_list = poll_list & (~awake_list);
+ for (core = 0; core < PGP_NCORES; core++, core_list <<=1) {
+ if (core_list & (0x1 << (PGP_NCORES - 1))) {
+ rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP0_REG, core), &pmgp0.value);
+ if (rc) break;
+ if (pmgp0.fields.special_wkup_done) {
+ awake_list |= (0x1 << (PGP_NCORES - 1 - core));
+ } else {
+ if (time >= timeout_ms) {
+ timeout_list |= (0x1<<(PGP_NCORES-1-core));
+ }
+ }
+ }
+ }
+ }
+ } else { // clear special wakeup
+ core_list = cores;
+ for (core = 0; core < PGP_NCORES; core++, core_list <<=1) {
+ if (core_list & (0x1 << (PGP_NCORES - 1))) {
+ if (G_special_wakeup_count[core] == 1) {
+ ppr.fields.occ_special_wakeup = 0;
+ rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMSPCWKUPOCC_REG, core), ppr.value);
+ }
+ if (rc) break;
+ --G_special_wakeup_count[core];
+ success_list |= (0x1 << (PGP_NCORES - 1 - core));
+ }
+ }
+ if (rc) break;
+ }
+ } while (0);
+
+ // bad rc recovery (recovery of counts, etc?)
+
+ *o_timeouts = timeout_list;
+ return rc;
+
+}
+
+
diff --git a/src/lib/special_wakeup.h b/src/lib/special_wakeup.h
new file mode 100644
index 0000000..88b8ec0
--- /dev/null
+++ b/src/lib/special_wakeup.h
@@ -0,0 +1,32 @@
+#ifndef __SPECIAL_WAKEUP_H__
+#define __SPECIAL_WAKEUP_H__
+
+// $Id: special_wakeup.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/special_wakeup.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file special_wakeup.h
+/// \brief Container for special wakeup related procedures
+
+#ifndef __ASSEMBLER__
+
+#include <stdint.h>
+#include "pgp_config.h"
+#include "ssx.h"
+
+#define SPWU_INVALID_ARGUMENT 0x00779801
+
+extern uint32_t G_special_wakeup_count[PGP_NCORES];
+
+int
+occ_special_wakeup(int set,
+ ChipConfigCores cores,
+ int timeout_ms,
+ ChipConfigCores *o_timeouts);
+
+#endif /* __ASEMBLER__ */
+#endif /* __SPECIAL_WAKEUP_H__ */
diff --git a/src/lib/sprintf.c b/src/lib/sprintf.c
new file mode 100755
index 0000000..458cd39
--- /dev/null
+++ b/src/lib/sprintf.c
@@ -0,0 +1,112 @@
+// $Id: sprintf.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/sprintf.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file sprintf.c \brief Implementations of sprintf() and snprintf() using
+/// StringStream.
+
+#include "ssx.h"
+#include "ssx_io.h"
+#include "string_stream.h"
+
+
+// sprintf() is easy - just blindly copy in the data.
+
+static int
+sprintf_swrite(FILE *stream, const void *buf, size_t size, size_t *written)
+{
+ StringStream *string = (StringStream *)stream;
+
+ memcpy((void *)(&(string->data[string->next_write])), buf, size);
+ string->next_write += size;
+ *written = size;
+ return 0;
+}
+
+
+// snprintf() requires that bytes that won't fit in the array are simply
+// discarded, but still accounted for. Note that vfprintf() is doing the
+// high-level accounting.
+
+static int
+snprintf_swrite(FILE *stream, const void *buf, size_t size, size_t *written)
+{
+ StringStream *string = (StringStream *)stream;
+
+ size_t to_write = MIN(size, string->size - string->next_write - 1);
+
+ memcpy((void *)(&(string->data[string->next_write])), buf, to_write);
+ string->next_write += to_write;
+ *written = size;
+ return 0;
+}
+
+
+// We use a StringStream to implement [v]sprintf() and [v]snprintf(). Once
+// the formatting is finished the NULL terminator is added.
+
+int
+vsprintf(char *str, const char *format, va_list argp)
+{
+ ssize_t rc;
+ StringStream stream;
+
+ _string_stream_create(&stream, str, 1, 0, sprintf_swrite);
+ rc = vfprintf((FILE *)(&stream), format, argp);
+ stream.data[stream.next_write] = 0;
+ return rc;
+}
+
+
+
+int
+sprintf(char *str, const char *format, ...)
+{
+ va_list argp;
+ ssize_t rc;
+
+ va_start(argp, format);
+ rc = vsprintf(str, format, argp);
+ va_end(argp);
+
+ return rc;
+}
+
+
+int
+vsnprintf(char *str, size_t size, const char *format, va_list argp)
+{
+ ssize_t rc;
+ StringStream stream;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ _string_stream_create(&stream, str, size, 0, snprintf_swrite);
+ rc = vfprintf((FILE *)(&stream), format, argp);
+ stream.data[stream.next_write] = 0;
+ return rc;
+}
+
+
+int
+snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list argp;
+ ssize_t rc;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ va_start(argp, format);
+ rc = vsnprintf(str, size, format, argp);
+ va_end(argp);
+
+ return rc;
+}
diff --git a/src/lib/ssx_dump.c b/src/lib/ssx_dump.c
new file mode 100644
index 0000000..93ce1ea
--- /dev/null
+++ b/src/lib/ssx_dump.c
@@ -0,0 +1,209 @@
+// $Id: ssx_dump.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_dump.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ssx_dump.c
+/// \brief Routines for dumping SSX kernel data structures
+///
+/// \note This is a quick hack to help solve a P8 bringup issue. This code is
+/// PPC405 (OCC) specific, i.e., not "portable" SSX. Ideally this type of dump
+/// would be implemented in an external debugging tool as well, however it's
+/// simplest to implement here in the execution context.
+
+#include "ssx_dump.h"
+
+#define SSX_DUMP_UNIMPLEMENTED 0x00d86701
+
+static const char* _threadState[] = {
+ 0,
+ "Suspended Runnable",
+ "Mapped",
+ "Suspended Blocked",
+ "Completed",
+ "Deleted",
+};
+
+static const char* _threadFlags[] = {
+ 0,
+ "Semaphore Pend",
+ "Timer Pend",
+ "Timer Pend | Semaphore Pend",
+ "Timed Out",
+ "Timed Out | Semaphore Pend",
+ "Timed Out | Timer Pend",
+ "Timed Out | Timer Pend | Semaphore Pend",
+};
+
+
+static void
+_dumpTimer(FILE* stream, SsxTimer* timer)
+{
+ fprintf(stream,
+ "-- Timer @ %p\n"
+ "-- Deque.previous = %p\n"
+ "-- Deque.next = %p\n"
+ "-- Timeout = 0x%016llx\n"
+ "-- Period = 0x%016llx\n"
+ "-- Callback = %p\n"
+ "-- Arg = %p\n"
+ "-- Options = 0x%02x\n",
+ timer,
+ timer->deque.previous,
+ timer->deque.next,
+ timer->timeout,
+ (unsigned long long)(timer->period),
+ timer->callback,
+ timer->arg,
+ timer->options);
+}
+
+
+static void
+_dumpThread(FILE* stream, SsxThread* thread)
+{
+ SsxThreadContext* threadCtx;
+ SsxThreadContextFullIrq* threadCtxIrq;
+ uint32_t srr[4], lr, sp;
+
+ fprintf(stream,
+ "-- Thread mapped at priority %d (%p)\n"
+ "-- Thread state = %s (%d)\n"
+ "-- Thread flags = %s (0x%02x)\n"
+ "-- Saved Stack Pointer = %p\n",
+ thread->priority, thread,
+ _threadState[thread->state], thread->state,
+ _threadFlags[thread->flags], thread->flags,
+ (void*)thread->saved_stack_pointer);
+
+ if (thread->flags & SSX_THREAD_FLAG_SEMAPHORE_PEND) {
+
+ fprintf(stream,
+ "-- Semaphore = %p\n",
+ (void*)thread->semaphore);
+ }
+ fprintf(stream,
+ "---------------------------------------------\n");
+
+ if (thread->flags & SSX_THREAD_FLAG_TIMER_PEND) {
+
+ _dumpTimer(stream, &(thread->timer));
+ fprintf(stream,
+ "---------------------------------------------\n");
+ }
+
+ if ((thread == ssx_current()) && !__ssx_kernel_context_any_interrupt()) {
+
+ fprintf(stream,
+ "-- This thread is executing ssx_dump()\n");
+
+ } else {
+
+ if (thread == ssx_current()) {
+
+ // This is the interrupted thread, and only has its volatile
+ // context saved. The thread stack pointer is stored in a global
+ // kernel variable.
+
+ if (__ssx_kernel_context_critical_interrupt()) {
+
+ SSX_PANIC(SSX_DUMP_UNIMPLEMENTED);
+ srr[0] = srr[1] = srr[2] = srr[3] = lr = sp = 0; /* For GCC */
+
+ } else {
+
+ threadCtxIrq =
+ (SsxThreadContextFullIrq*)__ssx_saved_sp_noncritical;
+ srr[0] = threadCtxIrq->srr0;
+ srr[1] = threadCtxIrq->srr1;
+ srr[2] = threadCtxIrq->srr2;
+ srr[3] = threadCtxIrq->srr3;
+ lr = threadCtxIrq->lr;
+ sp = threadCtxIrq->r1;
+ }
+ } else {
+
+ // This is a fully swapped-out thread. The context is saved in
+ // at the stored stack pointer.
+
+ threadCtx = (SsxThreadContext*)(thread->saved_stack_pointer);
+ srr[0] = threadCtx->srr0;
+ srr[1] = threadCtx->srr1;
+ srr[2] = threadCtx->srr2;
+ srr[3] = threadCtx->srr3;
+ lr = threadCtx->lr;
+ sp = ((uint32_t*)threadCtx->r1)[0];
+ }
+
+ fprintf(stream,
+ "-- SRR0: 0x%08x SRR1: 0x%08x "
+ "SRR2: 0x%08x SRR3: 0x%08x\n"
+ "-- LR: 0x%08x\n",
+ srr[0], srr[1], srr[2], srr[3],
+ lr);
+
+ fprintf(stream,
+ "---------------------------------------------\n");
+
+ // Unwind the stack
+
+ while (sp != 0) {
+
+ fprintf(stream,
+ "-- SP: 0x%08x *LR*:0x%08x\n",
+ sp, ((uint32_t*)sp)[1]);
+ sp = ((uint32_t*)sp)[0];
+ }
+ }
+}
+
+
+
+
+void
+ssx_dump(FILE* stream, int options)
+{
+ int i, sep;
+ SsxThread* thread;
+
+ fprintf(stream,
+ "------------------------------------------------------------\n");
+ fprintf(stream,
+ "-- SSX Kernel Dump @ 0x%016llx\n"
+ "-- USPRG0 = 0x%08x\n"
+ "-- __ssx_run_queue = 0x%08x\n",
+ ssx_timebase_get(),
+ mfspr(SPRN_USPRG0),
+ __ssx_run_queue);
+ fprintf(stream,
+ "------------------------------------------------------------\n");
+
+ sep = 0;
+
+ for (i = 0; i < SSX_THREADS; i++) {
+
+ ssx_thread_at_priority(i, &thread);
+ if (thread) {
+ if (sep) {
+ fprintf(stream,
+ "*********************************************\n");
+ }
+ _dumpThread(stream, thread);
+ sep = 1;
+ }
+ }
+
+ fprintf(stream,
+ "------------------------------------------------------------\n");
+}
+
+
+
+
+
+
+
+
diff --git a/src/lib/ssx_dump.h b/src/lib/ssx_dump.h
new file mode 100644
index 0000000..17455c0
--- /dev/null
+++ b/src/lib/ssx_dump.h
@@ -0,0 +1,58 @@
+#ifndef __SSX_DUMP_H__
+#define __SSX_DUMP_H__
+
+// $Id: ssx_dump.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_dump.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ssx_dump.h
+/// \brief Routines for dumping SSX kernel data structures
+
+#include "ssx.h"
+
+/// \defgroup ssx_dump_options Options for ssx_dump()
+///
+/// No options are currently specified.
+///
+/// @{
+
+/// @}
+
+#ifndef __ASSEMBLER__
+
+/// Dump the kernel state
+///
+/// \param i_stream The stream to receive the dump. If the dump is being
+/// generated prior to a kernel panic then this would typically be \a ssxout,
+/// the stream used by printk.
+///
+/// \param i_options AN OR-mask of option flags; See \ref ssx_dump_options
+///
+/// The SSX kernel dump produces a formatted snapshot of the state of the
+/// kernel and the mapped threads. This API does not manipulate the machine
+/// context; If it is required to produce a precise snapshot then the caller
+/// will need to make the call from a critical section.
+///
+/// The following information is standard in the dump
+///
+/// - The interrupt and thread state of the kernel
+/// - The state of each thread
+/// - A stack trace for each thread
+///
+/// Options : TBD
+///
+/// \bug There are likely several bugs in the current implementation due to
+/// the assumption that the code is being called from a state in which the
+/// kernel context is not changing. We don't have time to code and test the
+/// most general implementation now. To guarantee correct operation the API
+/// must currently be called from an SSX_CRITICAL critical section.
+void
+ssx_dump(FILE* stream, int options);
+
+#endif // __ASSEMBLER__
+
+#endif // __SSX_DUMP_H__
diff --git a/src/lib/ssx_io.c b/src/lib/ssx_io.c
new file mode 100755
index 0000000..e69dcbe
--- /dev/null
+++ b/src/lib/ssx_io.c
@@ -0,0 +1,311 @@
+// $Id: ssx_io.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_io.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ssx_io.c
+/// \brief SSX analog-replacement for C \<stdio.h\> and \<unistd.h\> functions
+///
+/// The SSX library provides simple analogs for I/O functions found in the
+/// typical C libraries and Posix standards. I/O is not considered part of the
+/// SSX kernel per-se - I/O is implemented by the application creating I/O
+/// abstractions and drivers using the simple SSX kernel services. [However,
+/// the ssx_io library does provide a printk() function for use by kernel
+/// drivers, so the lines are not completely clear.]
+///
+/// This library provides a FILE structure or 'base class' to represent a
+/// character stream. Abstractions for different devices will 'derive' a
+/// subclass from this base class by including the FILE as the first member of
+/// the subclass, and then likely adding more data members. The FILE
+/// structure contains pointers to the routines that implement I/O operations
+/// for derived classes. There is no requiremengt that a stream type implement
+/// every possible stream operation. If an unsupported operation is invoked
+/// on a stream then the call will simply return -ENXIO, unless otherwise
+/// specified.
+///
+/// The generic I/O calls use the same error-handling protocol as used by the
+/// SSX kernel services. A configuration setting (\c SSX_ERROR_CHECK_API)
+/// determines if error checks are made at all, and another setting (\c
+/// SSX_ERROR_PANIC) determines whether the presence of a fatal error causes a
+/// bad return code or an immediate panic.
+///
+/// The following errors are never considered fatal errors:
+///
+/// \b EOF Used to signal end-of-file
+///
+/// \b EAGAIN Used to signal that an operation was made to a non-blocking
+/// stream and the call would block.
+///
+/// Following are the specifications and requirements for the stream methods
+/// that implement a stream type.
+///
+/// <b> int sread(FILE *stream, void *buf, size_t count, size_t *read) </b>
+///
+/// The low-level read() function is named sread() here due to differences in
+/// the prototype and semantics from the Unix counterpart. The sread()
+/// functions attempts to read \a count bytes from the \a stream into \a buf,
+/// sets the number of bytes read, and returns either 0 for success or a
+/// negative error code. The only reason that sread() may terminate without
+/// reading \a count bytes is if sread() also returns a negative return
+/// code.
+///
+/// The device-specific sread() function may assume that the \a stream, \a buf
+/// and \a read parameters are non-NULL at entry, and the \a count is greater
+/// than 0. These conditions are checked/guaranteed by the generic interface.
+///
+/// <b> int swrite(FILE *stream, void *buf, size_t count, size_t *written) </b>
+///
+/// The low-level write() function is named swrite() here due to differences
+/// in the prototype and semantics from the Unix counterpart. The swrite()
+/// function attempts to writes \a count bytes from buf to the \a stream, sets
+/// the number of bytes written, and returns either 0 for success or a
+/// negative error code. The only reason that swrite() may terminate without
+/// writing \a count bytes is if write() also returns a negative return
+/// code.
+///
+/// The device-specific swrite() function may assume that the \a stream, \a
+/// buf and \a written parameters are non-NULL at entry, and the \a count is
+/// greater than 0. These conditions are checked/guaranteed by the generic
+/// interface.
+///
+/// <b> int fflush(FILE *stream) </b>
+///
+/// SSX implements the fflush() call, even though the semantics are slightly
+/// different from the Posix standard. The Posix fflush() returns either 0
+/// for success, or EOF to indicate an error. Here, fflush() returns negative
+/// 'errno' codes directly in the event of errors. The Posix standard
+/// specifies that calling fflush() with a NULL \a stream causes all open
+/// files to be flushed - here it returns the -EBADF error.
+///
+/// The stream-specific fflush() implementation should return one of the
+/// 'errno' codes specified by the Posix standard in the event of
+/// problems.
+
+#include "ssx.h"
+#include "ssx_io.h"
+
+/// Initialize an SSX I/O FILE structure
+///
+/// \param file A pointer to an uninitialized FILE
+///
+/// \param flags Flags for the generic FILE
+///
+/// This API is designed to be called from "constructors of classes derived
+/// from FILE", e.g., the StringStream. The generic FILE structure is
+/// cleared, and the flags are installed and other initialization based on the
+/// flags is performed. The subclass constructors then install function
+/// pointers for any of the FILE functions that they implement.
+///
+/// \retval 0 Success
+///
+/// \retval -FILE_INVALID_OBJECT The \a file pointer is null (0).
+///
+/// \retval -FILE_INVALID_ARGUMENT The \a flags are not valid.
+
+int
+FILE_create(FILE* file, int flags)
+{
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(file == 0, FILE_INVALID_OBJECT);
+ SSX_ERROR_IF(flags & ~SSX_FILE_VALID_FLAGS, FILE_INVALID_ARGUMENT);
+ SSX_ERROR_IF(__builtin_popcount(flags & SSX_FILE_OP_LOCK_OPTIONS) > 1,
+ FILE_INVALID_ARGUMENT);
+ }
+ memset((void*)file, 0, sizeof(FILE));
+ file->flags = flags;
+ ssx_semaphore_create(&(file->fop_sem), 1, 1);
+ return 0;
+}
+
+
+/// Assign an error code to a stream, returning a non-zero value for errors
+/// considered fatal.
+
+int
+ssx_io_error_set(FILE *stream, int code)
+{
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(stream == 0, EBADF);
+ }
+ stream->error = code;
+ if ((code == EOF) ||
+ (code == EAGAIN)) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+
+static int
+null_stream_sread(FILE *stream, void *buf, size_t count, size_t *read)
+{
+ *read = 0;
+ ssx_io_error_set(stream, EOF);
+ return EOF;
+}
+
+
+static int
+null_stream_swrite(FILE *stream, const void *buf, size_t count, size_t *written)
+{
+ *written = count;
+ return 0;
+}
+
+
+static int
+null_stream_fflush(FILE *stream)
+{
+ return 0;
+}
+
+
+/// A null stream; equivalent to Unix /dev/null
+///
+/// This stream is statically initialized as it is required to be assigned to
+/// \a ssxout at load time. The NULL stream does not require any locking.
+
+FILE _null_stream = {
+ .sread = null_stream_sread,
+ .swrite = null_stream_swrite,
+ .fflush = null_stream_fflush,
+ .error = 0,
+ .flags = 0
+};
+
+
+// It's up to the application to initialize the standard streams if it wishes
+// to use them. Reading or writing a NULL stream will signal an error.
+
+FILE *stdin = 0;
+FILE *stdout = 0;
+FILE *stderr = 0;
+
+/// Stream used by printk()
+///
+/// \a ssxout is the stream used by printk(). \a ssxout \e must be assigned
+/// to a non-blocking stream as it may be called from interrupt handlers. \a
+/// ssxout defaults to _null_stream, the equivalent of Unix' /dev/null.
+
+FILE *ssxout = &_null_stream;
+
+
+/// /dev/null for SSX
+FILE* ssxnull = &_null_stream;
+
+
+/// Lock low-level file operations
+///
+/// The decision on if and how low-level FILE operations needs to be locked is
+/// made by the class derived from FILE, and may also require an argument
+/// about its intended use. The minimum bar is always correctness. The next
+/// bar is whether the application allows data from multiple writers to be
+/// intermixed, or allows multiple readers to access the same input stream.
+///
+/// A device like the StringStream used to implement sprintf() and snprintf()
+/// does not require locking at all because it is only called from a single
+/// context. String streams that implement circular buffers must be locked if
+/// there is the possibility of multiple readers or writers, and the wrapping
+/// buffer always needs to be locked.
+///
+/// The lock implemented here is a lock on the low-level stream operations
+/// sread(), swrite() and fflush().
+
+#define LOCK_FILE_OPERATION(stream, operation) \
+ ({ \
+ int __rc; \
+ SsxMachineContext __ctx = SSX_THREAD_MACHINE_CONTEXT_DEFAULT; /* GCC */ \
+ if ((stream)->flags & SSX_FILE_OP_LOCK_OPTIONS) { \
+ if ((stream)->flags & SSX_FILE_OP_LOCK_CRITICAL) { \
+ ssx_critical_section_enter(SSX_CRITICAL, &__ctx); \
+ } else if ((stream)->flags & SSX_FILE_OP_LOCK_NONCRITICAL) { \
+ ssx_critical_section_enter(SSX_NONCRITICAL, &__ctx); \
+ } else { \
+ ssx_semaphore_pend(&((stream)->fop_sem), SSX_WAIT_FOREVER); \
+ } \
+ } \
+ __rc = (operation); \
+ if ((stream)->flags & SSX_FILE_OP_LOCK_OPTIONS) { \
+ if ((stream)->flags & (SSX_FILE_OP_LOCK_CRITICAL | \
+ SSX_FILE_OP_LOCK_NONCRITICAL)) { \
+ ssx_critical_section_exit(&__ctx); \
+ } else { \
+ ssx_semaphore_post(&((stream)->fop_sem)); \
+ } \
+ } \
+ __rc; \
+ })
+
+
+/// Call the sread() operation of a stream
+
+int
+sread(FILE *stream, void *buf, size_t count, size_t *read)
+{
+ ssize_t rc;
+ size_t _read;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((stream == 0), EBADF);
+ SSX_IO_ERROR_IF(stream, (stream->sread == 0), ENXIO);
+ SSX_IO_ERROR_IF(stream, (buf == 0), EINVAL);
+ }
+ if (count == 0) {
+ if (read != 0) {
+ *read = 0;
+ }
+ return 0;
+ }
+ rc = LOCK_FILE_OPERATION(stream, stream->sread(stream, buf, count, &_read));
+ if (read != 0) {
+ *read = _read;
+ }
+ return rc;
+}
+
+
+/// Call the swrite() operation of a stream
+
+ssize_t
+swrite(FILE *stream, const void *buf, size_t count, size_t *written)
+{
+ ssize_t rc;
+ size_t _written;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((stream == 0), EBADF);
+ SSX_IO_ERROR_IF(stream, (stream->swrite == 0), ENXIO);
+ SSX_IO_ERROR_IF(stream, (buf == 0), EINVAL);
+ }
+ if (count == 0) {
+ rc = 0;
+ _written = 0;
+ } else {
+ rc = LOCK_FILE_OPERATION(stream,
+ stream->swrite(stream, buf, count, &_written));
+ }
+ if (written != 0) {
+ *written = _written;
+ }
+ return rc;
+}
+
+
+/// Call the fflush() operation of the stream
+
+int
+fflush(FILE *stream)
+{
+ int rc;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_IO_ERROR_IF(stream, (stream == 0), EBADF);
+ SSX_IO_ERROR_IF(stream, (stream->fflush == 0), ENXIO);
+ }
+ rc = LOCK_FILE_OPERATION(stream, stream->fflush(stream));
+ return rc;
+}
diff --git a/src/lib/ssx_io.h b/src/lib/ssx_io.h
new file mode 100755
index 0000000..794fe5d
--- /dev/null
+++ b/src/lib/ssx_io.h
@@ -0,0 +1,204 @@
+#ifndef __SSX_IO_H__
+#define __SSX_IO_H__
+
+// $Id: ssx_io.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_io.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ssx_io.h
+/// \brief SSX analog-replacement for C <stdio.h> and <unistd.h> functions
+
+#if !defined(_STDIO_H) && !defined(_UNISTD_H)
+
+#include "ssx_macros.h"
+#include "ssx_api.h"
+
+#include "ctype.h"
+#include "errno.h"
+#include "string.h"
+
+#define EOF -1
+
+#define FILE_INVALID_OBJECT 0x00345301
+#define FILE_INVALID_ARGUMENT 0x00345302
+
+#ifndef __ASSEMBLER__
+
+#include <stdarg.h>
+
+/// The SSX implementation of the FILE structure
+
+typedef struct FILE {
+
+ /// The sread() method
+ int (*sread)(struct FILE *stream, void *buf,
+ size_t count, size_t *read);
+
+ /// The swrite() method
+ int (*swrite)(struct FILE *stream, const void *buf,
+ size_t count, size_t *written);
+
+ /// The fflush() method
+ int (*fflush)(struct FILE *stream);
+
+ /// The last error code encountered
+ ///
+ /// This field is not set in the event of an error panic.
+ int error;
+
+ /// SSX file/stream flags; See \ref ssx_file_flags
+ int flags;
+
+ /// The semaphore used to lock low-level file operations if required.
+ SsxSemaphore fop_sem;
+
+ /// The number of newline characters read by fgetc.
+ ///
+ /// This variable supports an SSX extension to <stdio.h>, the flines()
+ /// API. flines() returns the number of newline characters read by
+ /// fgetc(). This counter does not count newline characters read using
+ /// sread() on the stream directly.
+ size_t lines;
+
+ /// The character pushed back by ungetc()
+ ///
+ /// If a character is back the flag SSX_FILE_HAS_CHARACTER will be
+ /// set. Note that characters pushed back with ungetc() are not returned
+ /// by any subsequent sread() call on the stream, so it is best not to mix
+ /// sread() and fgetc().
+ unsigned char character;
+
+} FILE;
+
+
+/// \defgroup ssx_file_flags SSX File/Stream Flags
+/// @{
+
+/// Low-level access is locked by an SSX_CRITICAL critical section
+#define SSX_FILE_OP_LOCK_CRITICAL 0x1
+
+/// Low-level access is locked by an SSX_NONCRITICAL critical section
+#define SSX_FILE_OP_LOCK_NONCRITICAL 0x2
+
+/// Low-level access is locked by a semaphore
+#define SSX_FILE_OP_LOCK_SEMAPHORE 0x4
+
+/// Mask of all locking options;
+#define SSX_FILE_OP_LOCK_OPTIONS 0x7
+
+/// The FILE has a character pushed back by ungetc()
+#define SSX_FILE_HAS_CHARACTER 0xf
+
+/// All valid flags
+#define SSX_FILE_VALID_FLAGS 0xf
+
+/// @}
+
+
+extern FILE *stdin;
+extern FILE *stdout;
+extern FILE *stderr;
+
+/// SSX kernel output stream
+///
+/// This stream implements the printk() API. It must be a non-blocking stream
+/// so that it can be used in interrupt contexts.
+
+extern FILE *ssxout;
+
+extern FILE *ssxnull;
+
+int
+FILE_create(FILE *stream, int flags);
+
+int
+sread(FILE *stream, void *buf, size_t count, size_t *read);
+
+int
+swrite(FILE *stream, const void *buf, size_t count, size_t *written);
+
+int
+fflush(FILE *stream);
+
+int
+vfprintf(FILE *stream, const char *format, va_list argp)
+ __attribute__ ((format (printf, 2, 0)));
+
+int
+vprintf(const char *format, va_list argp)
+ __attribute__ ((format (printf, 1, 0)));
+
+int
+fprintf(FILE *stream, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+int
+printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+int
+printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+int
+vsprintf(char *str, const char *format, va_list argp)
+ __attribute__ ((format (printf, 2, 0)));
+
+int
+sprintf(char *str, const char *format, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+int
+vsnprintf(char *str, size_t size, const char *format, va_list argp)
+ __attribute__ ((format (printf, 3, 0)));
+
+int
+snprintf(char *str, size_t size, const char *format, ...)
+ __attribute__ ((format (printf, 3, 4)));
+
+int
+fputc(int c, FILE *stream);
+
+int
+fputs(const char *s, FILE *stream);
+
+int
+puts(const char *s);
+
+int
+putchar(int c);
+
+int
+fgetc(FILE* stream);
+
+int
+ungetc(int c, FILE* stream);
+
+size_t
+flines(FILE* stream);
+
+int
+ssx_io_error_set(FILE *stream, int code);
+
+/// Handle I/O errors including panic configurations
+#define SSX_IO_ERROR_IF(stream, condition, code) \
+ do { \
+ if (condition) { \
+ if (ssx_io_error_set(stream, code) && SSX_ERROR_PANIC) { \
+ SSX_PANIC(code); \
+ } else { \
+ return -(code); \
+ } \
+ } \
+ } while (0)
+
+#define SSX_IO_ERROR(stream, code) SSX_IO_ERROR_IF((stream), 1, (code))
+
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* !defined(_STDIO_H) && !defined(_UNISTD_H) */
+
+#endif /* __SSX_IO_H__ */
diff --git a/src/lib/stdlib.c b/src/lib/stdlib.c
new file mode 100755
index 0000000..2dcc009
--- /dev/null
+++ b/src/lib/stdlib.c
@@ -0,0 +1,91 @@
+// $Id: stdlib.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/stdlib.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file stdlib.c
+/// \brief Functions from <stdlib.h>
+///
+/// \note The strtoX() APIs are defined in strtox.[ch]
+
+#include "ssx.h"
+#include "ctype.h"
+#include "libssx.h"
+#include <stdlib.h>
+
+
+/// Convert a string to a long integer - base 10 only
+///
+/// atol(str) is defined here as strtol(str, 0, 10) in sympathy with the POSIX
+/// standard. Note that by specification "atol() does not detect
+/// errors", however here it will behave the same as the strtol() call just
+/// mentioned.
+
+long
+atol(const char *str)
+{
+ return strtol(str, 0, 10);
+}
+
+
+/// Convert a string to an integer - base 10 only
+///
+/// atoi(str) is defined here as strtol(str, 0, 10) in sympathy with the POSIX
+/// standard. Note that by specification "atoi() does not detect errors",
+/// however in this implementation the long integer returned by the strtol()
+/// call just mentioned is simply converted to an int.
+
+int
+atoi(const char *str)
+{
+ return strtol(str, 0, 10);
+}
+
+
+/// 'Exit' an application
+///
+/// An SSX application can not really 'exit'. By convention, exit(0) is the
+/// same as ssx_halt(). Calling exit() with a non-zero code causes a kernel
+/// panic - the exit code will be found in R3 on PowerPC.
+///
+/// Note that to exit a thread, the thread can either return from the thread
+/// entry routine or explicitly call ssx_complete(). exit() was implemented
+/// to allow porting of the EEMBC benchmarks.
+
+void
+exit(int status)
+{
+ if (status) {
+ SSX_PANIC(ERROR_EXIT);
+ }
+ ssx_halt();
+}
+
+
+/// Compute the absolute value of the integer argument
+int
+abs(int i)
+{
+ return ((i < 0) ? -i : i);
+}
+
+
+/// Compute the absolute value of the long integer argument
+long int
+labs(long int i)
+{
+ return ((i < 0) ? -i : i);
+}
+
+
+/// Compute the absolute value of the long long integer argument
+long long int
+llabs(long long int i)
+{
+ return ((i < 0) ? -i : i);
+}
+
+
diff --git a/src/lib/strcasecmp.c b/src/lib/strcasecmp.c
new file mode 100644
index 0000000..361a387
--- /dev/null
+++ b/src/lib/strcasecmp.c
@@ -0,0 +1,66 @@
+// $Id: strcasecmp.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strcasecmp.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file strcasecmp.c
+/// \brief Implementation of strcasecmp() and strncasecmp()
+///
+/// These routines are rarely used, hence broken out into a separate file to
+/// save code space for most applications.
+
+#include "ssx.h"
+#include "string.h"
+
+/// Compare two strings ignoring case
+///
+/// The strcasecmp() function compares the two strings \a s1 and \a s2,
+/// ignoring the case of the characters. It returns an integer less than,
+/// equal to, or greater than zero if \a s1 is found, respectively, to be less
+/// than, to match, or be greater than \a s2.
+
+int
+strcasecmp(const char* s1, const char* s2)
+{
+ int rc;
+
+ if (s1 == s2) {
+ rc = 0;
+ } else {
+ while(*s1 && (tolower(*s1) == tolower(*s2))) {
+ s1++;
+ s2++;
+ }
+ rc = *((unsigned char *)s1) - *((unsigned char *)s2);
+ }
+ return rc;
+}
+
+
+/// Compare a portion of two strings ignoring case
+///
+/// The strncmp() function compares at most the first \n characters of the two
+/// strings \a s1 and \a s2, ignoring the case of the characters. It returns
+/// an integer less than, equal to, or greater than zero if (the prefix of) \a
+/// s1 is found, respectively, to be less than, to match, or be greater than
+/// (the prefix of) \a s2.
+
+int
+strncasecmp(const char* s1, const char* s2, size_t n)
+{
+ int rc;
+
+ if ((s1 == s2) || (n == 0)) {
+ rc = 0;
+ } else {
+ while(*s1 && (tolower(*s1) == tolower(*s2)) && n--) {
+ s1++;
+ s2++;
+ }
+ rc = *((unsigned char *)s1) - *((unsigned char *)s2);
+ }
+ return rc;
+}
diff --git a/src/lib/strdup.c b/src/lib/strdup.c
new file mode 100755
index 0000000..c6ac04c
--- /dev/null
+++ b/src/lib/strdup.c
@@ -0,0 +1,39 @@
+// $Id: strdup.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strdup.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file strdup.c
+/// \brief Functions from <string.h> that require malloc()
+///
+/// These APIs are split from string.c for the benefit of applications like
+/// OCC FW that don't use malloc().
+
+#include <stdlib.h>
+#include <string.h>
+
+/// Duplicate a string
+///
+/// \param s The string to duplicate
+///
+/// The strdup() function returns a pointer to a new string which is a
+/// duplicate of the input string \a s. Memory for the new string is obtained
+/// with malloc(), and can be freed with free().
+///
+/// \returns The strdup() function returns a pointer to the duplicated string,
+/// or NULL (0) if insufficient memory was available.
+
+char *
+strdup(const char* s)
+{
+ char* dup;
+
+ dup = (char*)malloc(strlen(s) + 1);
+ if (dup != 0) {
+ strcpy(dup, s);
+ }
+ return dup;
+}
diff --git a/src/lib/string.c b/src/lib/string.c
new file mode 100755
index 0000000..81d1778
--- /dev/null
+++ b/src/lib/string.c
@@ -0,0 +1,168 @@
+// $Id: string.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file string.c
+/// \brief strlen(), strcmp() etc. functions
+
+#include "ssx.h"
+#include "string.h"
+
+/// Compute the length of a string
+///
+/// The strlen() function calculates the length of the string \a s, not
+/// including the terminating \b '\0' character. The strlen() function
+/// returns the number of characters in \a s.
+
+size_t
+strlen(const char *s)
+{
+ const char *p = s;
+
+ while (*p) {
+ p++;
+ }
+
+ return p - s;
+}
+
+
+/// Compare two strings
+///
+/// The strcmp() function compares the two strings \a s1 and \a s2. It
+/// returns an integer less than, equal to, or greater than zero if \a s1 is
+/// found, respectively, to be less than, to match, or be greater than \a s2.
+
+int
+strcmp(const char* s1, const char* s2)
+{
+ int rc;
+
+ if (s1 == s2) {
+ rc = 0;
+ } else {
+ while(*s1 && (*s1 == *s2)) {
+ s1++;
+ s2++;
+ }
+ rc = *((unsigned char *)s1) - *((unsigned char *)s2);
+ }
+ return rc;
+}
+
+
+/// Compare a portion of two strings
+///
+/// The strncmp() function compares at most the first \n characters of the two
+/// strings \a s1 and \a s2. It returns an integer less than, equal to, or
+/// greater than zero if (the prefix of) \a s1 is found, respectively, to be
+/// less than, to match, or be greater than (the prefix of) \a s2.
+
+int
+strncmp(const char* s1, const char* s2, size_t n)
+{
+ int rc;
+
+ if ((s1 == s2) || (n == 0)) {
+ rc = 0;
+ } else {
+ while(*s1 && (*s1 == *s2) && n--) {
+ s1++;
+ s2++;
+ }
+ rc = *((unsigned char *)s1) - *((unsigned char *)s2);
+ }
+ return rc;
+}
+
+
+/// Copy a string
+///
+/// The strcpy() function copies the string pointed to by \a src (including
+/// the terminating null character) to the array pointed to by \a dest. The
+/// strings may not overlap, and the destination string \a dest must be large
+/// enough to receive the copy.
+///
+/// The strcpy() function return a pointer to the destination string \a dest.
+
+char *
+strcpy(char *dest, const char *src)
+{
+ char *rv = dest;
+
+ while (*src) {
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+
+ return rv;
+}
+
+
+/// Safely copy all or part of a string
+///
+/// The strncpy() function copies the string pointed to by \a src (including
+/// the terminating null character) to the array pointed to by \a dest, except
+/// that no more than \a n bytes of \a src are copied. This, if there is no
+/// null byte among the first \a n bytes of \a src, the result will not be
+/// null-terminated. In the case where the length of \a src is less than \a n,
+/// the remainder of \a dest will be padded with null bytes. The strings may
+/// not overlap.
+///
+/// The strncpy() function return a pointer to the destination string \a dest.
+
+char *
+strncpy(char *dest, const char *src, size_t n)
+{
+ char *rv = dest;
+
+ while (*src && n--) {
+ *dest++ = *src++;
+ }
+ memset(dest, 0, n);
+
+ return rv;
+}
+
+
+/// Compare two memory areas
+///
+/// The memcmp() function compares the first \a n bytes of the memory areas \a
+/// s1 and \a s2. It returns an integer less than, equal to, or greater than
+/// zero if \a s1 is found, respectively, to be less than, to match, or be
+/// greater than \a s2.
+int
+memcmp(const void* s1, const void* s2, size_t n)
+{
+ unsigned char *p1, *p2;
+ int rc;
+
+ p1 = (unsigned char*) s1;
+ p2 = (unsigned char*) s2;
+
+ if (s1 == s2) {
+
+ rc = 0;
+
+ } else {
+
+ while (n && (*p1 == *p2)) {
+ n--;
+ p1++;
+ p2++;
+ }
+
+ if (n == 0) {
+ rc = 0;
+ } else {
+ rc = (*p1 - *p2);
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/lib/string.h b/src/lib/string.h
new file mode 100755
index 0000000..2376463
--- /dev/null
+++ b/src/lib/string.h
@@ -0,0 +1,64 @@
+#ifndef __STRING_H__
+#define __STRING_H__
+
+// $Id: string.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file string.h
+/// \brief Replacement for <string.h>
+///
+/// The SSX library does not implement the entire <string.h> function.
+/// However the real reason for this header was the finding that under certain
+/// optimization modes, we were geting errors from the default <string.h>
+/// supplied with the MPC environment. So we created this replacement that
+/// only calls out what is implemented, exactly as it is implemented for SSX.
+
+#ifndef __ASSEMBLER__
+
+#include <stddef.h>
+
+// APIs inmplemented by string.c
+
+size_t
+strlen(const char *s);
+
+int
+strcmp(const char* s1, const char* s2);
+
+int
+strncmp(const char* s1, const char* s2, size_t n);
+
+int
+strcasecmp(const char* s1, const char* s2);
+
+int
+strncasecmp(const char* s1, const char* s2, size_t n);
+
+char *
+strcpy(char *dest, const char *src);
+
+char *
+strncpy(char *dest, const char *src, size_t n);
+
+void *
+memcpy(void *dest, const void *src, size_t n);
+
+void *
+memset(void *s, int c, size_t n);
+
+int
+memcmp(const void* s1, const void* s2, size_t n);
+
+// APIs implemented by strdup.c
+
+char *
+strdup(const char* s);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __STRING_H__ */
diff --git a/src/lib/string_stream.c b/src/lib/string_stream.c
new file mode 100755
index 0000000..f3b1889
--- /dev/null
+++ b/src/lib/string_stream.c
@@ -0,0 +1,386 @@
+// $Id: string_stream.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string_stream.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file string_stream.c
+/// \brief Implementations of string streams.
+///
+/// \bug Need to work out overwrite/multiple-write protection.
+
+#include "ssx.h"
+#include "string_stream.h"
+
+
+// 'Flush' a string stream by making it empty
+
+static int
+string_stream_fflush(FILE* stream)
+{
+ StringStream* string = (StringStream*) stream;
+
+ string->next_read = 0;
+ string->next_write = 0;
+ string->flags &= ~STRING_STREAM_FULL;
+ return 0;
+}
+
+
+// Read the remaining tail (if any) of the circular buffer, assuming the
+// buffer is not empty.
+
+static size_t
+read_tail(StringStream* string, void* buf, size_t size)
+{
+ size_t read, remainder;
+
+ read = 0;
+ if (string->next_read >= string->next_write) {
+
+ remainder = string->size - string->next_read;
+ read = MIN(size, remainder);
+ memcpy(buf, (void *)(&(string->data[string->next_read])), read);
+ if (read != remainder) {
+ string->next_read += read;
+ } else {
+ string->next_read = 0;
+ }
+ }
+ return read;
+}
+
+
+// Read the area (if any) from the read pointer to the write pointer
+
+static size_t
+read_head(StringStream* string, void* buf, size_t size)
+{
+ size_t read, remainder;
+
+ read = 0;
+ if (string->next_write > string->next_read) {
+
+ remainder = string->next_write - string->next_read;
+ read = MIN(size, remainder);
+ memcpy(buf, (void *)(&(string->data[string->next_read])), read);
+ string->next_read += read;
+ }
+ return read;
+}
+
+
+// Read as much data as possible from a circular buffer. Return -EAGAIN if the
+// buffer would underflow.
+
+static int
+string_stream_sread(FILE* stream, void* buf, size_t size, size_t* read)
+{
+ StringStream *string = (StringStream *)stream;
+ size_t were_read;
+ int rc;
+
+ if ((string->next_read == string->next_write) &&
+ !(string->flags & STRING_STREAM_FULL)) {
+ were_read = 0;
+ } else {
+ were_read = read_tail(string, buf, size);
+ if (were_read != size) {
+ were_read += read_head(string, buf + were_read, size - were_read);
+ }
+ }
+ if (were_read != 0) {
+ string->flags &= ~STRING_STREAM_FULL;
+ }
+ *read = were_read;
+ if (were_read < size) {
+ rc = -EAGAIN;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+
+
+
+// Write the remaining tail (if any) of the circular buffer, assuming the
+// buffer is not full.
+
+static size_t
+write_tail(StringStream* string, const void* buf, size_t size)
+{
+ size_t written, remainder;
+
+ written = 0;
+ if (string->next_write >= string->next_read) {
+
+ remainder = string->size - string->next_write; // string->size is the size of the stream(our buffer)
+ written = MIN(size, remainder);
+ // memcpy(void *dest, const void *src, size_t n) from /lib/memcpy.c
+ memcpy((void *)(&(string->data[string->next_write])), buf, written);
+ if (written != remainder) {
+ string->next_write += written;
+ } else {
+ string->next_write = 0;
+ }
+ }
+ return written;
+}
+
+// Write the area (if any) from the write pointer to the read pointer
+
+static size_t
+write_head(StringStream* string, const void* buf, size_t size)
+{
+ size_t written, remainder;
+
+ written = 0;
+ if (string->next_read > string->next_write) {
+
+ remainder = string->next_read - string->next_write;
+ written = MIN(size, remainder);
+ memcpy((void *)(&(string->data[string->next_write])), buf, written);
+ string->next_write += written;
+ }
+ return written;
+}
+
+
+// Write as much data as possible to a circular buffer. Return -EAGAIN if the
+// buffer would overflow.
+
+static int
+circular_swrite(FILE* stream, const void* buf, size_t size, size_t* written)
+{
+ StringStream *string = (StringStream *)stream;
+ size_t wrote;
+ int rc;
+
+ if (string->flags & STRING_STREAM_FULL) {
+ wrote = 0;
+ } else {
+ wrote = write_tail(string, buf, size);
+ if (wrote != size) {
+ wrote += write_head(string, buf + wrote, size - wrote);
+ }
+ }
+ if ((wrote != 0) && (string->next_read == string->next_write)) {
+ string->flags |= STRING_STREAM_FULL;
+ }
+ *written = wrote;
+ if (wrote < size) {
+ rc = -EAGAIN;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+
+// Effectively write all data to a circular buffer with wrapping semantics.
+
+static int
+wrapping_swrite(FILE* stream, const void* buf, size_t size, size_t* written)
+{
+ StringStream *string = (StringStream *)stream;
+ size_t wrote;
+ int rc;
+
+ if (size >= string->size) { // If size of data >= size of buffer
+
+ // If the amount of data will fill or overflow the entire buffer size
+ // then we effectively fill the buffer with the final bytes of data.
+
+ string->next_read = 0;
+ string->next_write = 0;
+ string->flags |= STRING_STREAM_FULL;
+ memcpy((void *)string->data, buf + (size - string->size), size);
+
+ } else {
+
+ // If the string is not full, try to fill it with the
+ // circular_swrite().
+
+ if (string->flags & STRING_STREAM_FULL) {
+ wrote = 0;
+ rc = -EAGAIN;
+ } else {
+ rc = circular_swrite(stream, buf, size, &wrote);
+ }
+ if (rc) {
+
+ // The string is full and we need to overflow. We know that size
+ // is less than the buffer size, and the next_read == next_write.
+ // Mark the stream not full so a new circular write will work, and
+ // at the end reset the full condition.
+
+ string->flags &= ~STRING_STREAM_FULL;
+ rc = circular_swrite(stream, buf + wrote, size - wrote, &wrote);
+ if (rc) {
+ SSX_PANIC(STRING_STREAM_BUG);
+ }
+ string->next_read = string->next_write;
+ string->flags |= STRING_STREAM_FULL;
+ }
+ }
+ *written = size;
+ return 0;
+}
+
+
+static int
+linear_swrite(FILE* stream, const void* buf, size_t size, size_t* written)
+{
+ // buf and size correspond to the data we are passing to our own buffer
+ uint32_t bit_0_mask = 0x80000000;
+ StringStream *string = (StringStream *)stream;
+ size_t wrote = 0; // right aligned
+ // int rc;
+ uint32_t num_bytes_written;
+ // uint32_t register_contents;
+
+
+ //if (wrote != size) {
+ // register_contents = in32(PMC_PORE_SCRATCH_REG1);
+ // register_contents = register_contents & bit_0_mask;
+
+ //if (!register_contents) {
+ // Before writing to SRAM, flush everything so it will write to the top
+ // of the buffer each time
+ string_stream_fflush(stream);
+
+ // Write printk statement to SRAM
+ // wrote will contain the number of bytes written
+
+ wrote += write_tail(string, buf + wrote, size - wrote);
+ // if wrote != size, tell Tcl to read the whole buffer, When Tcl is done loop back to write_tail
+ // buf= buf+wrote
+ // size= size - wrote
+ // continue loop until size = 0
+
+ // Sync
+ eieio();
+
+ // Store "wrote" to register
+ // Set bit 0 to 1
+ // out32(addr, data)
+ num_bytes_written = (uint32_t)wrote | bit_0_mask;
+ out32( PMC_PORE_SCRATCH_REG1 , num_bytes_written );
+
+ // Sync
+ eieio();
+ //}
+ //}
+
+ // outside of loop, set *written = wrote (which is equal to size once all data has been copied to our buffer)
+ *written = wrote;
+ return 0;
+
+}
+
+
+int
+_string_stream_create(StringStream* stream,
+ void* buf, size_t size, int flags,
+ int (*swrite)(FILE* stream,
+ const void* buf,
+ size_t size,
+ size_t* written))
+{
+ FILE* file = (FILE*)stream;
+ int rc;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((stream == 0) ||
+ ((buf == 0) && (size != 0)),
+ STRING_STREAM_INVALID_ARGUMENT);
+ }
+
+ rc = FILE_create(file, flags);
+
+ if (!rc) {
+ file->swrite = swrite;
+ file->sread = string_stream_sread;
+ file->fflush = string_stream_fflush;
+ stream->data = buf;
+ stream->size = size;
+ stream->next_read = 0;
+ stream->next_write = 0;
+ stream->flags = 0;
+ }
+ return rc;
+}
+
+
+int
+circular_stream_create(CircularStream* stream,
+ void* buf, size_t size, int flags)
+{
+ return _string_stream_create(stream, buf, size, flags, circular_swrite);
+}
+
+
+int
+wrapping_stream_create(CircularStream* stream,
+ void* buf, size_t size, int flags)
+{
+ return _string_stream_create(stream, buf, size, flags, wrapping_swrite);
+}
+
+
+int
+linear_stream_create(CircularStream* stream,
+ void* buf, size_t size, int flags)
+{
+ FILE* file = (FILE*)stream;
+ int rc;
+
+ rc = _string_stream_create(stream, buf, size, flags, linear_swrite);
+ if (!rc) {
+
+ file->sread = NULL;
+ // Write to register where location of buffer is
+ out32( PMC_PORE_SCRATCH_REG1, (uint32_t)buf);
+ }
+ return rc;
+}
+
+
+// InputStream uses string_stream_sread(), however returns EOF once all data
+// has been read.
+
+static int
+input_stream_sread(FILE* stream, void* buf, size_t size, size_t* read)
+{
+ int rc;
+
+ rc = string_stream_sread(stream, buf, size, read);
+ if (rc == -EAGAIN) {
+ rc = EOF;
+ }
+
+ return rc;
+}
+
+
+// For simplicity (and ease of maintainence) we create a normal string stream
+// then overwrite a few key fields.
+
+int
+input_stream_create(StringStream* stream, void* buf, size_t size, int flags)
+{
+ int rc;
+
+ rc = _string_stream_create(stream, buf, size, flags, 0);
+ if (!rc) {
+ stream->stream.sread = input_stream_sread;
+ stream->stream.fflush = 0;
+ stream->flags = STRING_STREAM_FULL;
+ }
+ return rc;
+}
+
+
diff --git a/src/lib/string_stream.h b/src/lib/string_stream.h
new file mode 100755
index 0000000..939489c
--- /dev/null
+++ b/src/lib/string_stream.h
@@ -0,0 +1,253 @@
+#ifndef __STRING_STREAM_H__
+#define __STRING_STREAM_H__
+
+// $Id: string_stream.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string_stream.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file string_stream.h
+/// \brief Implementations of string streams.
+
+#include "ssx_io.h"
+
+/// A string stream
+///
+/// This structure is used for the public CircularStream and WrappingStream
+/// types, as well as for the library-internal types used to implement
+/// sprintf() and snprintf().
+
+typedef struct {
+
+ /// The base class
+ FILE stream;
+
+ /// Data storage - provided by the creator
+ uint8_t *data;
+
+ /// The size of the data storage
+ size_t size;
+
+ /// The index of the next byte to write
+ size_t next_write;
+
+ /// The index of the next byte to read
+ size_t next_read;
+
+ /// Stream flags, see \ref string_stream_flags
+ int flags;
+
+} StringStream;
+
+
+/// A StringStream with circular buffer semantics
+///
+/// The swrite() method copies the input data to the stream buffer, which is
+/// treated as a circular buffer. If the swrite() would overflow the buffer,
+/// then as much data as possible is written and swrite returns -EAGAIN. In
+/// all cases swrite() returns the number of bytes actually written to the
+/// buffer.
+///
+/// The sread() method copies data from the StringStream circular buffer to
+/// the caller's buffer until either the caller's request is satisfied or all
+/// of the immediately available data has been read from the buffer. If the
+/// caller's request can not be immediately granted then as much data as
+/// possible is copied and sread() returns -EAGAIN. In all cases sread()
+/// returns the number of bytes actually read from the buffer.
+///
+/// The fflush() method marks the buffer as empty, effectively losing any data
+/// currently stored in the buffer.
+
+typedef StringStream CircularStream;
+
+
+/// A StringStream with wrapping circular buffer semantics
+///
+/// The swrite() method copies the input data to the stream buffer, which is
+/// treated as a circular buffer. If the swrite() would overflow the buffer,
+/// then unread data is overwritten with new data. If the size of the
+/// swrite() exceeds the buffer length then the effect is simply to fill the
+/// buffer with the final bytes of the caller's data. swrite() always returns
+/// the number of bytes requested to be written.
+///
+/// The sread() method copies data from the StringStream circular buffer to
+/// the caller's buffer until either the caller's request is satisfied or all
+/// of the immediately available data has been read from the buffer. If the
+/// caller's request can not be immediately granted then as much data as
+/// possible is copied and sread() returns -EAGAIN. In all cases sread()
+/// returns the number of bytes actually read from the buffer.
+///
+/// The fflush() method marks the buffer as empty, effectively losing any data
+/// currently stored in the buffer.
+
+typedef StringStream WrappingStream;
+
+
+/// Create a StringStream
+///
+/// \param stream The StringStream to initialize
+///
+/// \param buf The stream data buffer
+///
+/// \param size The size of the data buffer in bytes
+///
+/// \param flags Flags for FILE_create()
+///
+/// \param swrite The function to be installed as the swrite() function for
+/// the underlying FILE object. The sread() and fflush() functions are fixed.
+///
+/// This API initializes a StringStream structure for use in I/O operations.
+/// This API will typically only be used by the creation functions of specific
+/// StringStream types.
+///
+/// \retval 0 Success
+///
+/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0)
+/// or \a buf is NULL(0) and \a size is non-0.
+
+int
+_string_stream_create(StringStream* stream,
+ void* buf, size_t size, int flags,
+ int (*swrite)(FILE* stream,
+ const void* buf,
+ size_t size,
+ size_t* written));
+
+/// Create a CircularStream
+///
+/// \param stream The CircularStream to initialize
+///
+/// \param buf The stream data buffer
+///
+/// \param size The size of the data buffer in bytes
+///
+/// \param flags Flags for FILE_create()
+///
+/// This API initializes a CircularStream structure for use in I/O
+/// operations. Once created, the pointer to the CircularStream stream can be
+/// cast to a FILE* and used for sread(), swrite(), fflush() and fprintf()
+/// operations.
+///
+/// \retval 0 Success
+///
+/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0)
+/// or \a buf is NULL(0) and \a size is non-0.
+
+int
+circular_stream_create(CircularStream* stream,
+ void* buf, size_t size, int flags);
+
+
+/// Create a WrappingStream
+///
+/// \param stream The WrappingStream to initialize
+///
+/// \param buf The stream data buffer
+///
+/// \param size The size of the data buffer in bytes
+///
+/// \param flags Flags for FILE_create()
+///
+/// This API initializes a WrappingStream structure for use in I/O
+/// operations. Once created, the pointer to the WrappingStream stream can be
+/// cast to a FILE* and used for sread(), swrite(), fflush() and fprintf()
+/// operations.
+///
+/// \retval 0 Success
+///
+/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0)
+/// or \a buf is NULL(0) and \a size is non-0.
+
+int
+wrapping_stream_create(CircularStream* stream,
+ void* buf, size_t size, int flags);
+
+
+/// Create a LinearStream
+///
+/// \param stream The LinearStream to initialize
+///
+/// \param buf The stream data buffer
+///
+/// \param size The size of the data buffer in bytes
+///
+/// \param flags Flags for FILE_create()
+///
+/// This API initializes a LinearStream structure for use in I/O
+/// operations. Once created, the pointer to the WrappingStream stream can be
+/// cast to a FILE* and used for sread(), swrite(), fflush() and fprintf()
+/// operations.
+///
+/// \retval 0 Success
+///
+/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0)
+/// or \a buf is NULL(0) and \a size is non-0.
+
+
+int
+linear_stream_create(CircularStream* stream,
+ void* buf, size_t size, int flags);
+
+
+/// A StringStream used as a read-only input stream
+///
+/// This is a CircualarStream created with a full buffer and without a write
+/// method. It uses a special read method that returns EOF once the buffer is
+/// empty rather than -EAGAIN.
+
+typedef StringStream InputStream;
+
+
+/// Create an InputStream
+///
+/// \param stream The InputStream to initialize
+///
+/// \param buf The stream data buffer.
+///
+/// \param size The size of the readable portion of the data buffer in bytes
+///
+/// \param flags Flags for FILE_create()
+///
+/// This API initializes an InputStream structure for use in input operations.
+/// The stream data buffer contains \a size bytes of data which can be read.
+/// Once created, the pointer to the InputStream stream can be cast to a FILE*
+/// and used for sread(), fgetc() and ungetc() operations. Neither fflush()
+/// nor swrite() are supported on this class of stream. The read operations
+/// will succeed until \a size bytes have been returned from the stream, at
+/// which point the stream will return EOF on any subsequent reads.
+///
+/// \retval 0 Success
+///
+/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0)
+/// or \a buf is NULL(0) and \a size is non-0.
+
+int
+input_stream_create(InputStream* stream,
+ void* buf, size_t size, int flags);
+
+
+/// \defgroup string_stream_flags StringStream Flags
+///
+/// @{
+
+/// The StringStream is full
+#define STRING_STREAM_FULL 0x1
+
+/// @}
+
+
+/// \defgroup string_stream_codes StringStream Error/Panic Codes
+///
+/// @{
+
+/// A bug was detected in a StringStream function
+#define STRING_STREAM_BUG 0x00787701
+#define STRING_STREAM_INVALID_ARGUMENT 0x00787702
+
+/// @}
+
+
+#endif // __STRING_STREAM_H__
diff --git a/src/lib/strtox.c b/src/lib/strtox.c
new file mode 100755
index 0000000..d901158
--- /dev/null
+++ b/src/lib/strtox.c
@@ -0,0 +1,593 @@
+// $Id: strtox.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strtox.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file strtox.c
+/// \brief Implementation of strtol(), strtoul(), strtoll() and strtoull()
+///
+/// <b> Standard String Conversion Routines </b>
+///
+/// This file contains implementaions of strtol(), strtoul(), strtoll() and
+/// strtoull(). These APIs are all called as
+///
+/// \code
+/// strtoX(const char* str, char** endptr, int base)
+/// \endcode
+///
+/// where X is
+///
+/// - l : Convert to a long integer
+/// - ul : Convert to an unsigned long integer
+/// - ll : Convert to a long long integer
+/// - ull : Convert to an unsigned long long integer
+///
+/// \param str The string to convert
+///
+/// \param endptr If non-null, will be set to a pointer to the portion of the
+/// string following the convertable portion. If no conversion is performed
+/// then the original \a str is returned here.
+///
+/// \param base Either 0 to indicate that the base should be derived from
+/// radix markers in the string, or a number in the range 2 to 36 inclusive.
+///
+/// The APIs convert the initial portion of the string pointed to by \a str to
+/// an integer, which is either a long integer (strtol), an unsigned long
+/// (strtoul()), a long long (strtoll), or an unsigned long long
+/// (strtoull). First, the APIs decompose the input string into three parts:
+///
+/// - An initial, possibly empty, sequence of white-space characters (as
+/// specified by isspace())
+///
+/// - A subject sequence interpreted as an integer represented in some radix
+/// determined by the value of \a base
+///
+/// - A final string of one or more unrecognized characters, including the
+/// terminating null byte of the input string.
+///
+/// The APIs then attempt to convert the subject sequence to an integer of the
+/// required type and returns the result.
+///
+/// If the value of \a base is 0, the expected form of the subject sequence is
+/// that of a decimal constant, octal constant, or hexadecimal constant, any
+/// of which may be preceded by a '+' or '-' sign. A decimal constant begins
+/// with a non-zero digit, and consists of a sequence of decimal digits. An
+/// octal constant consists of the prefix '0' optionally followed by a
+/// sequence of the digits '0' to '7' only. A hexadecimal constant consists of
+/// the prefix 0x or 0X followed by a sequence of the decimal digits and
+/// letters 'a' (or 'A' ) to 'f' (or 'F' ) with values 10 to 15 respectively.
+///
+/// If the value of \a base is between 2 and 36, the expected form of the
+/// subject sequence is a sequence of letters and digits representing an
+/// integer with the radix specified by base, optionally preceded by a '+' or
+/// '-' sign. The letters from 'a' (or 'A' ) to 'z' (or 'Z' ) inclusive are
+/// ascribed the values 10 to 35; only letters whose ascribed values are less
+/// than that of base are permitted. If the value of base is 16, the
+/// characters 0x or 0X may optionally precede the sequence of letters and
+/// digits, following the sign if present.
+///
+/// The subject sequence is defined as the longest initial subsequence of the
+/// input string, starting with the first non-white-space character that is of
+/// the expected form. The subject sequence contains no characters if the
+/// input string is empty or consists entirely of white-space characters, or if
+/// the first non-white-space character is other than a sign or a permissible
+/// letter or digit.
+///
+/// If the subject sequence has the expected form and the value of base is 0,
+/// the sequence of characters starting with the first digit will be
+/// interpreted as an integer constant. If the subject sequence has the
+/// expected form and the value of base is between 2 and 36, it will be used
+/// as the base for conversion, ascribing to each letter its value as given
+/// above. If the subject sequence begins with a minus sign, the value
+/// resulting from the conversion will be negated. A pointer to the final
+/// string will be stored in the object pointed to by \a endptr, provided that
+/// \a endptr is not a null pointer.
+///
+/// If the subject sequence is empty or does not have the expected form, no
+/// conversion is performed; the value of \a str is stored in the object
+/// pointed to by \a endptr, provided that \a endptr is not a null pointer.
+///
+/// Note that the unsigned APIs silently convert signed representations into
+/// the equivalent unsigned number.
+///
+/// Since 0, (L)LONG_MIN and (U)(L)LONG_MAX are returned on error and are
+/// also valid returns on success, there is no way for an SSX application to
+/// determine whether the conversion succeeded or failed (since SSX does not
+/// support \a errno). For this reason it is recommended that SSX-only
+/// applications use the underlying APIs _strtol(), _strtoul(), _strtoll() and
+/// _strtoull(), or even better the extended APIs strtoi32(), strtou32(),
+/// strtoi64() or strtou64() discussed further below.
+///
+/// Upon successful completion, strtoX() returns the converted
+/// value, if any. If no conversion could be performed or there was an error
+/// in the base specification, 0 is returned.
+///
+/// If the correct value is outside the range of representable values,
+/// (L)LONG_MIN or (U)(L)LONG_MAX will be returned (according to the sign
+/// and type of the value).
+///
+/// Note: This specification is adapted from IEEE Std. 10003.1, 2003 Edition
+///
+///
+/// <b> Underlying APIs </b>
+///
+/// The APIs underlying the standard APIs are all called as
+///
+/// \code
+/// int _strtoX(const char* str, char** endptr, int radix, <type>* value)
+/// \endcode
+///
+/// where X is
+///
+/// - l : Convert to a long integer
+/// - ul : Convert to an unsigned long integer
+/// - ll : Convert to a long long integer
+/// - ull : Convert to an unsigned long long integer
+///
+/// \param str The string to convert
+///
+/// \param endptr If non-null, will be set to a pointer to the portion of the
+/// string following the convertable portion. If no conversion is performed
+/// then the original \a str is returned here.
+///
+/// \param base Either 0 to indicate that the base should be derived from
+/// radix markers in the string, or a number in the range 2 to 36 inclusive.
+///
+/// \param value The converted value, returned as the return value of the
+/// standard API.
+///
+/// The return value of the underlying APIs is one of the following
+///
+/// \retval 0 Success
+///
+/// \retval -STRTOX_NO_CONVERSION_EMPTY No conversion was performed because the
+/// string was effectively empty.
+///
+/// \retval -STRTOX_NO_CONVERSION_PARSE No conversion was performed because the
+/// string did not parse as an integer.
+///
+/// \retval -STRTOX_INVALID_ARGUMENT No conversion was performed because the
+/// \a base specification was not valid.
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOL1 Conversion resulted in underflow
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOL2 Conversion resulted in underflow
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOLL1 Conversion resulted in underflow
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOLL2 Conversion resulted in underflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOL1 Conversion resulted in overflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOL2 Conversion resulted in overflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOLL1 Conversion resulted in overflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOLL2 Conversion resulted in overflow
+///
+///
+/// <b> Extended APIs </b>
+///
+/// The extended APIs are the preferred way to do portable integer
+/// conversion. These APIs are all called as
+///
+/// \code
+/// int strtoX(const char* str, char** endptr, int radix, <type>* value)
+/// \endcode
+///
+/// where X is
+///
+/// - i32 : Convert to an int32_t
+/// - u32 : Convert to a uint32_t
+/// - i64 : Convert to an int64_t
+/// - u64 : Convert to a uint64_t
+///
+/// \param str The string to convert
+///
+/// \param endptr If non-null, will be set to a pointer to the portion of the
+/// string following the convertable portion. If no conversion is performed
+/// then the original \a str is returned here.
+///
+/// \param base Either 0 to indicate that the base should be derived from
+/// radix markers in the string, or a number in the range 2 to 36 inclusive.
+///
+/// \param value The converted value
+///
+/// The return value of the underlying APIs is one of the following
+///
+/// \retval 0 Success
+///
+/// \retval -STRTOX_NO_CONVERSION_EMPTY No conversion was performed because the
+/// string was effectively empty.
+///
+/// \retval -STRTOX_NO_CONVERSION_PARSE No conversion was performed because the
+/// string did not parse as an integer.
+///
+/// \retval -STRTOX_INVALID_ARGUMENT No conversion was performed because the
+/// \a base specification was not valid.
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOL1 Conversion resulted in underflow
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOL2 Conversion resulted in underflow
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOLL1 Conversion resulted in underflow
+///
+/// \retval -STRTOX_UNDERFLOW_STRTOLL2 Conversion resulted in underflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOL1 Conversion resulted in overflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOL2 Conversion resulted in overflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOLL1 Conversion resulted in overflow
+///
+/// \retval -STRTOX_OVERFLOW_STRTOLL2 Conversion resulted in overflow
+///
+
+#include "ssx.h"
+#include "ctype.h"
+#include "libssx.h"
+#include "strtox.h"
+
+
+// Skip whitespace
+
+static const char *
+skip_whitespace(const char *s)
+{
+ while (isspace(*s)) {
+ s++;
+ }
+ return s;
+}
+
+// Pick up a +/- sign. This is a predicate returning 1 if the value is
+// negated.
+
+static int
+sign(const char** s)
+{
+ if (**s == '+') {
+ (*s)++;
+ return 0;
+ } else if (**s == '-') {
+ (*s)++;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+// Look for a radix mark (0, 0[xX]). The string pointer is advanced if it is a
+// hex mark (0[xX]), but not for a simple '0' which could be either the start
+// of an octal constant or simply the number 0. The return value is either 8,
+// 10 or 16.
+
+static int
+radix_mark(const char** s)
+{
+ const char* p = *s;
+
+ if (p[0] == '0') {
+ if ((p[1] == 'x') || (p[1] == 'X')) {
+ *s += 2;
+ return 16;
+ } else {
+ return 8;
+ }
+ } else {
+ return 10;
+ }
+}
+
+
+// Parse a character as a radix-base digit. Return the value of the digit or
+// -1 if it is not a legal digit for the radix.
+
+static int
+parse_digit(char c, int radix)
+{
+ if (isdigit(c)) {
+ if ((c - '0') < radix) {
+ return c - '0';
+ } else {
+ return -1;
+ }
+ } else if (radix <= 10) {
+ return -1;
+ } else {
+ if (islower(c)) {
+ if ((c - 'a') < (radix - 10)) {
+ return c - 'a' + 10;
+ } else {
+ return -1;
+ }
+ } else if (isupper(c)) {
+ if ((c - 'A') < (radix - 10)) {
+ return c - 'A' + 10;
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+}
+
+
+// The most basic API is strtox(), which converts a string to an unsigned long
+// long. All of the base APIs are written in terms of this. This is legal due
+// to the fact that conversion is defined to continue even in the event of
+// overflow. This API may return the codes STRTOX_NO_CONVERSION_EMPTY,
+// STRTOX_NO_CONVERSION_PARSE or STRTOX_INVALID_ARGUMENT,
+// which the standard APIs always convert to a 0
+// return value. Otherwise the flags 'overflow' and 'negative' are used by
+// the base APIs to determine how to handle special cases.
+
+static int
+strtox(const char *str, char **endptr, int base,
+ unsigned long long* value, int* negative, int* overflow)
+{
+ const char* s;
+ unsigned long long new;
+ int rc, radix, digit;
+
+
+ do {
+
+ s = str;
+ *value = 0;
+ *negative = 0;
+ *overflow = 0;
+
+ // Initial error checks
+
+ if ((base != 0) && ((base < 2) || (base > 36))) {
+ rc = STRTOX_INVALID_ARGUMENT;
+ break;
+ }
+
+ // Skip whitespace
+
+ s = skip_whitespace(s);
+ if (*s == '\0') {
+ rc = STRTOX_NO_CONVERSION_EMPTY;
+ break;
+ }
+
+ // Process a +/- sign. Only one is allowed.
+
+ *negative = sign(&s);
+
+ // Look for a radix mark. Note that if base == 16 this will cause the
+ // skip of a leading 0 in the string not followed by [xX], but that's
+ // OK because it doesn't change the result of the conversion.
+
+ if (base == 0) {
+ radix = radix_mark(&s);
+ } else {
+ radix = base;
+ if (radix == 16) {
+ radix_mark(&s);
+ }
+ }
+
+ // Parse. Note that once overflow is detected we continue to parse
+ // (but ignore the data).
+
+ rc = STRTOX_NO_CONVERSION_PARSE;
+
+ while ((digit = parse_digit(*s, radix)) >= 0) {
+ s++;
+ if (!*overflow) {
+ rc = 0;
+ new = (*value * radix) + digit;
+ if (new < *value) {
+ *overflow = 1;
+ } else {
+ *value = new;
+ }
+ }
+ }
+ } while(0);
+
+ if (endptr) {
+ if (rc == 0) {
+ *endptr = (char*)s;
+ } else {
+ *endptr = (char*)str;
+ }
+ }
+
+ return rc;
+}
+
+
+/// See documentation for the file strtox.c
+int
+_strtol(const char* str, char** endptr, int base, long* value)
+{
+ int rc, negative, overflow;
+ unsigned long long value_ull;
+
+ rc = strtox(str, endptr, base, &value_ull, &negative, &overflow);
+ if (rc) {
+ *value = 0;
+ } else {
+ if (overflow || (value_ull != (unsigned long)value_ull)) {
+ if (negative) {
+ rc = STRTOX_UNDERFLOW_STRTOL1;
+ *value = LONG_MIN;
+ } else {
+ rc = STRTOX_OVERFLOW_STRTOL1;
+ *value = LONG_MAX;
+ }
+ } else if (negative) {
+ if (value_ull > ((unsigned long long)LONG_MAX + 1ull)) {
+ rc = STRTOX_UNDERFLOW_STRTOL2;
+ *value = LONG_MIN;
+ } else {
+ *value = ~value_ull + 1;
+ }
+ } else if (value_ull > (unsigned long long)LONG_MAX) {
+ rc = STRTOX_OVERFLOW_STRTOL2;
+ *value = LONG_MAX;
+ } else {
+ *value = value_ull;
+ }
+ }
+ return rc;
+}
+
+
+/// See documentation for the file strtox.c
+int
+_strtoll(const char* str, char** endptr, int base, long long* value)
+{
+ int rc, negative, overflow;
+ unsigned long long value_ull;
+
+ rc = strtox(str, endptr, base, &value_ull, &negative, &overflow);
+ if (rc) {
+ *value = 0;
+ } else {
+ if (overflow) {
+ if (negative) {
+ rc = STRTOX_UNDERFLOW_STRTOLL1;
+ *value = LLONG_MIN;
+ } else {
+ rc = STRTOX_OVERFLOW_STRTOLL1;
+ *value = LLONG_MAX;
+ }
+ } else if (negative) {
+ if (value_ull > ((unsigned long long)LLONG_MAX + 1ull)) {
+ rc = STRTOX_UNDERFLOW_STRTOLL2;
+ *value = LLONG_MIN;
+ } else {
+ *value = ~value_ull + 1;
+ }
+ } else if (value_ull > (unsigned long long)LLONG_MAX) {
+ rc = STRTOX_OVERFLOW_STRTOLL2;
+ *value = LLONG_MAX;
+ } else {
+ *value = value_ull;
+ }
+ }
+ return rc;
+}
+
+
+/// See documentation for the file strtox.c
+int
+_strtoul(const char* str, char** endptr, int base, unsigned long* value)
+{
+ int rc, negative, overflow;
+ unsigned long long value_ull;
+
+ rc = strtox(str, endptr, base, &value_ull, &negative, &overflow);
+ if (rc) {
+ *value = 0;
+ } else {
+ if (overflow || (value_ull != (unsigned long)value_ull)) {
+ rc = STRTOX_OVERFLOW_STRTOUL;
+ *value = ULONG_MAX;
+ } else {
+ *value = value_ull;
+ if (negative) {
+ *value = ~*value + 1;
+ }
+ }
+ }
+ return rc;
+}
+
+/// See documentation for the file strtox.c
+int
+_strtoull(const char* str, char** endptr, int base, unsigned long long* value)
+{
+ int rc, negative, overflow;
+
+ rc = strtox(str, endptr, base, value, &negative, &overflow);
+ if (rc) {
+ *value = 0;
+ } else {
+ if (overflow) {
+ rc = STRTOX_OVERFLOW_STRTOULL;
+ *value = ULLONG_MAX;
+ } else {
+ if (negative) {
+ *value = ~*value + 1;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/// See documentation for the file strtox.c
+long int
+strtol(const char* str, char** endptr, int base)
+{
+ long int value;
+
+ _strtol(str, endptr, base, &value);
+ return value;
+}
+
+
+/// See documentation for the file strtox.c
+long long int
+strtoll(const char* str, char** endptr, int base)
+{
+ long long int value;
+
+ _strtoll(str, endptr, base, &value);
+ return value;
+}
+
+
+/// See documentation for the file strtox.c
+unsigned long int
+strtoul(const char* str, char** endptr, int base)
+{
+ unsigned long int value;
+
+ _strtoul(str, endptr, base, &value);
+ return value;
+}
+
+
+/// See documentation for the file strtox.c
+unsigned long long int
+strtoull(const char* str, char** endptr, int base)
+{
+ unsigned long long int value;
+
+ _strtoull(str, endptr, base, &value);
+ return value;
+}
+
+
+#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 1))
+
+/// Internal version of strtol()
+///
+/// ppcnf-mcp5 (GCC 4.1) requires that the entry point __strtol_internal() be
+/// present at certain optimization levels. This is equivalent to strtol()
+/// except that it takes an extra argument that must be == 0. The \a group
+/// parameter is supposed to control locale-specific thousands grouping.
+
+long int
+__strtol_internal(const char* str, char** endptr, int base, int group)
+{
+ if (group != 0) {
+ SSX_PANIC(STRTOX_INVALID_ARGUMENT_STRTOL);
+ }
+ return strtol(str, endptr, base);
+}
+
+#endif
+
diff --git a/src/lib/strtox.h b/src/lib/strtox.h
new file mode 100755
index 0000000..b005ca9
--- /dev/null
+++ b/src/lib/strtox.h
@@ -0,0 +1,127 @@
+#ifndef __STRTOX_H__
+#define __STRTOX_H__
+
+// $Id: strtox.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strtox.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file strtox.h
+/// \brief Underlying and extended APIs that support strtoX macros
+///
+/// See the Doxygen comments of the file strtox.c for descriptions of the
+/// facilities provided by this header.
+
+#ifndef __ASSEMBLER__
+
+#include <limits.h>
+
+// Error codes
+
+#define STRTOX_NO_CONVERSION_EMPTY 0x00787901
+#define STRTOX_NO_CONVERSION_PARSE 0x00787902
+#define STRTOX_INVALID_ARGUMENT 0x00787903
+#define STRTOX_INVALID_ARGUMENT_STRTOL 0x00787904
+#define STRTOX_UNDERFLOW_STRTOL1 0x00787905
+#define STRTOX_UNDERFLOW_STRTOL2 0x00787906
+#define STRTOX_UNDERFLOW_STRTOLL1 0x00787907
+#define STRTOX_UNDERFLOW_STRTOLL2 0x00787908
+#define STRTOX_OVERFLOW_STRTOL1 0x00787909
+#define STRTOX_OVERFLOW_STRTOL2 0x0078790a
+#define STRTOX_OVERFLOW_STRTOLL1 0x0078790b
+#define STRTOX_OVERFLOW_STRTOLL2 0x0078790c
+#define STRTOX_OVERFLOW_STRTOUL 0x0078790d
+#define STRTOX_OVERFLOW_STRTOULL 0x0078790e
+
+// Earlier GCC configurations (ppcnf-mcp5-gcc) are not configured to define
+// these standard constants, which exist in the include tree under various
+// switches and configuration settings (from <limits.h>). They are defined by
+// default in later standard cross builds however (GCC 4.5, 4.6). However we
+// always assume that (long long) is a 64-bit type. It's likely that this is
+// the only place these constant will be used (as they are defined as the
+// values for under/overflow of strtoX() conversions), however it may be
+// necessary in the future to move these #defines somewhere else.
+
+#ifndef LLONG_MIN
+# define LLONG_MIN (0x8000000000000000ll)
+#endif
+
+#ifndef LLONG_MAX
+# define LLONG_MAX (0x7fffffffffffffffll)
+#endif
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX (0xffffffffffffffffull)
+#endif
+
+int
+_strtol(const char* str, char** endptr, int base, long* value);
+
+int
+_strtoul(const char* str, char** endptr, int base, unsigned long* value);
+
+int
+_strtoll(const char* str, char** endptr, int base, long long* value);
+
+int
+_strtoull(const char* str, char** endptr, int base, unsigned long long* value);
+
+
+// The way the sizeof(long) is discovered by default depends on which version
+// of gcc/cpp we're using as these macros are predefined by cpp.
+
+#if (__SIZEOF_LONG__ == 4) || (__LONG_MAX__ == 2147483647L)
+
+/// See documentation for the file strtox.c
+static inline int
+strtoi32(const char* str, char** endptr, int base, int32_t* value)
+{
+ long int value_l;
+ int rc;
+
+ rc = _strtol(str, endptr, base, &value_l);
+ *value = value_l;
+ return rc;
+}
+
+/// See documentation for the file strtox.c
+static inline int
+strtou32(const char* str, char** endptr, int base, uint32_t* value)
+{
+ unsigned long int value_ul;
+ int rc;
+
+ rc = _strtoul(str, endptr, base, &value_ul);
+ *value = value_ul;
+ return rc;
+}
+
+#else
+
+#error "No port of strtox.h yet for systems with sizeof(long) != 4"
+
+#endif
+
+// It is assumed that long long is always 64 bits; There is no standard macro
+// for this size constant
+
+/// See documentation for the file strtox.c
+static inline int
+strtoi64(const char* str, char** endptr, int base, int64_t* value)
+{
+ return _strtoll(str, endptr, base, value);
+}
+
+/// See documentation for the file strtox.c
+static inline int
+strtou64(const char* str, char** endptr, int base, uint64_t* value)
+{
+ return _strtoull(str, endptr, base, value);
+}
+
+#endif // __ASSEMBLER__
+
+#endif // __STRTOX_H__
diff --git a/src/lib/time.c b/src/lib/time.c
new file mode 100755
index 0000000..bc1f2e7
--- /dev/null
+++ b/src/lib/time.c
@@ -0,0 +1,58 @@
+// $Id: time.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/time.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file time.c
+/// \brief Implementation of functions from <time.h>
+///
+/// The following standard APIs are currently supported:
+///
+/// - clock_gettime() with the single clock id CLOCK_REALTIME
+///
+/// Since SSX does not currently have any per-thread CPU time statistics, we
+/// can not implement process-CPU-time-based APIs like the simple clock() or
+/// clock_gettime() with CLOCK_REALTIME.
+
+#include "ssx.h"
+#include <errno.h>
+#include <time.h>
+
+/// Get time from a timer
+///
+/// \param clock_id This must be the constant CLOCK_REALTIME defined in
+/// <time.h>.
+///
+/// \param tp A pointer to a struct timespec populated by this API.
+///
+/// Although the Posix standard requires that clock_gettime() support
+/// CLOCK_REALTIME, CLOCK_REALTIME measures standard Unix time (seconds since
+/// the epoch) which is not available to SSX. SSX currently only supports
+/// CLOCK_MONOTONIC, which is derived from the SSX timebase.
+///
+/// \returns Either 0 for success, or -EINVAL in the event of an invalid
+/// argument (unrecognized \a clock_id, NULL \a tp pointer).
+
+int
+clock_gettime(clockid_t clock_id, struct timespec* tp)
+{
+ int rc;
+ SsxTimebase now;
+
+ if ((clock_id != CLOCK_MONOTONIC) || (tp == 0)) {
+ rc = -EINVAL;
+ } else {
+
+ now = ssx_timebase_get();
+ tp->tv_sec = now / SSX_TIMEBASE_FREQUENCY_HZ;
+ tp->tv_nsec =
+ ((now % SSX_TIMEBASE_FREQUENCY_HZ) * 1000000000) /
+ SSX_TIMEBASE_FREQUENCY_HZ;
+ rc = 0;
+ }
+
+ return rc;
+}
diff --git a/src/lib/vrm.c b/src/lib/vrm.c
new file mode 100755
index 0000000..57f19fa
--- /dev/null
+++ b/src/lib/vrm.c
@@ -0,0 +1,394 @@
+// $Id: vrm.c,v 1.2 2014/02/03 01:30:26 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/vrm.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file vrm.c
+/// \brief PgP SPIVRM procedures
+
+#include "vrm.h"
+
+// The semaphore used to block threads waiting for o2s operations to complete
+
+static SsxSemaphore o2s_protocol_semaphore;
+
+
+/// o2s_initialize follows the steps for setting up the o2s bridge as outlined
+/// In the Energy Management spec (PMC section)
+int
+o2s_initialize()
+{
+ pmc_o2s_ctrl_reg0a_t pocr0a;
+ pmc_o2s_ctrl_reg0b_t pocr0b;
+ pmc_o2s_ctrl_reg1_t pocr1;
+
+
+ ssx_semaphore_create(&o2s_protocol_semaphore, 0, 1);
+
+ ssx_irq_disable(PGP_IRQ_OCI2SPIVID_ONGOING);
+
+ ssx_irq_setup(PGP_IRQ_OCI2SPIVID_ONGOING,
+ SSX_IRQ_POLARITY_ACTIVE_LOW,
+ SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);
+
+ ssx_irq_handler_set(PGP_IRQ_OCI2SPIVID_ONGOING,
+ ssx_semaphore_post_handler,
+ (void *)(&o2s_protocol_semaphore),
+ SSX_NONCRITICAL);
+
+ pocr0a.value = in32(PMC_O2S_CTRL_REG0A);
+ pocr0a.fields.o2s_frame_size = 32;
+ pocr0a.fields.o2s_out_count1 = 32;
+ pocr0a.fields.o2s_in_delay1 = 0x3F;
+ pocr0a.fields.o2s_in_count1 = 0;
+ out32(PMC_O2S_CTRL_REG0A, pocr0a.value);
+
+ pocr0b.value = in32(PMC_O2S_CTRL_REG0B);
+ pocr0b.fields.o2s_out_count2 = 0;
+ pocr0b.fields.o2s_in_delay2 = 0;
+ pocr0b.fields.o2s_in_count2 = 32;
+ out32(PMC_O2S_CTRL_REG0B, pocr0b.value);
+
+ pocr1.value = in32(PMC_O2S_CTRL_REG1);
+ pocr1.fields.o2s_bridge_enable = 1;
+ pocr1.fields.o2s_cpol = 0;
+ pocr1.fields.o2s_cpha = 0;
+ pocr1.fields.o2s_clock_divider = 29; // 10 MHz(?)
+ pocr1.fields.o2s_nr_of_frames = 1; // 2 frames
+ out32(PMC_O2S_CTRL_REG1, pocr1.value);
+
+ return 0;
+
+}
+
+/// similar to o2s_intialize, but for the automated spivid fsm
+/// \param vrm_select A 3-bit vector of VRM selected for the operation.
+///
+/// NOTE: The spivid is normally initialized by Host Boot
+///
+/// \retval 0 Success
+int
+spivid_initialize(int vrm_select)
+{
+ pmc_spiv_ctrl_reg0a_t pocr0a;
+ pmc_spiv_ctrl_reg0b_t pocr0b;
+ pmc_spiv_ctrl_reg1_t pocr1;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((vrm_select <= 0) ||
+ (vrm_select > 0x7),
+ VRM_INVALID_ARGUMENT_INIT);
+ }
+
+
+ pocr0a.value = in32(PMC_SPIV_CTRL_REG0A);
+ pocr0a.fields.spivid_frame_size = 32;
+ pocr0a.fields.spivid_out_count1 = 32;
+ pocr0a.fields.spivid_in_delay1 = 0x3F;
+ pocr0a.fields.spivid_in_count1 = 0;
+ out32(PMC_SPIV_CTRL_REG0A, pocr0a.value);
+
+ pocr0b.value = in32(PMC_SPIV_CTRL_REG0B);
+ pocr0b.fields.spivid_out_count2 = 0;
+ pocr0b.fields.spivid_in_delay2 = 0;
+ pocr0b.fields.spivid_in_count2 = 32;
+ out32(PMC_SPIV_CTRL_REG0B, pocr0b.value);
+
+ pocr1.value = in32(PMC_SPIV_CTRL_REG1);
+ pocr1.fields.spivid_fsm_enable = 1;
+ pocr1.fields.spivid_cpol = 0;
+ pocr1.fields.spivid_cpha = 0;
+ pocr1.fields.spivid_clock_divider = 29; // 10 MHz(?)
+ pocr1.fields.spivid_port_enable = vrm_select;
+ out32(PMC_SPIV_CTRL_REG1, pocr1.value);
+
+ return 0;
+
+}
+
+
+// Start an O2S transaction and poll for completion. Optionally return the
+// input data.
+
+static void
+o2s_start_poll(uint64_t out, uint64_t *in)
+{
+
+ out32(PMC_O2S_WDATA_REG, out >> 32);
+
+ if (!ssx_irq_status_get(PGP_IRQ_OCI2SPIVID_ONGOING)) {
+ ssx_irq_enable(PGP_IRQ_OCI2SPIVID_ONGOING);
+ ssx_semaphore_pend(&o2s_protocol_semaphore, SSX_WAIT_FOREVER);
+ }
+
+
+ if (in != 0) {
+ *in = in32(PMC_O2S_RDATA_REG);
+ }
+}
+
+
+/// Write a voltage using the O2S bridge
+///
+/// \param vrm_select A 3-bit vector of VRM selected for the operation.
+///
+/// \param vdd_vid The VRM-11 VID code for Vdd.
+///
+/// \param vcs_offset The signed offset (Vdiff) equal to Vcs - Vdd expressed
+/// in VRM-11 VID units.
+///
+/// \param phases The number of phases enabled.
+///
+/// This is a polling CPU procedure that writes a new voltage to a set of one
+/// or more VRM then does a status read to make sure it succeeded.
+///
+/// \retval 0 Success
+///
+/// \retval -VRM_INVALID_ARGUMENT_VWRITE One of the arguments was invalid in
+/// some way.
+///
+/// \retval -O2S_BUSY_VRM_VOLTAGE_WRITE The O2S bridge is currently busy
+///
+/// \retval -O2S_READ_NOT_READY A 'read not ready' condition occurred on the
+/// status read.
+///
+/// \retval -O2S_WRITE_NOT_VALID The voltage write was invalid
+///
+/// \retval -O2S_ECC_ERROR An ECC error occurred
+///
+/// \todo We need to understand what the firmware is expected to do when the
+/// 'read not ready' or other error responses come back. Here they will
+/// likely panic.
+
+int
+vrm_voltage_write(int vrm_select,
+ uint8_t vdd_vid,
+ int8_t vcs_offset,
+ int phases)
+{
+ int i, port;
+ uint64_t result;
+ pmc_o2s_ctrl_reg1_t pocr;
+ pmc_o2s_status_reg_t posr;
+ vrm_write_transaction_t vwt;
+ vrm_write_resp_t vwr;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((vrm_select <= 0) ||
+ (vrm_select > 0x7) ||
+ (phases < 0) ||
+ (phases > 0xf),
+ VRM_INVALID_ARGUMENT_VWRITE);
+ }
+
+ // Check for O2S busy
+
+ posr.value = in32(PMC_O2S_STATUS_REG);
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(posr.fields.o2s_ongoing, O2S_BUSY_VRM_VOLTAGE_WRITE);
+ }
+
+ // Use O2S to set voltage and read status, one port at a time.
+
+ for (i = 0; i < SPIVRM_NPORTS; i++) {
+
+ port = vrm_select & SPIVRM_PORT(i);
+ if (port != 0) {
+
+ // Set the (singular) O2S port
+
+ pocr.value = in32(PMC_O2S_CTRL_REG1);
+ pocr.fields.o2s_port_enable = port;
+ out32(PMC_O2S_CTRL_REG1, pocr.value);
+
+ // Create and initiate a voltage write command
+
+ vwt.value = 0;
+ vwt.fields.command = VRM_WRITE_VOLTAGE;
+ vwt.fields.vdd_vid = vdd_vid;
+ vwt.fields.vcs_offset = vcs_offset;
+ vwt.fields.phase_enable = phases;
+
+
+ o2s_start_poll(vwt.value, &result);
+ // Check the status
+ vwr.value = result << 32;
+
+ // results are duplicated 3x, using first byte for checking
+
+ SSX_ERROR_IF(vwr.fields.write_status0 == 0x00, O2S_READ_NOT_READY);
+ SSX_ERROR_IF(vwr.fields.write_status0 == 0x55, O2S_WRITE_ECC_ERROR);
+ SSX_ERROR_IF(vwr.fields.write_status0 != 0xAA, O2S_WRITE_NOT_VALID);
+ }
+ }
+ return 0;
+}
+
+/// Read VRM state using the O2S bridge
+///
+/// \param vrm_select A 3-bit vector of VRM selected for the operation.
+/// This procedure only allows 1 VRM to be selected.
+///
+/// \param vrail A 4-bit value for selecting a voltage rail
+///
+/// \param[out] o_vid The resulting 8-bit VRM-11 encoded voltage ID
+///
+///
+/// \retval 0 Success
+///
+/// \retval -VRM_INVALID_ARGUMENT_SREAD One of the arguments was invalid in some
+/// way.
+///
+/// \retval -O2S_BUSY_VRM_READ_STATE The O2S bridge is currently busy
+///
+
+int
+vrm_read_state(int vrm_select,
+ int *mnp1,
+ int *mn,
+ int *vfan,
+ int *vovertmp)
+{
+ int i, port;
+ uint64_t result;
+ pmc_o2s_status_reg_t posr;
+ pmc_o2s_ctrl_reg1_t pocr;
+ vrm_read_state_t vrs;
+ vrm_read_state_resp_t vrsr;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((!((vrm_select == SPIVRM_PORT(0)) ||
+ (vrm_select == SPIVRM_PORT(1)) ||
+ (vrm_select == SPIVRM_PORT(2)))),
+ VRM_INVALID_ARGUMENT_SREAD);
+ }
+
+
+ // Check for O2S busy
+
+ posr.value = in32(PMC_O2S_STATUS_REG);
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(posr.fields.o2s_ongoing, O2S_BUSY_VRM_READ_STATE);
+ }
+
+ // Use O2S to read voltage for selected rail.
+
+ for (i = 0; i < SPIVRM_NPORTS; i++) {
+
+ port = vrm_select & SPIVRM_PORT(i);
+ if (port != 0) {
+
+ // Set the (singular) O2S port
+
+ pocr.value = in32(PMC_O2S_CTRL_REG1);
+ pocr.fields.o2s_port_enable = port;
+ out32(PMC_O2S_CTRL_REG1, pocr.value);
+
+ // Create and initiate a voltage read command
+
+ vrs.value = 0;
+ vrs.fields.command = VRM_READ_STATE;
+
+
+ o2s_start_poll(vrs.value, &result);
+ // Check the status
+ vrsr.value = result << 32;
+
+ // results are duplicated 3x, returning first byte
+ *mnp1 = vrsr.fields.minus_nplus1_0;
+ *mn = vrsr.fields.minus_n0;
+ *vfan = vrsr.fields.vrm_fan0;
+ *vovertmp = vrsr.fields.vrm_overtemp0;
+
+ }
+ }
+ return 0;
+}
+
+
+/// Read a voltage using the O2S bridge
+///
+/// \param vrm_select A 3-bit vector of VRM selected for the operation.
+/// This procedure only allows 1 VRM to be selected.
+///
+/// \param vrail A 4-bit value for selecting a voltage rail
+///
+/// \param[out] o_vid The resulting 8-bit VRM-11 encoded voltage ID
+///
+///
+/// \retval 0 Success
+///
+/// \retval -VRM_INVALID_ARGUMENT_VREAD One of the arguments was invalid in some
+/// way.
+///
+/// \retval -O2S_BUSY_VRM_VOLTAGE_READ The O2S bridge is currently busy
+///
+
+int
+vrm_voltage_read(int vrm_select,
+ uint8_t vrail,
+ uint8_t *o_vid)
+{
+ int i, port;
+ uint64_t result;
+ pmc_o2s_status_reg_t posr;
+ pmc_o2s_ctrl_reg1_t pocr;
+ vrm_read_voltage_t vrv;
+ vrm_read_voltage_resp_t vrvr;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((!((vrm_select == SPIVRM_PORT(0)) ||
+ (vrm_select == SPIVRM_PORT(1)) ||
+ (vrm_select == SPIVRM_PORT(2)))) ||
+ (vrail > (SPIVRM_NRAILS - 1)),
+ VRM_INVALID_ARGUMENT_VREAD);
+ }
+
+
+ // Check for O2S busy
+
+ posr.value = in32(PMC_O2S_STATUS_REG);
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(posr.fields.o2s_ongoing, O2S_BUSY_VRM_VOLTAGE_READ);
+ }
+
+ // Use O2S to read voltage for selected rail.
+
+ for (i = 0; i < SPIVRM_NPORTS; i++) {
+
+ port = vrm_select & SPIVRM_PORT(i);
+ if (port != 0) {
+
+ // Set the (singular) O2S port
+
+ pocr.value = in32(PMC_O2S_CTRL_REG1);
+ pocr.fields.o2s_port_enable = port;
+ out32(PMC_O2S_CTRL_REG1, pocr.value);
+
+ // Create and initiate a voltage read command
+
+ vrv.value = 0;
+ vrv.fields.command = VRM_READ_VOLTAGE;
+ vrv.fields.rail = vrail;
+
+
+ o2s_start_poll(vrv.value, &result);
+ // Check the status
+ vrvr.value = result << 32;
+
+ // results are duplicated 3x, returning first byte
+ *o_vid = vrvr.fields.vid0;
+ }
+ }
+ return 0;
+}
+
+
+
+
diff --git a/src/lib/vrm.h b/src/lib/vrm.h
new file mode 100755
index 0000000..2efea4d
--- /dev/null
+++ b/src/lib/vrm.h
@@ -0,0 +1,59 @@
+#ifndef __VRM_H__
+#define __VRM_H__
+
+// $Id: vrm.h,v 1.2 2014/02/03 01:30:26 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/vrm.h,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file vrm.h
+/// \brief PgP SPIVRM procedures
+
+#include "ssx.h"
+
+#ifndef __ASSEMBLER__
+
+int
+o2s_initialize();
+
+int
+spivid_initialize();
+
+
+int
+vrm_voltage_write(int vrm_select,
+ uint8_t vdd_vid,
+ int8_t vcs_offset,
+ int phases);
+
+int
+vrm_read_state(int vrm_select,
+ int *mnp1,
+ int *mn,
+ int *vfan,
+ int *vovertmp);
+
+int
+vrm_voltage_read(int vrm_select,
+ uint8_t vrail,
+ uint8_t *o_vid);
+
+#endif /* __ASEMBLER__ */
+
+// Error/panic codes
+
+#define O2S_BUSY_VRM_VOLTAGE_READ 0x00627001
+#define O2S_BUSY_VRM_VOLTAGE_WRITE 0x00627002
+#define O2S_BUSY_VRM_READ_STATE 0x00627003
+#define O2S_READ_NOT_READY 0x00627004
+#define O2S_WRITE_NOT_VALID 0x00627005
+#define O2S_WRITE_ECC_ERROR 0x00627006
+#define VRM_INVALID_ARGUMENT_VREAD 0x00627007
+#define VRM_INVALID_ARGUMENT_VWRITE 0x00627008
+#define VRM_INVALID_ARGUMENT_SREAD 0x00627009
+#define VRM_INVALID_ARGUMENT_INIT 0x0062700a
+
+#endif /* __VRM_H__ */
OpenPOWER on IntegriCloud