From 9976c207cdb20871880bd2f4cf123cf4cb6a8b0f Mon Sep 17 00:00:00 2001 From: Stephan Broyles Date: Wed, 5 Nov 2014 19:09:37 -0600 Subject: Added remaining occ files. Change-Id: I91a748d3dcf3161a6a3eedcb376fcaf1e4dfe655 --- src/lib/Makefile | 79 ++ src/lib/assert.c | 47 ++ src/lib/ctype.c | 22 + src/lib/ctype.h | 125 +++ src/lib/ctype_table.c | 278 +++++++ src/lib/errno.h | 25 + src/lib/fgetc.c | 88 ++ src/lib/gpe.h | 78 ++ src/lib/gpe_control.h | 171 ++++ src/lib/gpe_control.pS | 160 ++++ src/lib/gpe_data.h | 672 ++++++++++++++++ src/lib/gpe_data.pS | 1585 ++++++++++++++++++++++++++++++++++++ src/lib/gpe_pba.c | 148 ++++ src/lib/gpe_pba.h | 116 +++ src/lib/gpe_pba_pgas.pS | 110 +++ src/lib/gpe_scom.h | 471 +++++++++++ src/lib/gpe_scom.pS | 709 +++++++++++++++++ src/lib/gpsm.c | 600 ++++++++++++++ src/lib/gpsm.h | 191 +++++ src/lib/gpsm_dcm.c | 753 ++++++++++++++++++ src/lib/gpsm_dcm.h | 192 +++++ src/lib/gpsm_dcm_fast_handler.S | 147 ++++ src/lib/gpsm_init.c | 1638 ++++++++++++++++++++++++++++++++++++++ src/lib/heartbeat.c | 328 ++++++++ src/lib/heartbeat.h | 46 ++ src/lib/libfiles.mk | 57 ++ src/lib/libgpefiles.mk | 30 + src/lib/libssx.h | 20 + src/lib/memcpy.c | 78 ++ src/lib/memset.c | 118 +++ src/lib/pgas.h | 1153 +++++++++++++++++++++++++++ src/lib/pgas_ppc.h | 529 ++++++++++++ src/lib/pgp_config.h | 108 +++ src/lib/pmc_dcm.c | 425 ++++++++++ src/lib/pmc_dcm.h | 102 +++ src/lib/polling.c | 73 ++ src/lib/polling.h | 94 +++ src/lib/pore_hooks.h | 171 ++++ src/lib/printf.c | 679 ++++++++++++++++ src/lib/pstates.c | 410 ++++++++++ src/lib/pstates.h | 601 ++++++++++++++ src/lib/puts.c | 95 +++ src/lib/simics_stdio.c | 150 ++++ src/lib/simics_stdio.h | 69 ++ src/lib/simics_stdio_addresses.h | 67 ++ src/lib/special_wakeup.c | 149 ++++ src/lib/special_wakeup.h | 32 + src/lib/sprintf.c | 112 +++ src/lib/ssx_dump.c | 209 +++++ src/lib/ssx_dump.h | 58 ++ src/lib/ssx_io.c | 311 ++++++++ src/lib/ssx_io.h | 204 +++++ src/lib/stdlib.c | 91 +++ src/lib/strcasecmp.c | 66 ++ src/lib/strdup.c | 39 + src/lib/string.c | 168 ++++ src/lib/string.h | 64 ++ src/lib/string_stream.c | 386 +++++++++ src/lib/string_stream.h | 253 ++++++ src/lib/strtox.c | 593 ++++++++++++++ src/lib/strtox.h | 127 +++ src/lib/time.c | 58 ++ src/lib/vrm.c | 394 +++++++++ src/lib/vrm.h | 59 ++ 64 files changed, 17181 insertions(+) create mode 100755 src/lib/Makefile create mode 100755 src/lib/assert.c create mode 100755 src/lib/ctype.c create mode 100755 src/lib/ctype.h create mode 100644 src/lib/ctype_table.c create mode 100755 src/lib/errno.h create mode 100755 src/lib/fgetc.c create mode 100755 src/lib/gpe.h create mode 100755 src/lib/gpe_control.h create mode 100755 src/lib/gpe_control.pS create mode 100755 src/lib/gpe_data.h create mode 100755 src/lib/gpe_data.pS create mode 100755 src/lib/gpe_pba.c create mode 100644 src/lib/gpe_pba.h create mode 100755 src/lib/gpe_pba_pgas.pS create mode 100644 src/lib/gpe_scom.h create mode 100644 src/lib/gpe_scom.pS create mode 100755 src/lib/gpsm.c create mode 100755 src/lib/gpsm.h create mode 100755 src/lib/gpsm_dcm.c create mode 100755 src/lib/gpsm_dcm.h create mode 100755 src/lib/gpsm_dcm_fast_handler.S create mode 100755 src/lib/gpsm_init.c create mode 100755 src/lib/heartbeat.c create mode 100755 src/lib/heartbeat.h create mode 100755 src/lib/libfiles.mk create mode 100755 src/lib/libgpefiles.mk create mode 100755 src/lib/libssx.h create mode 100755 src/lib/memcpy.c create mode 100755 src/lib/memset.c create mode 100755 src/lib/pgas.h create mode 100755 src/lib/pgas_ppc.h create mode 100755 src/lib/pgp_config.h create mode 100755 src/lib/pmc_dcm.c create mode 100755 src/lib/pmc_dcm.h create mode 100644 src/lib/polling.c create mode 100644 src/lib/polling.h create mode 100755 src/lib/pore_hooks.h create mode 100755 src/lib/printf.c create mode 100755 src/lib/pstates.c create mode 100755 src/lib/pstates.h create mode 100755 src/lib/puts.c create mode 100755 src/lib/simics_stdio.c create mode 100755 src/lib/simics_stdio.h create mode 100755 src/lib/simics_stdio_addresses.h create mode 100644 src/lib/special_wakeup.c create mode 100644 src/lib/special_wakeup.h create mode 100755 src/lib/sprintf.c create mode 100644 src/lib/ssx_dump.c create mode 100644 src/lib/ssx_dump.h create mode 100755 src/lib/ssx_io.c create mode 100755 src/lib/ssx_io.h create mode 100755 src/lib/stdlib.c create mode 100644 src/lib/strcasecmp.c create mode 100755 src/lib/strdup.c create mode 100755 src/lib/string.c create mode 100755 src/lib/string.h create mode 100755 src/lib/string_stream.c create mode 100755 src/lib/string_stream.h create mode 100755 src/lib/strtox.c create mode 100755 src/lib/strtox.h create mode 100755 src/lib/time.c create mode 100755 src/lib/vrm.c create mode 100755 src/lib/vrm.h (limited to 'src/lib') 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 + +#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 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 +/// +/// The Gnu 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 can not include 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 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 functions. +/// +/// This table is used by the functions for a quick lookup of +/// character type information. Because the true functional forms of +/// 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 +#include + +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 +/// +/// 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 \ 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. + + 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. + + 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, +/// in addition to the \a undervolting_bias already built into the Pstate +/// table . 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 = + // LR = + // 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 = + // LR = + // 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); +/// +/// Send voltage_pstate and frequency_pstate to the slave and wait for +/// confirmation that the procedure has completed. +/// +/// gpsm_enable_pstates_slave(&info, voltage_pstate, frequency_pstate); +/// gpsm_hw_mode(); +/// +/// Send command to the slave to execute gpsm_hw_mode() +/// +/// \endcode +/// +/// The DCM slave executes the following sequence +/// \code +/// PstateSuperStructure* pss; +/// GlobalPstateTable *gpst; +/// Pstate voltage_pstate, frequency_pstate; +/// +/// gpsm_initialize(pss, gpst); +/// +/// Receive voltage_pstate and frequency_pstate from the masterand wait for +/// confirmation that the procedure has completed. +/// +/// gpsm_enable_pstates_slave(0, voltage_pstate, frequency_pstate); +/// +/// Wait for a command from the master to execute gpsm_hw_mode(). +/// +/// \endcode +/// +/// Preconditions +/// +/// - 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. +/// +/// +/// Standard/Benign Postconditions after executing +/// gpsm_enable_pstates_slave() +/// +/// - 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. +/// +/// Side-Effects +/// +/// - 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 + +#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 PGAS : +/// PORE GAS (GNU Assembler) User's and Reference Manual . +/// +/// 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 - 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] - 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 _ + // + // 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___ + // + // where 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 + // ) 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 PGAS : +/// PORE GAS (GNU Assembler) User's and Reference Manual . +/// +/// 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 + // + // 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 + // + // 000 + // + // 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 + // + // 000000 + + .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 + +/// 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 + +/// 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 fputc(c, stdout) . + +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 +#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 \ and \ 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. +/// +/// int sread(FILE *stream, void *buf, size_t count, size_t *read) +/// +/// 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. +/// +/// int swrite(FILE *stream, void *buf, size_t count, size_t *written) +/// +/// 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. +/// +/// int fflush(FILE *stream) +/// +/// 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 and 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 + +/// 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 , 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 +/// +/// \note The strtoX() APIs are defined in strtox.[ch] + +#include "ssx.h" +#include "ctype.h" +#include "libssx.h" +#include + + +/// 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 that require malloc() +/// +/// These APIs are split from string.c for the benefit of applications like +/// OCC FW that don't use malloc(). + +#include +#include + +/// 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 +/// +/// The SSX library does not implement the entire function. +/// However the real reason for this header was the finding that under certain +/// optimization modes, we were geting errors from the default +/// 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 + +// 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() +/// +/// Standard String Conversion Routines +/// +/// 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 +/// +/// +/// Underlying APIs +/// +/// The APIs underlying the standard APIs are all called as +/// +/// \code +/// int _strtoX(const char* str, char** endptr, int radix, * 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 +/// +/// +/// Extended APIs +/// +/// 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, * 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 + +// 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 ). 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 +/// +/// 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 +#include + +/// Get time from a timer +/// +/// \param clock_id This must be the constant CLOCK_REALTIME defined in +/// . +/// +/// \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__ */ -- cgit v1.2.1