diff options
Diffstat (limited to 'src/lib')
116 files changed, 9285 insertions, 12936 deletions
diff --git a/src/lib/Makefile b/src/lib/Makefile deleted file mode 100755 index 2d257a6..0000000 --- a/src/lib/Makefile +++ /dev/null @@ -1,71 +0,0 @@ -# $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 - -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 deleted file mode 100755 index 36e3caf..0000000 --- a/src/lib/assert.c +++ /dev/null @@ -1,47 +0,0 @@ -// $Id: assert.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/assert.c,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file assert.c -/// \brief Implementation of library routines implied by <assert.h> - -#include "ssx.h" -#include "ssx_io.h" -#include "libssx.h" - -/// The __assert_fail() function is used to implement the assert() interface -/// of ISO POSIX (2003). The __assert_fail() function prints the given \a -/// file filename, \a line line number, \a function function name and a -/// message on the standard error stream then causes a kernel panic. If there -/// is no standard error stream then the error message is printed on the \a -/// ssxout (printk()) stream. -/// -/// If function is NULL, __assert_fail() omits information about the -/// function. The aguments \a assertion, \a file, and \a line must be -/// non-NULL. - -void -__assert_fail(const char *assertion, - const char *file, - unsigned line, - const char *function) -{ - FILE *stream; - - stream = stderr; - if (stream == 0) { - stream = ssxout; - } - - fprintf(stream, "%s:%u:%s%s Assertion '%s' failed\n", - file, line, - function ? function : "", function ? ":" : "", - assertion); - - SSX_PANIC(ASSERTION_FAILURE); -} - diff --git a/src/lib/common/Makefile b/src/lib/common/Makefile new file mode 100644 index 0000000..5147b59 --- /dev/null +++ b/src/lib/common/Makefile @@ -0,0 +1,57 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/common/Makefile $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile currently builds a single archive, 'libcommon.a', from +# various library source files. +# +# part of the complete application build. +# + +#all generated files from this makefile will end up in obj/$(IMAGE_NAME)/common +export SUB_OBJDIR = /commonlib + +include img_defs.mk +include libcommonfiles.mk + +OBJS := $(addprefix $(OBJDIR)/, $(LIBCOMMON_OBJECTS)) + +libcommon.a: local + $(AR) crs $(OBJDIR)/libcommon.a $(OBJDIR)/*.o + +.PHONY: clean + +local: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +clean: + rm -fr $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +#include $(OBJS:.o=.d) +endif + diff --git a/src/lib/common/README.txt b/src/lib/common/README.txt new file mode 100644 index 0000000..760da25 --- /dev/null +++ b/src/lib/common/README.txt @@ -0,0 +1 @@ +This directory contains code that can run on any PPE or 405 processor. diff --git a/src/lib/common/kernel.h b/src/lib/common/kernel.h new file mode 100644 index 0000000..063d35c --- /dev/null +++ b/src/lib/common/kernel.h @@ -0,0 +1,363 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/kernel.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __KERNEL_H__ +#define __KERNEL_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file kernel.h +/// \brief Kernel agnostic macros that allow the same code to work with +/// different kernels. +/// +/// Programmers can include this instead of pk.h or ssx.h +/// +/// +#ifndef __ASSEMBLER__ +#ifdef __SSX__ + +/// ----------------------- Use SSX kernel interfaces -------------------------- + +#include "ssx.h" + +//Types +#define KERN_SEMAPHORE SsxSemaphore +#define KERN_SEMAPHORE_COUNT SsxSemaphoreCount +#define KERN_DEQUE SsxDeque +#define KERN_THREAD SsxThread +#define KERN_THREAD_PRIORITY SsxThreadPriority +#define KERN_THREAD_STATE SsxThreadState +#define KERN_THREAD_ROUTINE SsxThreadRoutine +#define KERN_THREAD_FLAGS SsxThreadFlags +#define KERN_ADDRESS SsxAddress +#define KERN_TIMER SsxTimer +#define KERN_TIMER_CALLBACK SsxTimerCallback +#define KERN_INTERVAL SsxInterval +#define KERN_TIMEBASE SsxTimebase +#define KERN_IRQ_ID SsxIrqId +#define KERN_MACHINE_CONTEXT SsxMachineContext + +//Constants +#define KERN_SEMAPHORE_PEND_TIMED_OUT SSX_SEMAPHORE_PEND_TIMED_OUT +#define KERN_SEMAPHORE_PEND_NO_WAIT SSX_SEMAPHORE_PEND_NO_WAIT +#define KERN_NONCRITICAL SSX_NONCRITICAL +#define KERN_CRITICAL SSX_CRITICAL +#define KERN_SUPERCRITICAL SSX_SUPERCRITICAL +#define KERN_ERROR_CHECK_API SSX_ERROR_CHECK_API +#define KERN_NO_WAIT SSX_NO_WAIT +#define KERN_WAIT_FOREVER SSX_WAIT_FOREVER + +//Functions +#define KERN_SECONDS(s) SSX_SECONDS(s) +#define KERN_MILLISECONDS(m) SSX_MILLISECONDS(m) +#define KERN_MICROSECONDS(u) SSX_MICROSECONDS(u) +#define KERN_NANOSECONDS(n) SSX_NANOSECONDS(n) + +#define KERN_TIMEBASE_GET() ssx_timebase_get() +#define KERN_TIMEBASE_SET(tb) ssx_timebase_set(tb) + +#define KERN_INTERRUPT_PREEMPTION_ENABLE() ssx_interrupt_preemption_enable() +#define KERN_INTERRUPT_PREEMPTION_DISABLE() ssx_interrupt_preemption_disable() + +#define KERN_TIMER_CREATE(timer, callback, arg) \ + ssx_timer_create(timer, callback, arg) +#define KERN_TIMER_CREATE_NONPREEMPTIBLE(timer, callback, arg) \ + ssx_timer_create_nonpreemptible(timer, callback, arg) +#define KERN_TIMER_SCHEDULE_ABSOLUTE(timer, time, period) \ + ssx_timer_schedule_absolute(timer, time, period) +#define KERN_TIMER_SCHEDULE(timer, interval, period) \ + ssx_timer_schedule(timer, interval, period) +#define KERN_TIMER_CANCEL(timer) \ + ssx_timer_cancel(timer) +#define KERN_TIMER_INFO_GET(timer, timeout, active) \ + ssx_timer_info_get(timer, timeout, active) + +#define KERN_THREAD_CREATE(thread, thread_routine, arg, stack, stack_size, priority) \ + ssx_thread_create(thread, thread_routine, arg, stack, stack_size, priority) +#define KERN_THREAD_INFO_GET(thread, state, priority, runnable) \ + ssx_thread_info_get(thread, state, priority, runnable) +#define KERN_THREAD_PRIORTY_CHANGE(thread, new_priority, old_priority) \ + ssx_thread_priority_change(thread, new_priority, old_priority) +#define KERN_THREAD_AT_PRIORITY(priority, thread) \ + ssx_thread_at_priority(priority, thread) +#define KERN_THREAD_PRIORITY_SWAP(thread_a, thread_b) \ + ssx_thread_priority_swap(thread_a, thread_b) + +#define KERN_START_THREADS() ssx_start_threads() +#define KERN_THREAD_RESUME(thread) ssx_thread_resume(thread) +#define KERN_THREAD_SUSPEND(thread) ssx_thread_suspend(thread) +#define KERN_THREAD_DELETE(thread) ssx_thread_delete(thread) +#define KERN_COMPLETE() ssx_complete() +#define KERN_SLEEP_ABSOLUTE(time) ssx_sleep_absolute(time) +#define KERN_SLEEP(interval) ssx_sleep(interval) + +#define KERN_SEMAPHORE_CREATE(semaphore, initial_count, max_count) \ + ssx_semaphore_create(semaphore, initial_count,max_count) +#define KERN_SEMAPHORE_STATIC_CREATE(sem, initial_count, max_count) \ + SSX_SEMAPHORE(sem, initial_count, max_count) +#define KERN_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) \ + SSX_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) +#define KERN_SEMAPHORE_INFO_GET(semaphore, count, pending) \ + ssx_semaphore_info_get(semaphore, count, pending) + +#define KERN_SEMAPHORE_POST(semaphore) \ + ssx_semaphore_post(semaphore) +#define KERN_SEMAPHORE_PEND(semaphore, timeout) \ + ssx_semaphore_pend(semaphore, timeout) +#define KERN_SEMAPHORE_RELEASE_ALL(semaphore) \ + ssx_semaphore_release_all(semaphore) +#define KERN_SEMAPHORE_POST_HANDLER(arg, irq, priority) \ + ssx_semaphore_post_handler(arg, irq, priority) + + +#define KERN_HALT() \ + ssx_halt() +#define KERN_PANIC(code) \ + SSX_PANIC(code) + +#define KERN_DEQUE_SENTINEL_CREATE(deque) \ + ssx_deque_sentinel_create(deque) +#define KERN_DEQUE_SENTINEL_STATIC_CREATE(deque) \ + SSX_DEQUE_SENTINEL_STATIC_CREATE(deque) +#define KERN_DEQUE_SENTINEL_INIT(dq_addr) \ + SSX_DEQUE_SENTINEL_INIT(dq_addr) +#define KERN_DEQUE_ELEMENT_CREATE(element) \ + ssx_deque_element_create(element) +#define KERN_DEQUE_ELEMENT_STATIC_CREATE(deque) \ + SSX_DEQUE_ELEMENT_STATIC_CREATE(deque) +#define KERN_DEQUE_ELEMENT_INIT() \ + SSX_DEQUE_ELEMENT_INIT() +#define KERN_DEQUE_IS_EMPTY(deque) \ + ssx_deque_is_empty(deque) +#define KERN_DEQUE_IS_QUEUED(element) \ + ssx_deque_is_queued(element) +#define KERN_DEQUE_PUSH_BACK(deque, element) \ + ssx_deque_push_back(deque, element) +#define KERN_DEQUE_PUSH_FRONT(deque, element) \ + ssx_deque_push_front(deque, element) +#define KERN_DEQUE_POP_FRONT(deque) \ + ssx_deque_pop_front(deque) +#define KERN_DEQUE_DELETE(element) \ + ssx_deque_delete(element) + +#define KERN_IRQ_HANDLER(f) \ + SSX_IRQ_HANDLER(f) +#define KERN_IRQ_SETUP(irq, polarity, trigger) \ + ssx_irq_setup(irq, polarity, trigger) +#define KERN_IRQ_HANDLER_SET(irq, handler, arg, priority) \ + ssx_irq_handler_set(irq, handler, arg, priority) +#define KERN_IRQ_ENABLE(irq) \ + ssx_irq_enable(irq) +#define KERN_IRQ_DISABLE(irq) \ + ssx_irq_disable(irq) +#define KERN_IRQ_STATUS_CLEAR(irq) \ + ssx_irq_status_clear(irq) +#define KERN_IRQ_STATUS_SET(irq, value) \ + ssx_irq_status_set(irq, value) +#define KERN_IRQ_FAST2FULL(fast_handler, full_handler) \ + SSX_IRQ_FAST2FULL(fast_handler, full_handler) + +#define KERN_CRITICAL_SECTION_ENTER(priority, pctx) \ + ssx_critical_section_enter(priority, pctx) +#define KERN_CRITICAL_SECTION_EXIT(pctx) \ + ssx_critical_section_exit(pctx) +#define KERN_CONTEXT_CRITICAL_INTERRUPT() \ + __ssx_kernel_context_critical_interrupt() + +#define KERN_ERROR_IF(condition, code) SSX_ERROR_IF(condition, code) + +#define KERN_CAST_POINTER(t, p) SSX_CAST_POINTER(t, p) + +#define KERN_STATIC_ASSERT(cond) SSX_STATIC_ASSERT(cond) + +#elif defined(__PK__) + +/// ----------------------- Use PK kernel interfaces -------------------------- + +#include "pk.h" + +//Types +#define KERN_SEMAPHORE PkSemaphore +#define KERN_SEMAPHORE_COUNT PkSemaphoreCount +#define KERN_DEQUE PkDeque +#define KERN_THREAD PkThread +#define KERN_THREAD_PRIORITY PkThreadPriority +#define KERN_THREAD_STATE PkThreadState +#define KERN_THREAD_ROUTINE PkThreadRoutine +#define KERN_THREAD_FLAGS PkThreadFlags +#define KERN_ADDRESS PkAddress +#define KERN_TIMER PkTimer +#define KERN_TIMER_CALLBACK PkTimerCallback +#define KERN_INTERVAL PkInterval +#define KERN_TIMEBASE PkTimebase +#define KERN_IRQ_ID PkIrqId +#define KERN_MACHINE_CONTEXT PkMachineContext + +//Constants +#define KERN_SEMAPHORE_PEND_TIMED_OUT PK_SEMAPHORE_PEND_TIMED_OUT +#define KERN_SEMAPHORE_PEND_NO_WAIT PK_SEMAPHORE_PEND_NO_WAIT +#define KERN_NONCRITICAL 0 +#define KERN_CRITICAL 0 +#define KERN_SUPERCRITICAL 0 +#define KERN_ERROR_CHECK_API PK_ERROR_CHECK_API +#define KERN_NO_WAIT PK_NO_WAIT +#define KERN_WAIT_FOREVER PK_WAIT_FOREVER + +//Functions +#define KERN_SECONDS(s) PK_SECONDS(s) +#define KERN_MILLISECONDS(m) PK_MILLISECONDS(m) +#define KERN_MICROSECONDS(u) PK_MICROSECONDS(u) +#define KERN_NANOSECONDS(n) PK_NANOSECONDS(n) + +#define KERN_TIMEBASE_GET() pk_timebase_get() +#define KERN_TIMEBASE_SET(tb) pk_timebase_set(tb) + +#define KERN_INTERRUPT_PREEMPTION_ENABLE() pk_interrupt_preemption_enable() +#define KERN_INTERRUPT_PREEMPTION_DISABLE() pk_interrupt_preemption_disable() + +#define KERN_TIMER_CREATE(timer, callback, arg) \ + pk_timer_create(timer, callback, arg) +#define KERN_TIMER_CREATE_NONPREEMPTIBLE(timer, callback, arg) \ + pk_timer_create_nonpreemptible(timer, callback, arg) +#define KERN_TIMER_SCHEDULE_ABSOLUTE(timer, time, period) \ + pk_timer_schedule_absolute(timer, time, period) +#define KERN_TIMER_SCHEDULE(timer, interval, period) \ + pk_timer_schedule(timer, interval, period) +#define KERN_TIMER_CANCEL(timer) \ + pk_timer_cancel(timer) +#define KERN_TIMER_INFO_GET(timer, timeout, active) \ + pk_timer_info_get(timer, timeout, active) + +#define KERN_THREAD_CREATE(thread, thread_routine, arg, stack, stack_size, priority) \ + pk_thread_create(thread, thread_routine, arg, stack, stack_size, priority) +#define KERN_THREAD_INFO_GET(thread, state, priority, runnable) \ + pk_thread_info_get(thread, state, priority, runnable) +#define KERN_THREAD_PRIORTY_CHANGE(thread, new_priority, old_priority) \ + pk_thread_priority_change(thread, new_priority, old_priority) +#define KERN_THREAD_AT_PRIORITY(priority, thread) \ + pk_thread_at_priority(priority, thread) +#define KERN_THREAD_PRIORITY_SWAP(thread_a, thread_b) \ + pk_thread_priority_swap(thread_a, thread_b) + +#define KERN_START_THREADS() pk_start_threads() +#define KERN_THREAD_RESUME(thread) pk_thread_resume(thread) +#define KERN_THREAD_SUSPEND(thread) pk_thread_suspend(thread) +#define KERN_THREAD_DELETE(thread) pk_thread_delete(thread) +#define KERN_COMPLETE() pk_complete() +#define KERN_SLEEP_ABSOLUTE(time) pk_sleep_absolute(time) +#define KERN_SLEEP(interval) pk_sleep(interval) + +#define KERN_SEMAPHORE_CREATE(semaphore, initial_count, max_count) \ + pk_semaphore_create(semaphore, initial_count,max_count) +#define KERN_SEMAPHORE_STATIC_CREATE(sem, initial_count, max_count) \ + PK_SEMAPHORE(sem, initial_count, max_count) +#define KERN_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) \ + PK_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) +#define KERN_SEMAPHORE_INFO_GET(semaphore, count, pending) \ + pk_semaphore_info_get(semaphore, count, pending) + +#define KERN_SEMAPHORE_POST(semaphore) \ + pk_semaphore_post(semaphore) +#define KERN_SEMAPHORE_PEND(semaphore, timeout) \ + pk_semaphore_pend(semaphore, timeout) +#define KERN_SEMAPHORE_RELEASE_ALL(semaphore) \ + pk_semaphore_release_all(semaphore) +#define KERN_SEMAPHORE_POST_HANDLER(arg, irq, priority) \ + pk_semaphore_post_handler(arg, irq, priority) + + +#define KERN_HALT() \ + pk_halt() +#define KERN_PANIC(code) \ + PK_PANIC(code) + +#define KERN_DEQUE_SENTINEL_CREATE(deque) \ + pk_deque_sentinel_create(deque) +#define KERN_DEQUE_SENTINEL_STATIC_CREATE(deque) \ + PK_DEQUE_SENTINEL_STATIC_CREATE(deque) +#define KERN_DEQUE_SENTINEL_INIT(dq_addr) \ + PK_DEQUE_SENTINEL_INIT(dq_addr) +#define KERN_DEQUE_ELEMENT_CREATE(element) \ + pk_deque_element_create(element) +#define KERN_DEQUE_ELEMENT_STATIC_CREATE(deque) \ + PK_DEQUE_ELEMENT_STATIC_CREATE(deque) +#define KERN_DEQUE_ELEMENT_INIT() \ + PK_DEQUE_ELEMENT_INIT() +#define KERN_DEQUE_IS_EMPTY(deque) \ + pk_deque_is_empty(deque) +#define KERN_DEQUE_IS_QUEUED(element) \ + pk_deque_is_queued(element) +#define KERN_DEQUE_PUSH_BACK(deque, element) \ + pk_deque_push_back(deque, element) +#define KERN_DEQUE_PUSH_FRONT(deque, element) \ + pk_deque_push_front(deque, element) +#define KERN_DEQUE_POP_FRONT(deque) \ + pk_deque_pop_front(deque) +#define KERN_DEQUE_DELETE(element) \ + pk_deque_delete(element) + +#define KERN_IRQ_HANDLER(f) \ + PK_IRQ_HANDLER(f) +#define KERN_IRQ_SETUP(irq, polarity, trigger) \ + pk_irq_setup(irq, polarity, trigger) +#define KERN_IRQ_HANDLER_SET(irq, handler, arg, priority) \ + pk_irq_handler_set(irq, handler, arg) +#define KERN_IRQ_ENABLE(irq) \ + pk_irq_enable(irq) +#define KERN_IRQ_DISABLE(irq) \ + pk_irq_disable(irq) +#define KERN_IRQ_STATUS_CLEAR(irq) \ + pk_irq_status_clear(irq) +#define KERN_IRQ_STATUS_SET(irq, value) \ + pk_irq_status_set(irq, value) +#define KERN_IRQ_FAST2FULL(fast_handler, full_handler) \ + PK_IRQ_FAST2FULL(fast_handler, full_handler) + +#define KERN_CRITICAL_SECTION_ENTER(priority, pctx) \ + pk_critical_section_enter(pctx) +#define KERN_CRITICAL_SECTION_EXIT(pctx) \ + pk_critical_section_exit(pctx) +#define KERN_CONTEXT_CRITICAL_INTERRUPT() \ + (0) + +#define KERN_ERROR_IF(condition, code) PK_ERROR_IF(condition, code) + +#define KERN_CAST_POINTER(t, p) PK_CAST_POINTER(t, p) + +#define KERN_STATIC_ASSERT(cond) PK_STATIC_ASSERT(cond) + +#else + +/// ----------------------- Kernel type not defined -------------------------- + +#error "Kernel type must be defined in img_defs.mk" + +#endif /*__SSX__*/ + +#endif /*__ASSEMBLER__*/ + +#endif /* __KERNEL_H__ */ diff --git a/src/lib/common/libcommonfiles.mk b/src/lib/common/libcommonfiles.mk new file mode 100644 index 0000000..9fe7592 --- /dev/null +++ b/src/lib/common/libcommonfiles.mk @@ -0,0 +1,54 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/common/libcommonfiles.mk $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file libcommonfiles.mk +# +# @brief mk for libcommon.a object files +# +# @page ChangeLogs Change Logs +# @section ofiles.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# INCLUDES +########################################################################## + +C-SOURCES = \ + memcpy.c \ + memset.c \ + rand32.c \ + string.c \ + sync.c + + +S-SOURCES = + +LIBCOMMON_OBJECTS = $(C-SOURCES:.c=.o) $(S-SOURCES:.S=.o) diff --git a/src/lib/memcpy.c b/src/lib/common/memcpy.c index ab508ea..77827dc 100755..100644 --- a/src/lib/memcpy.c +++ b/src/lib/common/memcpy.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/memcpy.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- @@ -9,7 +33,7 @@ /// \file memcpy.c /// \brief The memcpy() function -#include "ssx.h" +#include "kernel.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 diff --git a/src/lib/memset.c b/src/lib/common/memset.c index 98fdda2..1d98677 100755..100644 --- a/src/lib/memset.c +++ b/src/lib/common/memset.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/memset.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- @@ -9,7 +33,7 @@ /// \file memset.c /// \brief The memset() function -#include "ssx.h" +#include "kernel.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 diff --git a/src/lib/common/rand.h b/src/lib/common/rand.h new file mode 100644 index 0000000..369b8fd --- /dev/null +++ b/src/lib/common/rand.h @@ -0,0 +1,124 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/rand.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __RAND_H__ +#define __RAND_H__ + +// $Id$ + +/// \file rand.h +/// \brief Random number generation + +#include <stdint.h> + +// Error/Panic codes + +#define RAND64_UNIMPLEMENTED 0x00726301 + + +/// RAND32_ALL is used as the \a limit argument to rand32() and _rand32() to +/// request the return of a full 32-bit random unsigned integer. + +#define RAND32_ALL 0 + +extern uint32_t _seed32; + +uint32_t +_rand32(uint32_t *seed, uint32_t limit); + +uint32_t +rand32(uint32_t limit); + +void +srand32(uint32_t seed); + + +/// RAND64_ALL is used as the \a limit argument to rand64() and _rand64() to +/// request the return of a full 64-bit random unsigned integer. + +#define RAND64_ALL 0 + +extern uint64_t _seed64; + +//void +//davidmult64to128(uint64_t u, uint64_t v, uint64_t &h, uint64_t &l); + +uint64_t +_rand64(uint64_t *seed, uint64_t limit); + +uint64_t +rand64(uint64_t limit); + +void +srand64(uint64_t seed); + + +/// A random weighting map for integer selections +/// +/// See rand_map_int() for details. + +typedef struct { + + /// The relative weight of this selection + /// + /// The final weight of the map array \e must be 0 to terminate the map. + unsigned weight; + + /// The selection + int selection; + +} RandMapInt; + + +/// A random weighting map for pointer selections +/// +/// See rand_map_ptr() for details. + +typedef struct { + + /// The relative weight of this selection + /// + /// The final weight of the map array \e must be 0 to terminate the map. + unsigned weight; + + /// The selection + void *selection; + +} RandMapPtr; + + +int +_rand_map_int(uint32_t *seed, RandMapInt *map, int *index); + +int +rand_map_int(RandMapInt *map); + +void * +_rand_map_ptr(uint32_t *seed, RandMapPtr *map, int *index); + +void * +rand_map_ptr(RandMapPtr *map); + + +#endif /* __RAND_H__ */ diff --git a/src/lib/common/rand32.c b/src/lib/common/rand32.c new file mode 100644 index 0000000..c75fc76 --- /dev/null +++ b/src/lib/common/rand32.c @@ -0,0 +1,384 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/rand32.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file rand32.c +/// \brief 32-bit unsigned pseudo-random number generation + +#include "rand.h" + +/// The default seed for rand32() + +uint32_t _seed32 = 405405405; + + +/// Generate a random 32-bit unsigned integer from an explicit seed +/// +/// \param seed A pointer to the random seed (updated by this routine). +/// +/// \param limit The (exclusive) upper bound of the range of generated random +/// integers. The (inclusive) lower bound is always 0. +/// +/// \retval A pseudo-random unsigned 32-bit integer uniformly selected from +/// the range 0 to \a limit - 1 (inclusive). However if the \a limit parameter +/// is \c RAND32_ALL (0), then the return value is a full 32-bit random bit +/// vector. +/// +/// This is a 32-bit linear congruential generator, taken from the 'ranqd1' +/// generator from "Numerical Recipes in C". The authors' only praise for +/// this generator is that it is "\e very fast"; the quality of random numbers +/// is deemed "entirely adequate for many uses". +/// +/// The initial 32 pseudo-random result is treated as a 32-bit binary fraction +/// that is multipled by the limit to yield the final random 32-bit +/// integer. If the limit is 0, then the full 32-bit result is returned. As +/// with all LCG, do not count on the low-order bits to be particularly +/// random. + +uint32_t +_rand32(uint32_t *seed, uint32_t limit) +{ + uint64_t x; + + *seed = (*seed * 1664525) + 1013904223; + if (limit == RAND32_ALL) { + return *seed; + } else { + x = (uint64_t)(*seed) * limit; + return x >> 32; + } +} + + +/// Generate a random 32-bit unsigned integer from a system-wide seed +/// +/// \param limit The (exclusive) upper bound of the range of generated random +/// integers. The (inclusive) lower bound is always 0. +/// +/// \retval A pseudo-random unsigned 32-bit integer uniformly selected from +/// the range 0 to \a limit - 1 (inclusive). However if the \a limit parameter +/// is \c RAND32_ALL (0), then the return value is a full 32-bit random bit +/// vector. +/// +/// rand32() is not thread safe. There is a small possibility that multiple +/// threads may observe the same random numbers, and it is also possible that +/// the random sequence may appear to repeat due to thread interactions. If +/// these are concerns then the application should either call rand32() from +/// within a critical section, or provide a unique seed to each thread or +/// process and use the underlying _rand32() API explicitly. + +uint32_t +rand32(uint32_t limit) +{ + return _rand32(&_seed32, limit); +} + + +/// Set the global random seed for rand32() + +void +srand32(uint32_t seed) +{ + _seed32 = seed; +} + + +/// Select an integer from a weighted distribution using a specific seed +/// +/// \param seed A 32-bit unsigned random seed (accumulator) +/// +/// \param map An array of RandMapInt structures, the final element of which +/// must have the \a weight field = 0. This array will typically be allocated +/// statically. +/// +/// \param index An optional pointer to an integer which will recieve the +/// index of the item selected. NULL \a index are ignored. This is provided +/// for appplications that require statistics on selections. +/// +/// \retval One of the \a selection from the array with a non-0 \a weight. If +/// the weight array is NULL (= {{0, \<dont care\>}}), then by convention the +/// return value is 0, and the return index is -1. +/// +/// This routine selects items from the \a map randomly, given the weighting +/// implied by (map[i].weight / SUM(i = 0,...,N, map[i].weight)). For +/// example, the following two maps are equivalent in that they select 'a' and +/// 'c' with 25% probability, and 'b' with 50% probability: +/// +/// RandMapInt map0[] = {{1, 'a'}, {2, 'b'}, {1, 'c'}, {0, 0}}; +/// +/// RandMapInt map1[] = {{25, 'a'}, {50, 'b'}, {25, 'c'}, {0, 0}}; +/// +/// Note that several errors including negative weights, or the overflow of +/// the sum of weights as an \a unsigned number are neither detected nor +/// reported. +/// +/// \todo We could probably merge the code for the integer and pointer versions +/// somewhat. This is a great example of where C++ would be nice, as we could +/// easily cache the sum of weights when the map was constructed. + +int +_rand_map_int(uint32_t *seed, RandMapInt *map, int *index) +{ + unsigned weight, sum; + RandMapInt *p; + uint32_t rand; + int i = -1; + int selection = 0; + + sum = 0; + p = map; + while (p->weight != 0) { + sum += p->weight; + p++; + } + + if (sum != 0) { + + rand = _rand32(seed, sum); + + weight = 0; + p = map; + i = 0; + while (p->weight != 0) { + weight += p->weight; + if (rand < weight) { + selection = p->selection; + break; + } + p++; + i++; + } + } + + if (index != 0) { + *index = i; + } + + return selection; +} + + +/// Select an integer from a weighted distribution using the system-side seed +/// \a _seed32 +/// +/// See _rand_map_int() for documentation + +int +rand_map_int(RandMapInt *map) +{ + return _rand_map_int(&_seed32, map, 0); +} + + +/// Select a pointer from a weighted distribution using a specific seed +/// +/// \param seed A 32-bit unsigned random seed (accumulator) +/// +/// \param map An array of RandMapPtr structures, the final element of which +/// must have the \a weight field = 0. This array will typically be allocated +/// statically. +/// +/// \param index An optional pointer to an integer which will recieve the +/// index of the item selected. NULL \a index are ignored. This is provided +/// for appplications that require statistics on selections. +/// +/// \retval One of the \a selection from the array with a non-0 \a weight. If +/// the weight array is NULL (= {{0, \<dont care\>}}), then by convention the +/// return value is 0, and the return index is -1; +/// +/// This routine selects items from the \a map randomly, given the weighting +/// implied by (map[i].weight / SUM(i = 0,...,N, map[i].weight)). For +/// example, the following two maps are equivalent in that they select &a and +/// &c with 25% probability, and &b with 50% probability: +/// +/// RandMapPtr map0[] = {{1, &a}, {2, &b}, {1, &c}, {0, 0}}; +/// +/// RandMapPtr map1[] = {{25, &a}, {50, &b}, {25, &c}, {0, 0}}; +/// +/// Note that several errors including negative weights, or the overflow of +/// the sum of weights as an \a unsigned number are neither detected nor +/// reported. + + +void * +_rand_map_ptr(uint32_t *seed, RandMapPtr *map, int *index) +{ + unsigned weight, sum; + RandMapPtr *p; + uint32_t rand; + int i = -1; + void *selection = 0; + + sum = 0; + p = map; + while (p->weight != 0) { + sum += p->weight; + p++; + } + + if (sum != 0) { + + rand = _rand32(seed, sum); + + weight = 0; + p = map; + i = 0; + while (p->weight != 0) { + weight += p->weight; + if (rand < weight) { + selection = p->selection; + break; + } + p++; + i++; + } + } + + if (index != 0) { + *index = i; + } + + return selection; +} + + +/// Select a pointer from a weighted distribution using the system-side seed +/// \a _seed32 +/// +/// See _rand_map_ptr() for documentation + +void * +rand_map_ptr(RandMapPtr *map) +{ + return _rand_map_ptr(&_seed32, map, 0); +} + + +//////////////////////////////////////////////////////////////////////////// + +#ifdef __TEST_RAND_C__ + +#include <stdio.h> +#include <stdlib.h> + +#ifdef RANDOM_MAP + +// Weighted distribution testing + +int a, b, c; +int aa, bb, cc; + +int x[3]; + +RandMapPtr map0[] = {{1, &a}, {2, &b}, {1, &c}, {0, 0}}; +RandMapPtr map1[] = {{25, &aa}, {50, &bb}, {25, &cc}, {0, 0}}; + +RandMapInt map2[] = {{25, 0}, {50, 1}, {25, 2}, {0, 0}}; + +int +main() +{ + int i, j; + int *p; + + for (i = 0; i < 1000000; i++) { + p = (int *)(rand_map_ptr(map0)); + *p = *p + 1; + p = (int *)(rand_map_ptr(map1)); + *p = *p + 1; + j = rand_map_int(map2); + x[j]++; + } + + printf("%d %d %d\n", a, b, c); + printf("%d %d %d\n", aa, bb, cc); + printf("%d %d %d\n", x[0], x[1], x[2]); + + return 0; +} + +#endif /* RANDOM_MAP */ + + +#ifdef BASIC_TEST + +// Simple self-checking uniform distrubution tests for rand32. + +void +test(int *a, int size, int count, double max_error) +{ + int i; + double error; + + for (i = 0; i < size; i++) { + a[i] = 0; + } + + for (i = 0; i < size * count; i++) { + a[rand32(size)]++; + } + + for (i = 0; i < size; i++) { + error = (a[i] / (double)count) - 1.0; + printf("a[%4d] : %10d %.5f\n", i, a[i], error); + if (abs(error) > max_error) { + printf("Too much error\n"); + exit(1); + } + } +} + +#define MAX_SIZE 128 + +int +main() +{ + int a[MAX_SIZE]; + int count = 1000000; + double max_error = .005; + int i; + + for (i = 2; i <= MAX_SIZE; i *= 2) { + printf("\nTest %d\n\n", i); + test(a, i, count, max_error); + } + + srand32(0); + for (i = 2; i <= MAX_SIZE; i *= 2) { + printf("\nTest %d\n\n", i); + test(a, i, count, max_error); + } + + srand32(0xdeadbeef); + for (i = 2; i <= MAX_SIZE; i *= 2) { + printf("\nTest %d\n\n", i); + test(a, i, count, max_error); + } +} + +#endif /* BASIC_TEST */ + +#endif /* __TEST_RAND_C__ */ + diff --git a/src/lib/string.c b/src/lib/common/string.c index 81d1778..7c82653 100755..100644 --- a/src/lib/string.c +++ b/src/lib/common/string.c @@ -1,5 +1,27 @@ -// $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 $ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/string.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ //----------------------------------------------------------------------------- // *! (C) Copyright International Business Machines Corp. 2013 // *! All Rights Reserved -- Property of IBM @@ -9,7 +31,6 @@ /// \file string.c /// \brief strlen(), strcmp() etc. functions -#include "ssx.h" #include "string.h" /// Compute the length of a string diff --git a/src/lib/common/string.h b/src/lib/common/string.h new file mode 100644 index 0000000..5cfa470 --- /dev/null +++ b/src/lib/common/string.h @@ -0,0 +1,88 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/string.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STRING_H__ +#define __STRING_H__ + +// $Id: string.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file string.h +/// \brief Replacement for <string.h> +/// +/// The SSX library does not implement the entire <string.h> function. +/// However the real reason for this header was the finding that under certain +/// optimization modes, we were geting errors from the default <string.h> +/// supplied with the MPC environment. So we created this replacement that +/// only calls out what is implemented, exactly as it is implemented for SSX. + +#ifndef __ASSEMBLER__ + +#include <stddef.h> + +// APIs inmplemented by string.c + +size_t +strlen(const char *s); + +int +strcmp(const char* s1, const char* s2); + +int +strncmp(const char* s1, const char* s2, size_t n); + +int +strcasecmp(const char* s1, const char* s2); + +int +strncasecmp(const char* s1, const char* s2, size_t n); + +char * +strcpy(char *dest, const char *src); + +char * +strncpy(char *dest, const char *src, size_t n); + +void * +memcpy(void *dest, const void *src, size_t n); + +void * +memset(void *s, int c, size_t n); + +int +memcmp(const void* s1, const void* s2, size_t n); + +// APIs implemented by strdup.c + +char * +strdup(const char* s); + +#endif /* __ASSEMBLER__ */ + +#endif /* __STRING_H__ */ diff --git a/src/lib/common/sync.c b/src/lib/common/sync.c new file mode 100644 index 0000000..a489be8 --- /dev/null +++ b/src/lib/common/sync.c @@ -0,0 +1,294 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/sync.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file sync.c +/// \brief A library of higher-level synchronization primitives based on +/// low-level kernel services. +/// +/// The APIs provided here are currently based on kernel services, but the +/// specifications should allow applications to be ported to other +/// environments if required. Note that like kernel services, data +/// structures manipulated by this code are protected in KERN_NONCRITICAL +/// critical sections. + +#include "kernel.h" +#include "sync.h" + +/// Create a Barrier +/// +/// \param barrier A pointer to an uninitialized or currently unused Barrier +/// +/// \param count The number of threads required to pend at the barrier before +/// all threads are released again for execution. Note that \a count values +/// of 0 and 1 are treated as equivalent - threads will not pend at all in +/// these cases. Also note that if a watchdog thread is being used, the +/// watchdog thread should not be included in the count. +/// +/// \retval 0 Success +/// +/// \retval -SYNC_INVALID_OBJECT The \a barrier is NULL (0) + +int +barrier_create(Barrier *barrier, KERN_SEMAPHORE_COUNT count) +{ + if (KERN_ERROR_CHECK_API) { + KERN_ERROR_IF(barrier == 0, SYNC_INVALID_OBJECT); + } + + barrier->entry_sem = &(barrier->sem[0]); + barrier->exit_sem = &(barrier->sem[1]); + + KERN_SEMAPHORE_CREATE((KERN_SEMAPHORE *)(barrier->entry_sem), 0, 0); + KERN_SEMAPHORE_CREATE((KERN_SEMAPHORE *)(barrier->exit_sem), 0, 0); + + barrier->entry_count = 0; + barrier->exit_count = 0; + barrier->target_count = count; + + barrier->watchdog_pending = 0; + barrier->watchdog_entries = 0; + + barrier->callback = 0; + barrier->arg = 0; + barrier->run_callback = 0; + + return 0; +} + + +/// Install a barrier callback +/// +/// \param barrier A pointer to an initialized Barrier object +/// +/// \param callback A function taking a single (void *) argument, to be +/// executed in the context of the first thread to exit the barrier when the +/// barrier condition is met. +/// +/// \param arg The argument of the \a callback. +/// +/// The Barrier object supports an optional callback function. The callback +/// (with the customary single (void *) parameter) is made when the barrier +/// condition is met, in the thread context of the first (highest priority) +/// thread to exit the barrier. The callback is made inside the barrier_pend() +/// call, but outside of a critical section. The specification of the callback +/// is not part of the barrier_create() call, but is provided later by this +/// API. Setting a NULL (0) callback disables the callback mechanism. +/// +/// \retval 0 Success +/// +/// \retval -SYNC_INVALID_OBJECT The \a barrier is NULL (0) + +int +barrier_callback_set(Barrier *barrier, + BarrierCallback callback, + void *arg) +{ + KERN_MACHINE_CONTEXT ctx; + + if (KERN_ERROR_CHECK_API) { + KERN_ERROR_IF(barrier == 0, SYNC_INVALID_OBJECT); + } + + KERN_CRITICAL_SECTION_ENTER(KERN_NONCRITICAL, &ctx); + + barrier->callback = callback; + barrier->arg = arg; + + KERN_CRITICAL_SECTION_EXIT(&ctx); + + return 0; +} + + +static int +_barrier_pend(Barrier *barrier, int watchdog) +{ + KERN_MACHINE_CONTEXT ctx; + int rc = 0; + KERN_SEMAPHORE *temp_sem; + BarrierCallback callback = 0; /* Make GCC Happy */ + void *arg = 0; /* Make GCC Happy */ + int run_callback; + + if (KERN_ERROR_CHECK_API) { + KERN_ERROR_IF(barrier == 0, SYNC_INVALID_OBJECT); + } + + KERN_CRITICAL_SECTION_ENTER(KERN_NONCRITICAL, &ctx); + + // A normal thread will pend at the entry unless 1) the thread satisfies + // the barrier condition, or 2) a watchdog thread is pending here. A + // watchdog thread only pends if no other threads are pending. + + if (watchdog) { + barrier->watchdog_entries++; + } + + barrier->entry_count++; + if (!barrier->watchdog_pending && + ((watchdog && (barrier->entry_count == 1)) || + (!watchdog && (barrier->entry_count < barrier->target_count)))) { + + if (watchdog) { + barrier->watchdog_pending = 1; + } + + // The thread must pend here + + rc = KERN_SEMAPHORE_PEND((KERN_SEMAPHORE *)(barrier->entry_sem), + KERN_WAIT_FOREVER); + if (rc) { + goto exit_critical; + } + + } else { + + // The barrier condition is met - or the watchdog thread is blocked + // here. The entry and exit semaphores and counts are swapped. The + // callback is marked to be called. + + // If the barrier is used incorrectly, or threads are deleted without + // adjusting the barrier target count then the following condition + // could become true, which could lead to bad behavior. + + if (barrier->exit_count != 0) { + KERN_PANIC(SYNC_BARRIER_INVARIANT); + } + + barrier->entry_count--; // Undo preincrement above + barrier->watchdog_pending = 0; + + temp_sem = (KERN_SEMAPHORE *)(barrier->exit_sem); + barrier->exit_sem = barrier->entry_sem; + barrier->entry_sem = temp_sem; + + barrier->exit_count = barrier->entry_count; + barrier->entry_count = 0; + + barrier->run_callback = 1; + } + + // This thread either continues to run or just woke up after having + // blocked at the barrier. The current thread makes the next thread (if + // any) runnable as well. Normally the current thread will be of a higher + // priority than any blocked threads, so no context switch will occur. The + // thread that satisfies the barrier condition \e will cause a context + // switch here, unless it just happens to be the highest priority thread + // in the barrier group. + + if (barrier->exit_count != 0) { + barrier->exit_count--; + rc = KERN_SEMAPHORE_POST((KERN_SEMAPHORE *)(barrier->exit_sem)); + } + +exit_critical: + + if (rc) { + KERN_CRITICAL_SECTION_EXIT(&ctx); + return rc; + } + + // In the case of a satisfied barrier condition, the first thread to exit + // the critical section will be the higest priority thread blocked at the + // barrier. This thread is tasked with executing the callback, outside of + // the critical section. + + run_callback = barrier->run_callback; + barrier->run_callback = 0; + if (run_callback) { + callback = barrier->callback; + arg = (void *)(barrier->arg); /* Cast away 'volatile' */ + } + + KERN_CRITICAL_SECTION_EXIT(&ctx); + + if (run_callback && callback) { + callback(arg); + } + + return 0; +} + + +/// Pend at a Barrier +/// +/// \param barrier An initialized barrier object +/// +/// A thread will pend at a barrier until \a count number of threads (supplied +/// in the call of barrier_create()) are pending. If \a count is 0 or 1, the +/// API always returns immediately. +/// +/// If barrier watchdog thread is being used (correctly), then the watchdog +/// will cause thread pending on the barrier to be released whenever all +/// threads in the group are blocked, regardless of whether some of the +/// group's threads are blocked elesewhere. +/// +/// \retval 0 Success +/// +/// \retval -SYNC_INVALID_OBJECT The \a barrier is NULL (0) +/// +/// Other errors may be returned by the embedded call of ssx_semephore_pend(). +/// In particular this API will fail if called outside of a thread context +/// since it requires blocking indefinitely on a semaphore. +/// +/// \bug The semaphore should be able to provide the number of pending threads +/// - which we should really be using here instead of the barrier counts. The +/// current implementation can produce some strange behavior if threads are +/// deleted. + +int +barrier_pend(Barrier *barrier) +{ + return _barrier_pend(barrier, 0); +} + + +/// Barrier watchdog thread +/// +/// \param arg A pointer to the Barrier object this thread should manage. +/// +/// A barrier_watchdog() thread is attached to a Barrier object, and forces +/// the barrier condition to be met whenever it runs. This thread is designed +/// to be mapped at a priority immediately below the priorities of a group of +/// threads that pend on the barrier. +/// +/// In this way, should every thread in the group become blocked, the watchdog +/// will allow any threads that are blocked on the barrier to run. If all the +/// threads are blocked elsewhere, then the watchdog blocks on the barrier, +/// and as soon as any thread pends again at the barrier the thread remains +/// runnable and the watchdog becomes runnable. +/// +/// The barrier_watchdog() thread is not required. Without the watchdog, +/// otherwise runnable threads in the barrier group will remain blocked on the +/// barrier as long as any of their cohorts remain blocked elsewhere. + +void +barrier_watchdog(void *arg) +{ + do { + _barrier_pend((Barrier *)arg, 1); + } while (1); +} diff --git a/src/lib/common/sync.h b/src/lib/common/sync.h new file mode 100644 index 0000000..5657c78 --- /dev/null +++ b/src/lib/common/sync.h @@ -0,0 +1,167 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/common/sync.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SYNC_H__ +#define __SYNC_H__ + +// $Id$ + +/// \file sync.h +/// \brief A library of higher-level synchronization primitives based on +/// low-level kernel services. +/// +/// The APIs provided here are currently based on SSX services, but the +/// specifications should allow applications to be fairly easily ported to +/// other environments if required. +/// +/// \todo Consider implementing a subset of the POSIX pthreads standards +/// instead of these non-standard synchronization primitives. Ideally all +/// synchronization primitives would be part of the SSX kernel so that they +/// would correctly handle thread suspension and deletion while pending. + +#include "kernel.h" + +// Error/panic codes + +#define SYNC_INVALID_OBJECT 0x00896201 +#define SYNC_INVALID_ARGUMENT 0x00896202 +#define SYNC_BARRIER_PEND_TIMED_OUT 0x00896203 +#define SYNC_BARRIER_OVERFLOW 0x00896204 +#define SYNC_BARRIER_UNDERFLOW 0x00896205 +#define SYNC_BARRIER_INVARIANT 0x00896206 +#define SYNC_SHARED_UNDERFLOW 0x00896207 + +//////////////////////////////////////////////////////////////////////////// +// Barrier +//////////////////////////////////////////////////////////////////////////// + +typedef void (*BarrierCallback)(void *); + +/// A thread barrier object +/// +/// A Barrier allows multiple threads to pend until a group of threads are all +/// pending at the barrier. Once all threads are pending at the barrier, all +/// threads are released again for execution. The barrier guarantees that +/// once released, lower-priority threads will have the chance to execute +/// before the barrier condition is satisfied again. Thus the Barrier can be +/// used as a form of fair scheduling for a group of threads that execute in a +/// loop of doing work followed by pending at the barrier. +/// +/// Although the barrier guarantees fairness, it can not by itself guarantee +/// progress. If several threads in a barrier group are +/// pending at the barrier while other threads in the group are blocked away +/// from the barrier, the threads at the barrier should be running (if allowed +/// by the priority mappings etc.). The only way to guarantee constant +/// progress by any unblocked thread in a group is to created another, +/// 'watchdog' thread that has lower priority than the other threads in the +/// group. The watchdog thread only executes when all threads in the group +/// are blocked, and ensures progress. This implementation provides a +/// watchdog thread routine as barrier_watchdog(). +/// +/// Normally the threads in a barrier group will be assigned consecutive +/// priorities - otherwise various forms of priority inversion can arise. The +/// watchdog thread, if any, will normally be assigned the priority +/// immediately lower than the lowest priority thread in the group. +/// +/// The Barrier object supports an optional callback function. The callback +/// (with the customary single (void *) parameter) is made when the barrier +/// condition is met, in the thread context of the first (highest priority) +/// thread to exit the barrier. The callback is made inside the barrier_pend() +/// call, but outside of a critical section. The specification of the callback +/// is not part of the barrier_create() call, but is provided later by the +/// barrier_callback_set() call. A NULL (0) callback (the default) is ignored. +/// +/// NB: All barrier APIs (other than barrier_create()) must be made from +/// thread mode - they will fail if called from interrupt handlers or before +/// threads have started. + +typedef struct Barrier { + + /// Semaphore array; see \a entry_sem and \a exit_sem; + KERN_SEMAPHORE sem[2]; + + /// The entry semaphore. + /// + /// Threads pending at the barrier initially block here. Once all of the + /// threads in the group are pending here, the entry and exit semaphores + /// are swapped and threads are released (in priority order) from the new + /// exit semaphore. + volatile KERN_SEMAPHORE *entry_sem; + + /// The exit semaphore. + volatile KERN_SEMAPHORE *exit_sem; + + /// The current count of threads pending at \a entry_sem. + volatile KERN_SEMAPHORE_COUNT entry_count; + + /// The current count of threads pending at \a exit_sem. + volatile KERN_SEMAPHORE_COUNT exit_count; + + /// The target number of threads required to release the barrier + volatile KERN_SEMAPHORE_COUNT target_count; + + /// A flag - Is the watchdog thread pending at the barrier? + volatile int watchdog_pending; + + /// Statistics - The number of times the watchdog has entered the barrier. + volatile uint32_t watchdog_entries; + + /// The barrier condition callback function + volatile BarrierCallback callback; + + /// The argument of the callback function + volatile void *arg; + + /// This flag is set to tell the first thread to exit the barrier to + /// execute the callback (if any). + volatile int run_callback; + +} Barrier; + +int +barrier_create(Barrier *barrier, KERN_SEMAPHORE_COUNT count); + +int +barrier_callback_set(Barrier *barrier, + BarrierCallback callback, + void *arg); + +int +barrier_pend(Barrier *barrier); + +void +barrier_watchdog(void *arg); + + +#endif // __SYNC_H__ + + + + + + + + + + diff --git a/src/lib/ctype.c b/src/lib/ctype.c deleted file mode 100755 index 5b10635..0000000 --- a/src/lib/ctype.c +++ /dev/null @@ -1,22 +0,0 @@ -// $Id: ctype.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype.c,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file ctype.c -/// \brief Replacement for <ctype.h> functions -/// -/// This file contains entry point equivalents for the "ctype.h" macros. -/// These would only ever be used by assembler programs, therefore it's likely -/// that the object file will never be linked into an image. - -#define __CTYPE_C__ -#include "ctype.h" -#undef __CTYPE_C__ - - - - diff --git a/src/lib/errno.h b/src/lib/errno.h deleted file mode 100755 index a45bb84..0000000 --- a/src/lib/errno.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __ERRNO_H__ -#define __ERRNO_H__ - -// $Id: errno.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/errno.h,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file errno.h -/// \brief Replacement for <errno.h> -/// -/// SSX does not support a per-thread or global 'errno'. The standard Unix -/// errno values returned by library functions are defined here. The prefix -/// code is the 'telephone code' for "errn". - -#define EINVAL 0x00377601 -#define EBADF 0x00377602 -#define EAGAIN 0x00377603 -#define ENXIO 0x00377604 -#define ENOMEM 0x00377605 - -#endif /* __ERRNO_H__ */ diff --git a/src/lib/gpe.h b/src/lib/gpe.h deleted file mode 100755 index e759c7d..0000000 --- a/src/lib/gpe.h +++ /dev/null @@ -1,78 +0,0 @@ -#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 deleted file mode 100755 index 355330f..0000000 --- a/src/lib/gpe_control.h +++ /dev/null @@ -1,171 +0,0 @@ -#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 deleted file mode 100755 index 3f3c790..0000000 --- a/src/lib/gpe_control.pS +++ /dev/null @@ -1,160 +0,0 @@ -// $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 deleted file mode 100755 index 790d82b..0000000 --- a/src/lib/gpe_data.h +++ /dev/null @@ -1,672 +0,0 @@ -#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 deleted file mode 100755 index 2338276..0000000 --- a/src/lib/gpe_data.pS +++ /dev/null @@ -1,1585 +0,0 @@ -// $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 deleted file mode 100755 index c3b0a00..0000000 --- a/src/lib/gpe_pba.c +++ /dev/null @@ -1,148 +0,0 @@ -// $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 deleted file mode 100644 index 5f8ea2c..0000000 --- a/src/lib/gpe_pba.h +++ /dev/null @@ -1,116 +0,0 @@ -#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 deleted file mode 100755 index 346234d..0000000 --- a/src/lib/gpe_pba_pgas.pS +++ /dev/null @@ -1,110 +0,0 @@ -// $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 deleted file mode 100644 index 3f8f26e..0000000 --- a/src/lib/gpe_scom.h +++ /dev/null @@ -1,471 +0,0 @@ -#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 deleted file mode 100644 index 2df1b78..0000000 --- a/src/lib/gpe_scom.pS +++ /dev/null @@ -1,709 +0,0 @@ -// $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 deleted file mode 100755 index e2b71d6..0000000 --- a/src/lib/gpsm.c +++ /dev/null @@ -1,600 +0,0 @@ -// $Id: gpsm.c,v 1.2 2014/02/03 01:30:24 daviddu Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm.c,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file gpsm.c -/// \brief Global Pstate Mechanism procedures -/// -/// \todo : Should we initialize any/all iVRM delays in gpsm_lpsa_install()? - -#include "ssx.h" -#include "pstates.h" -#include "gpe_control.h" -#include "gpsm.h" -#include "vrm.h" - -/// The semaphore used to block threads waiting for GPSM protocol actions - -SsxSemaphore G_gpsm_protocol_semaphore; - - -//////////////////////////////////////////////////////////////////////////// -// Private Utilities -//////////////////////////////////////////////////////////////////////////// - -// The mechanical transition to Firmware Pstate Mode - Not a procedure - -static void -_gpsm_fw_mode(void) -{ - pmc_mode_reg_t pmr; - - pmr.value = in32(PMC_MODE_REG); - pmr.fields.enable_hw_pstate_mode = 0; - pmr.fields.enable_fw_auction_pstate_mode = 0; - pmr.fields.enable_fw_pstate_mode = 1; - out32(PMC_MODE_REG, pmr.value); -} - - -// The mechanical transition to Firmware Auction Pstate Mode - Not a procedure - -static void -_gpsm_fw_auction_mode(void) -{ - pmc_mode_reg_t pmr; - - pmr.value = in32(PMC_MODE_REG); - pmr.fields.enable_hw_pstate_mode = 0; - pmr.fields.enable_fw_auction_pstate_mode = 1; - pmr.fields.enable_fw_pstate_mode = 0; - out32(PMC_MODE_REG, pmr.value); -} - - -// The mechanical transition to Hardware Pstate Mode - Not a procedure. -// Disable voltage change via safe_mode_without_spivid -// before enter hw mode to prevent possible glitch that -// hw momentarily flush turbo to pstate actual, -// After enter hw mode, we will enable back the spivid - -static void -_gpsm_hw_mode(void) -{ - pmc_mode_reg_t pmr; - - if (!gpsm_dcm_slave_p()) { - pmr.value = in32(PMC_MODE_REG); - pmr.fields.safe_mode_without_spivid = 1; - out32(PMC_MODE_REG, pmr.value); - } - - pmr.value = in32(PMC_MODE_REG); - pmr.fields.enable_hw_pstate_mode = 1; - pmr.fields.enable_fw_auction_pstate_mode = 0; - pmr.fields.enable_fw_pstate_mode = 0; - out32(PMC_MODE_REG, pmr.value); - - if (!gpsm_dcm_slave_p()) { - pmr.value = in32(PMC_MODE_REG); - pmr.fields.safe_mode_without_spivid = 0; - out32(PMC_MODE_REG, pmr.value); - } - -} - - -//////////////////////////////////////////////////////////////////////////// -// Private Sub-Procedures -//////////////////////////////////////////////////////////////////////////// - -// By definition, quiescing the GPSM always leaves the system in firmware -// Pstate mode. This is necessary for a consistent specification due to the -// fact that in general, Hardware Pstate mode can not be quiesced without -// leaving that mode. - -// To quiesce the GPSM in firmware or firmware auction mode requires waiting -// for both the voltage and frequency changes to be complete. Note that they -// will never be ongoing simultaneously. This predicate is used for all -// firmware-mode quiesce, even though normally only one part or the other -// (voltage/protocol) is active. -// -// Recall that PMC interrupts are level-low, so if ongoing status is clear, -// the operation is ongoing. - -static int -gpsm_fw_quiesce(void) -{ - int rc = 0; - - if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) { - ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING); - rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER); - } - - if ((!rc) && !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING)) { - ssx_irq_enable(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING); - rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER); - } - - if (!rc) { - _gpsm_fw_mode(); - } - - return rc; -} - - -// To quiesce the GPSM in hardware mode requires waiting for any ongoing -// Pstate change to be complete. Note that there is no guarantee that this -// condition will ever be true in general unless something external to PMC -// ensures that Global bids stop coming in to the GPSM. An alternative used -// here is to 'lock' the GPSM temporarily by setting the rail bounds min and -// max to the current Global Pstate Actual. The GPSM will eventually quiesce -// at the global actual, and we can safely move to Firmware Pstate mode and -// release the lock. -// -// Recall that PMC 'ongoing' interrupts are level-low, so if ongoing status is -// clear, the operation is ongoing. - -static int -gpsm_hw_quiesce(void) -{ - int rc = 0; - pmc_rail_bounds_register_t prbr, original_prbr; - pmc_pstate_monitor_and_ctrl_reg_t ppmacr; - - ppmacr.value = in32(PMC_PSTATE_MONITOR_AND_CTRL_REG); - - original_prbr.value = prbr.value = in32(PMC_RAIL_BOUNDS_REGISTER); - prbr.fields.pmin_rail = ppmacr.fields.gpsa; - prbr.fields.pmax_rail = ppmacr.fields.gpsa; - out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value); - - rc = _gpsm_hw_quiesce(); - - if (!rc) { - _gpsm_fw_mode(); - out32(PMC_RAIL_BOUNDS_REGISTER, original_prbr.value); - } - - return rc; -} - -//////////////////////////////////////////////////////////////////////////// -// Public Predicates -//////////////////////////////////////////////////////////////////////////// - -/// Is the Global Pstate Mechanism quiesced? -/// -/// This predicate can only truly be answered 'true' if we are not in -/// hardware Pstate mode. -/// -/// \retval 0 Either we're in Hardware Pstate Mode, or a Voltage/Frequency -/// operation is ongoing. -/// -/// \retval 1 We're not in Hardware Pstate Mode and no Voltage/Frequency -/// operation is ongoing. - -int -gpsm_quiesced_p(void) -{ - return !(gpsm_hw_mode_p() || - !ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING) || - !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING)); -} - -/// Predicate: Is the PMC in hardware Pstate mode? -/// -/// \returns 0/1 - -int -gpsm_hw_mode_p(void) -{ - pmc_mode_reg_t pmr; - - pmr.value = in32(PMC_MODE_REG); - return (pmr.fields.enable_hw_pstate_mode != 0); -} - - -/// Predicate: Is the PMC in firmware auction Pstate mode? -/// -/// \returns 0/1 - -int -gpsm_fw_auction_mode_p(void) -{ - pmc_mode_reg_t pmr; - - pmr.value = in32(PMC_MODE_REG); - return (pmr.fields.enable_fw_auction_pstate_mode != 0); -} - - -/// Predicate: Is the PMC in firmware Pstate mode? -/// -/// \returns 0/1 - -int -gpsm_fw_mode_p(void) -{ - pmc_mode_reg_t pmr; - - pmr.value = in32(PMC_MODE_REG); - return (pmr.fields.enable_fw_pstate_mode != 0); -} - - -/// Predicate: Is the chip configured as a DCM? -/// -/// \returns 0/1 - -int -gpsm_dcm_mode_p(void) -{ - pmc_mode_reg_t pmc_mode_reg; - pmc_mode_reg.value = in32(PMC_MODE_REG); - return pmc_mode_reg.fields.enable_interchip_interface; -} - - -/// Predicate: Is the chip configured as a DCM Slave? -/// -/// \returns 0/1 - -int -gpsm_dcm_master_p(void) -{ - pmc_mode_reg_t pmc_mode_reg; - pmc_mode_reg.value = in32(PMC_MODE_REG); - return - pmc_mode_reg.fields.enable_interchip_interface && - pmc_mode_reg.fields.interchip_mode; -} - - -/// Predicate: Is the chip configured as a DCM Slave? -/// -/// \returns 0/1 - -int -gpsm_dcm_slave_p(void) -{ - pmc_mode_reg_t pmc_mode_reg; - pmc_mode_reg.value = in32(PMC_MODE_REG); - return - pmc_mode_reg.fields.enable_interchip_interface && - (pmc_mode_reg.fields.interchip_mode == 0); -} - - - - -//////////////////////////////////////////////////////////////////////////// -// Procedures -//////////////////////////////////////////////////////////////////////////// - -/// Recover the GlobalPstateTable object from the PMC -/// -/// \note It is assumed that the pointer to the Global Pstate table installed -/// in the PMC is actually a pointer to a complete GlobalPstateTable object -/// (which contains a Global Pstate table as its first element). -/// -/// \returns A pointer to the currently active GlobalPstateTable object. - -GlobalPstateTable* -gpsm_gpst(void) -{ - pmc_parameter_reg1_t ppr1; - - ppr1.value = in32(PMC_PARAMETER_REG1); - return (GlobalPstateTable*) - (ppr1.fields.ba_sram_pstate_table << GLOBAL_PSTATE_TABLE_ALIGNMENT); -} - - -/// Quiesce the GPSM to firmware mode from any other mode -/// -/// At the exit of this procedure, the PMC will be in Firmware Pstate Mode and -/// there will be no ongoing voltage or frequency transitions. -int -gpsm_quiesce(void) -{ - int rc; - - if (gpsm_hw_mode_p()) { - rc = gpsm_hw_quiesce(); - } else { - rc = gpsm_fw_quiesce(); - } - - return rc; -} - -/// Quiesce the GPSM in Hardware Pstate Mode -/// -/// In general there is no guarantee that the GPSM will ever quiesce, or -/// remain quiesced in Hardware Pstate Mode unless something like the -/// procedure in gpsm_hw_quiesce() is used. This procedure is provided for -/// the benefit of applications that are in complete control of Pstates -/// (including idle state Pstates) to simply wait for the Pstate protocol to -/// quiesce, without quiescing and entering Firmware Pstate mode like -/// gpsm_hw_quiesce(). - -int -_gpsm_hw_quiesce(void) -{ - int rc; - - do { - rc = 0; - - if (!gpsm_hw_mode_p()) { - rc = -GPSM_ILLEGAL_MODE_HW_QUIESCE; - break; - } - - if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) { - ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING); - rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, - SSX_WAIT_FOREVER); - if (rc) break; - } - } while (0); - - return rc; -} - - -/// Change to GPSM firmware mode from any mode - -int -gpsm_fw_mode(void) -{ - return gpsm_quiesce(); -} - - -/// Change to GPSM firmware auction mode from any mode - -int -gpsm_fw_auction_mode(void) -{ - int rc; - - rc = gpsm_quiesce(); - if (!rc) { - _gpsm_fw_auction_mode(); - } - return rc; -} - - -/// Change to Hardware Pstate Mode -/// -/// The (unchecked) assumption behind this procedure is that the caller has -/// run through Pstate intialization and enablement, and the system is in a -/// state where the entry to Hardware Pstate Mode is safe once the GPSM is -/// quiesced. - -int -gpsm_hw_mode(void) -{ - int rc; - - TRACE_GPSM(TRACE_GPSM_HW_MODE); - - rc = gpsm_quiesce(); - if (!rc) { - _gpsm_hw_mode(); - } - return rc; -} - - -/// The default GPSM auction procedure -/// -/// The default auction returns the value of -/// PMC_HARDWARE_AUCTION_PSTATE_REG.haps. - -Pstate -gpsm_default_auction(void) -{ - pmc_hardware_auction_pstate_reg_t phapr; - - phapr.value = in32(PMC_HARDWARE_AUCTION_PSTATE_REG); - return phapr.fields.haps; -} - - -/// Update a user-supplied vector of Pstates with the current Global bid of -/// each core. -/// -/// \param[out] o_bids A vector of Pstates; The vector must be large enough to -/// hold the bid of every possible core. -/// -/// This routine is provided for use by non-default Global Pstate auction -/// procedures. - -void -gpsm_get_global_bids(Pstate *o_bids) -{ - // This takes advantage of the implicit layout of the - // PMC_CORE_PSTATE_REG<n>. - - uint32_t *bids32 = (uint32_t *)o_bids; - - bids32[0] = in32(PMC_CORE_PSTATE_REG0); - bids32[1] = in32(PMC_CORE_PSTATE_REG1); - bids32[2] = in32(PMC_CORE_PSTATE_REG2); - bids32[3] = in32(PMC_CORE_PSTATE_REG3); -} - - -/// Update a current Global bid of each core from a user supplied vector. -/// -/// \param[in] i_bids An array of Global Pstate bids. -/// -/// This routine is provided for use by test procedures; there is likely no -/// product-level energy management application for this procedure. - -void -gpsm_set_global_bids(const Pstate *i_bids) -{ - // This takes advantage of the implicit layout of the - // PMC_CORE_PSTATE_REG<n>. - - uint32_t *bids32 = (uint32_t *)i_bids; - - out32(PMC_CORE_PSTATE_REG0, bids32[0]); - out32(PMC_CORE_PSTATE_REG1, bids32[1]); - out32(PMC_CORE_PSTATE_REG2, bids32[2]); - out32(PMC_CORE_PSTATE_REG3, bids32[3]); -} - - -/// Application-controlled Global Actual Broadcast -/// -/// \param[in] i_pstate The Global Actual Pstate to broadcast -/// -/// \param[in] i_entry A gpst_entry_t containing the information to be used by -/// the iVRM. If iVRM are not enabled then \a entry can be initialized to 0. -/// -/// This API is provided for advanced applications to have complete control -/// over a firmware-mode Global Actual broadcast. There is no error -/// checking. Most applications in Firware Pstate mode will use the -/// higher-level gpsm_broadcast_global_actual() API. - -void -_gpsm_broadcast_global_actual(const Pstate i_pstate, - const gpst_entry_t i_entry) -{ - pmc_pstate_monitor_and_ctrl_reg_t ppmacr; - pmc_eff_global_actual_voltage_reg_t pegavr; - - ppmacr.value = 0; - ppmacr.fields.gpsa = i_pstate; - out32(PMC_PSTATE_MONITOR_AND_CTRL_REG, ppmacr.value); - - pegavr.value = 0; - pegavr.fields.maxreg_vdd = i_entry.fields.maxreg_vdd; - pegavr.fields.maxreg_vcs = i_entry.fields.maxreg_vcs; - pegavr.fields.eff_evid_vdd = i_entry.fields.evid_vdd_eff; - pegavr.fields.eff_evid_vcs = i_entry.fields.evid_vcs_eff; - out32(PMC_EFF_GLOBAL_ACTUAL_VOLTAGE_REG, pegavr.value); - - TRACE_GPSM(TRACE_GPSM_BROADCAST_GLOBAL_ACTUAL); -} - - -/// Broadcast the Global Actual Pstate in firmware Pstate mode. -/// -/// \param[in] i_gpst An initialized GlobalPstateTable structure used to -/// define the legal Pstate range, and to provide the voltage settings -/// (maxreg_vxx and eff_evid_vxx) for the internal VRM. -/// -/// \param[in] i_pstate The pstate specfiying the Global Actual Pstate to be -/// broadast to the core chiplets. -/// -/// \param[in] i_bias This is a signed bias used to obtain the voltage Pstate, -/// <em> in addition to the \a undervolting_bias already built into the Pstate -/// table </em>. The iVRM information sent with the Global Actual Pstate comes -/// from the \a pstate - \a undervolting_bias + \a bias entry of the Pstate -/// table. -/// -/// This API can be used in firware Pstate mode to broadcast a Global Actual -/// Pstate and iVRM settings to the core chiplets. The API also supports -/// optional under/over-volting. The requested Pstate will be broadcast along -/// with the voltage information from the associated Pstate table entry. -/// -/// Under/over-volting is specified by setting the \a bias to a non-0 -/// (signed) value. For example, to undervfolt by one Pstate (if possible), -/// call the API with \a bias = -1. -/// -/// This API always waits for the Global Pstate Machine to quiesce before -/// proceeding with the Global Actual broadcast. Therefore it can only be -/// called from thread mode, or from a non-thread mode guaranteed by the -/// caller to have quiesced. -/// -/// \note The application can use the _gpsm_broadcast_global_actual() API for -/// complete control over the information transmitted to the cores. -/// -/// The following return codes are not considered errors: -/// -/// \retval 0 Success -/// -/// \retval -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA The requested Pstate does not -/// exist in the table. The maximum Pstate entry in the table has been -/// broadcast as the voltage Pstate. -/// -/// \retval -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA The requested Pstate does not -/// exist in the table. The minimum Pstate entry in the table has been -/// broadcast as the voltage Pstate. -/// -/// The following return codes are considered errors: -/// -/// \retval -GPSM_INVALID_OBJECT The Global Pstate Table is either null (0) or -/// otherwise invalid. -/// -/// \retval -GPSM_INVALID_ARGUMENT One or more arguments are invalid or -/// inconsistent in some way. -/// -/// \retval -GPSM_ILLEGAL_MODE_BGA The PMC is not in firmware pstate mode. -/// -/// This API may also return errors from the SSX semaphore operations that -/// implement the wait for quiescence. - -int -gpsm_broadcast_global_actual(const GlobalPstateTable* i_gpst, - const Pstate i_pstate, - const int i_bias) -{ - int rc, bias_rc, entry_rc; - gpst_entry_t entry; - Pstate voltage_pstate; - - do { - - if (!gpsm_fw_mode_p()) { - rc = GPSM_ILLEGAL_MODE_BGA; - break; - } - - // Bias the pstate, fetch the Pstate entry, quiesce and broadcast. - // bias_pstate() only returns saturation warnings. These are turned - // into bounds warnings if necessary (indicating that the Pstate - // saturated but the PMAX or PMIN was also a legal entry in the - // table). - - bias_rc = bias_pstate(i_pstate, i_bias, &voltage_pstate); - entry_rc = gpst_entry(i_gpst, voltage_pstate, 0, &entry); - if (entry_rc && - (entry_rc != -GPST_PSTATE_CLIPPED_LOW_GPST_ENTRY) && - (entry_rc != -GPST_PSTATE_CLIPPED_HIGH_GPST_ENTRY)) { - rc = entry_rc; - break; - } - - rc = gpsm_quiesce(); - if (rc) break; - - _gpsm_broadcast_global_actual(i_pstate, entry); - - if (entry_rc != 0) { - rc = entry_rc; - } else if (bias_rc == -PSTATE_OVERFLOW_BIAS_PS) { - rc = -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA; - } else if (bias_rc == -PSTATE_UNDERFLOW_BIAS_PS) { - rc = -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA; - } - } while (0); - - return rc; -} - - diff --git a/src/lib/gpsm.h b/src/lib/gpsm.h deleted file mode 100755 index 5af2e8d..0000000 --- a/src/lib/gpsm.h +++ /dev/null @@ -1,191 +0,0 @@ -#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 deleted file mode 100755 index 452f7cf..0000000 --- a/src/lib/gpsm_dcm.c +++ /dev/null @@ -1,753 +0,0 @@ -// $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 deleted file mode 100755 index 407ddbb..0000000 --- a/src/lib/gpsm_dcm.h +++ /dev/null @@ -1,192 +0,0 @@ -#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 deleted file mode 100755 index 01cbb2d..0000000 --- a/src/lib/gpsm_dcm_fast_handler.S +++ /dev/null @@ -1,147 +0,0 @@ -// $Id: gpsm_dcm_fast_handler.S,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ - -/// \file gpsm_dcm_fast_handler.S -/// \brief Assembler support for GPSM_DCM procedures -/// \cond - .nolist -#include "ssx.h" -#include "pmc_dcm.h" -#include "gpsm_dcm.h" - .list - -#define GPSM_IC_CONTINUOUS_DATA (PMC_IC_MSG_CC << 4 | GPSM_IC_DATA) - - // The gpsm_dcm_write fast handler. - - // Register use: - // - // R3 = Entry: void *arg; *arg -> GpsmDcmFast - // R4 = Entry: SsxIrqId irq; irq : constant - // R5 = Entry: int priority; priority -> data - // R6 = data_pointer - // R7 = remaining_size - // CR = <condition> - // LR = <return address> - // No other registers can be used, other than SPRG - - .global_function gpsm_dcm_fast_write - -gpsm_dcm_fast_write: - - _ssx_irq_status_clear %r4, %r6, %r7 //clear status first - - lwz %r6, 0(%r3) //load data pointer - lwz %r7, 4(%r3) //load remaining size - - cmpwi %r7, 0 //if size == 0 - beq exit_write_handler //exit fast write - cmpwi %r7, 1 //if size == 1 - beq load_one_byte //load only one byte - - lhz %r5, 0(%r6) //otherwise load two bytes - addi %r6, %r6, 2 //data pointer+2 - addi %r7, %r7,-2 //remaining size-2 - b form_and_send_packet //then form and send packet - -load_one_byte: - - lbz %r5, 0(%r6) //load one byte from buffer - addi %r6, %r6, 1 //data pointer+1 - addi %r7, %r7,-1 //remaining size-1 - -form_and_send_packet: - - stw %r6, 0(%r3) //store updated data pointer - stw %r7, 4(%r3) //store updated remaining size - - lis %r6, GPSM_IC_CONTINUOUS_DATA //load cmd code to upper bits - or %r5, %r5, %r6 //load data into lower bits - slwi %r5, %r5, 8 //packet<<8 to give ecc field - _stwi %r5, %r7, PMC_INTCHP_MSG_WDATA //send packet - blr //return - -exit_write_handler: - - bl __ssx_irq_fast2full //convert fast to full irq handler - _ssx_irq_disable %r4, %r6, %r7 //disable this irq - addi %r3, %r3, 8 //argument pointer to semaphore - bl ssx_semaphore_post //post to that semaphore - b __ssx_irq_full_mode_exit //exit - - .epilogue gpsm_dcm_fast_write - - - - // The gpsm_dcm_read fast handler. - - // Register use: - // - // R3 = Entry: void *arg; *arg -> GpsmDcmFast(data,size,semaphore) - // R4 = Entry: SsxIrqId irq; irq : constant - // R5 = Entry: int priority; priority -> data - // R6 = data_pointer - // R7 = remaining_size - // CR = <condition> - // LR = <return address> - // No other registers can be used, other than SPRG - - .global_function gpsm_dcm_fast_read - -gpsm_dcm_fast_read: - - _ssx_irq_status_clear %r4, %r6, %r7 //clear status first - - lwz %r7, 4(%r3) //load remaining size - cmpwi %r7, 0 //if size == 0 - beq exit_read_handler //then exit fast read - - _lwzi %r6, %r7, PMC_INTCHP_MSG_RDATA //receive packet - - extrwi %r5, %r6, 16, 8 //extract data to r5 - extrwi %r6, %r6, 8, 0 //extract cmd code to r6 - li %r7, GPSM_IC_CONTINUOUS_DATA //designated cmd code to r7 - - cmpw %r6, %r7 //compare and check cmd code - bne panic_read_packet //panic on wrong cmd code - - lwz %r6, 0(%r3) //load data pointer - lwz %r7, 4(%r3) //load remaining size - - cmpwi %r7, 1 //if only one byte left - beq store_one_byte //then store only one byte - - sth %r5, 0(%r6) //otherwise store two bytes to buf - addi %r6, %r6, 2 //data pointer+2 - addi %r7, %r7,-2 //remaining size-2 - b check_read_status //check read status - -store_one_byte: - - stb %r5, 0(%r6) //store one byte to buf - addi %r6, %r6, 1 //data pointer+1 - addi %r7, %r7,-1 //remaining size-1 - -check_read_status: - - stw %r6, 0(%r3) //store updated data pointer - stw %r7, 4(%r3) //store updated remaining size - - cmpwi %r7, 0 //if size == 0 - beq exit_read_handler //then terminate fast read - blr //return if still data left - -exit_read_handler: - - bl __ssx_irq_fast2full //convert fast to full irq handler - _ssx_irq_disable %r4, %r6, %r7 //disable this irq - addi %r3, %r3, 8 //argument pointer to semaphore - bl ssx_semaphore_post //post to that semaphore - b __ssx_irq_full_mode_exit //exit - -panic_read_packet: - - SSX_PANIC(GPSM_DCM_READ_NOT_WRITE_DATA) //PANIC if cmd code is wrong - - .epilogue gpsm_dcm_fast_read -/// \endcond - diff --git a/src/lib/gpsm_init.c b/src/lib/gpsm_init.c deleted file mode 100755 index ff0f68b..0000000 --- a/src/lib/gpsm_init.c +++ /dev/null @@ -1,1639 +0,0 @@ -// $Id: gpsm_init.c,v 1.10 2015/05/16 17:43:12 daviddu Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm_init.c,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file gpsm_init.c -/// \brief Global Pstate Machine procedures only required to initialize Pstate -/// and Pstate modes. -/// -/// In OCC product firmware this code is only needed immediately after IPL to -/// set up Pstate tables and enable Pstates. This code could be run from an -/// applet or otherwise removed once Pstates are initialized. Note that this -/// file cointains code only; some global variables referenced here are -/// defined in gpsm.c, along with the run-time APIs for GPSM. -/// -/// The following sequence of procedures is required to initialize the GPSM -/// mechanism, enable Pstate mode, and further enable Hardware Pstate Mode. -/// -/// \code -/// -/// PstateSuperStructure* pss; -/// GlobalPstateTable *gpst; -/// GpsmEnablePstatesMasterInfo info; -/// Pstate voltage_pstate, frequency_pstate; -/// -/// gpsm_initialize(pss, gpst); -/// gpsm_enable_pstates_master(&info, &voltage_pstate, &frequency_pstate); -/// gpsm_enable_pstates_slave(&info, voltage_pstate, frequency_pstate); -/// gpsm_hw_mode(); -/// -/// \endcode -/// -/// Executing these procedures enables Pstate control of voltage and -/// frequency, and leaves the chip quiesced in Firmware Pstate mode. These -/// procedures initializes both central (PMC) and remote (PCB Slave) Pstate -/// hardware to fully enable Pstate control of voltage and frequency. -/// Carefully note the procedure sequence preconditions and postconditions. -/// -/// In the case of a DCM environment, the DCM master must execute the sequence -/// as follows: -/// -/// \code -/// -/// PstateSuperStructure* pss; -/// GlobalPstateTable *gpst; -/// GpsmEnablePstatesMasterInfo info; -/// Pstate voltage_pstate, frequency_pstate; -/// -/// gpsm_initialize(pss, gpst); -/// gpsm_enable_pstates_master(&info, &voltage_pstate, &frequency_pstate); -/// -/// <em> Send voltage_pstate and frequency_pstate to the slave and wait for -/// confirmation that the procedure has completed. </em> -/// -/// gpsm_enable_pstates_slave(&info, voltage_pstate, frequency_pstate); -/// gpsm_hw_mode(); -/// -/// <em> Send command to the slave to execute gpsm_hw_mode() </em> -/// -/// \endcode -/// -/// The DCM slave executes the following sequence -/// \code -/// PstateSuperStructure* pss; -/// GlobalPstateTable *gpst; -/// Pstate voltage_pstate, frequency_pstate; -/// -/// gpsm_initialize(pss, gpst); -/// -/// <em> Receive voltage_pstate and frequency_pstate from the masterand wait for -/// confirmation that the procedure has completed. </em> -/// -/// gpsm_enable_pstates_slave(0, voltage_pstate, frequency_pstate); -/// -/// <em> Wait for a command from the master to execute gpsm_hw_mode(). </em> -/// -/// \endcode -/// -/// <b> Preconditions </b> -/// -/// - This sequence must be called from a thread as it must be able to block -/// for the completion of GPSM events. -/// -/// - The fundamental precondition is the assumption that any snapshot of the -/// voltage/frequency state of the system yields a legal state. As long as -/// this is true, then this sequence should never take the system to an -/// illegal V/F state. -/// -/// - CPM-DPLL mode should be disabled, and all undervolting controls are -/// assumed to be at their nominal values. Correctness can not guaranteed -/// otherwise. -/// -/// - iVRM mode should be disabled prior to calling this procedure. -/// Correctness can not be guaranteed otherwise. -/// -/// -/// <b> Standard/Benign Postconditions after executing -/// gpsm_enable_pstates_slave() </b> -/// -/// - The system will be in Firmware Pstate Mode, using the Local and Global -/// Pstate tables installed by gpsm_initialize(). Pstate 0 will be mapped to -/// the nominal frequency (modulo rounding down) of the -/// 'nominal_frequency_khz' field of the Global Pstate table. -/// -/// - All core chiplet frequencies will be under the control of the PMCR Local -/// Pstate. -/// -/// - The Global Actual Pstate immediately prior to the final entry of Pstate -/// mode is the Pstate that most closely matches an arbitrary snapshot of the -/// system voltage during the execution of this procedure. -/// -/// - The PMCR and PMICR are not modified, therefore the Global Pstate -/// subsequent to the release of Hardware Pstate mode is arbitrary. -/// -/// - The PMC_RAIL_BOUNDS register is set to the maximum legal bounds allowed -/// by Global Pstate Table -/// -/// - IVRM is setup and enabled if Local Pstate Table is installed. -/// -/// - Resonant Clock is setup and enabled if Resonant Clock is installed. -/// -/// <b> Side-Effects </b> -/// -/// - The dpll_fmax and dpll_fmin fields of the FREQ_CTRL_REG in each core are -/// set to an arbitrary value. The dpll_fmax_bias is set to the value of the -/// 'dpll_fmax_bias' field of the \a gpst. -/// -/// - The core-level PCBS_POWER_MANAGEMENT_BOUNDS_REG registers are -/// set to the maximum legal bounds. This is required due to Pstate table -/// constraints. -/// -/// \todo - How to handle redundant SPIVID interfaces? Here we get the -/// current volatge from interface 0. -/// -/// \bug Check to make sure that the PMC_CORE_DECONFIGURATION_REG matches -/// multicast group 1. -/// -/// \bug Code marked with ** VBU ** is necessary for VBU/EPO simulation, needs -/// to be scrubbed once this is working in VBU. - -#include "ssx.h" -#include "ssx_io.h" -#include "gpsm.h" -#include "vrm.h" -#include "heartbeat.h" -#include "special_wakeup.h" - -// Debugging support - -#if 0 -#define _BREAK \ - { \ - fprintf(stderr, "%s:%d: _BREAK trapped error rc = 0x%08x (-0x%08x)\n", \ - __FILE__, __LINE__, rc, -rc); \ - SSX_PANIC(GPSM_ERROR_BREAK); \ - } -#else -#define _BREAK break; -#endif - - -/// Flag set once gpsm_initialize() has been successfully completed. -uint8_t G_gpsm_initialized = 0; - - -/// Install a (new) Global Pstate Table -/// -/// \param[out] o_gpst A pointer to a properly-aligned GlobalPstateTable -/// -/// \param[in] i_source A pointer to an initialized source GlobalPstateTable -/// that will be copied to \a gpst (if not in fact the same as \a gpst). -/// -/// This procedure will likely only be called once, at initialization, and -/// then only as part of the gpsm_initialize() procedure. The procedure: -/// -/// - Copies the \a source to the \a gpst if required. -/// -/// - Installs a pointer to the Global Pstate Table in the PMC hardware. -/// -/// - Sets up the PMC Pstate clipping bounds. -/// -/// - Sets up the Pstate stepping parameters from the GlobalPstateTable -/// -/// - Clears the local undervolting register (via multicast) and sets the -/// default undervolting bounds to the entire Local Pstate Table. -/// -/// - Broadcasts the safe mode Pstate Psafe to the cores and clears the other -/// fields of the PCBS_OCC_HEARTBEAT_REG, disabling the heartbeat timer. -/// However the heartbeat timer must not have been active anyway. -/// -/// \note This procedure does modify the rail bounds. -/// -/// \note The caller is responsible for the mode-correctness of this -/// procedure. This procedure must only be called when the PMC is in Firmware -/// Pstate Mode. -/// -/// \retval 0 Success -/// -/// \retval -GPSM_INVALID_ARGUMENT_GPST_INSTALL The Global Pstate table argument -/// was either NULL (0) or improperly aligned, or the \a source was NULL (0). -/// -/// \retval -GPSM_ILLEGAL_MODE_GPST_INSTALL The PMC does not indicate that the -// system is in Firmware Pstate Mode, or the heartbeat is enabled. - -int -gpsm_gpst_install(GlobalPstateTable* o_gpst, - const GlobalPstateTable* i_source) -{ - pmc_occ_heartbeat_reg_t pohr; - pmc_parameter_reg0_t ppr0; - pmc_parameter_reg1_t ppr1; - pmc_global_pstate_bounds_reg_t pgpbr; - pmc_rail_bounds_register_t prbr; - pmc_undervolting_reg_t pur; - pcbs_occ_heartbeat_reg_t pcbsohr; - - int rc; - - TRACE_GPSM(TRACE_GPSM_GPST_INSTALL); - - do { - - // Optional bypass of the procedure - - if (i_source->options.options & PSTATE_NO_INSTALL_GPST) { - - rc = 0; - break; - } - - // Check presence and alignment of the Pstate table, and proper Pstate - // and heartbeat modes. - - if ((o_gpst == 0) || - ((unsigned long)o_gpst % POW2_32(GLOBAL_PSTATE_TABLE_ALIGNMENT))) { - rc = -GPSM_INVALID_ARGUMENT_GPST_INSTALL; - _BREAK; - } - - pohr.value = in32(PMC_OCC_HEARTBEAT_REG); - rc = getscom(MC_ADDRESS(PCBS_OCC_HEARTBEAT_REG, - MC_GROUP_EX, PCB_MULTICAST_OR), - &(pcbsohr.value)); - if (rc) _BREAK; - - if (!gpsm_fw_mode_p() || pohr.fields.pmc_occ_heartbeat_en - || pcbsohr.fields.occ_heartbeat_enable) { - rc = -GPSM_ILLEGAL_MODE_GPST_INSTALL; - _BREAK; - } - - - // Copy \a source to \a gpst if required, then install the Pstate - // table, Pvsafe and Pstate stepping parameters, and set the clipping - // bounds as well as the rail bounds - - if ((o_gpst != i_source) && - !(i_source->options.options & PSTATE_NO_COPY_GPST)) { - - memcpy(o_gpst, i_source, sizeof(*o_gpst)); - } - - ppr1.value = in32(PMC_PARAMETER_REG1); - ppr1.fields.ba_sram_pstate_table = - (unsigned long)o_gpst >> GLOBAL_PSTATE_TABLE_ALIGNMENT; - ppr1.fields.pvsafe = i_source->pvsafe; - - // This fix is added per SW260911 - // Minimum Frequency in the system is given by MRW attribute - // PState Datablock procedure will read the attribute then - // convert it into pstate _pfloor_ and put it into - // Global Pstate Table. GPSM here consumes the value - // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS) - // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be - // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin) - // so that we should never run with frequency below the floor - // even in safe mode - if (ppr1.fields.pvsafe < i_source->pfloor && i_source->pfloor != 0) - ppr1.fields.pvsafe = i_source->pfloor; - - out32(PMC_PARAMETER_REG1, ppr1.value); - - pgpbr.value = 0; - pgpbr.fields.gpsi_min = gpst_pmin(i_source) - PSTATE_MIN; - pgpbr.fields.gpst_number_of_entries_minus_one = i_source->entries - 1; - out32(PMC_GLOBAL_PSTATE_BOUNDS_REG, pgpbr.value); - - ppr0.value = in32(PMC_PARAMETER_REG0); - ppr0.fields.pstate_stepsize = i_source->pstate_stepsize; - ppr0.fields.vrm_stepdelay_range = i_source->vrm_stepdelay_range; - ppr0.fields.vrm_stepdelay_value = i_source->vrm_stepdelay_value; - ppr0.fields.gpsa_timeout_value_sel = 1; - out32(PMC_PARAMETER_REG0, ppr0.value); - - prbr.value = 0; - prbr.fields.pmin_rail = gpst_pmin(i_source)+1; - prbr.fields.pmax_rail = gpst_pmax(i_source); - - // This fix is added per SW260911 - // Minimum Frequency in the system is given by MRW attribute - // PState Datablock procedure will read the attribute then - // convert it into pstate _pfloor_ and put it into - // Global Pstate Table. GPSM here consumes the value - // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS) - // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be - // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin) - // so that we should never run with frequency below the floor - // even in safe mode - if (prbr.fields.pmin_rail < i_source->pfloor && i_source->pfloor != 0) - prbr.fields.pmin_rail = i_source->pfloor; - - out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value); - - // Clear the undervolting control, and set the undervolting range to - // the entire Global Pstate Table range. - - pur.value = 0; - pur.fields.puv_min = gpst_pmin(i_source); - pur.fields.puv_max = gpst_pmax(i_source); - pur.fields.kuv_request = 0; - out32(PMC_UNDERVOLTING_REG, pur.value); - - - // Broadcast the safe mode Pstate Psafe to the cores, disabling the - // heartbeat (which must have been disabled anyway) and clearing any - // other heartbeat setup. - - pcbsohr.value = 0; - pcbsohr.fields.psafe = i_source->psafe; - - // This fix is added per SW260911 - // Minimum Frequency in the system is given by MRW attribute - // PState Datablock procedure will read the attribute then - // convert it into pstate _pfloor_ and put it into - // Global Pstate Table. GPSM here consumes the value - // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS) - // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be - // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin) - // so that we should never run with frequency below the floor - // even in safe mode - if (pcbsohr.fields.psafe < i_source->pfloor && i_source->pfloor != 0) - pcbsohr.fields.psafe = i_source->pfloor; - - rc = putscom(MC_ADDRESS(PCBS_OCC_HEARTBEAT_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - pcbsohr.value); - if (rc) _BREAK; - - rc = 0; - - } while(0); - - return rc; -} - - -/// Install a (new) Local Pstate Array -/// -/// \param[in] i_lpsa A pointer to a LocalPstateArray to install in every -/// configured core. -/// -/// \param[in] i_options Options controlling the installation, or a NULL (0) -/// pointer to indicate fully default behavior. -/// -/// This procedure will likely only be called once, at initialization, and -/// then only as part of the gpsm_initialize() procedure. The procedure: -/// -/// - Power on PFET Voltage Reference Circuit -/// -/// - Perform the binary search for IVRM Calibration -/// -/// - Uploads the LocalPstateArray to every core using multicast. -/// -/// - Sets up the Local Pstate table bounds in every core using multicast. -/// -/// - Sets the step delay parameters for every core using multicast. -/// -/// - Clears the local undervolting register (via multicast) and sets the -/// default undervolting bounds to the entire Local Pstate Table. -/// -/// - Setup IVRM delay parameters -/// -/// - Enable IVRM -/// -/// \note This procedure \e does \e not modify the rail bounds. -/// -/// \note The caller is responsible for the mode-correctness of this -/// procedure. This procedure must only be called when the iVRM are -/// disabled, the DPLL is in "normal" (not CPM-DPLL) mode, and core heartbeats -/// are disabled. -/// -/// \retval 0 Success -/// -/// \retval -GPSM_INVALID_ARGUMENT_LPST_INSTALL The Local Pstate array argument -/// was NULL (0). -/// -/// \retval -GPSM_ILLEGAL_MODE_LPST_INSTALL iVRM mode, CPM-DPLL mode, or the -/// local heartbeat appears to be enabled in at least one core. -/// -/// \retval -GPSM_IVRM_CALIBRATION_TIMEOUT, if IVRM Calibration does not -/// complete in time. -/// -/// \retval -GPSM_IVRM_GROSS_OR_FINE, if ivrm_gross_or_fine_err is set -/// -/// \retval -GPSM_PSTATE_ENABLED, if pstate is enabled before enabling IVRM -/// -/// \retval others This API may also return non-0 codes from -/// getscom()/putscom() - -int -gpsm_lpsa_install(const LocalPstateArray* i_lpsa, - const PstateOptions* i_options) -{ - pcbs_ivrm_control_status_reg_t picsr; - pcbs_dpll_cpm_parm_reg_t pdcpr; - pcbs_pstate_index_bound_reg_t ppibr; - pcbs_ivrm_vid_control_reg0_t pivcr0; - pcbs_ivrm_vid_control_reg1_t pivcr1; - pcbs_undervolting_reg_t pur; - pcbs_pmerr_reg_t ppr; - pcbs_pcbspm_mode_reg_t ppmr; - pmc_core_deconfiguration_reg_t pcdr; - ChipConfigCores cores, wakedup; - SsxTimebase timeout; - int i, rc, timeout_rc = 0; - uint32_t configured_cores; - int flag, core; - - TRACE_GPSM(TRACE_GPSM_LPSA_INSTALL); - - do { - - // Optional bypass of this procedure - - if ((i_options != 0) && - (i_options->options & PSTATE_NO_INSTALL_LPSA)) { - - rc = 0; - break; - } - - // No LPST Install and IVRM Enable if there is no configued cores - - configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG); - flag = 1; - for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) { - if (!(configured_cores & 0x80000000)) continue; - flag = 0; - } - if (flag == 1) { - rc = 0; - break; - } - - // Check the array for existence. Do an OR-combining multicast read - // to see if any of the cores have iVRM enabled, have the heartbeat - // enabled, or any cores are running in CPM-DPLL mode. - - if (i_lpsa == 0) { - rc = -GPSM_INVALID_ARGUMENT_LPST_INSTALL; - _BREAK; - } - rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, - MC_GROUP_EX, PCB_MULTICAST_OR), - &(picsr.value)); - if (rc) _BREAK; - rc = getscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG, - MC_GROUP_EX, PCB_MULTICAST_OR), - &(pdcpr.value)); - if (rc) _BREAK; - - if (picsr.fields.ivrm_fsm_enable || - pdcpr.fields.cpm_filter_enable) { - rc = -GPSM_ILLEGAL_MODE_LPST_INSTALL; - _BREAK; - } - - // In case cores are in deep winkle so that ivrm caliburation - // will fail, insert special wakeup first - pcdr.value = in32(PMC_CORE_DECONFIGURATION_REG); - cores = ~pcdr.fields.core_chiplet_deconf_vector; - rc = occ_special_wakeup(1, cores, 25, &wakedup); - if (rc) _BREAK; - - // Power on PFET Voltage Reference Circuit - picsr.fields.pvref_en = 1; - rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - picsr.value); - if (rc) _BREAK; - - // Wait 10us for circuit to power on - timeout = ssx_timebase_get() + SSX_MICROSECONDS(10); - while (ssx_timebase_get() < timeout) {;} - - // Perform the binary search - picsr.fields.binsearch_cal_ena = 1; - rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - picsr.value); - if (rc) _BREAK; - - // Check IVRM Calibration is completed - // Poll for up to 100us for done before erroring out - timeout_rc = -GPSM_IVRM_CALIBRATION_TIMEOUT; - timeout = ssx_timebase_get() + SSX_MICROSECONDS(100); - while (ssx_timebase_get() < timeout) { - rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, - MC_GROUP_EX, PCB_MULTICAST_AND), - &(picsr.value)); - if (rc) _BREAK; - if (picsr.fields.binsearch_cal_done) { - timeout_rc=0; - break; - } - } - if (timeout_rc||rc) _BREAK; - - // IVRM Calibration complete, Clear binary search enable - picsr.fields.binsearch_cal_ena = 0; - rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - picsr.value); - if (rc) _BREAK; - - // Check if IVRM Gross or Fine Error is set after calibration! - rc = getscom(MC_ADDRESS(PCBS_PMERR_REG, - MC_GROUP_EX, PCB_MULTICAST_OR), - &(ppr.value)); - if (rc) _BREAK; - if (ppr.fields.pcbs_ivrm_gross_or_fine_err) { - rc = -GPSM_IVRM_GROSS_OR_FINE; - _BREAK; - } - - // Deassert Special Wakeup - rc = occ_special_wakeup(0, cores, 25, &wakedup); - if (rc) _BREAK; - - // Upload the Local Pstate Array. The array is loaded via multicast, - // using the built-in auto-increment mechanism. Then upload the - // Pstate bounds register via multicast. Pstate clipping is not - // modified. - - rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_CTRL_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - 0); - if (rc) _BREAK; - - for (i = 0; i < LOCAL_PSTATE_ARRAY_ENTRIES+VDSVIN_ARRAY_ENTRIES; i++) { - - if (i < LOCAL_PSTATE_ARRAY_ENTRIES) { - rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - i_lpsa->pstate[i].value); - if (rc) _BREAK; - } else { - rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - i_lpsa->vdsvin[i-LOCAL_PSTATE_ARRAY_ENTRIES].value); - if (rc) _BREAK; - } - - } - - ppibr.value = 0; - ppibr.fields.lpsi_min = lpst_pmin(i_lpsa) - PSTATE_MIN; - ppibr.fields.lpsi_entries_minus_1 = i_lpsa->entries - 1; - rc = putscom(MC_ADDRESS(PCBS_PSTATE_INDEX_BOUND_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - ppibr.value); - if (rc) _BREAK; - - - // Install the step delay parameters, then clear the undervolting - // control (applicable to the entire range) via multicast. - - pivcr0.value = 0; - if (i_lpsa->stepdelay_rising) - pivcr0.fields.ivrm_req_pstate_stepdelay_rising = - i_lpsa->stepdelay_rising; - else - pivcr0.fields.ivrm_req_pstate_stepdelay_rising = 0xFF; - if (i_lpsa->stepdelay_lowering) - pivcr0.fields.ivrm_req_pstate_stepdelay_lowering = - i_lpsa->stepdelay_lowering; - else - pivcr0.fields.ivrm_req_pstate_stepdelay_lowering = 0XFF; - rc = putscom(MC_ADDRESS(PCBS_IVRM_VID_CONTROL_REG0, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - pivcr0.value); - if (rc) _BREAK; - - ///bug need to determine where these values come from - pivcr1.value = 0; - pivcr1.fields.ivrm_stabilize_delay_run = 0x40; - pivcr1.fields.ivrm_stabilize_delay_idle = 0x40; - pivcr1.fields.ivrm_pfstr_prop_delay = 0x1E; - pivcr1.fields.ivrm_pfstrvalid_prop_delay = 0xFF; - pivcr1.fields.ivrm_vpump_poweron_time = 0xFF; - pivcr1.fields.ivrm_bypass_delay = 0x1E; - pivcr1.fields.pfet_vpump_enable_delay = 0x4E; - pivcr1.fields.ivrm_vid_vout_threshold = 0x00; - rc = putscom(MC_ADDRESS(PCBS_IVRM_VID_CONTROL_REG1, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - pivcr1.value); - if (rc) _BREAK; - - pur.value = 0; - pur.fields.puv_min = lpst_pmin(i_lpsa); - pur.fields.puv_max = lpst_pmax(i_lpsa); - pur.fields.kuv = 0; - rc = putscom(MC_ADDRESS(PCBS_UNDERVOLTING_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - pur.value); - if (rc) _BREAK; - - // Set pre_vret_pstate to Non-Functional Pstate - // \bug currently set to pmin, is it always same as psafe? - rc = getscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG, - MC_GROUP_EX, PCB_MULTICAST_OR), - &(pdcpr.value)); - if (rc) _BREAK; - pdcpr.fields.pre_vret_pstate = lpst_pmin(i_lpsa); - rc = putscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - pdcpr.value); - if (rc) _BREAK; - - // Checking that PStates are NOT enabled - rc = getscom(MC_ADDRESS(PCBS_PCBSPM_MODE_REG, - MC_GROUP_EX, PCB_MULTICAST_OR), - &(ppmr.value)); - if (rc) _BREAK; - if (ppmr.fields.enable_pstate_mode) { - rc = -GPSM_PSTATE_ENABLED; - _BREAK; - } - - // Enable I-VRM FSM - rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, - MC_GROUP_EX, PCB_MULTICAST_OR), - &(picsr.value)); - if (rc) _BREAK; - picsr.fields.ivrm_fsm_enable = 1; - rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - picsr.value); - if (rc) _BREAK; - - } while (0); - - if (timeout_rc && !rc) - return timeout_rc; - else - return rc; -} - - -/// Install (new) Resonant Clocking Setup -/// -/// \param[in] i_resclk A pointer to a ResonantClockingSetup to install in -/// every configured core. -/// -/// \param[in] i_options Options controlling the installation, or a NULL (0) -/// pointer to indicate fully default behavior. -/// -/// This procedure will likely only be called once, at initialization, and -/// then only as part of the gpsm_initialize() procedure. The procedure: -/// -/// - Initializes the Pstate resonance range limits in the register -/// PCBS_RESONANT_CLOCK_CONTROL_REG1 by multicast. -/// -/// - Setup parameters in PCBS_RESONANT_CLOCK_CONTROL_REG0 and turn on -/// Resonant Clock in Hardware Pstate Mode. -/// -/// \note The caller is responsible for the mode-correctness of this -/// procedure. This procedure must only be called when resonant clocking is -/// disabled and the controls are set for manual mode. Because of the way the -/// resonant clocking controls are designed we must enable resonant clocking -/// to update the Pstate bounds! After the bounds are updated resonant -/// clocking is disabled again. -/// -/// \retval 0 Success -/// -/// \retval -GPSM_INVALID_ARGUMENT_RCLK_INSTALL The ResonantClockingSetup -/// argument was NULL (0). -/// -/// \retval -GPSM_ILLEGAL_MODE_RCLK_INSTALL Resonant clocking appears to be -/// enabled or not in manual mode in at least one configured core. -/// -/// \retval others This API may also return non-0 codes from -/// getscom()/putscom() - -int -gpsm_resclk_install(const ResonantClockingSetup* i_resclk, - const GlobalPstateTable* i_gpst, - const PstateOptions* i_options) -{ - ResonantClockingSetup d_resclk; - pcbs_resonant_clock_control_reg0_t prccr0; - pcbs_resonant_clock_control_reg1_t prccr1; - int rc; - uint32_t configured_cores; - int flag, core; - - TRACE_GPSM(TRACE_GPSM_RESCLK_INSTALL); - - do { - - // Optional bypass of this procedure - - if ((i_options != 0) && - (i_options->options & PSTATE_NO_INSTALL_RESCLK)) { - - rc = 0; - break; - } - - // No Resonant Clock Install and Enable if there is no configued cores - - configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG); - flag = 1; - for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) { - if (!(configured_cores & 0x80000000)) continue; - flag = 0; - } - if (flag == 1) { - rc = 0; - break; - } - - // Check the setup for existence. Do an AND-combining multicast read - // to see if any of the cores have resonant clocking enabled, or are - // not in manual mode. - - if (i_resclk == 0) { - rc = -GPSM_INVALID_ARGUMENT_RCLK_INSTALL; - _BREAK; - } - rc = getscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG0, - MC_GROUP_EX, PCB_MULTICAST_AND), - &(prccr0.value)); - if (rc) _BREAK; - - if (!prccr0.fields.resclk_dis || !prccr0.fields.resclk_control_mode) { - rc = -GPSM_ILLEGAL_MODE_RCLK_INSTALL; - _BREAK; - } - - - // Resonant clocking is specified such that it must be enabled (in a - // benign manual mode) in order to be set up. - - // Enable resonant clocking in the GP3 register (AND), bit 22. Our - // Simics environment does not model the GP3->PRCCR0 connection - // currently, and does not enforce the register locks. - - if (!SIMICS_ENVIRONMENT) { - - rc = putscom(MC_ADDRESS(0x100f0013, MC_GROUP_EX, - PCB_MULTICAST_WRITE), - ~0x0000020000000000ull); - if (rc) _BREAK; - } - - // Write the PCBS_RESONANT_CLOCK_CONTROL_REG1 with the - // Pstate setup, clearing all manual fields. - - // If at least one resonant clocking parm was 0 in PstateSuperStructure - // write the register with default values - // Low Band : 2 GHZ to 3.2 GHz - // High Band : 3.2 GHZ - Up - - gpst_frequency2pstate(i_gpst, 0, &(d_resclk.full_csb_ps)); - gpst_frequency2pstate(i_gpst, 2000000, &(d_resclk.res_low_lower_ps)); - gpst_frequency2pstate(i_gpst, 3200000, &(d_resclk.res_low_upper_ps)); - gpst_frequency2pstate(i_gpst, 3200000, &(d_resclk.res_high_lower_ps)); - gpst_frequency2pstate(i_gpst, 9999999, &(d_resclk.res_high_upper_ps)); - - prccr1.value = 0; - if (!(i_resclk->full_csb_ps && - i_resclk->res_low_lower_ps && - i_resclk->res_low_upper_ps && - i_resclk->res_high_lower_ps && - i_resclk->res_high_upper_ps)) { - prccr1.fields.full_csb_ps = d_resclk.full_csb_ps; - prccr1.fields.res_low_lower_ps = d_resclk.res_low_lower_ps; - prccr1.fields.res_low_upper_ps = d_resclk.res_low_upper_ps; - prccr1.fields.res_high_lower_ps = d_resclk.res_high_lower_ps; - prccr1.fields.res_high_upper_ps = d_resclk.res_high_upper_ps; - } else { - prccr1.fields.full_csb_ps = i_resclk->full_csb_ps; - prccr1.fields.res_low_lower_ps = i_resclk->res_low_lower_ps; - prccr1.fields.res_low_upper_ps = i_resclk->res_low_upper_ps; - prccr1.fields.res_high_lower_ps = i_resclk->res_high_lower_ps; - prccr1.fields.res_high_upper_ps = i_resclk->res_high_upper_ps; - } - - ///bug need to determine where these values come from - prccr1.fields.nonres_csb_value_ti = 0xC; - prccr1.fields.full_csb_value_ti = 0xF; - - rc = putscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG1, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - prccr1.value); - if (rc) _BREAK; - - - // Disable resonant clocking in the GP3 register (OR), bit 22. - - if (!SIMICS_ENVIRONMENT) { - - rc = putscom(MC_ADDRESS(0x100f0014, MC_GROUP_EX, - PCB_MULTICAST_WRITE), - 0x0000020000000000ull); - if (rc) _BREAK; - } - - // Enable resonant clock pstate hardware mode(control_mode = 0) - // Sync Pulse Width maximum value : 0x7 of nest/4 cycles - // Sync Delay maximum value : 0x7F of nest/4 cycles - // Sector Buffer Strength Instruction . Low Band : 0xAAA - // Sector Buffer Strength Instruction . High Band : 0xAAA - - prccr0.fields.resclk_control_mode = 0; - prccr0.fields.resclk_sync_pw = 0x7; - prccr0.fields.res_sync_delay_cnt = 0x7F; - prccr0.fields.res_csb_str_instr_lo = 0xAAA; - prccr0.fields.res_csb_str_instr_hi = 0x1FF; - - rc = putscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG0, - MC_GROUP_EX, PCB_MULTICAST_WRITE), - prccr0.value); - if (rc) break; - - // Enable resonant clocking in the GP3 register (AND), bit 22. - if (!SIMICS_ENVIRONMENT) { - - rc = putscom(MC_ADDRESS(0x100f0013, MC_GROUP_EX, - PCB_MULTICAST_WRITE), - ~0x0000020000000000ull); - if (rc) break; - } - - } while (0); - - return rc; -} - - -/// Initialize the GPSM procedure mechanism -/// -/// \param[in] i_pss A pointer to the PstateSuperStructure containing the -/// Global and Local Pstate tables, plus resonant clocking setup and other -/// options. -/// -/// \param[out] o_gpst A pointer to a 1-KB aligned GlobalPstateTable which -/// will be updated with a copy of the GlobalPstateTable from the -/// PstateSuperStructure. -/// -/// This API is designed to be called once at system initialization, to set up -/// GPSM mechanisms, install the Global and Local Pstate tables, and set up -/// resonant clocking from the PstateSuperStructure. At the entry of this -/// procedure it is assumed that the system firmware has initialized the PMC -/// mode register to either no mode, or to indicate Firmware Pstate Mode. It -/// is further assumed that the core chiplets are in a state which will allow -/// the Local Pstate tables and resonant clocking setup to be installed -/// without affecting system stability. Such a state must be guaranteed at -/// system initialization and after any OCC reset. If called from any other -/// context the caller is responsible for ensuring that the system is in a -/// state that will allow the procedure to run correctly. -/// -/// This procedure does not enable Pstates or enter any Pstate mode, and does -/// not alter any voltage or frequency settings. After the Pstate tables have -/// been installed, Pstate mode is enabled by calls of -/// gpsm_enable_pstates_master() and gpsm_enable_pstates_slave() as described -/// in the commenst for gpsm_init.c. -/// -/// The initialization of Pstates was split up into these three steps to best -/// handle the initialization of the slave chip in a DCM. This procedure -/// (gpsm_initialize()) can be called by the DCM slave whenever a -/// PstateSuperStructure is available. By requirememt and convention this -/// Pstate SuperStructure will be identical with the one installed by the DCM -/// master. -/// -/// The GPSM driver makes few assumptions about how the system firmware has -/// set up the PMC, but does require some critical setup. -/// -/// - It is assumed that for DCM configurations the system firmware will have -/// set the PMC_MODE_REG.enable_interchip_interface (to indicate a DCM -/// configuration), and set the PMC_MODE_REG.interchip_mode appropriately for -/// the master and the slave. -/// -/// - It is assumed that the PMC Core Deconfiguration register implies the -/// same set of configured cores as the set included in the PCB multicast -/// group covering all cores. -/// -/// All GPSM procedures use the same semaphore, which is set by the interrupt -/// handler for all GPSM interrupts. The GPSM driver claims the PMC protocol -/// ongoing, voltage change ongoing, and PMC Sync interrupts. -/// -/// Once the interrupts are set up, the GlobalPstateTable is copied from the -/// PstateSuperStructure to its proper location and installed. Next, the -/// Local Pstate tables and resonant clocking setup are installed into all -/// cores by multicast SCOM. -/// -/// \retval 0 Success -/// -/// \retval -GPSM_INVALID_OBJECT Either the \a i_pss or \a o_gpst are NULL (0). -/// -/// \retval -GPSM_INVALID_MAGIC The 'magic number' of the PstateSuperStructure -/// is different from that expected. -/// -/// \retval -GPSM_ILLEGAL_MODE_GPSM_INIT Either the PMC indicates a Pstate mode -/// is active, one or more cores appear to have iVRM enabled, or one or more -/// cores appear to have resonant clocking enabled. -/// -/// \retval others This API may also return codes from gpsm_gpst_install(), -/// gpsm_lpsa_install() and gpsm_resclk_install(). - -int -gpsm_initialize(const PstateSuperStructure* i_pss, - GlobalPstateTable* o_gpst) -{ - pmc_mode_reg_t pmr; - int rc; - - TRACE_GPSM(TRACE_GPSM_INITIALIZE); - - do { - - // Check for a valid PstateSuperStructure and GlobalPstateTable - - if ((i_pss == 0) || (o_gpst == 0)) { - - rc = -GPSM_INVALID_OBJECT; - _BREAK; - } - - if (i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD1 && - i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD2 && - i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD3 && - i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD4) { - - 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 deleted file mode 100755 index 51be390..0000000 --- a/src/lib/heartbeat.c +++ /dev/null @@ -1,328 +0,0 @@ -// $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 deleted file mode 100755 index 6c8616b..0000000 --- a/src/lib/heartbeat.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef __HEARTBEAT_H__ -#define __HEARTBEAT_H__ - -// $Id: heartbeat.h,v 1.3 2014/02/12 05:48:48 daviddu Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/heartbeat.h,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file heartbeat.h -/// \brief PgP PMC/PCBS heartbeat configuration procedures - -#ifndef __ASSEMBLER__ - -#include <stdint.h> - -#define HB_INVALID_ARGUMENT_PMC 0x00482801 -#define HB_INVALID_ARGUMENT_PCBS 0x00482802 -#define HB_UNDERFLOW_DIVIDER_PMC 0x00482803 -#define HB_OVERFLOW_DIVIDER_PMC 0x00482804 -#define HB_OVERFLOW_PULSES_PMC 0x00482805 -#define HB_OVERFLOW_PULSES_PCBS 0x00482806 -#define HB_UNDERFLOW_PULSES_PCBS 0x00482807 - -int -pmc_hb_config(unsigned int enable, - unsigned int req_time_us, - unsigned int force, - unsigned int *o_time_us); - -int -pcbs_hb_config(unsigned int enable, - ChipConfigCores cores, - uint32_t hb_reg, - unsigned int req_time_us, - unsigned int force, - unsigned int *o_time_us); - - - -#endif /* __ASEMBLER__ */ - - -#endif /* __HEARTBEAT_H__ */ diff --git a/src/lib/libfiles.mk b/src/lib/libfiles.mk deleted file mode 100755 index 55773a4..0000000 --- a/src/lib/libfiles.mk +++ /dev/null @@ -1,57 +0,0 @@ -# $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 deleted file mode 100755 index 3f4efe1..0000000 --- a/src/lib/libgpefiles.mk +++ /dev/null @@ -1,30 +0,0 @@ -# $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 deleted file mode 100755 index 2bdffd4..0000000 --- a/src/lib/libssx.h +++ /dev/null @@ -1,20 +0,0 @@ -#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/occlib/Makefile b/src/lib/occlib/Makefile new file mode 100644 index 0000000..9f86413 --- /dev/null +++ b/src/lib/occlib/Makefile @@ -0,0 +1,57 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/occlib/Makefile $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile currently builds a single archive, 'libocc.a', from +# various library source files. +# +# part of the complete application build. +# + +#all generated files from this makefile will end up in obj/$(IMAGE_NAME)/occ +export SUB_OBJDIR = /occlib + +include img_defs.mk +include liboccfiles.mk + +OBJS := $(addprefix $(OBJDIR)/, $(LIBOCC_OBJECTS)) + +libocc.a: local + $(AR) crs $(OBJDIR)/libocc.a $(OBJDIR)/*.o + +.PHONY: clean + +local: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +clean: + rm -fr $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +include $(OBJS:.o=.d) +endif + diff --git a/src/lib/occlib/README.txt b/src/lib/occlib/README.txt new file mode 100644 index 0000000..2f3667f --- /dev/null +++ b/src/lib/occlib/README.txt @@ -0,0 +1 @@ +This directory contains all code that is common for all processors in the OCC complex (405 + 4 GPE's) diff --git a/src/lib/occlib/ipc_api.h b/src/lib/occlib/ipc_api.h new file mode 100644 index 0000000..e4756f5 --- /dev/null +++ b/src/lib/occlib/ipc_api.h @@ -0,0 +1,519 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_api.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __IPC_API_H__ +#define __IPC_API_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ipc_api.h +/// \brief Common header for Interprocessor Communications API +/// +#include "ipc_structs.h" + +#ifndef __ASSEMBLER__ +/////////////////////////////////////////////////////////////////////////////// +/// Initialize an IPC command message +/// +/// \param msg A pointer to the command message +/// +/// \param func_id A user-defined function ID that is known to both the sender +/// and receiver of a command message. (Defined in \a ipc_func_ids.h) +/// +/// \param resp_callback A user-defined function that should be called when +/// the message is returned as a response to the command. This must be set +/// to 0 if no callback function should be called. +/// +/// \param callback_arg A pointer to user-defined data that will be passed in +/// to the callback function when it is called. This should be set to 0 if +/// no data needs to be passed. +/// +/// This function (or \a ipc_init_msgq_msg) must be called on a message at +/// least once before it is sent via the \a ipc_send_msg interface. +/// +/// There are two types of function ID's. Function ID's that only work on a +/// single processor are called \e single-target ID's. These function +/// ID's have the target ID embedded as part of the ID. FUnction ID's +/// that are supported on multiple processors are called \e multi-target ID's. +/// Command messages associated with multi-target function ID's must go through +/// the extra step of setting the target ID by calling the \a ipc_set_cmd_target +/// interface on the command message. +/// +/// If a callback function is provided, that callback function should cause +/// (directly or indirectly) the \ipc_free_msg interface to be called once it +/// is known that it is safe for the message to be reused (sent as a command +/// again). +/// +void ipc_init_msg(ipc_msg_t* msg, + uint32_t func_id, + ipc_msg_handler_t resp_callback, + void* callback_arg); + +//Use these to statically initialize an IPC message +#define IPC_MSG_INIT(_func_id, _resp_callback, _callback_arg) \ +{\ + {.node = KERN_DEQUE_ELEMENT_INIT()}, \ + .func_id.word32 = _func_id, \ + .ipc_rc = IPC_RC_SUCCESS, \ + .resp_callback = _resp_callback, \ + .callback_arg = _callback_arg \ +} + +#define IPC_MSG_CREATE(msg_name, _func_id, _resp_callback, _callback_arg) \ +ipc_msg_t msg_name = IPC_MSG_INIT(_func_id, _resp_callback, _callback_arg) + +/////////////////////////////////////////////////////////////////////////////// +/// Free up a message to be reused. +/// +/// \param msg a pointer to a message. +/// +/// This interface should be called on a message when it is known that it is +/// safe to reuse the message. Normally, this would be one of the last things +/// performed in the response callback function for a command, but it may also +/// be called when it is known that a peer has stopped functioning. +/// +static inline void ipc_free_msg(ipc_msg_t* msg) +{ + msg->func_id.active_flag = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Set the target ID for a multi-target command message. +/// +/// \param cmd A pointer to an initialized command message. +/// +/// \param target_id The target ID of the processor the command is intended +/// for. +/// +/// The following return codes are possible: +/// +/// \retval IPC_RC_SUCCESS The command's target ID was updated. +/// +/// \retval IPC_RC_INVALID_FUNC_ID The function ID associated with this +/// command is not a valid mult-target function ID. +/// +int ipc_set_cmd_target(ipc_msg_t* cmd, uint32_t target_id); + + +/////////////////////////////////////////////////////////////////////////////// +/// Send a message as a command +/// +/// \param cmd A pointer to an initialized command message +/// +/// It is expected that at some point prior to calling this function the +/// message was initialized with a call to \a ipc_init_msg or +/// \a ipc_init_msgq_msg. +/// +/// Once a message has been sent it is not safe to send again until it has been +/// sent back to the sender as a response. +/// +/// The following return codes are possible: +/// +/// \retval IPC_RC_SUCCESS The message was successfully placed on the target's +/// receive buffer. +/// +/// \retval IPC_RC_SELF_BLOCKED The call was made prior to calling +/// \a ipc_enable. +/// +/// \retval IPC_RC_INVALID_FUNC_ID The command was initialized with an invalid +/// function ID. +/// +/// \retval IPC_RC_MSG_ACTIVE The message is currently in use. +/// +/// \retval IPC_RC_INVALID_TARGET_ID The message was initialized with an +/// invalid target ID. This can happen if a multi-target command has not had +/// its target set via the \a ipc_set_cmd_target function at least one time. +/// +/// \retval IPC_RC_BUFFER_FULL The command could not be sent because the target's +/// receive buffer is full. +/// +/// \retval IPC_RC_TARGET_BLOCKED The command could not be sent because the +/// target is blocking any new messages. +/// +int ipc_send_cmd(ipc_msg_t* cmd); + + +//////////////////////////////////////////////////////////////////////////////// +/// Send a command message back to the sender as a response message with status. +/// +/// \param rsp A pointer to a message that was recieved as a command message. +/// +/// \param ipc_rc This should be either \a IPC_RC_SUCCESS if the command was +/// successful or \a IPC_RC_CMD_FAILED if the command failed. If +/// command-specific return codes are needed, they should be returned as +/// command-specific fields instead of returning them here so that there +/// is no risk of overlapping return codes. +/// +/// It is expected that at some point prior to calling this function the +/// message was initialized with a call to \a ipc_init_msg or +/// \a ipc_init_msgq_msg. +/// +/// Once a message has been sent it is not safe to send again until it has been +/// sent back to the sender as a response. +/// +/// The following return codes are possible: +/// +/// \retval IPC_RC_SUCCESS The message was successfully placed on the target's +/// receive buffer. +/// +/// \retval IPC_RC_MSG_NOT_ACTIVE The message is not from an active command. +/// +/// \retval IPC_RC_INVALID_TARGET_ID The target id for the sender is invalid. +/// This likely means that the message has been corrupted. +/// +/// \retval IPC_RC_BUFFER_FULL The response could not be sent because the target's +/// recieve buffer is full. +/// +/// \retval IPC_RC_TARGET_BLOCKED The response could not be sent because the +/// target is blocking any new messages. +/// +int ipc_send_rsp(ipc_msg_t* rsp, uint32_t return_code); + + +/////////////////////////////////////////////////////////////////////////////// +/// Retrieves the IPC return code embedded in the response message. +/// +/// \param rsp A pointer to a response message. +/// +/// The embedded IPC return code is how the remote processor communicates +/// IPC internal failures to the local processor. It can also be used by +/// non-IPC code on the remote processor to signal success or failure when it +/// sends the response message via the \a ipc_send_rsp interface. +/// +/// The IPC return code should always be checked to verify that a command +/// message was processed successfully. +/// +/// The following return codes are possible: +/// +/// \retval IPC_RC_SUCCESS The message was successfully processed. +/// +/// \retval IPC_RC_CMD_FAILED The command was processed on the remote end but +/// did not complete successfully. +/// +/// \retval IPC_RC_CMD_NOT_SUPPORTED The function ID for the command is valid +/// but the remote end does not have support for that function ID. +/// +/// \retval IPC_RC_INVALID_FUNC_ID The function ID for the command is invalid. +/// +static inline int ipc_get_rc(ipc_msg_t* rsp) +{ + return rsp->ipc_rc; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Retrieve the IPC function ID for a message +/// +/// \param msg A pointer to an IPC message +/// +/// This interface should be used to extract the IPC function ID of a message. +/// +/// The IPC function ID is returned. +/// +static inline int ipc_get_funcid(ipc_msg_t* msg) +{ + int func_id = msg->func_id.word32; + + //Multi-target function ID's always have the target ID set to + //so that if the caller doesn't set it to a proper target id it will + //flag an error. + if(func_id & IPC_FLAG_MT) + { + func_id |= IPC_TARGET_MASK; + } + + //Clear the active and response flags in case they are set along + //with the sender ID. + func_id &= ~(IPC_FLAG_ACTIVE | IPC_FLAG_RESPONSE | IPC_SENDER_MASK); + + return func_id; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Determine if a message is a response or a command +/// +/// \param msg A pointer to an IPC message +/// +/// This function should be used to determine if a message is a response or a +/// command. The function will return a non-zero value if the message is a +/// response and 0 otherwise. +/// +static inline int ipc_is_a_response(ipc_msg_t* msg) +{ + return msg->func_id.response_flag; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Determine if a message is free to be re-used +/// +/// \param msg A pointer to an IPC message +/// +/// This function should be used to determine if a message is free to re-use. +/// The function will return a non-zero value if the message is free and +/// 0 otherwise. +/// +static inline int ipc_is_free(ipc_msg_t* msg) +{ + return !msg->func_id.active_flag; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Retrieve the sender ID of a message. +/// +/// \param msg A pointer to an IPC message +/// +/// This function should be used to retrieve the sender ID of a message. It +/// returns the sender ID. +/// +static inline int ipc_sender_id(ipc_msg_t* msg) +{ + return msg->func_id.sender_id; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Retrieve the target ID of a message. +/// +/// \param msg A pointer to an IPC message +/// +/// This function should be used to retrieve the target ID of a message. It +/// returns the target ID. +/// +static inline int ipc_target_id(ipc_msg_t* msg) +{ + return msg->func_id.target_id; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Initializes IPC control structures. +/// +/// Clears the IPC buffers for this processor and places them in a state +/// where new messages are blocked. Also initializes the IPC handler table +/// for this processor if STATIC_IPC_TABLES has not been defined. +/// +/// This function always returns \a IPC_RC_SUCCESS +/// +int ipc_init(void); + + +/////////////////////////////////////////////////////////////////////////////// +/// Enables IPC communications. +/// +/// Unmasks the IPC interrupt for this processor and places the circular +/// buffers in a state where they can recieve messages. +/// +/// This function must be called before using the \a ipc_send_cmd function or +/// it will return \a IPC_RC_SELF_BLOCKED. +/// +/// The function always returns \a IPC_RC_SUCCESS. +/// +int ipc_enable(void); + + +/////////////////////////////////////////////////////////////////////////////// +/// Disable recieving new IPC commands for a processor. +/// +/// \param target_id The target ID of the processor to disable receiving new +/// IPC commands on. +/// +/// This interface should be used by a processor that knows it is about to go +/// down or by a processor that knows that one of its peers has halted. +/// +/// Calling this function on a processor will cause other processors to get +/// a return code of \a IPC_RC_TARGET_BLOCKED with subsequent calls to +/// \a ipc_send_cmd. Calling this function on one's self (using a target ID of +/// OCCHW_INST_ID_SELF) will cause subsequent calls to \a ipc_send_cmd to +/// return \a IPC_RC_SELF_BLOCKED. +/// +/// Possible return codes for this function are: +/// +/// \retval IPC_RC_SUCCESS The target processor was successfully disabled. +/// +/// \retval IPC_RC_INVALID_TARGET_ID The target ID is invalid. +/// +int ipc_disable(uint32_t target_id); + + +/////////////////////////////////////////////////////////////////////////////// +/// Associates an IPC function ID with a handler function +/// +/// \param func_id A user-defined function ID that is known to both the sender +/// and receiver of a command message. (Defined in \a ipc_func_ids.h) +/// +/// \param handler A pointer to the function that handles command messages that +/// have been initialized with \a func_id. +/// +/// \param callback_arg A pointer to data that is passed as an argument to the +/// handler function when it is called. +/// +/// This function should be used to link an IPC function ID with a function. +/// Once this has been done, if the local processor recieves a command message +/// with an IPC function ID that matches \a func_id then it will call the +/// handler function that was specified by \a handler. +/// +/// NOTE: All handler functions will be called from an interrupt context. +/// +/// Possible return codes are: +/// +/// \retval IPC_RC_SUCCESS The operation completed successfully. +/// +/// \retval IPC_RC_INVALID_TARGET_ID The function ID is a single-target +/// function ID that does not target this processor. +/// +/// \retval IPC_RC_INVALID_FUNC_ID The function ID is not a valid IPC function +/// ID. +/// +/// \retval IPC_RC_INVALID_ARG The handler parameter must be a non-zero value. +/// +int ipc_set_handler(uint32_t func_id, + ipc_msg_handler_t handler, + void* callback_arg); + + +/////////////////////////////////////////////////////////////////////////////// +/// Initialize an IPC message queue. +/// +/// \param msgq A pointer to a message queue. +/// +/// All message queues must be initialized one time prior to use with other +/// interfaces. +/// +void ipc_init_msgq(ipc_msgq_t* msgq); + +//Use this to statically initialize an IPC message queue +#define IPC_MSGQ_CREATE(msgq) \ +ipc_msgq_t msgq = \ +{\ + .msg_head = KERN_DEQUE_SENTINEL_INIT(&msgq.msg_head),\ + .msg_sem = KERN_SEMAPHORE_INITIALIZATION(0, 0)\ +} + +/////////////////////////////////////////////////////////////////////////////// +/// Initialize an IPC message and associate it with an IPC message queue +/// +/// \param msg A pointer to an IPC message. +/// +/// \param func_id A user-defined function ID that is known to both the sender +/// and receiver of the command message. (Defined in \a ipc_func_ids.h) +/// +/// \param msgq A pointer to an initialized IPC message queue. +/// +/// This interface should be used in place of \a ipc_init_msg when the caller +/// wishes to have the command response placed on the specified IPC message +/// queue. This allows a thread to block (via the \a ipc_msq_recv interface) +/// on the message queue until a response to a command has arrived. +/// +/// \note An IPC message queue can be associated with more than one IPC +/// message. +/// +/// See \a ipc_init_msg for more information. +/// +void ipc_init_msgq_msg(ipc_msg_t* msg, uint32_t func_id, ipc_msgq_t* msgq); + +//Use this to statically create an initialized IPC message queue message +#define IPC_MSGQ_MSG_CREATE(msg_name, func_id, msgq) \ + IPC_MSG_CREATE(msg_name, func_id, ipc_msgq_handler, msgq) + +/////////////////////////////////////////////////////////////////////////////// +/// Wait (with timeout) for an IPC message on an IPC message queue. +/// +/// \param msg A pointer to an IPC message pointer. +/// +/// \param msgq A pointer to an initialized IPC message queue. +/// +/// \param timeout The time to wait for the next IPC message to arrive. +/// +/// This interface can be used in a thread context to block while waiting for +/// the next message (command or response) to arrive on an IPC message queue. +/// +/// For an IPC command message to be placed on an IPC message queue, the +/// function ID for the command must first be associated with the message queue +/// by making a call to \a ipc_register_msgq. +/// +/// For an IPC response message to be placed on an IPC message queue, the +/// message must be initialized via the \a ipc_init_msgq_msg interface. +/// +/// If \a ipc_msgq_recv returns a value of \a IPC_RC_SUCCESS then \a msg is +/// guaranteed to point to a new message. Otherwise, an IPC message was not +/// retrieved from the message queue and \a msg will be set to 0. +/// +/// Possible return codes for this function are: +/// +/// \retval IPC_RC_SUCCESS A new IPC message was received and \a msg has been +/// set to point to the new message. +/// +/// \retval IPC_RC_TIMEOUT No new messages were recieved within the timeout +/// period and \msg was set to 0. +/// +/// \retval IPC_RC_NO_MSG This should never happen, but if it does it indicates +/// an internal failure occurred. +/// +int ipc_msgq_recv(ipc_msg_t** msg, ipc_msgq_t* msgq, KERN_INTERVAL timeout); + + +/////////////////////////////////////////////////////////////////////////////// +/// Associate an IPC message queue with an IPC function ID +/// +/// \param func_id A user-defined function ID that is known to both the sender +/// and receiver of a command message. (Defined in \a ipc_func_ids.h) +/// +/// \param msgq A pointer to an initialized IPC message queue. +/// +/// This interface associates an IPC function ID with an IPC message queue so +/// that when the calling processor recieves a command with the specified +/// function ID the message will be place on the message queue and a thread +/// that is blocked waiting for a message on the queue (using the +/// \a ipc_msgq_recv interface) will be woken up and given the message. +/// +/// This function should be called in place of the \a ipc_set_handler +/// interface. +/// +/// NOTE: Multiple function ID's can be associated with a single queue. +/// +/// Possible return codes are: +/// +/// \retval IPC_RC_SUCCESS The operation completed successfully. +/// +/// \retval IPC_RC_INVALID_TARGET_ID The function ID is a single-target +/// function ID that does not target this processor. +/// +/// \retval IPC_RC_INVALID_FUNC_ID The function ID is not a valid IPC function +/// ID. +/// +int ipc_register_msgq(uint32_t func_id, ipc_msgq_t* msgq); + +/////////////////////////////////////////////////////////////////////////////// +/// Internal function that places an IPC message on an IPC message queue +/// +void ipc_msgq_handler(ipc_msg_t* msg, void* arg); + +/////////////////////////////////////////////////////////////////////////////// +/// The default IPC command handler simply sends a response with the IPC return +/// code set to IPC_RC_CMD_NOT_SUPPORTED +/// +void ipc_default_handler(ipc_msg_t* msg, void* arg); + +#endif /*__ASSEMBLER__*/ +#endif /* __IPC_API_H__ */ diff --git a/src/lib/occlib/ipc_async_cmd.h b/src/lib/occlib/ipc_async_cmd.h new file mode 100644 index 0000000..db09e9e --- /dev/null +++ b/src/lib/occlib/ipc_async_cmd.h @@ -0,0 +1,49 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_async_cmd.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __IPC_ASYNC_CMD_H__ +#define __IPC_ASYNC_CMD_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ipc_async_cmd.h +/// \brief This header is shared between the 405 and GPE's that need to +/// support IPC command messages that are sent from the occhw_async_gpe.c +/// code. +/// + +#include "ipc_api.h" + +#ifndef __ASSEMBLER__ + +typedef struct { + ipc_msg_t cmd; + void* cmd_data; +}ipc_async_cmd_t; + +#endif /*__ASSEMBLER__*/ +#endif /*__IPC_ASYNC_CMD_H__*/ diff --git a/src/lib/occlib/ipc_core.c b/src/lib/occlib/ipc_core.c new file mode 100644 index 0000000..342f024 --- /dev/null +++ b/src/lib/occlib/ipc_core.c @@ -0,0 +1,484 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_core.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ipc_core.c +/// \brief Implementation of core IPC (InterProcessor Communication) routines + +#include "kernel.h" +#include "ipc_api.h" +#include "occhw_shared_data.h" + +/// If G_ipc_enabled is zero then calls to ipc_send_cmd() will return +/// IPC_RC_SELF_BLOCKED. +uint8_t G_ipc_enabled = 0; + +#ifndef STATIC_IPC_TABLES +ipc_func_table_entry_t G_ipc_mt_handlers[IPC_MT_MAX_FUNCTIONS]; +ipc_func_table_entry_t G_ipc_st_handlers[IPC_ST_MAX_FUNCTIONS]; +#endif + +/////////////////////////////////////////////////////////////////////////////// +/// Helper function used by ipc_send_cmd and ipc_send_rsp to send a message +/// +int ipc_send_msg(ipc_msg_t* msg, uint32_t target_id) +{ + ipc_target_t* target_cbufs; + uint8_t* read_count; + uint8_t* write_count; + ipc_msg_t** msgs; + KERN_MACHINE_CONTEXT ctx; + int rc = IPC_RC_SUCCESS; + uint8_t num_entries; + + do + { + // Check for invalid target ID + if(target_id > OCCHW_INST_ID_MAX) + { + rc = IPC_RC_INVALID_TARGET_ID; + break; + } + + target_cbufs = &OSD_PTR->ipc_data.targets[target_id]; + msgs = &target_cbufs->cbufs[OCCHW_INST_ID_SELF][0]; + read_count = &target_cbufs->counts.reads.counts8[OCCHW_INST_ID_SELF]; + write_count = &target_cbufs->counts.writes.counts8[OCCHW_INST_ID_SELF]; + + //Prevent other threads on this processor from updating the cbuf + KERN_CRITICAL_SECTION_ENTER(KERN_CRITICAL, &ctx); + + //Determine the number of entries in the buffer + num_entries = *write_count - *read_count; + + //If the cbuf isn't full, then add the message and raise an interrupt + if(num_entries < IPC_CBUF_SIZE) + { + // Mark the message as being active + msg->func_id.active_flag = 1; + + msgs[*write_count % IPC_CBUF_SIZE] = msg; + (*write_count)++; + + //raise the IPC interrupt on the target + KERN_IRQ_STATUS_SET(IPC_GET_IRQ(target_id), 1); + } + else + { + //Check if cbuf is just full or is blocked + if(num_entries == IPC_CBUF_SIZE) + { + rc = IPC_RC_BUFFER_FULL; + } + else + { + rc = IPC_RC_TARGET_BLOCKED; + } + } + KERN_CRITICAL_SECTION_EXIT(&ctx); + }while(0); + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Send a message as a command +/// +int ipc_send_cmd(ipc_msg_t* cmd) +{ + int rc; + do + { + //don't allow sending new commands if IPC is disabled + if(!G_ipc_enabled) + { + rc = IPC_RC_SELF_BLOCKED; + break; + } + + //don't send a command if the valid flag is not set + if(!cmd->func_id.valid_flag) + { + rc = IPC_RC_INVALID_FUNC_ID; + break; + } + + //don't send a command if the active flag is set + if(cmd->func_id.active_flag) + { + rc = IPC_RC_MSG_ACTIVE; + break; + } + + cmd->func_id.response_flag = 0; + + //Set the sender ID here. Remote side uses this for sending responses. + cmd->func_id.sender_id = OCCHW_INST_ID_SELF; + + cmd->ipc_rc = 0; + + //place the message on the target's circular buffer + rc = ipc_send_msg(cmd, cmd->func_id.target_id); + + cmd->ipc_rc = rc; + }while(0); + return rc; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Send a command message back to the sender as a response message with status. +/// +int ipc_send_rsp(ipc_msg_t* rsp, uint32_t ipc_rc) +{ + int rc; + if(rsp->func_id.active_flag) + { + rsp->func_id.response_flag = 1; + rsp->ipc_rc = ipc_rc; + rc = ipc_send_msg(rsp, rsp->func_id.sender_id); + } + else + { + rc = IPC_RC_MSG_NOT_ACTIVE; + } + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Default IPC handler that is called when no IPC handler has been +/// installed for the IPC function that is being requested. +void ipc_default_handler(ipc_msg_t* msg, void* arg) +{ + //Return code is ignored. If failure occurs in sending + //the response then the sender of the command should eventually + //time out waiting for a response or the sender may be incapacitated. + ipc_send_rsp(msg, IPC_RC_CMD_NOT_SUPPORTED); +} + + +/////////////////////////////////////////////////////////////////////////////// +/// Set the target ID for a multi-target command message. +/// +int ipc_set_cmd_target(ipc_msg_t* cmd, uint32_t target_id) +{ + int rc = IPC_RC_SUCCESS; + do + { + //verify that this is a muti-target function + if(!cmd->func_id.multi_target_flag) + { + rc = IPC_RC_INVALID_FUNC_ID; + break; + } + else + { + cmd->func_id.target_id = target_id; + } + }while(0); + + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Processes an incoming message (response or command) after it has been +/// removed from the circular buffer. +/// This function is for internal use only! +/// +void ipc_process_msg(ipc_msg_t* msg) +{ + uint32_t table_index; + uint32_t table_limit; + ipc_func_table_entry_t *func_table; + + do + { + // If this is a response message, call the response callback function + if(msg->func_id.response_flag) + { + if(msg->resp_callback) + { + msg->resp_callback(msg, msg->callback_arg); + } + else + { + //normally, the resp_callback function would call this function + //to notify users of the message that it is free to be re-used. + //Since there is no callback for this message we call it here. + ipc_free_msg(msg); + } + + break; + } + + // extract the function table index + table_index = msg->func_id.table_index; + + //setup for multi-target commands + if(msg->func_id.multi_target_flag) + { + table_limit = IPC_MT_NUM_FUNCIDS; + func_table = G_ipc_mt_handlers; + } + //setup for single-target commands + else + { + table_limit = IPC_ST_NUM_FUNCIDS; + func_table = G_ipc_st_handlers; + } + + //Common command handling code + if(table_index < table_limit) + { + func_table[table_index].handler(msg, func_table[table_index].arg); + } + else + { + //drop errors if this fails. If target was waiting for a response + //it should eventually time out and log the message as FFDC. + ipc_send_rsp(msg, IPC_RC_INVALID_FUNC_ID); + } + }while(0); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Removes messages from the circular buffer for the processor associated with +/// sender_id and processes them one at a time. +/// This function is for internal use only! +/// +void ipc_process_cbuf(uint32_t sender_id) +{ + ipc_target_t *my_cbufs = &OSD_PTR->ipc_data.targets[OCCHW_INST_ID_SELF]; + uint8_t *read_count = &my_cbufs->counts.reads.counts8[sender_id]; + uint8_t *write_count = &my_cbufs->counts.writes.counts8[sender_id]; + ipc_msg_t **msg_ptrs = &my_cbufs->cbufs[sender_id][0]; + ipc_msg_t *cur_msg; + + + while(*read_count != *write_count) + { + // extract the message pointer + cur_msg = msg_ptrs[*read_count % IPC_CBUF_SIZE]; + + // increment the read count + (*read_count)++; + + ipc_process_msg(cur_msg); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/// The IPC interrupt handler. Finds which circular buffers have messages +/// and processes them. +/// +#ifdef __SSX__ +KERN_IRQ_HANDLER(ipc_irq_handler_full) +#else +KERN_IRQ_HANDLER(ipc_irq_handler) +#endif +{ + ipc_counts_t xored_counts; + ipc_target_t *my_cbufs; + uint32_t sender_id; + + // Processors could be sending us new packets while we're + // processing this interrupt. We need to mask all new + // IPI interrupts until we are done processing so that we don't + // end up processing an interrupt that was already handled. + KERN_IRQ_DISABLE(IPC_GET_IRQ(OCCHW_INST_ID_SELF)); + + // Clear the interrupt bit in the OISR before we check for + // status. Checking status and then clearing the OISR bit + // can lead to a race condition where we loose an interrupt. + KERN_IRQ_STATUS_CLEAR(IPC_GET_IRQ(OCCHW_INST_ID_SELF)); + + my_cbufs = &OSD_PTR->ipc_data.targets[OCCHW_INST_ID_SELF]; + + // Make sure we get the most recent write counts from SRAM + // dcbf(&my_cbufs->counts.writes.counts64); + + // Use XOR to find the buffers that aren't empty (read count != write count) + xored_counts.counts64 = my_cbufs->counts.reads.counts64 ^ + my_cbufs->counts.writes.counts64; + + while(1) + { + // Use cntlzw to find the first buffer that isn't empty + sender_id = cntlz64(xored_counts.counts64) / IPC_CBUF_COUNT_BITS; + + + // If all buffers are empty then we're done + if(sender_id > OCCHW_INST_ID_MAX) + { + break; + } + + // Mark the buffer as empty in our local snapshot + xored_counts.counts8[sender_id] = 0; + + // Process all new messages in the buffer + ipc_process_cbuf(sender_id); + } + + // Unmask the irq before returning + KERN_IRQ_ENABLE(IPC_GET_IRQ(OCCHW_INST_ID_SELF)); +} + + +/////////////////////////////////////////////////////////////////////////////// +/// This macro creates an assembly function named ipc_irq_handler which handles +/// saving/restoring the context that is required for calling a C +/// function. +/// +/// NOTE: This is only needed for SSX. PK only supports full interrupts. +/// +#ifdef __SSX__ +KERN_IRQ_FAST2FULL(ipc_irq_handler, ipc_irq_handler_full); +#endif + + +/////////////////////////////////////////////////////////////////////////////// +/// Initialize IPC control structures. +/// +int ipc_init(void) +{ + //prevent new messages from coming in. + ipc_disable(OCCHW_INST_ID_SELF); + +#ifndef STATIC_IPC_TABLES + int i; + for(i = 0; i < IPC_MT_MAX_FUNCTIONS; i++) + { + G_ipc_mt_handlers[i].handler = ipc_default_handler; + G_ipc_mt_handlers[i].arg = 0; + } + for(i = 0; i < IPC_ST_MAX_FUNCTIONS; i++) + { + G_ipc_st_handlers[i].handler = ipc_default_handler; + G_ipc_st_handlers[i].arg = 0; + } +#endif + return IPC_RC_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Enables IPC communications. +/// +int ipc_enable(void) +{ + int rc; + ipc_target_t* my_cbufs; + + do + { + // Install the IPI interrupt handler for this processor + rc = KERN_IRQ_HANDLER_SET(IPC_GET_IRQ(OCCHW_INST_ID_SELF), + ipc_irq_handler, + 0, + KERN_CRITICAL); + + if(rc) + { + break; + } + + my_cbufs = &OSD_PTR->ipc_data.targets[OCCHW_INST_ID_SELF]; + + //Any messages that were placed on the cbufs before this point + //are dropped. Clear any interrupts that might have been raised + //before this point. + KERN_IRQ_STATUS_CLEAR(IPC_GET_IRQ(OCCHW_INST_ID_SELF)); + + // Clear and open up all receive buffers for this processor + // by setting the read counts equal to the write counts + my_cbufs->counts.reads.counts64 = my_cbufs->counts.writes.counts64; + + // Unmask the IPI interrupt for this processor + KERN_IRQ_ENABLE(IPC_GET_IRQ(OCCHW_INST_ID_SELF)); + + //Allow us to send out new commands + G_ipc_enabled = 1; + + }while(0); + + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Disable recieving new IPC commands for a processor. +/// +int ipc_disable(uint32_t target_id) +{ + int i; + int rc = IPC_RC_SUCCESS; + ipc_target_t* target_cbufs; + KERN_MACHINE_CONTEXT ctx; + + do + { + // Check for invalid target ID + if(target_id > OCCHW_INST_ID_MAX) + { + rc = IPC_RC_INVALID_TARGET_ID; + break; + } + + // Prevent us from sending out new commands + if(target_id == OCCHW_INST_ID_SELF) + { + G_ipc_enabled = 0; + } + + //disable interrupts to prevent the IPC interrupt handler or other threads + //on this instance from interrupting us and changing the read count or + //interrupt mask bits under our feet. + KERN_CRITICAL_SECTION_ENTER(KERN_CRITICAL, &ctx); + + //mask off the IPC interrupt for the target (this is mostly for the case + //where we are diabling IPC for ourselves). + KERN_IRQ_DISABLE(IPC_GET_IRQ(target_id)); + + target_cbufs = &OSD_PTR->ipc_data.targets[target_id]; + + // Make each cbuf appear to be more than full. This signals to + // senders that the buffer is not just full, but blocked. When + // the sender sees this it knows not to place more messages on the cbuf. + // NOTE: we are updating the read register, which is allowed if the cbuf + // is owned by the instance this code is running on OR if the instance + // is known to be halted. + for(i = 0; i <= OCCHW_INST_ID_MAX; i++) + { + target_cbufs->counts.reads.counts8[i] = + target_cbufs->counts.writes.counts8[i] - (IPC_CBUF_SIZE * 2); + } + + KERN_CRITICAL_SECTION_EXIT(&ctx); + + }while(0); + return rc; +} + diff --git a/src/lib/occlib/ipc_init.c b/src/lib/occlib/ipc_init.c new file mode 100644 index 0000000..4d732b7 --- /dev/null +++ b/src/lib/occlib/ipc_init.c @@ -0,0 +1,153 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_init.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ipc_init.c +/// \brief Implementation of IPC (InterProcessor Communication) routines for +/// Setting up the IPC function tables, messages and message queues. +/// +/// NOTE: The functions that these interfaces peform can all be done statically. +/// This code will not be included in an image if none of the functions are +/// referenced. + +#include "ipc_api.h" +#include "ipc_ping.h" + +/////////////////////////////////////////////////////////////////////////////// +/// Associate an IPC function ID with a handler function +/// +int ipc_set_handler(uint32_t function_id, + ipc_msg_handler_t handler, + void* callback_arg) +{ + ipc_func_table_entry_t *func_table; + uint32_t table_limit; + int rc = IPC_RC_SUCCESS; + ipc_func_id_t func_id = {{0}}; + + do + { + func_id.word32 = function_id; + + //setup for multi-target commands + if(func_id.multi_target_flag) + { + table_limit = IPC_MT_NUM_FUNCIDS; + func_table = G_ipc_mt_handlers; + } + //setup for single-target commands + else + { + //make sure the function id targets this processor + if(func_id.target_id != OCCHW_INST_ID_SELF) + { + rc = IPC_RC_INVALID_TARGET_ID; + break; + } + table_limit = IPC_ST_NUM_FUNCIDS; + func_table = G_ipc_st_handlers; + } + + //make sure the function id is valid + if((func_id.table_index >= table_limit) || !func_id.valid_flag) + { + rc = IPC_RC_INVALID_FUNC_ID; + break; + } + + if(!handler) + { + rc = IPC_RC_INVALID_ARG; + break; + } + + func_table[func_id.table_index].handler = handler; + func_table[func_id.table_index].arg = callback_arg; + }while(0); + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Initialize an IPC command message +/// +void ipc_init_msg(ipc_msg_t* msg, + uint32_t func_id, + ipc_msg_handler_t resp_callback, + void* callback_arg) +{ + KERN_DEQUE_ELEMENT_CREATE(&msg->node); + msg->func_id.word32 = func_id; + msg->ipc_rc = IPC_RC_SUCCESS; + msg->resp_callback = resp_callback; + msg->callback_arg = callback_arg; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Initialize an IPC message queue. +/// +void ipc_init_msgq(ipc_msgq_t* msgq) +{ + KERN_DEQUE_SENTINEL_CREATE(&msgq->msg_head); + + //set the initial count to 0 with no max count + KERN_SEMAPHORE_CREATE(&msgq->msg_sem, 0, 0); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Initialize an IPC message and associate it with an IPC message queue +/// +void ipc_init_msgq_msg(ipc_msg_t* msg, uint32_t func_id, ipc_msgq_t* msgq) +{ + ipc_init_msg(msg, func_id, ipc_msgq_handler, msgq); +} + + +/////////////////////////////////////////////////////////////////////////////// +/// Initialize an IPC ping command message +/// +#ifdef IPC_ENABLE_PING +int ipc_ping_cmd_init(ipc_ping_cmd_t* ping_cmd) +{ + int rc; + + do + { + //initialize the message + ipc_init_msg(&ping_cmd->msg, IPC_MT_PING, ipc_ping_response, 0); + + //initialize the semaphore count to 0 and set the max count to 1 + rc = KERN_SEMAPHORE_CREATE(&ping_cmd->sem, 0, 1); + if(rc) + { + break; + } + }while(0); + return rc; +} +#endif /*IPC_ENABLE_PING*/ diff --git a/src/lib/occlib/ipc_macros.h b/src/lib/occlib/ipc_macros.h new file mode 100644 index 0000000..c95bff6 --- /dev/null +++ b/src/lib/occlib/ipc_macros.h @@ -0,0 +1,197 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_macros.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __IPC_MACROS_H__ +#define __IPC_MACROS_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ipc_macros.h +/// \brief Contains macros related to the Interprocessor Communications (IPC) +/// API. +/// + +/////////////////////////////////////////////////////////////////////////////// +/// Retrieves the IRQ number for the specified OCC processor instance +/// +#define IPC_GET_IRQ(instance_id) (OCCHW_IRQ_IPI0_HI_PRIORITY + instance_id) + +/////////////////////////////////////////////////////////////////////////////// +/// Marks the start of the IPC function ID table +/// +#define IPC_FUNCIDS_TABLE_START \ + typedef enum \ +{ + +/////////////////////////////////////////////////////////////////////////////// +/// Marks the end of the IPC function ID table +/// +#define IPC_FUNCIDS_TABLE_END \ +} ipc_func_enum_t; + +/////////////////////////////////////////////////////////////////////////////// +/// Marks the start of the IPC multi-target function IDs within the IPC +/// function ID table. +/// +#define IPC_FUNCIDS_MT_START \ + IPC_MT_START = (int)((IPC_TARGET_MASK | IPC_FLAG_MT | IPC_FLAG_VALID | (((uint32_t)((uint8_t)OCCHW_INST_ID_SELF)) << 16)) - 1), + +/////////////////////////////////////////////////////////////////////////////// +/// Marks the end of the IPC multi-target function IDs within the IPC function +/// ID table. +/// +#define IPC_FUNCIDS_MT_END \ + IPC_MT_END, + +#define IPC_CONCAT_INST(name, inst) name ## inst + +/////////////////////////////////////////////////////////////////////////////// +/// Marks the start of the IPC single-target function IDs within the IPC +/// ID table. +/// +/// \param target_id The instance ID of the processor that the following +/// function IDs will target. +/// +/// Each processor has it's own set of single-target function IDs. Messages +/// that are initialized with these function ID's can only be sent to the +/// processor specified by \a target_id. +/// +#define IPC_FUNCIDS_ST_START(target_id) \ + IPC_CONCAT_INST(IPC_ST_START_, target_id) = \ + (int)(((((uint32_t)target_id) << 24) | IPC_FLAG_VALID ) - 1), + +/////////////////////////////////////////////////////////////////////////////// +/// Marks the end of the IPC single-target function IDs for the specified +/// target ID, \a target_id. +/// +#define IPC_FUNCIDS_ST_END(target_id) \ + IPC_CONCAT_INST(IPC_ST_END_, target_id), + +/////////////////////////////////////////////////////////////////////////////// +/// Create an IPC function ID. +/// +/// \param The name of the IPC function ID +/// +/// This macro should only be used inside the IPC function ID table. Under +/// the covers, an enum with a name of \a name is created. +/// +#define IPC_FUNC_ID(name) \ + name, + +/////////////////////////////////////////////////////////////////////////////// +#define IPC_MT_NUM_FUNCIDS \ + ((IPC_MT_END - IPC_MT_START) - 1) + +/////////////////////////////////////////////////////////////////////////////// +#define IPC_ST_TARGET_NUM_FUNCIDS(target_id) \ + ((IPC_CONCAT_INST(IPC_ST_END_, target_id) - IPC_CONCAT_INST(IPC_ST_START_, target_id)) - 1) + +/////////////////////////////////////////////////////////////////////////////// +#define IPC_ST_NUM_FUNCIDS IPC_ST_TARGET_NUM_FUNCIDS(OCCHW_INST_ID_SELF) + +/////////////////////////////////////////////////////////////////////////////// +/// Macros for statically initializing the IPC function tables +#ifdef STATIC_IPC_TABLES + +#define IPC_MT_FUNC_TABLE_START \ + ipc_func_table_entry_t G_ipc_mt_handlers[IPC_MT_MAX_FUNCTIONS] = \ +{ + +#define IPC_ST_FUNC_TABLE_START \ + ipc_func_table_entry_t G_ipc_st_handlers[IPC_ST_MAX_FUNCTIONS] = \ +{ + +#define IPC_HANDLER(func, arg) \ + {func, arg}, + +#define IPC_HANDLER_DEFAULT \ + {ipc_default_handler, 0}, + +#define IPC_MSGQ_HANDLER(msgq_ptr) \ + {ipc_msgq_handler, msgq_ptr}, + +#define IPC_MT_FUNC_TABLE_END \ +}; + +#define IPC_ST_FUNC_TABLE_END \ +}; + +#else + +#define IPC_MT_FUNC_TABLE_START + +#define IPC_ST_FUNC_TABLE_START + +#define IPC_HANDLER(func, arg) + +#define IPC_HANDLER_DEFAULT + +#define IPC_MSGQ_HANDLER(msgq_ptr) + +#define IPC_MT_FUNC_TABLE_END + +#define IPC_ST_FUNC_TABLE_END + +#endif /*STATIC_IPC_TABLES*/ + +/////////////////////////////////////////////////////////////////////////////// +/// Convenience macro for defering handling of a command or +/// response in a noncritical interrupt context. This was +/// specifically added for ipc functions that need to call +/// ssx functions on the 405. (ssx functions can not be called +/// inside a critical interrupt context). +#ifdef __SSX__ +#define IPC_DEFER_TO_NONCRITICAL(ipc_msg) \ +{ \ + ssx_deque_push_back(&G_ipc_deferred_queue, &ipc_msg->node); \ + ssx_irq_status_set(OCCHW_IRQ_ASYNC_IPI, 1); \ +} + +#else +#define IPC_DEFER_TO_NONCRITICAL(ipc_msg) +#endif + +/////////////////////////////////////////////////////////////////////////////// +/// Determine if an IPC function ID is a multi-target ID +/////////////////////////////////////////////////////////////////////////////// +#define IPC_FUNCID_IS_MT(funcid) (IPC_FLAG_MT & (funcid)) + +/////////////////////////////////////////////////////////////////////////////// +/// Set the target ID of a multi-target function ID +/// Sets the target to be invalid if it's not a multi-target function id +/////////////////////////////////////////////////////////////////////////////// +#define IPC_SET_MT_TARGET(funcid, targetid) \ +(((funcid) & IPC_FLAG_MT)? \ + (((targetid) << IPC_TARGET_SHIFT) | ((funcid) & ~(IPC_TARGET_MASK))): \ + (IPC_TARGET_MASK | (funcid))) + +/////////////////////////////////////////////////////////////////////////////// +/// Retrieve the target ID from an IPC function id +/////////////////////////////////////////////////////////////////////////////// +#define IPC_GET_TARGET_ID(funcid) (((uint32_t)(funcid)) >> IPC_TARGET_SHIFT) + +#endif /*__IPC_MACROS_H__*/ diff --git a/src/lib/occlib/ipc_msgq.c b/src/lib/occlib/ipc_msgq.c new file mode 100644 index 0000000..8048b72 --- /dev/null +++ b/src/lib/occlib/ipc_msgq.c @@ -0,0 +1,108 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_msgq.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ipc_msgq.c +/// \brief Implementation of IPC (InterProcessor Communication) routines that +/// involve using message queues (primarily in a thread context). + +#include "ipc_api.h" + +/////////////////////////////////////////////////////////////////////////////// +// Handles msgq messages (commands and responses) by placing them on a message +// queue and then posting the message queue semaphore. +// This function is for internal use only! +void ipc_msgq_handler(ipc_msg_t* msg, void* arg) +{ + ipc_msgq_t *msgq = (ipc_msgq_t*)arg; + + //NOTE: this is hard coded to 0 on PPE + if(KERN_CONTEXT_CRITICAL_INTERRUPT()) + { + //NOTE: this is a no-op on PPE + IPC_DEFER_TO_NONCRITICAL(msg); + } + else + { + KERN_DEQUE_PUSH_BACK(&msgq->msg_head, &msg->node); + KERN_SEMAPHORE_POST(&msgq->msg_sem); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/// Wait (with timeout) for an IPC message on an IPC message queue. +/// +int ipc_msgq_recv(ipc_msg_t** msg, ipc_msgq_t* msgq, KERN_INTERVAL timeout) +{ + int rc; + ipc_msg_t *popped_msg = 0; + KERN_MACHINE_CONTEXT ctx; + + rc = KERN_SEMAPHORE_PEND(&msgq->msg_sem, timeout); + if(rc) + { + if(rc == -KERN_SEMAPHORE_PEND_TIMED_OUT || + rc == -KERN_SEMAPHORE_PEND_NO_WAIT) + { + rc = IPC_RC_TIMEOUT; + } + } + else + { + //The queue is also modified in the IPC interrupt context so + //we need to make sure interrupts are disabled while we modify it. + KERN_CRITICAL_SECTION_ENTER(KERN_CRITICAL, &ctx); + popped_msg = (ipc_msg_t*)KERN_DEQUE_POP_FRONT(&msgq->msg_head); + KERN_CRITICAL_SECTION_EXIT(&ctx); + + if(popped_msg) + { + rc = IPC_RC_SUCCESS; + } + else + { + rc = IPC_RC_NO_MSG; + } + } + *msg = popped_msg; + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Associate a message queue with a function ID (commands recieved with the +/// specified function id will be placed on the message queue). +/// +int ipc_register_msgq(uint32_t func_id, ipc_msgq_t* msgq) +{ + return ipc_set_handler(func_id, ipc_msgq_handler, msgq); +} + + + diff --git a/src/lib/occlib/ipc_ping.c b/src/lib/occlib/ipc_ping.c new file mode 100644 index 0000000..f295c99 --- /dev/null +++ b/src/lib/occlib/ipc_ping.c @@ -0,0 +1,96 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_ping.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "ipc_ping.h" + +#ifdef IPC_ENABLE_PING +//server side ping message handler +void ipc_ping_handler(ipc_msg_t* cmd, void* arg) +{ + //NOTE: this will run in a critical interrupt when sent to the 405 + //ignore return codes + ipc_send_rsp(cmd, IPC_RC_SUCCESS); +} + +//Note: This runs in a critical interrupt on the 405 but SSX functions +// can not be called from a critical interrupt. Instead, it must be +// deferred to a non-critical handler. +void ipc_ping_response(ipc_msg_t* rsp, void* arg) +{ + ipc_ping_cmd_t *ping_cmd = (ipc_ping_cmd_t*)rsp; + + if(KERN_CONTEXT_CRITICAL_INTERRUPT()) + { + //NOTE: this is a no-op on PPE + IPC_DEFER_TO_NONCRITICAL(rsp); + } + else + { + KERN_SEMAPHORE_POST(&ping_cmd->sem); + ipc_free_msg(&ping_cmd->msg); + } + +} + + +//Command that can be run in a thread context to ping another target +//The message is allocated on the stack +int ipc_ping(ipc_ping_cmd_t* ping_cmd, uint32_t target_id) +{ + int rc; + + do + { + //set the target (since this is a multi-target command) + rc = ipc_set_cmd_target(&ping_cmd->msg, target_id); + if(rc) + { + break; + } + + //send the command + rc = ipc_send_cmd(&ping_cmd->msg); + if(rc) + { + break; + } + + //assume that if we timed out then the target must have gone down. + rc = KERN_SEMAPHORE_PEND(&ping_cmd->sem, KERN_SECONDS(1)); + if(rc) + { + if(rc == -KERN_SEMAPHORE_PEND_TIMED_OUT) + { + rc = IPC_RC_TIMEOUT; + } + break; + } + + //response message was received. Now return the ipc_rc + rc = ipc_get_rc(&ping_cmd->msg); + + }while(0); + return rc; +} +#endif /*IPC_ENABLE_PING*/ diff --git a/src/lib/occlib/ipc_ping.h b/src/lib/occlib/ipc_ping.h new file mode 100644 index 0000000..0b315ee --- /dev/null +++ b/src/lib/occlib/ipc_ping.h @@ -0,0 +1,53 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_ping.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "ipc_api.h" + +typedef struct +{ + ipc_msg_t msg; + KERN_SEMAPHORE sem; +}ipc_ping_cmd_t; + +#ifdef IPC_ENABLE_PING +//server side ping message handler +void ipc_ping_handler(ipc_msg_t* cmd, void* arg); + +//Initialize a ping command. +int ipc_ping_cmd_init(ipc_ping_cmd_t* ping_cmd); + +//function for handling the ping response on the local processor +void ipc_ping_response(ipc_msg_t* rsp, void* arg); + +//Statically initialize a ping command +#define IPC_PING_CMD_CREATE(name) \ +ipc_ping_cmd_t name = \ +{\ + .msg = IPC_MSG_INIT(IPC_MT_PING, ipc_ping_response, 0), \ + .sem = KERN_SEMAPHORE_INITIALIZATION(0, 1) \ +} + +//blocking command that can be run in a thread context to ping another target +int ipc_ping(ipc_ping_cmd_t* ping_cmd, uint32_t target_id); +#endif /*IPC_ENABLE_PING*/ diff --git a/src/lib/occlib/ipc_structs.h b/src/lib/occlib/ipc_structs.h new file mode 100644 index 0000000..5f1c90b --- /dev/null +++ b/src/lib/occlib/ipc_structs.h @@ -0,0 +1,226 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/ipc_structs.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __IPC_STRUCTS_H__ +#define __IPC_STRUCTS_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ipc_structs.h +/// \brief Common header for Interprocessor Communication structures in the +/// OCC complex +/// +#include "kernel.h" //kernel wrapper macros +#include "occhw_common.h" +#include "ipc_macros.h" + + +#ifndef IPC_CBUF_SIZE +#define IPC_CBUF_SIZE 8 //number of messages must be a power of 2 +#endif + +//the maximum number of multi-target functions (common functions) +//This can be overriden in a global header that's included by all occ images +//so that it is the same across all processors. +#ifndef IPC_MT_MAX_FUNCTIONS +#define IPC_MT_MAX_FUNCTIONS 8 +#endif + +//the maximum number of single-target functions (processor-specific functions) +//This can be overridden in a local header file and does not need to be the +//same across all processors. +#ifndef IPC_ST_MAX_FUNCTIONS +#define IPC_ST_MAX_FUNCTIONS 16 +#endif + +/// IPC return codes +#define IPC_RC_SUCCESS 0 +#define IPC_RC_CMD_FAILED 0x00472000 +#define IPC_RC_BUFFER_FULL 0x00472001 +#define IPC_RC_SELF_BLOCKED 0x00472002 +#define IPC_RC_TARGET_BLOCKED 0x00472003 +#define IPC_RC_MSG_ACTIVE 0x00472004 +#define IPC_RC_INVALID_FUNC_ID 0x00472005 +#define IPC_RC_CMD_NOT_SUPPORTED 0x00472006 +#define IPC_RC_INVALID_TARGET_ID 0x00472007 +#define IPC_RC_INVALID_ARG 0x00472008 +#define IPC_RC_MSG_NOT_ACTIVE 0x00472009 +#define IPC_RC_NO_MSG 0x0047200a +#define IPC_RC_TIMEOUT 0x0047200b + +/// IPC Message Flags +#define IPC_FLAG_MT 0x00000100 +#define IPC_FLAG_RESPONSE 0x00000200 +#define IPC_FLAG_VALID 0x00000400 +#define IPC_FLAG_ACTIVE 0x00000800 + +// Function ID field masks +#define IPC_TARGET_MASK 0xff000000 +#define IPC_SENDER_MASK 0x00ff0000 +#define IPC_FLAGS_MASK 0x0000ff00 +#define IPC_INDEX_MASK 0x000000ff + +#define IPC_TARGET_SHIFT 24 + +#define IPC_CBUF_COUNT_BYTES 1 +#define IPC_CBUF_COUNT_BITS 8 + +#ifndef __ASSEMBLER__ + +// If an occ application does not wish to use IPC then it should not +// define the GLOBAL_CFG_USE_IPC macro. This allows IPC to compile +// without errors. +#ifdef GLOBAL_CFG_USE_IPC +#include "ipc_func_ids.h" +#else +IPC_FUNCIDS_TABLE_START + IPC_FUNCIDS_MT_START + IPC_FUNCIDS_MT_END + IPC_FUNCIDS_ST_START(OCCHW_INST_ID_GPE0) + IPC_FUNCIDS_ST_END(OCCHW_INST_ID_GPE0) + IPC_FUNCIDS_ST_START(OCCHW_INST_ID_GPE1) + IPC_FUNCIDS_ST_END(OCCHW_INST_ID_GPE1) + IPC_FUNCIDS_ST_START(OCCHW_INST_ID_GPE2) + IPC_FUNCIDS_ST_END(OCCHW_INST_ID_GPE2) + IPC_FUNCIDS_ST_START(OCCHW_INST_ID_GPE3) + IPC_FUNCIDS_ST_END(OCCHW_INST_ID_GPE3) + IPC_FUNCIDS_ST_START(OCCHW_INST_ID_PPC) + IPC_FUNCIDS_ST_END(OCCHW_INST_ID_PPC) +IPC_FUNCIDS_TABLE_END +#endif /*GLOBAL_CFG_USE_IPC*/ + +//Statically check that the function tables are large enough +KERN_STATIC_ASSERT(IPC_MT_NUM_FUNCIDS <= IPC_MT_MAX_FUNCTIONS); +KERN_STATIC_ASSERT(IPC_ST_NUM_FUNCIDS <= IPC_ST_MAX_FUNCTIONS); + +#ifdef __SSX__ +extern KERN_DEQUE G_ipc_deferred_queue; +#endif + +struct ipc_msg; +typedef struct ipc_msg ipc_msg_t; + +typedef union +{ + uint64_t counts64; + uint8_t counts8[sizeof(uint64_t) / IPC_CBUF_COUNT_BYTES]; +} ipc_counts_t; + +/// Circular buffer read and write counts are grouped together by target_id +/// so that an interrupt hander can quickly tell which buffer requires +/// service. Each counter is 1 byte. +/// +/// Note: index 0 (or most significant byte) is for GPE0 +typedef struct +{ + ipc_counts_t reads; + ipc_counts_t writes; +}ipc_rwcounts_t; + +typedef struct +{ + ipc_rwcounts_t counts; + ipc_msg_t* cbufs[OCCHW_MAX_INSTANCES][IPC_CBUF_SIZE]; + uint8_t pad[16]; +}ipc_target_t; //size is 6 x 32 = 192 bytes + +/// All of the shared data for IPC is contained in this structure +typedef struct +{ + ipc_target_t targets[OCCHW_MAX_INSTANCES]; //880 bytes +}ipc_shared_data_t; + +//prototype for ipc handlers and callback functions +typedef void (*ipc_msg_handler_t)(ipc_msg_t*, void *); + +//function table entry +typedef struct +{ + ipc_msg_handler_t handler; + void* arg; +}ipc_func_table_entry_t; + +extern ipc_func_table_entry_t G_ipc_mt_handlers[IPC_MT_MAX_FUNCTIONS]; + +extern ipc_func_table_entry_t G_ipc_st_handlers[IPC_ST_MAX_FUNCTIONS]; + +typedef union +{ + struct + { + uint32_t target_id: 8; + uint32_t sender_id: 8; + uint32_t reserved: 4; + uint32_t active_flag: 1; + uint32_t valid_flag: 1; + uint32_t response_flag: 1; + uint32_t multi_target_flag: 1; + uint32_t table_index: 8; + }; + uint32_t word32; +}ipc_func_id_t; + +#define IPC_MSG_DEQUEUE_SZ 8 //expected size of a deque structure +struct ipc_msg +{ + // but this file is shared by both, so use a void* type + // instead. + union + { + KERN_DEQUE node; + uint8_t pad[IPC_MSG_DEQUEUE_SZ]; + }; + + //function ID of the function that the sender wants executed + volatile ipc_func_id_t func_id; + + //The IPC return code + volatile uint32_t ipc_rc; + + //function to call if this is a response message + ipc_msg_handler_t resp_callback; + + //Argument passed into the callback function + void *callback_arg; +}; + +//Do a static check on the size of KERN_DEQUE. IPC_MSG_DEQUE_SZ must be <= sizeof(KERN_DEQUE). +//If the compiler fails here then you probably need to update the value of IPC_MSG_DEQUEUE_SZ. +//NOTE: this is needed because ipc_msg_t is used in both PK and SSX environments and there is +// no guarantee that the size of a deque will be the same in both environments. +KERN_STATIC_ASSERT(sizeof(KERN_DEQUE) <= IPC_MSG_DEQUEUE_SZ); + +//A message queue that threads can block on while they wait for new messages. +typedef struct +{ + KERN_DEQUE msg_head; + KERN_SEMAPHORE msg_sem; //posted whenever a new message is queued +}ipc_msgq_t; + +#endif /*__ASSEMBLER__*/ + +#endif /* __IPC_STRUCTS_H__ */ diff --git a/src/lib/occlib/liboccfiles.mk b/src/lib/occlib/liboccfiles.mk new file mode 100644 index 0000000..7b3c80a --- /dev/null +++ b/src/lib/occlib/liboccfiles.mk @@ -0,0 +1,53 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/occlib/liboccfiles.mk $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file liboccfiles.mk +# +# @brief mk for libocc.a object files +# +# @page ChangeLogs Change Logs +# @section liboccfiles.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# INCLUDES +########################################################################## + +C-SOURCES = \ + ipc_core.c \ + ipc_init.c \ + ipc_msgq.c \ + ipc_ping.c \ + occhw_xir_dump.c + +S-SOURCES = + +LIBOCC_OBJECTS = $(C-SOURCES:.c=.o) $(S-SOURCES:.S=.o) diff --git a/src/lib/occlib/occhw_scom_cmd.h b/src/lib/occlib/occhw_scom_cmd.h new file mode 100644 index 0000000..c6051f5 --- /dev/null +++ b/src/lib/occlib/occhw_scom_cmd.h @@ -0,0 +1,93 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/occhw_scom_cmd.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __OCCHW_SCOM_CMD_H__ +#define __OCCHW_SCOM_CMD_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_scom_cmd.h +/// \brief Defines the shared scom command control block +/// +/// This file contains definitions that are used by both ppc405 and GPE +/// instances inside the OCC. + +// The most significant bit of scom addresses is always 0, so we use this +// bit to tell the SCOM proxy server (GPE) whether a getscom or putscom is +// needed. +#define OCCHW_SCOM_READ_MASK 0x80000000 + +// This is a status value that the ppc405 will write to the status +// word prior to starting a new request. This value can be used to +// detect that the GPE has not yet written status for the request. +// Note: The GPE simply copies it's MSR value to the status field +// when it completes. This value is not a valid MSR value. +#define OCCHW_SCOM_PENDING 0x0000EF00 + +// Define the structure offsets for use in assembly code on the GPE +#define OCCHW_SCOM_STATUS_OFFSET 0 +#define OCCHW_SCOM_ADDR_OFFSET 4 +#define OCCHW_SCOM_DATA_OFFSET 8 + +#ifndef __ASSEMBLER__ +typedef union +{ + uint32_t status32; + struct + { + uint32_t rsvd0 : 1; + uint32_t mask : 7; + uint32_t is0 : 1; + uint32_t sibrc : 3; + uint32_t lp : 1; + uint32_t we : 1; + uint32_t is1 : 1; + uint32_t uie : 1; + uint32_t ee : 1; + uint32_t rsvd1 : 2; + uint32_t me : 1; + uint32_t rsvd2 : 3; + uint32_t ipe : 1; + uint32_t sibrca : 8; + }; +}occhw_scom_status_t; + +typedef struct +{ + //MSR is saved here + volatile occhw_scom_status_t scom_status; + + //msg is used to communicate read/!write + volatile uint32_t scom_addr; + + //data for putscom or from getscom + volatile uint64_t scom_data; +} occhw_scom_cmd_t; + +#endif /*__ASSEMBLER__*/ + +#endif /* __OCCHW_SCOM_CMD_H__ */ diff --git a/src/lib/occlib/occhw_shared_data.h b/src/lib/occlib/occhw_shared_data.h new file mode 100644 index 0000000..643691a --- /dev/null +++ b/src/lib/occlib/occhw_shared_data.h @@ -0,0 +1,93 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/occhw_shared_data.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __OCCHW_OSD_H__ +#define __OCCHW_OSD_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_osd.h +/// \brief Common header for shared data within the OCC complex +/// + +#include "kernel.h" +#include "ipc_structs.h" +#include "occhw_scom_cmd.h" + +/// Hardcoded address for the location of the OCC shared data segment +/// This is placed in the non-cacheable aliased region of SRAM space +#ifndef OSD_ADDR +#define OSD_ADDR 0xf7f00000 +#endif + +/// Total space of the OCC shared data segment +#define OSD_TOTAL_SHARED_DATA_BYTES 4096 + +/// Reserve space for IPC data in case it needs to grow +#define OSD_IPC_RESERVED_BYTES 2048 + +/// Reserve space for Debug +#define OSD_DEBUG_RESERVED_BYTES 512 + +#define OSD_GPE_SCOM_ADDR (OSD_ADDR + OSD_IPC_RESERVED_BYTES + OSD_DEBUG_RESERVED_BYTES) + +#define OSD_GPE_SCOM_RESERVED_BYTES 32 + +#ifndef __ASSEMBLER__ +typedef union +{ + struct + { + union + { + ipc_shared_data_t ipc_data; //880 bytes + uint8_t ipc_reserved[OSD_IPC_RESERVED_BYTES]; + }; + union + { + //debug_shared_data_t debug_data; + uint8_t debug_reserved[OSD_DEBUG_RESERVED_BYTES]; + }; + union + { + occhw_scom_cmd_t scom_cmd; + uint8_t gpe_scom_reserved[OSD_GPE_SCOM_RESERVED_BYTES]; + }; + + }; + uint8_t total_reserved[OSD_TOTAL_SHARED_DATA_BYTES]; +} occhw_osd_t; + +// Fail to compile if ipc_shared_data exceeds the reserved space +KERN_STATIC_ASSERT((sizeof(ipc_shared_data_t) <= OSD_IPC_RESERVED_BYTES)); + +/// Hardcoded pointer for the location of the OCC shared data segment +#define OSD_PTR ((occhw_osd_t*) OSD_ADDR) + +#endif /*__ASSEMBLER__*/ + +#endif /* __OCCHW_OSD_H__ */ diff --git a/src/lib/occlib/occhw_xir_dump.c b/src/lib/occlib/occhw_xir_dump.c new file mode 100644 index 0000000..7ff2e4e --- /dev/null +++ b/src/lib/occlib/occhw_xir_dump.c @@ -0,0 +1,60 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/occhw_xir_dump.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_xir_dump.c +/// \brief Implementation of the occhw_xir_dump function +#include "kernel.h" +#include "occhw_common.h" +#include "occhw_xir_dump.h" + +int32_t occhw_xir_dump(uint32_t inst_id, occhw_xir_dump_t* xir_dump) +{ + int rc; + do + { + if(!xir_dump) + { + rc = OCCHW_XIR_INVALID_POINTER; + break; + } + + if(inst_id > OCCHW_INST_ID_MAX_GPE) + { + rc = OCCHW_XIR_INVALID_GPE; + break; + } + + //TODO: dump the XIR regs once the addresses are available + rc = 0; + + }while(0); + + return rc; +} diff --git a/src/lib/occlib/occhw_xir_dump.h b/src/lib/occlib/occhw_xir_dump.h new file mode 100644 index 0000000..85e52ad --- /dev/null +++ b/src/lib/occlib/occhw_xir_dump.h @@ -0,0 +1,70 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/occlib/occhw_xir_dump.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __OCCHW_XIR_DUMP_H__ +#define __OCCHW_XIR_DUMP_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_xir_dump.h +/// \brief header for the occhw_xir_dump function +/// + +#ifndef __ASSEMBLER__ +#include "stdint.h" + +/// Structure for dumping XIR data for a GPE +typedef struct +{ + uint32_t xsr; + uint32_t sprg0; + uint32_t edr; + uint32_t ir; + uint32_t iar; + uint32_t sib_upper; + uint32_t sib_lower; +} occhw_xir_dump_t; + +/////////////////////////////////////////////////////////////////////////////// +/// Dump the XIR registers for a GPE engine +/// +/// \param inst_id The instance ID of the target GPE. +/// +/// \param xir_dump Pointer to a occhw_xir_dump_t structure. +/// +/// Possible return codes are: +/// +/// \retval 0 XIR registers were successfully dumped +/// +/// \retval OCCHW_XIR_INVALID_GPE \a inst_id is not for a valid GPE instance. +/// +/// \retval OCCHW_XIR_INVALID_POINTER \a xir_dump is NULL +/// +int32_t occhw_xir_dump(uint32_t inst_id, occhw_xir_dump_t* xir_dump); + +#endif /*__ASSEMBLER__*/ +#endif /*__OCCHW_XIR_DUMP_H__*/ diff --git a/src/lib/pgas.h b/src/lib/pgas.h deleted file mode 100755 index bdd3ba1..0000000 --- a/src/lib/pgas.h +++ /dev/null @@ -1,1153 +0,0 @@ -#ifndef __PGAS_H__ -#define __PGAS_H__ - -#define __PGAS__ - -// $Id: pgas.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pgas.h,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -// ** WARNING : This file is maintained as part of the OCC firmware. Do ** -// ** not edit this file in the PMX area, the hardware procedure area, ** -// ** or the PoreVe area as any changes will be lost. ** - -/// \file pgas.h -/// \brief Pore GAS -/// -/// PGAS is documented in a seperate standalone document entitled <em> PGAS : -/// PORE GAS (GNU Assembler) User's and Reference Manual </em>. -/// -/// This file defines support macros for the GNU PORE assembler, and the PORE -/// inline assembler and disassebler which follow the PGAS assembly syntax. -/// If the compile swith PGAS_PPC is defined in the environment then pgas.h -/// includes pgas_ppc.h which transforms a PowerPC assembler into an assembler -/// for PORE. - -// These are the opcodes and mnemonics as defined by the PORE hardware -// manual. Many of them will change names slightly in PGAS. - -#define PORE_OPCODE_NOP 0x0f -#define PORE_OPCODE_WAIT 0x01 -#define PORE_OPCODE_TRAP 0x02 -#define PORE_OPCODE_HOOK 0x4f - -#define PORE_OPCODE_BRA 0x10 -#define PORE_OPCODE_BRAZ 0x12 -#define PORE_OPCODE_BRANZ 0x13 -#define PORE_OPCODE_BRAI 0x51 -#define PORE_OPCODE_BSR 0x14 -#define PORE_OPCODE_BRAD 0x1c -#define PORE_OPCODE_BSRD 0x1d -#define PORE_OPCODE_RET 0x15 -#define PORE_OPCODE_CMPBRA 0x56 -#define PORE_OPCODE_CMPNBRA 0x57 -#define PORE_OPCODE_CMPBSR 0x58 -#define PORE_OPCODE_LOOP 0x1f - -#define PORE_OPCODE_ANDI 0x60 -#define PORE_OPCODE_ORI 0x61 -#define PORE_OPCODE_XORI 0x62 - -#define PORE_OPCODE_AND 0x25 -#define PORE_OPCODE_OR 0x26 -#define PORE_OPCODE_XOR 0x27 - -#define PORE_OPCODE_ADD 0x23 -#define PORE_OPCODE_ADDI 0x24 -#define PORE_OPCODE_SUB 0x29 -#define PORE_OPCODE_SUBI 0x28 -#define PORE_OPCODE_NEG 0x2a - -#define PORE_OPCODE_COPY 0x2c -#define PORE_OPCODE_ROL 0x2e - -#define PORE_OPCODE_LOAD20 0x30 -#define PORE_OPCODE_LOAD64 0x71 -#define PORE_OPCODE_SCR1RD 0x32 -#define PORE_OPCODE_SCR1RDA 0x73 -#define PORE_OPCODE_SCR2RD 0x36 -#define PORE_OPCODE_SCR2RDA 0x77 -#define PORE_OPCODE_WRI 0x78 -#define PORE_OPCODE_BS 0x74 -#define PORE_OPCODE_BC 0x75 -#define PORE_OPCODE_SCR1WR 0x39 -#define PORE_OPCODE_SCR2WR 0x3a -#define PORE_OPCODE_SCAND 0x7c - - -// These are the PGAS versions of the PORE opcodes used in the legacy PGAS_PPC -// assembler and the current PORE inline assembler/disassembler. - -#define PGAS_OPCODE_NOP PORE_OPCODE_NOP -#define PGAS_OPCODE_WAITS PORE_OPCODE_WAIT -#define PGAS_OPCODE_TRAP PORE_OPCODE_TRAP -#define PGAS_OPCODE_HOOKI PORE_OPCODE_HOOK - -#define PGAS_OPCODE_BRA PORE_OPCODE_BRA -#define PGAS_OPCODE_BRAZ PORE_OPCODE_BRAZ -#define PGAS_OPCODE_BRANZ PORE_OPCODE_BRANZ -#define PGAS_OPCODE_BRAI PORE_OPCODE_BRAI -#define PGAS_OPCODE_BSR PORE_OPCODE_BSR -#define PGAS_OPCODE_BRAD PORE_OPCODE_BRAD -#define PGAS_OPCODE_BSRD PORE_OPCODE_BSRD -#define PGAS_OPCODE_RET PORE_OPCODE_RET -#define PGAS_OPCODE_CMPIBRAEQ PORE_OPCODE_CMPBRA -#define PGAS_OPCODE_CMPIBRANE PORE_OPCODE_CMPNBRA -#define PGAS_OPCODE_CMPIBSREQ PORE_OPCODE_CMPBSR -#define PGAS_OPCODE_LOOP PORE_OPCODE_LOOP - -#define PGAS_OPCODE_ANDI PORE_OPCODE_ANDI -#define PGAS_OPCODE_ORI PORE_OPCODE_ORI -#define PGAS_OPCODE_XORI PORE_OPCODE_XORI - -#define PGAS_OPCODE_AND PORE_OPCODE_AND -#define PGAS_OPCODE_OR PORE_OPCODE_OR -#define PGAS_OPCODE_XOR PORE_OPCODE_XOR - -#define PGAS_OPCODE_ADD PORE_OPCODE_ADD -#define PGAS_OPCODE_ADDS PORE_OPCODE_ADDI -#define PGAS_OPCODE_SUB PORE_OPCODE_SUB -#define PGAS_OPCODE_SUBS PORE_OPCODE_SUBI -#define PGAS_OPCODE_NEG PORE_OPCODE_NEG - -#define PGAS_OPCODE_MR PORE_OPCODE_COPY -#define PGAS_OPCODE_ROLS PORE_OPCODE_ROL - -#define PGAS_OPCODE_LS PORE_OPCODE_LOAD20 -#define PGAS_OPCODE_LI PORE_OPCODE_LOAD64 -#define PGAS_OPCODE_LD0 PORE_OPCODE_SCR1RD /* Used by LD */ -#define PGAS_OPCODE_LD0ANDI PORE_OPCODE_SCR1RDA /* Used by LDANDI */ -#define PGAS_OPCODE_LD1 PORE_OPCODE_SCR2RD /* Used by LD */ -#define PGAS_OPCODE_LD1ANDI PORE_OPCODE_SCR2RDA /* Used by LDANDI */ -#define PGAS_OPCODE_STI PORE_OPCODE_WRI -#define PGAS_OPCODE_STD0 PORE_OPCODE_SCR1WR /* Used by STD */ -#define PGAS_OPCODE_STD1 PORE_OPCODE_SCR2WR /* Used by STD */ -#define PGAS_OPCODE_SCAND PORE_OPCODE_SCAND - -#ifdef IGNORE_HW274735 - -// BSI and BCI are normally redacted due to HW274735. See also pgas.h - -#define PGAS_OPCODE_BSI PORE_OPCODE_BS -#define PGAS_OPCODE_BCI PORE_OPCODE_BC - -#endif // IGNORE_HW274735 - -// These are the programmer-visible register names as defined by the PORE -// hardware manual. All of these names (except the PC) appear differently in -// the PGAS syntax, in some cases to reduce confusion, in other cases just to -// have more traditional short mnemonics. - -#define PORE_REGISTER_PRV_BASE_ADDR0 0x0 -#define PORE_REGISTER_PRV_BASE_ADDR1 0x1 -#define PORE_REGISTER_OCI_BASE_ADDR0 0x2 -#define PORE_REGISTER_OCI_BASE_ADDR1 0x3 -#define PORE_REGISTER_SCRATCH0 0x4 -#define PORE_REGISTER_SCRATCH1 0x5 -#define PORE_REGISTER_SCRATCH2 0x6 -#define PORE_REGISTER_ERROR_MASK 0x7 -#define PORE_REGISTER_EXE_TRIGGER 0x9 -#define PORE_REGISTER_DATA0 0xa -#define PORE_REGISTER_PC 0xe -#define PORE_REGISTER_IBUF_ID 0xf - - -// PgP IBUF_ID values - -#define PORE_ID_GPE0 0x00 -#define PORE_ID_GPE1 0x01 -#define PORE_ID_SLW 0x08 -#define PORE_ID_SBE 0x04 - - -// Condition Codes - -#define PORE_CC_UGT 0x8000 -#define PORE_CC_ULT 0x4000 -#define PORE_CC_SGT 0x2000 -#define PORE_CC_SLT 0x1000 -#define PORE_CC_C 0x0800 -#define PORE_CC_V 0x0400 -#define PORE_CC_N 0x0200 -#define PORE_CC_Z 0x0100 - - -// Memory Spaces - -#define PORE_SPACE_UNDEFINED 0xffff -#define PORE_SPACE_OCI 0x8000 -#define PORE_SPACE_PNOR 0x800b -#define PORE_SPACE_OTPROM 0x0001 -#define PORE_SPACE_SEEPROM 0x800c -#define PORE_SPACE_PIBMEM 0x0008 - - -#ifdef __ASSEMBLER__ - -//////////////////////////////////////////////////////////////////////////// -// PGAS Base Assembler Support -//////////////////////////////////////////////////////////////////////////// - - - ////////////////////////////////////////////////////////////////////// - // Condition Codes - ////////////////////////////////////////////////////////////////////// - - .set CC_UGT, PORE_CC_UGT - .set CC_ULT, PORE_CC_ULT - .set CC_SGT, PORE_CC_SGT - .set CC_SLT, PORE_CC_SLT - .set CC_C, PORE_CC_C - .set CC_V, PORE_CC_V - .set CC_N, PORE_CC_N - .set CC_Z, PORE_CC_Z - - - ////////////////////////////////////////////////////////////////////// - // Utility Macros - ////////////////////////////////////////////////////////////////////// - - // 'Undefine' PowerPC mnemonics to trap programming errors - - .macro ..undefppc1, i - .ifnc \i, ignore - .macro \i, args:vararg - .error "This is a PowerPC opcode - NOT a PGAS opcode or extended mnemonic" - .endm - .endif - .endm - - .macro .undefppc, i0, i1=ignore, i2=ignore, i3=ignore - ..undefppc1 \i0 - ..undefppc1 \i1 - ..undefppc1 \i2 - ..undefppc1 \i3 - .endm - - - ////////////////////////////////////////////////////////////////////// - // Argument Checking Macros - ////////////////////////////////////////////////////////////////////// - // - // These macros remain in the final pgas.h file because 1) they are - // required for some PGAS pseudo-ops, and 2) to support robust - // assembler macro definitions. - - // Check an unsigned immediate for size - - .macro ..checku, x:req, bits:req, err="Unsigned value too large" - - .if (((\bits) <= 0) || ((\bits) > 63)) - .error "The number of bits must be in the range 0 < bits < 64" - .endif - - .iflt (\x) - .error "An unsigned value is required here" - .endif - - .ifgt ((\x) - (0xffffffffffffffff >> (64 - (\bits)))) - .error "\err" - .endif - - .endm - - // Check unsigned 16/22-bit immediates for size - // - // In general, PGAS can check immediate values for size restrictions, - // but unfortunately is not able to check address offset immediates for - // range. - - .macro ..check_u16, u16 - ..checku (\u16), 16, "Unsigned immediate is larger than 16 bits" - .endm - - .macro ..check_u24, u24 - ..checku (\u24), 24, "Unsigned immediate is larger than 24 bits" - .endm - - // Check a 16/20/22-bit signed immediate for size - - .macro ..check_s16, s16 - .iflt \s16 - .iflt \s16 + 0x8000 - .error "Immediate value too small for a signed 16-bit field" - .endif - .else - .ifgt \s16 - 0x7fff - .error "Immediate value too large for a signed 16-bit field" - .endif - .endif - .endm - - .macro ..check_s20, s20 - .iflt \s20 - .iflt \s20 + 0x80000 - .error "Immediate value too small for a signed 20-bit field" - .endif - .else - .ifgt \s20 - 0x7ffff - .error "Immediate value too large for a signed 20-bit field" - .endif - .endif - .endm - - .macro ..check_s22, s22 - .iflt \s22 - .iflt \s22 + 0x200000 - .error "Immediate value too small for a signed 22-bit field" - .endif - .else - .ifgt \s22 - 0x1fffff - .error "Immediate value too large for a signed 22-bit field" - .endif - .endif - .endm - - // Check a putative SCOM address for bits 0 and 8:11 == 0. - - .macro ..check_scom, address - .if ((\address) & 0x80f00000) - .error "Valid SCOM addresses must have bits 0 and 8:11 equal to 0." - .endif - .endm - - // A register required to be D0 - - .macro ..d0, reg - .if (\reg != D0) - .error "Data register D0 is required here" - .endif - .endm - - // A register pair required to be D0, D1 in order - - .macro ..d0d1, reg1, reg2 - .if (((\reg1) != D0) && ((\reg2) != D1)) - .error "Register-Register ALU operations are only defined on the source pair D0, D1" - .endif - .endm - - // A register pair required to be D0, D1 in any order - .macro ..dxdy, reg1, reg2, err="Expecting D0, D1 in either order" - .if !((((\reg1) == D0) && ((\reg2) == D1)) || \ - (((\reg1) == D1) && ((\reg2) == D0))) - .error "\err" - .endif - .endm - - // A register pair required to be A0, A1 in any order - .macro ..axay, reg1, reg2, err="Expecting A0, A1 in either order" - .if !((((\reg1) == A0) && ((\reg2) == A1)) || \ - (((\reg1) == A1) && ((\reg2) == A0))) - .error "\err" - .endif - .endm - - // A register pair required to be the same register - - .macro ..same, dest, src - .if ((\dest) != (\src)) - .error "PGAS requires the src and dest register of ADDS/SUBS to be explicit and identical" - .endif - .endm - - // A "Data" register - - .macro ..data, reg:req, err="Expecting a 'Data' register" - .if (\reg != D0) - .if (\reg != D1) - .error "\err" - .endif - .endif - .endm - - // An "Address" register - - .macro ..address, reg:req, err=:"Expecting an 'Address' register" - .if (\reg != A0) - .if (\reg != A1) - .error "\err" - .endif - .endif - .endm - - // A "Pervasive Chiplet ID" register - - .macro ..pervasive_chiplet_id, reg:req, err="Expecting a 'Pervasive Chiplet ID' register" - .if (\reg != P0) - .if (\reg != P1) - .error "\err" - .endif - .endif - .endm - - // A "Branch Compare Data" register - - .macro ..branch_compare_data, reg - .if (\reg != D0) - .if (\reg != D1) - .if (\reg != CTR) - .error "Expecting a 'Branch Compare Data' register" - .endif - .endif - .endif - .endm - - // An "LS Destination" register; Also the set for ADDS/SUBS - - .macro ..ls_destination, reg - .if (\reg != D0) - .if (\reg != D1) - .if (\reg != A0) - .if (\reg != A1) - .if (\reg != P0) - .if (\reg != P1) - .if (\reg != CTR) - .error "Expecting an 'LS Destination' register" - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endm - - // An "LI Destination" register - - .macro ..li_destination, reg - .if (\reg != D0) - .if (\reg != D1) - .if (\reg != A0) - .if (\reg != A1) - .if (\reg != CTR) - .error "Expecting an 'LI Destination' register" - .endif - .endif - .endif - .endif - .endif - .endm - - // An "LIA Destination" register - - .macro ..lia_destination, reg - .if (\reg != D0) - .if (\reg != D1) - .if (\reg != A0) - .if (\reg != A1) - .if (\reg != TBAR) - .error "Expecting an 'LIA Destination' register" - .endif - .endif - .endif - .endif - .endif - .endm - - // An "MR Source" register - - .macro ..mr_source, reg - .if (\reg != D0) - .if (\reg != D1) - .if (\reg != A0) - .if (\reg != A1) - .if (\reg != P0) - .if (\reg != P1) - .if (\reg != CTR) - .if (\reg != PC) - .if (\reg != ETR) - .if (\reg != SPRG0) - .if (\reg != IFR) - .if (\reg != EMR) - .error "Expecting an 'MR Source' register" - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endm - - // An "MR Destination" register - - .macro ..mr_destination, reg - .if (\reg != D0) - .if (\reg != D1) - .if (\reg != A0) - .if (\reg != A1) - .if (\reg != P0) - .if (\reg != P1) - .if (\reg != CTR) - .if (\reg != PC) - .if (\reg != ETR) - .if (\reg != SPRG0) - .if (\reg != EMR) - .error "Expecting an 'MR Destination' register" - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endif - .endm - - - ////////////////////////////////////////////////////////////////////// - // PORE address spaces - ////////////////////////////////////////////////////////////////////// - - // The ..set_address_space pseudo-op defines the default address - // space. It must be defined in order to use BRAA, BRAIA, BSR and - // CMPIBSR. Pseudo-ops are provided to set the default space of the - // program. Note that code assembled for PNOR will also work in the - // OCI space in the Sleep/Winkle engine. - - .macro ..set_default_space, s - ..check_u16 (\s) - .set _PGAS_DEFAULT_SPACE, (\s) - .endm - - .macro ..check_default_space - .if (_PGAS_DEFAULT_SPACE == PORE_SPACE_UNDEFINED) - .error "The PGAS default address space has not been defined" - .endif - .endm - - ..set_default_space PORE_SPACE_UNDEFINED - - .macro .oci - ..set_default_space PORE_SPACE_OCI - .endm - - .macro .pnor - ..set_default_space PORE_SPACE_PNOR - .endm - - .macro .seeprom - ..set_default_space PORE_SPACE_SEEPROM - .endm - - .macro .otprom - ..set_default_space PORE_SPACE_OTPROM - .endm - - .macro .pibmem - ..set_default_space PORE_SPACE_PIBMEM -#ifndef PGAS_PPC - .pibmem_port (PORE_SPACE_PIBMEM & 0xf) -#else - // NB: PGAS_PPC does not support relocatable PIBMEM addressing -#endif - .endm - - - ////////////////////////////////////////////////////////////////////// - // Address-Generation Pseudo Ops - ////////////////////////////////////////////////////////////////////// - - // .QUADA, .QUADIA - - .macro .quada, offset:req - ..check_default_space - .long _PGAS_DEFAULT_SPACE - .long (\offset) - .endm - - .macro .quadia, space:req, offset:req - ..check_u16 (\space) - .long (\space) - .long (\offset) - .endm - - ////////////////////////////////////////////////////////////////////// - // Bug workarounds - ////////////////////////////////////////////////////////////////////// - -#ifndef IGNORE_HW274735 - - // HW274735 documents that BC and BS are broken for the PORE-GPE0/1 - // pair. This bug is unfixed in POWER8, and by default we require BSI - // and BCI to be implemented as macros on all engines. For - // compatability we continue to require that dx == D0. - - .macro bsi, dx:req, offset:req, base:req, imm:req - ..d0 (\dx) - ld D0, (\offset), (\base) - ori D0, D0, (\imm) - std D0, (\offset), (\base) - .endm - - .macro bci, dx:req, offset:req, base:req, imm:req - ..d0 (\dx) - ldandi D0, (\offset), (\base), ~(\imm) - std D0, (\offset), (\base) - .endm - -#endif // IGNORE_HW274735 - - ////////////////////////////////////////////////////////////////////// - // "A"- and "IA"-form Instructions - ////////////////////////////////////////////////////////////////////// - - // BRAA (Branch Address) is a 'long branch' to an address in the - // default memory space. - - .macro braa, offset:req - braia _PGAS_DEFAULT_SPACE, (\offset) - .endm - - // LA (Load Address) loads the full address of an address in the - // default memory space. - - .macro la, dest:req, offset:req - lia (\dest), _PGAS_DEFAULT_SPACE, (\offset) - .endm - - // STA (Store Address) stores the full address of an address in the - // default memory space. - - .macro sta, mem_offset:req, base:req, addr_offset:req - stia (\mem_offset), (\base), _PGAS_DEFAULT_SPACE, (\addr_offset) - .endm - - // BSRIA is a subroutine branch into another memory space. This has to - // be emulated by a local subroutine branch and a BRAIA. - - .macro bsria, space:req, offset:req - bsr 27742f - bra 27743f -27742: - braia (\space), (\offset) -27743: - .endm - - -//////////////////////////////////////////////////////////////////////////// -// Extended Mnemonics, Macros and Special Cases -//////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////// - // TFB<c> - Test flags and branch conditionally - //////////////////////////////////////////////////////////////////////' - - .macro ..tfb, dest, target, flags - ..data (\dest) - mr (\dest), IFR - andi (\dest), (\dest), (\flags) - branz (\dest), (\target) - .endm - - .macro ..tfbn dest, target, flags - ..data (\dest) - mr (\dest), IFR - andi (\dest), (\dest), (\flags) - braz (\dest), (\target) - .endm - - .macro tfbcs, dest:req, target:req - ..tfb (\dest), (\target), CC_C - .endm - - .macro tfbcc, dest:req, target:req - ..tfbn (\dest), (\target), CC_C - .endm - - .macro tfbvs, dest:req, target:req - ..tfb (\dest), (\target), CC_V - .endm - - .macro tfbvc, dest:req, target:req - ..tfbn (\dest), (\target), CC_V - .endm - - .macro tfbns, dest:req, target:req - ..tfb (\dest), (\target), CC_N - .endm - - .macro tfbnc, dest:req, target:req - ..tfbn (\dest), (\target), CC_N - .endm - - .macro tfbeq, dest:req, target:req - ..tfb (\dest), (\target), CC_Z - .endm - - .macro tfbne, dest:req, target:req - ..tfbn (\dest), (\target), CC_Z - .endm - - .macro tfbult, dest:req, target:req - ..tfb (\dest), (\target), CC_ULT - .endm - - .macro tfbule, dest:req, target:req - ..tfbn (\dest), (\target), CC_UGT - .endm - - .macro tfbuge, dest:req, target:req - ..tfbn (\dest), (\target), CC_ULT - .endm - - .macro tfbugt, dest:req, target:req - ..tfb (\dest), (\target), CC_UGT - .endm - - .macro tfbslt, dest:req, target:req - ..tfb (\dest), (\target), CC_SLT - .endm - - .macro tfbsle, dest:req, target:req - ..tfbn (\dest), (\target), CC_SGT - .endm - - .macro tfbsge, dest:req, target:req - ..tfbn (\dest), (\target), CC_SLT - .endm - - .macro tfbsgt, dest:req, target:req - ..tfb (\dest), (\target), CC_SGT - .endm - - - ////////////////////////////////////////////////////////////////////// - // TEB[N]<eng> - Test Engine and branch if [not] engine. - ////////////////////////////////////////////////////////////////////// - // - // All but GPE0 use a 1-hot code. - - .macro tebgpe0, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), 0xf - braz (\dest), (\target) - .endm - - .macro tebgpe1, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), PORE_ID_GPE1 - branz (\dest), (\target) - .endm - - .macro tebslw, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), PORE_ID_SLW - branz (\dest), (\target) - .endm - - .macro tebsbe, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), PORE_ID_SBE - branz (\dest), (\target) - .endm - - - .macro tebngpe0, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), 0xf - branz (\dest), (\target) - .endm - - .macro tebngpe1, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), PORE_ID_GPE1 - braz (\dest), (\target) - .endm - - .macro tebnslw, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), PORE_ID_SLW - braz (\dest), (\target) - .endm - - .macro tebnsbe, dest:req, target:req - mr (\dest), IFR - andi (\dest), (\dest), PORE_ID_SBE - braz (\dest), (\target) - .endm - - - ////////////////////////////////////////////////////////////////////// - // EXTRPRC - Extract and right-justify the PIB/PCB return code - // TPRCB[N]Z - Test PIB return code and branch if [not] zero - // TPRCBGT - Test PIB return code and branch if greater-than - // TPRCBLE - Test PIB return code and branch if less-then or equal - ////////////////////////////////////////////////////////////////////// - // - // To support cases where PORE code expects or must explicitly handle - // non-0 PIB return codes, the PIB return code and parity indication - // are stored in bits 32 (parity) and 33-35 (return code) of the IFR. - // These macros extract the four PIB/PCB status bits from the IFR and - // right-justifies them into the data register provided. For EXTRPRC - // that is the total function of the macro. The TPRCB[N]Z macros - // provide a simple non-destructive test and branch for zero (success) - // and non-zero (potential problem) codes after the extraction. - // - // In complex error handling scenarios one would typically compare the - // PIB return code against an upper-bound, e.g., the offline response - // (0x2), and then take further action. If the parity error bit is set - // then this would produce an aggregate "return code" higher than any - // that one would typically want to ignore. The TPRCBGT/TPRCBLE macros - // provide this function; however the test destroys the extracted - // return code so that if further analysis is required the code will - // need to be a extracted again. - ////////////////////////////////////////////////////////////////////// - - .macro extrprc, dest:req - ..data (\dest) - mr (\dest), IFR - extrdi (\dest), (\dest), 4, 32 - .endm - - .macro tprcbz, dest:req, target:req - extrprc (\dest) - braz (\dest), (\target) - .endm - - .macro tprcbnz, dest:req, target:req - extrprc (\dest) - branz (\dest), (\target) - .endm - - .macro tprcbgt, dest:req, target:req, bound:req - extrprc (\dest) - subs (\dest), (\dest), (\bound) - tfbugt (\dest), (\target) - .endm - - .macro tprcble, dest:req, target:req, bound:req - extrprc (\dest) - subs (\dest), (\dest), (\bound) - tfbule (\dest), (\target) - .endm - - ////////////////////////////////////////////////////////////////////// - // LPCS - Load Pervasive Chiplet from Scom address - ////////////////////////////////////////////////////////////////////// - - .macro lpcs, dest:req, scom:req - ..pervasive_chiplet_id (\dest) - ..check_scom (\scom) - ls (\dest), (((\scom) >> 24) & 0x7f) - .endm - - - ////////////////////////////////////////////////////////////////////// - // Shift/Mask extended mnemonics - ////////////////////////////////////////////////////////////////////// - - // All of the 'dot-dot' macros assume that error and identity - // checking has been done on the arguments already. - - // The initial register-register rotate. If the incoming shift amount - // is 0 then the instruction generated is a simple MR. - - .macro ..rotlrr, ra, rs, sh - - .if (\sh) >= 32 - rols (\ra), (\rs), 32 - ..rotlr (\ra), ((\sh) - 32) - .elseif (\sh) >= 16 - rols (\ra), (\rs), 16 - ..rotlr (\ra), ((\sh) - 16) - .elseif (\sh) >= 8 - rols (\ra), (\rs), 8 - ..rotlr (\ra), ((\sh) - 8) - .elseif (\sh) >= 4 - rols (\ra), (\rs), 4 - ..rotlr (\ra), ((\sh) - 4) - .elseif (\sh) >= 1 - rols (\ra), (\rs), 1 - ..rotlr (\ra), ((\sh) - 1) - .else - mr (\ra), (\rs) - .endif - - .endm - - - // Subsequent rotation of the same register. The SH should never be 0 - // here. - - .macro ..rotlr, ra, sh - - .if (\sh) >= 32 - rols (\ra), (\ra), 32 - ..rotlr (\ra), ((\sh) - 32) - .elseif (\sh) >= 16 - rols (\ra), (\ra), 16 - ..rotlr (\ra), ((\sh) - 16) - .elseif (\sh) >= 8 - rols (\ra), (\ra), 8 - ..rotlr (\ra), ((\sh) - 8) - .elseif (\sh) >= 4 - rols (\ra), (\ra), 4 - ..rotlr (\ra), ((\sh) - 4) - .elseif (\sh) >= 1 - rols (\ra), (\ra), 1 - ..rotlr (\ra), ((\sh) - 1) - - .endif - - .endm - - - // RLDINM RA, RS, SH, MB, ME - // - // Defined as if there were an equivalent PowerPC instruction. The - // 'word' forms of the PowerPC instructions and extended mnemonics are - // undefined in order to catch programming typos. - - .undefppc rlwinm, extrwi, rotlwi, rotrwi - .undefppc slwi, srwi - - .macro rldinm, ra:req, rs:req, sh:req, mb:req, me:req - - .if ((\sh) < 0) || ((\sh) > 63) - .error "SH must be in the range 0..63" - .endif - .if ((\mb) < 0) || ((\mb) > 63) - .error "MB must be in the range 0..63" - .endif - .if ((\me) < 0) || ((\me) > 63) - .error "ME must be in the range 0..63" - .endif - - .if (((\mb) == 0) && ((\me) == 63) || ((\me) == ((\mb) - 1))) - - // The mask is effectively 0..63, i.e., no mask. This is a - // simple rotate. - - ..rotlrr (\ra), (\rs), (\sh) - - .else - - // We need a mask step. However if SH == 0 and RA == RS we can - // bypass the rotate step. - - .if ((\sh) != 0) || ((\ra) != (\rs)) - ..rotlrr (\ra), (\rs), (\sh) - .endif - .if ((\mb) <= (\me)) - - // This is a straightforward masking operation with a - // single mask. - - andi (\ra), (\ra), ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - (\me)))) - .else - - // This is a wrapped mask. - // It is created as 2 masks OR-ed together - 0-ME and MB-63 - - andi (\ra), (\ra), (((0xffffffffffffffff >> 0) & (0xffffffffffffffff << (63 - (\me)))) | ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - 63)))) - .endif - - .endif - - .endm - - // RLDINM Extended Mnemonics - // - // Defined as if they were equivalent to PowerPC 32-bit extended - // mnemonics - - .macro extldi, ra:req, rs:req, n:req, b:req - .if ((\n) < 0) - .error "EXTLDI requires N > 0" - .endif - rldinm (\ra), (\rs), (\b), 0, ((\n) - 1) - .endm - - .macro extrdi, ra:req, rs:req, n:req, b:req - .if ((\n) < 0) - .error "EXTRDI requires N > 0" - .endif - rldinm (\ra), (\rs), (((\b) + (\n)) % 64), (64 - (\n)), 63 - .endm - - .macro rotldi, ra:req, rs:req, n:req - rldinm (\ra), (\rs), (\n), 0, 63 - .endm - - - .macro rotrdi, ra:req, rs:req, n:req - rldinm (\ra), (\rs), (64 - (\n)), 0, 63 - .endm - - - .macro sldi, ra:req, rs:req, n:req - rldinm (\ra), (\rs), (\n), 0, (63 - (\n)) - .endm - - - .macro srdi, ra:req, rs:req, n:req - rldinm (\ra), (\rs), (64 - (\n)), (\n), 63 - .endm - - - // RLDIMI RA, RS, SH, MB, ME - // - // Defined as if there were an equivalent PowerPC instruction. The - // 'word' forms of the PowerPC instructions and extended mnemonics are - // undefined in order to catch programming typos. - // - // Note that unlike the PowerPC instructions, here RLDIMI must destroy - // RS by masking and shifting it, and RA and RS may not be the same - // register. - - .undefppc rlwimi, inslwi, insrwi - - .macro rldimi, ra:req, rs:req, sh:req, mb:req, me:req - - ..dxdy (\ra), (\rs) - - // SH error checks are done by rldinm - - .if (((\mb) == 0) && ((\me) == 63) || ((\me) == ((\mb) - 1))) - - // The mask is effectively 0..63, i.e., no mask. This is a - // simple rotate of RS into RA - - rotldi (\ra), (\rs), (\sh) - - .else - - // Rotate RS and AND with mask - - rldinm (\rs), (\rs), (\sh), (\mb), (\me) - - // Mask out the significant bits of RS, clear that section of - // RA, and logical OR RS into RA - - .if ((\mb) <= (\me)) - - // This is a straightforward masking operation with a - // single mask. - - andi (\ra), (\ra), \ - (~((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - (\me))))) - .else - - // This is a wrapped mask. - // It is created as 2 masks OR-ed together - 0-ME and MB-63 - - andi (\ra), (\ra), \ - (~(((0xffffffffffffffff >> 0) & (0xffffffffffffffff << (63 - (\me)))) | \ - ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - 63))))) - .endif - - or (\ra), D0, D1 - - .endif - - .endm - - // RLDIMI Extended Mnemonics - // - // Defined as if they were equivalent to PowerPC 32-bit extended - // mnemonics - - .macro insldi, ra:req, rs:req, n:req, b:req - .if ((\n) < 0) - .error "INSLDI requires N > 0" - .endif - rldimi (\ra), (\rs), (64 - (\b)), (\b), ((\b) + (\n) - 1) - .endm - - .macro insrdi, ra:req, rs:req, n:req, b:req - .if ((\n) < 0) - .error "INSRDI requires N > 0" - .endif - rldimi (\ra), (\rs), (64 - (\b) - (\n)), (\b), ((\b) + (\n) - 1) - .endm - - - ////////////////////////////////////////////////////////////////////// - // .HOOK - ////////////////////////////////////////////////////////////////////// - - // The PoreVe (PORE Virtual Environment) is a PORE simulation - // environment that allows the programmer to embed C/C++ code into the - // PORE assembler source code, and arranges for the C/C++ code to be - // executed in-line with the PORE assembly code. Instances of the - // .hook macro are inserted into the assembler input by the - // hook_extractor script, to mark the locations where hooks are - // present. The hook reference is a string that combines the source - // file name with an index number to uniquely identify the hook. - // - // .hook <file name>_<sequence number> - // - // The .hook macro marks the location of each hook in the relocatable - // binaries with special symbols. The symbol name includes the hook - // reference, which is used to locate the hook in the HookManager - // symbol table. Because hooks can be defined in macros, a hook that - // appears once in a source file may appear multiple times in the - // final binary. For this reason each hook must also be tagged with a - // unique index number to avoid symbol name collisions. The - // complexity of the .hook macro is due to the necessity to decode a - // dynamic symbol value (_PGAS_HOOK_INDEX) into its binary string form - // to create the unique symbol name. The final hook symbol has the - // form: - // - // __hook__<unique>_<reference> - // - // where <unique> is a binary string. It is then straightforward to - // locate these symbols in the 'nm' output of the final link and - // create a map of final addresses to the hook routine to call (the - // <reference>) before executing the instruction at that address. - // - // Note: The maximum nesting depth of the recursive ..hook_helper - // macro is log2(index), and the assembler supports nesting of at - // least 32 which is much more than sufficient. - - .set _PGAS_HOOK_INDEX, 0 - - .macro .hook, reference:req - .set _PGAS_HOOK_INDEX, (_PGAS_HOOK_INDEX + 1) - ..hook_helper _PGAS_HOOK_INDEX, "", \reference - .endm - - .macro ..hook_helper, index, unique, reference - .ifeq \index - __hook__\unique\()_\reference\(): - .elseif (\index % 2) - ..hook_helper (\index / 2), 1\unique, \reference - .else - ..hook_helper (\index / 2), 0\unique, \reference - .endif - .endm - - -//////////////////////////////////////////////////////////////////////////// -// Help for Conversion from Old to New PGAS syntax -//////////////////////////////////////////////////////////////////////////// - - .macro loadp, arg:vararg - .error "PGAS now implements 'lpcs' rather then 'loadp'" - .endm - - .macro loadx, arg:vararg - .error "PGAS now implements 'la' rather than 'loadx'" - .endm - -#endif // __ASSEMBLER__ - -#ifdef PGAS_PPC -#include "pgas_ppc.h" -#endif - -#endif // __PGAS_H__ diff --git a/src/lib/pgas_ppc.h b/src/lib/pgas_ppc.h deleted file mode 100755 index 6771e4b..0000000 --- a/src/lib/pgas_ppc.h +++ /dev/null @@ -1,529 +0,0 @@ -#ifndef __PGAS_PPC_H__ -#define __PGAS_PPC_H__ - -// $Id: pgas_ppc.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pgas_ppc.h,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -// ** WARNING : This file is maintained as part of the OCC firmware. Do ** -// ** not edit this file in the PMX area, the hardware procedure area, ** -// ** or the PoreVe area as any changes will be lost. ** - -/// \file pgas_ppc.h -/// \brief Legacy PGAS assembler implemented as PowerPC assembler macros. -/// -/// PGAS is documented in a seperate standalone document entitled <em> PGAS : -/// PORE GAS (GNU Assembler) User's and Reference Manual </em>. -/// -/// This file contains the legacy PGAS assembler, which was first implemented -/// as this set of assembler macros for the PowerPC assembler. This file is -/// included into pgas.h if the compile switch PGAS_PPC is defined in the -/// compile environment. - -#ifdef __ASSEMBLER__ - -//////////////////////////////////////////////////////////////////////////// -// PGAS Base Assembler -//////////////////////////////////////////////////////////////////////////// - - - ////////////////////////////////////////////////////////////////////// - // Symbolic Register Mnemonics - ////////////////////////////////////////////////////////////////////// - // - // PGAS uses gas symbols for register mnemonics so that they will - // appear as-is in assembler listings, but we can still do arithmetic - // on the mnemonics in the PGAS macros. - - .set P0, PORE_REGISTER_PRV_BASE_ADDR0 - .set P1, PORE_REGISTER_PRV_BASE_ADDR1 - .set A0, PORE_REGISTER_OCI_BASE_ADDR0 - .set A1, PORE_REGISTER_OCI_BASE_ADDR1 - .set CTR, PORE_REGISTER_SCRATCH0 - .set D0, PORE_REGISTER_SCRATCH1 - .set D1, PORE_REGISTER_SCRATCH2 - .set EMR, PORE_REGISTER_ERROR_MASK - .set ETR, PORE_REGISTER_EXE_TRIGGER - .set SPRG0, PORE_REGISTER_DATA0 - .set PC, PORE_REGISTER_PC - .set IFR, PORE_REGISTER_IBUF_ID - - - ////////////////////////////////////////////////////////////////////// - // Core Instruction Set - ////////////////////////////////////////////////////////////////////// - - // The final construction of an instruction word. The opcode is a - // 7-bit value and the operand is always a 24-bit value. Note that the - // parity bit is always 0. - - .macro ..instruction, opcode, operand - .long (\opcode << 25) | (\operand) - .endm - - // NOP, TRAP, RET - - .macro nop - ..instruction PGAS_OPCODE_NOP, 0 - .endm - - .macro trap - ..instruction PGAS_OPCODE_TRAP, 0 - .endm - - .macro ret - ..instruction PGAS_OPCODE_RET, 0 - .endm - - // WAITS, HALT, HOOKI - - .macro waits, u24:req - ..check_u24 (\u24) - .if ((\u24) == 0) - .error "PGAS does not allow WAITS 0; Use HALT if the intention is to halt" - .endif - ..instruction PGAS_OPCODE_WAITS, (\u24) - .endm - - .macro halt - ..instruction PGAS_OPCODE_WAITS, 0 - .endm - - .macro hooki, u24:req, imm:req - ..check_u24 (\u24) - ..instruction PGAS_OPCODE_HOOKI, (\u24) - .quad (\imm) - .endm - - .macro wait, args:vararg - .error "PGAS implements the 'waits' mnemonic instead of PORE 'wait'" - .endm - - .macro hook, args:vararg - .error "PGAS implements the 'hooki' mnemonic instead of PORE 'hook'" - .endm - - // BRA, LOOP - // - // Note that all branch offsets in PORE are WORD offsets, so the byte - // offsets computed by the underlying assembler need to be divided by - // 4. Unfortunately PGAS is not able to check whether the offsets fit - // in the allowed space. - - .macro ..bra, opcode, target - ..instruction \opcode, ((((\target) - $) / 4) & 0xffffff) - .endm - - .macro bra, target:req - ..bra PGAS_OPCODE_BRA, (\target) - .endm - - .macro loop, target:req - ..bra PGAS_OPCODE_LOOP, (\target) - .endm - - // BRAZ, BRANZ - - .macro ..brac, opcode, src, target - ..branch_compare_data (\src) - ..instruction \opcode, ((\src << 20) | ((((\target) - $) / 4) & 0xfffff)) - .endm - - .macro braz, src:req, target:req - ..brac PGAS_OPCODE_BRAZ, (\src), (\target) - .endm - - .macro branz, src:req, target:req - ..brac PGAS_OPCODE_BRANZ, (\src), (\target) - .endm - - // CMPIBRAEQ, CMPIBRANE - - .macro ..cmpibra, opcode, src, target, imm - ..d0 (\src) - ..instruction \opcode, ((((\target) - $) / 4) & 0xffffff) - .quad (\imm) - .endm - - .macro cmpibraeq, src:req, target:req, imm:req - ..cmpibra PGAS_OPCODE_CMPIBRAEQ, (\src), (\target), (\imm) - .endm - - .macro cmpibrane, src:req, target:req, imm:req - ..cmpibra PGAS_OPCODE_CMPIBRANE, (\src), (\target), (\imm) - .endm - - .macro cmpbra, args:vararg - .error "PGAS implements the 'cmpibraeq' mnemonic instead of PORE 'cmpbra'" - .endm - - .macro cmpnbra, args:vararg - .error "PGAS implements the 'cmpibrane' mnemonic instead of PORE 'cmpnbra'" - .endm - - // BRAD, BSRD - - .macro ..brad, opcode, src - ..data (\src) - ..instruction \opcode, ((\src) << 20) - .endm - - .macro brad, src:req - ..brad PGAS_OPCODE_BRAD, (\src) - .endm - - .macro bsrd, src:req - ..brad PGAS_OPCODE_BSRD, (\src) - .endm - - // ANDI, ORI, XORI - - .macro ..ilogic, opcode, dest, src, imm - ..data (\dest) - ..data (\src) - ..instruction \opcode, (((\dest) << 20) | ((\src) << 16)) - .quad \imm - .endm - - .macro andi, dest:req, src:req, imm:req - ..ilogic PGAS_OPCODE_ANDI, (\dest), (\src), (\imm) - .endm - - .macro ori, dest:req, src:req, imm:req - ..ilogic PGAS_OPCODE_ORI, (\dest), (\src), (\imm) - .endm - - .macro xori, dest:req, src:req, imm:req - ..ilogic PGAS_OPCODE_XORI, (\dest), (\src), (\imm) - .endm - - // AND, OR, XOR, ADD, SUB - - .macro ..alurr, opcode, dest, src1, src2 - ..data (\dest) - ..d0d1 (\src1), (\src2) - ..instruction \opcode, ((\dest) << 20) - .endm - - .macro and, dest:req, src1:req, src2:req - ..alurr PGAS_OPCODE_AND, (\dest), (\src1), (\src2) - .endm - - .macro or, dest:req, src1:req, src2:req - ..alurr PGAS_OPCODE_OR, (\dest), (\src1), (\src2) - .endm - - .macro xor, dest:req, src1:req, src2:req - ..alurr PGAS_OPCODE_XOR, (\dest), (\src1), (\src2) - .endm - - .macro add, dest:req, src1:req, src2:req - ..alurr PGAS_OPCODE_ADD, (\dest), (\src1), (\src2) - .endm - - .macro sub, dest:req, src1:req, src2:req - ..alurr PGAS_OPCODE_SUB, (\dest), (\src1), (\src2) - .endm - - // ADDS, SUBS - - .macro ..inc, opcode, dest, src, short - ..check_s16 (\short) - ..ls_destination (\dest) - ..same (\dest), (\src) - ..instruction (\opcode), (((\dest) << 20) | ((\short) & 0xffff)) - .endm - - .macro adds, dest:req, src:req, short:req - ..inc PGAS_OPCODE_ADDS, (\dest), (\src), (\short) - .endm - - .macro subs, dest:req, src:req, short:req - ..inc PGAS_OPCODE_SUBS, (\dest), (\src), (\short) - .endm - - .macro addi, args:vararg - .error "PGAS implements the 'adds' mnemonic instead of PORE 'addi'" - .endm - - .macro subi, args:vararg - .error "PGAS implements the 'subs' mnemonic instead of PORE 'subi'" - .endm - - // NEG - - .macro neg, dest:req, src:req - ..data (\dest) - ..data (\src) - ..instruction PGAS_OPCODE_NEG, (((\dest) << 20) | ((\src) << 16)) - .endm - - // MR - - .macro mr, dest:req, src:req - ..mr_destination (\dest) - ..mr_source (\src) - ..instruction PGAS_OPCODE_MR, (((\dest) << 20) | ((\src) << 16)) - .endm - - .macro copy, args:vararg - .error "PGAS implents the 'mr' mnemonic instead of PORE 'copy'" - .endm - - // ROLS - - .macro rols, dest:req, src:req, short:req - ..data (\dest) - ..data (\src) - .if ((\short) != 1) - .if ((\short) != 4) - .if ((\short) != 8) - .if ((\short) != 16) - .if ((\short) != 32) - .error "The legal ROLS shift amounts are 1, 4, 8, 16 and 32" - .endif - .endif - .endif - .endif - .endif - ..instruction PGAS_OPCODE_ROLS, (((\dest) << 20) | ((\src) << 16) | (\short)) - .endm - - .macro rol, args:vararg - .error "PGAS implements the 'rols' mnemonic instead of PORE 'rol'" - .endm - - // LS - - .macro ls, dest:req, short:req - ..ls_destination (\dest) - ..check_s20 (\short) - ..instruction PGAS_OPCODE_LS, (((\dest) << 20) | ((\short) & 0xfffff)) - .endm - - .macro load20, args:vararg - .error "PGAS implements the 'ls' mnemonic instead of PORE 'load20'" - .endm - - // LI, LIA - - .macro ..li, dest:req - ..li_destination (\dest) - ..instruction PGAS_OPCODE_LI, ((\dest) << 20) - .endm - - .macro li, dest:req, imm:req - ..li (\dest) - .quad (\imm) - .endm - - .macro lia, dest:req, space:req, offset:req - ..lia_destination (\dest) - ..li (\dest) - .quadia (\space), (\offset) - .endm - - .macro load64, args:vararg - .error "PGAS implements the 'li' mnemonic instead of PORE 'load64'" - .endm - - // LD, LDANDI, STD, STI, STIA, BSI, BCI - // - // For LD, LDANDI, and STD, PGAS does not expose the underlying - // register-specific opcodes but only provides the general form. - // - // The base register is used to determine if this is a load/store from - // the pervasive or memory address spaces. For memory space accesses - // the offset is a 22-bit unsigned value, and the final ima24 is - // - // 1<reg 0/1><offset> - // - // PGAS will not assemble relocatable offsets, and checks that offsets - // fit in 24 bits. - // - // For pervasive accesses, it is assumed that the offset provided is a - // 32-bit SCOM address. Here the final ima24 is - // - // 0<reg 0/1>00<port><local_address> - // - // PGAS checks that the 32-bit SCOM address looks like a SCOM address - // in that SCOM adresses are required to have bits 0 and 8:11 == 0. - // - // Note that memory and pervasive base registers use a 0/1 encoding - // here, not the 4-bit encoding used elsewhere in the ISA. The bit - // appearing in the instruction is the low-order bit of the register - // encoding. - - .macro ..pervasive_ima24, opcode, offset, base - ..check_scom (\offset) - ..instruction (\opcode), ((((\base) % 2) << 22) | ((\offset) & 0x3fffff)) - .endm - - .macro ..memory_ima24, opcode, offset, base - ..check_u24 (\offset) - .if ((\offset) % 8) - .error "The memory space offset is not a multiple of 8 - assumed alignment error" - .endif - ..instruction (\opcode), (0x800000 | (((\base) % 2) << 22) | ((\offset) & 0x3fffff)) - .endm - - .macro ..ima24, opcode, offset, base - .if ((\base == P0) || ((\base == P1))) - ..pervasive_ima24 (\opcode), (\offset), (\base) - .elseif ((\base == A0) || ((\base == A1))) - ..memory_ima24 (\opcode), (\offset), (\base) - .else - .error "Expecting either a 'Pervasive Chiplet ID' or an 'Address' register" - .endif - .endm - - .macro ..ima24_select, opcode0, opcode1, dest, offset, base - ..data (\dest) - .if ((\dest) == D0) - ..ima24 (\opcode0), (\offset), (\base) - .else - ..ima24 (\opcode1), (\offset), (\base) - .endif - .endm - - .macro ld, dest:req, offset:req, base:req - ..ima24_select PGAS_OPCODE_LD0, PGAS_OPCODE_LD1, (\dest), (\offset), (\base) - .endm - - .macro ldandi, dest:req, offset:req, base:req, imm:req - ..ima24_select PGAS_OPCODE_LD0ANDI, PGAS_OPCODE_LD1ANDI, (\dest), (\offset), (\base) - .quad (\imm) - .endm - - .macro std, dest:req, offset:req, base:req - ..ima24_select PGAS_OPCODE_STD0, PGAS_OPCODE_STD1, (\dest), (\offset), (\base) - .endm - - .macro sti, offset:req, base:req, imm:req - ..ima24 PGAS_OPCODE_STI, (\offset), (\base) - .quad (\imm) - .endm - - .macro stia, offset:req, base:req, space:req, addr:req - ..ima24 PGAS_OPCODE_STI, (\offset), (\base) - .quadia (\space), (\addr) - .endm - - .macro ..bsi, opcode, dest, offset, base, imm - ..d0 (\dest) - ..ima24 (\opcode), (\offset), (\base) - .quad (\imm) - .endm - -#ifdef IGNORE_HW274735 - - // BSI and BCI are normally redacted due to HW274735. See also pgas.h - - .macro bsi, dest:req, offset:req, base:req, imm:req - ..bsi PGAS_OPCODE_BSI, (\dest), (\offset), (\base), (\imm) - .endm - - .macro bci, dest:req, offset:req, base:req, imm:req - ..bsi PGAS_OPCODE_BCI, (\dest), (\offset), (\base), (\imm) - .endm - -#endif // IGNORE_HW274735 - - .macro scr1rd, args:vararg - .error "PGAS implements the 'ld' mnemonic instead of the PORE 'scr1rd'" - .endm - - .macro scr2rd, args:vararg - .error "PGAS implements the 'ld' mnemonic instead of the PORE 'scr2rd'" - .endm - - .macro scr1rda, args:vararg - .error "PGAS implements the 'ldandi' mnemonic instead of the PORE 'scr1rda'" - .endm - - .macro scr2rda, args:vararg - .error "PGAS implements the 'ldandi' mnemonic instead of the PORE 'scr2rda'" - .endm - - .macro scr1wr, args:vararg - .error "PGAS implements the 'std' mnemonic instead of the PORE 'scr1wr'" - .endm - - .macro scr2wr, args:vararg - .error "PGAS implements the 'std' mnemonic instead of the PORE 'scr2wr'" - .endm - - .macro wri, args:vararg - .error "PGAS implements the 'sti' mnemonic instead of the PORE 'wri'" - .endm - - .macro bs, args:vararg - .error "PGAS implements the 'bsi' mnemonic instead of the PORE 'bs'" - .endm - - .macro bc, args:vararg - .error "PGAS implements the 'bci' mnemonic instead of the PORE 'bc'" - .endm - - // SCAND - // - // The 24-bit operand here is - // - // <update><capture>000000<length> - - .macro scand, update:req, capture:req, length:req, select:req, offset:req - .if (((\update) != 0) && ((\update) != 1)) - .error "SCAND requires a binary value for 'update'" - .endif - .if (((\capture) != 0) && ((\capture) != 1)) - .error "SCAND requires a binary value for 'capture'" - .endif - ..check_u16 (\length) - ..instruction PGAS_OPCODE_SCAND, ((\update << 23) | (\capture << 22) | (\length)) - .long (\select) - .long (\offset) - .endm - - // BRAIA, BSR, CMPIBSREQ - // - // In order to support separate compilation in PGAS programs being - // linked with the PowerPC linker it is necessary to implement BSR and - // CMPIBSREQ in terms of BRAIA. These instructions require that the - // default address space have been defined. The BSR instructions - // first take a short local subroutine branch to create a stack frame, - // then use BRAIA to branch to the (relocatable) target address. The - // return from the subroutine then branches around the BRAIA to - // complete the sequence. - - .macro braia, space:req, offset:req - ..instruction PGAS_OPCODE_BRAI, 0 - .quadia (\space), (\offset) - .endm - - .macro ..bsr, target - ..bra PGAS_OPCODE_BSR, (\target) - .endm - - .macro bsr, target:req - ..check_default_space - ..bsr (. + 8) - bra (. + 16) - braia _PGAS_DEFAULT_SPACE, (\target) - .endm - - .macro cmpibsreq, src:req, target:req, imm:req - ..d0 (\src) - ..check_default_space - cmpibrane (\src), (. + 32), (\imm) - ..bsr (. + 8) - bra (. + 16) - braia _PGAS_DEFAULT_SPACE, (\target) - .endm - -#endif // __ASSEMBLER__ - -#endif // __PGAS_PPC_H__ diff --git a/src/lib/pgp_config.h b/src/lib/pgp_config.h deleted file mode 100755 index f4041d4..0000000 --- a/src/lib/pgp_config.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef __PGP_CONFIG_H__ -#define __PGP_CONFIG_H__ - -// $Id: pgp_config.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pgp_config.h,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pgp_config.h -/// \brief Chip configuration data structures for PgP OCC procedures - -#ifndef __ASSEMBLER__ - -#include <stdint.h> - -/// A bitmask defining a chip configuration -/// -/// Since we are using the conventional big-endian notation, any use of these -/// bitmasks requires that the data being tested is of this type - otherwise -/// the masks won't work. -/// -/// Layout: -/// -/// Bits 0:15 - Core chiplet 0..15 is configured -/// Bits 16:23 - MCS 0..7 is configured -/// Bits 24:31 - Centaur 0..7 is configured - -typedef uint64_t ChipConfig; -typedef uint16_t ChipConfigCores; -typedef uint8_t ChipConfigMcs; -typedef uint8_t ChipConfigCentaur; - - -/// Convert a ChipConfig into a mask suitable for use as the 32-bit chiplet -/// mask argument of a PORE wakeup program. - -static inline uint32_t -pore_exe_mask(ChipConfig config) -{ - return (uint32_t)((config >> 32) & 0xffff0000); -} - -/// Left justify and mask core chiplet configuration into a uint32_t - -static inline uint32_t -left_justify_core_config(ChipConfig config) -{ - return (uint32_t)((config >> 32) & 0xffff0000); -} - -/// Left justify and mask MCS configuration into a uint32_t - -static inline uint32_t -left_justify_mcs_config(ChipConfig config) -{ - return (uint32_t)((config >> 16) & 0xff000000); -} - -/// Left justify and mask Centaur configuration into a uint32_t - -static inline uint32_t -left_justify_centaur_config(ChipConfig config) -{ - return (uint32_t)((config >> 8) & 0xff000000); -} - -#endif // __ASSEMBLER__ - - -#define CHIP_CONFIG_CORE_BASE 0 -#define CHIP_CONFIG_CORE(n) \ - ((0x8000000000000000ull >> CHIP_CONFIG_CORE_BASE) >> (n)) - -#define CHIP_CONFIG_MCS_BASE 16 -#define CHIP_CONFIG_MCS(n) \ - ((0x8000000000000000ull >> CHIP_CONFIG_MCS_BASE) >> (n)) - -#define CHIP_CONFIG_CENTAUR_BASE 24 -#define CHIP_CONFIG_CENTAUR(n) \ - ((0x8000000000000000ull >> CHIP_CONFIG_CENTAUR_BASE) >> (n)) - - -// PGAS macros to left justify configuration groups, allowing each member to -// be tested in a loop that rotates the data (d) register left on each loop, -// assuming standard big-endian bit assignments. The macros mask off all other -// configuration bits so the destination register can also be tested for -// 0/non-0 to determine if any of a configuration class are selected. - -#ifdef __PGAS__ - - .macro left_justify_core_config, d - extldi (\d), (\d), PGP_NCORES, CHIP_CONFIG_CORE_BASE - .endm - - .macro left_justify_mcs_config, d - extldi (\d), (\d), PGP_NMCS, CHIP_CONFIG_MCS_BASE - .endm - - .macro left_justify_centaur_config, d - extldi (\d), (\d), PGP_NCENTAUR, CHIP_CONFIG_CENTAUR_BASE - .endm - -#endif /* __PGAS__ */ - -#endif /* __PGP_CONFIG_H__ */ diff --git a/src/lib/pmc_dcm.c b/src/lib/pmc_dcm.c deleted file mode 100755 index a0f7d82..0000000 --- a/src/lib/pmc_dcm.c +++ /dev/null @@ -1,425 +0,0 @@ -// $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 deleted file mode 100755 index 3141aec..0000000 --- a/src/lib/pmc_dcm.h +++ /dev/null @@ -1,102 +0,0 @@ -#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 deleted file mode 100644 index 42e9fef..0000000 --- a/src/lib/polling.c +++ /dev/null @@ -1,73 +0,0 @@ -// $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/pore_hooks.h b/src/lib/pore_hooks.h deleted file mode 100755 index f278acb..0000000 --- a/src/lib/pore_hooks.h +++ /dev/null @@ -1,171 +0,0 @@ -#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/ppc405lib/Makefile b/src/lib/ppc405lib/Makefile new file mode 100644 index 0000000..37ef2ea --- /dev/null +++ b/src/lib/ppc405lib/Makefile @@ -0,0 +1,57 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/ppc405lib/Makefile $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile currently builds a single archive, 'libppc405.a', from +# various library source files. +# +# part of the complete application build. +# + +#all generated files from this makefile will end up in obj/$(IMAGE_NAME)/ppc405lib +export SUB_OBJDIR = /ppc405lib + +include img_defs.mk +include libppc405files.mk + +OBJS := $(addprefix $(OBJDIR)/, $(LIBPPC405_OBJECTS)) + +libppc405.a: local + $(AR) crs $(OBJDIR)/libppc405.a $(OBJDIR)/*.o + +.PHONY: clean + +local: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +clean: + rm -fr $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +#include $(OBJS:.o=.d) +endif + diff --git a/src/lib/ppc405lib/README.txt b/src/lib/ppc405lib/README.txt new file mode 100644 index 0000000..cc0076f --- /dev/null +++ b/src/lib/ppc405lib/README.txt @@ -0,0 +1,4 @@ +This directory contains all of the library code that only can run on the ppc405. +For most of the files, the only reason it can not run on the ppe42 is because of +it's dependence on SSX. Eventually, we would like to fix this so that most, if +not all files can be made common. diff --git a/src/lib/ppc405lib/assert.c b/src/lib/ppc405lib/assert.c new file mode 100644 index 0000000..96143a4 --- /dev/null +++ b/src/lib/ppc405lib/assert.c @@ -0,0 +1,71 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/assert.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: assert.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/assert.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file assert.c +/// \brief Implementation of library routines implied by <assert.h> + +#include "ssx.h" +#include "ssx_io.h" +#include "libssx.h" + +/// The __assert_fail() function is used to implement the assert() interface +/// of ISO POSIX (2003). The __assert_fail() function prints the given \a +/// file filename, \a line line number, \a function function name and a +/// message on the standard error stream then causes a kernel panic. If there +/// is no standard error stream then the error message is printed on the \a +/// ssxout (printk()) stream. +/// +/// If function is NULL, __assert_fail() omits information about the +/// function. The aguments \a assertion, \a file, and \a line must be +/// non-NULL. + +void +__assert_fail(const char *assertion, + const char *file, + unsigned line, + const char *function) +{ + FILE *stream; + + stream = stderr; + if (stream == 0) { + stream = ssxout; + } + + fprintf(stream, "%s:%u:%s%s Assertion '%s' failed\n", + file, line, + function ? function : "", function ? ":" : "", + assertion); + + SSX_PANIC(ASSERTION_FAILURE); +} + diff --git a/src/lib/ppc405lib/byte_pool.c b/src/lib/ppc405lib/byte_pool.c new file mode 100644 index 0000000..d382abc --- /dev/null +++ b/src/lib/ppc405lib/byte_pool.c @@ -0,0 +1,1442 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/byte_pool.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file byte_pool.c +/// \brief An implementation of a constant-time malloc() algorithm. +/// +/// The 'byte-pool' API defined in this file is similar to ThreadX' byte-pool +/// operations, with the major difference that here there is no concept of a +/// thread blocking on memory allocation. This is a concept that is difficult +/// to implement correctly and efficiently, and several semantic options +/// exist for how allocation and freeing memory should work in the presence of +/// blocked threads. The application is always free to implement a blocking +/// API using these APIs and SSX synchronization primitives. +/// +/// For convenience this implementation also provides malloc() and calloc() +/// calls. These APIs depend on the application creating a byte pool and +/// assigning it to the library variable \a _malloc_byte_pool. +/// +/// \todo Consider separating the idea of creating a byte pool with a specific +/// maximum-sized block, from the idea of adding memory to a byte pool. The +/// idea is to allow pieces of memory required during initialization of the +/// application to be added back to the pool at run time. We could also +/// simply add an API to allow memory to be added to a previously created +/// pool, with the special case that if the block were 'too big' that it would +/// be split into smaller chunks. +/// +/// \todo Consider +/// adding an option to store a pointer to the originating byte pool in the +/// block header. This would allow deallocation of any block with free() +/// regardless of which pool it was allocated from, at a cost of 1 pointer per +/// block. This would simplify some of our validation test cases. We could +/// also accomplish this by having each pool register ranges of addresses that +/// it allocates from, but that would require a search every time we freed a +/// block. + +#include "ssx.h" +#include "byte_pool.h" + +// This is an implementation of the TLSF (Two-Level Segregate Fit) algorithm +// described by M. Masmano, I. Ripoll and A Crespol, +// http:/rtportal.upv.es/rtmalloc. A couple of their papers and presentations +// are archived in /lib/doc/tlsf. This is a 'clean-room' implementation of +// their published ideas based solely on their papers and presentations. No +// part of their GPL implementation was used to create the byte-pool facility +// implemented here. The algorithm as implemented here should port without +// problem to either 32-bit or 64-bit implementations. +// +// TLSF has the nice property that it is a constant-time algorithm, both for +// allocation and freeing allocated blocks. This property is guaranteed by +// always trading space for speed in the algorithm. This means that we can +// (and do) run the allocation and freeing in a critical section. With all +// error checking and statistics, a worst-case byte_bool_alloc() was +// timed at 318 PowerPC instructions in the PgP OCC (PPC405) Simics +// simulation. A worst-case byte_pool_free() was timed at 368 PowerPC +// instructions. These times are expected to equate to ~3us in OCC, a +// reasonable value for a critical section. [The above times include all +// error checking]. +// +// An allocation unit requires two pointers of overhead - a pointer to the +// previous block and a pointer to the next block (both in terms of linear +// addresses). The minimum block size also includes the requirement for two +// extra pointers used to link free blocks into their free lists. The final +// size of the block (including user data) is simply computed as (next - self) +// in terms of linear addresses. +// +// An allocated block is marked by setting the low-order bit of the 'previous' +// pointer, which bit is otherwise guaranteed to be 0 due to alignment +// restrictions. Whenever a block is freed it is immediately merged with the +// previous and next blocks, if possible. Several places in the block +// splitting and merging code take advantage of this invariant, and assume +// that if a block is not already merged with its 'next' partner, then the +// 'next' partner must be allocated. Sentinel blocks are allocated at either +// end of the managed area to avoid special checks for the first and last +// memory blocks during merging. +// +// The 'Two-Level' in TLSF refers to the fact that there are multiple free +// lists arranged in a 2-dimensional array. Each free lists contains blocks +// that fall into a particular size range. The free list pointers and other +// data structures described below are carved out of the initial free area +// when a byte pool is initialized. +// +// The first dimension of the free list array is simply the floor(log2(size)) +// of the block. For the second dimension, a tuning parameter selects how many +// columns each row in the table will contain. The number of columns must be +// an even power-of-2. Each column represents a fixed power-of-2 size +// increment of block sizes. Given a block size, it is easy to compute the +// row and column indices of a free list containing blocks of that size with +// shifting and masking operation. +// +// It is assumed that the C type 'unsigned long' is the same size as a +// pointer. Therefore the number of rows in the table is less than or equal +// to the number of bits in an unsigned long. The number of columns is also +// restricted to being in the range (1, 2, 4, ... number of bits in unsigned +// long). +// +// The above restrictions make it very fast (and constant time) to find a free +// list that contains a block that will satisfy the allocation request. The +// byte pool structure maintains a 'row status' word that indicates whether +// there are any blocks free in any of the free lists of the row. Each row +// also has an associated 'column status' word that indicates which free lists +// have blocks free. A row status bit is set if and only if at least one bit +// in the column status for that row is set. +// +// Note that although the 32-bit PowerPC implementation conceptually contains +// a 32x32 array of free list pointers, only the free list pointers actually +// required to hold the representable blocks are allocated. +// +// The algorithm uses the GCC __builtin_clzl() function to count leading zeros +// in the status words to find rows/columns that contain free blocks. This +// generates the 'cntlzw' instruction on 32-bit PowerPC and a similar +// instruction on X86. So the algorithm is also portable across machines - +// which simplifies testing. +// +// A couple of final facts: When the application requests memory, the block +// header overhead is added to the request and we look for a free list +// guaranteed to contain blocks of the requested size. That means that the +// request size must be rounded up to the next free list size, to avoid having +// to search a list that might not contain a block of the proper size. This +// leads to cases where allocation will fail, even though the requested memory +// is actually available. That's just the price we have to pay for a +// constant-time guarantee. +// +// This memory allocator will never be used in mission-mode for hard-real-time +// applications, so the statistics are always kept up-to-date. This adds some +// overhead, but does not effect the constant-time behavior. +// +// Given the above description, hopefuly the brief comments with the +// implementation will make sense. + + +/// The byte pool for use by malloc() and calloc(). +/// +/// The application must define a byte pool and assign it to _malloc_byte_pool +/// in order for malloc() and calloc() to work. + +BytePool *_malloc_byte_pool = 0; + + +// The byte pool memory block header. +// +// Each memory block requires 2 pointers of overhead - the pointers to the +// previous and next (in terms of linear addresses) blocks. The low-order bit +// of the \a previous pointer is used as the \e allocated flag, and is set +// when a block is allocated. The size of a block is computed simply as the +// \a next - \a self. This header layout makes it very simple to merge blocks +// when they are deallocated. + +typedef struct ByteBlock { + + // Pointer to the previous (in terms of linear address) block. + // + // The low-order bit of the pointer is set to indicate a block that has + // been allocated. + struct ByteBlock *previous; + + // Pointer to the next (in terms of linear address) block. + // + // The size of the block is computed simply as \a next - \a self. + struct ByteBlock *next; + +} ByteBlock; + + +// A free byte-pool memory block +// +// Blocks stored in free lists require an additional 2 pointers of +// overhead. The blocks are doubly-linked in the free lists to make deletion +// a constant-time operation. Note that the previous pointer is a pointer to +// a pointer to a ByteBlock - it may be pointing to the free list +// header. Since all blocks must be freeable, this structure defines the +// minimum block size. + +typedef struct FreeByteBlock { + + // The base object + ByteBlock block; + + // Pointer to the next block in the free list + struct FreeByteBlock *next; + + // Pointer to the \a next pointer of the previous element in the free + // list, or a pointer to the free list header. + struct FreeByteBlock **previous; + +} FreeByteBlock; + + +// All blocks will be aligned to this size, so this size also defines the +// minimum quantum of memory allocation. The coice of 8 should give +// good results for both 32-bit and 64-bit implementations. +// +// NB : This implmentation assumes that the ByteBlock and FreeByteBLock are +// aligned to this alignment - if this constant is ever changed from 8 then +// the ByteBlock and FreeByteBlock may need to be padded to meet the alignment +// assumptions, and the \a minimum_block_size may need to be adjusted. + +#define ALIGNMENT 8 + + +// An unsigned long, big-endian bit mask + +#define UL_BE_MASK(i) \ + ((unsigned long)1 << (BITS_PER_UNSIGNED_LONG - (i) - 1)) + + +// Align a value to the alignment. The direction is either positive or +// negative to indicate alignment up or down. + +static inline unsigned long +align(unsigned long x, int direction) +{ + if (x % ALIGNMENT) { + if (direction > 0) { + return x + (ALIGNMENT - (x % ALIGNMENT)); + } else { + return x - (x % ALIGNMENT); + } + } else { + return x; + } +} + + +// Compute the floor(log2(x)) of x. This is used to compute the row indices +// of blocks based on the block size. + +static inline int +floor_log2(unsigned long x) +{ + return BITS_PER_UNSIGNED_LONG - 1 - __builtin_clzl(x); +} + + +// In theory the tuning parameters might vary based on the amount of memory +// being managed, but for now we simply use constants. +// +// The minimum block size includes both the size of the header, as well as the +// requirement that the number of columns be <= the mimumum block size to make +// the addressing uniform. For example, on PPC405 the minimum block size is 16 +// bytes (4 pointers) -- unless the number of columns is 32, in which case it +// has to grow to 32 bytes. +// +// Note that no matter what, we may allocate free list pointers in the +// lower-numbered rows that will never be populated due to alignment +// constraints. + +#ifndef BYTE_POOL_TLSF_COLUMNS +#define BYTE_POOL_TLSF_COLUMNS 8 /* 1,2,4, ... BITS_PER_UNSIGNED_LONG */ +#endif + +static void +compute_tuning(BytePool *pool, size_t size, int columns) +{ + int log2_min_size; + + pool->columns = columns; + pool->log2_columns = floor_log2(pool->columns); + pool->column_mask = (1 << pool->log2_columns) - 1; + + log2_min_size = MAX(pool->log2_columns, floor_log2(sizeof(FreeByteBlock))); + pool->minimum_block_size = align(1 << log2_min_size, 1); +} + + +// Compute the size of a block + +static inline size_t +block_size(ByteBlock *block) +{ + return (unsigned long)(block->next) - (unsigned long)block; +} + + +/// Return implementation information for a block +/// +/// \param memory The memory block to query. This pointer must have been +/// returned by one of the byte_pool functions or derivitives, or may also be +/// 0. +/// +/// \param actual_address : Returned as the address of the block header. +/// +/// \param actual_size Returned as the size of the complete block, including +/// the header. +/// +/// \param useful_size : Returned as the actual amount of space available from +/// \a memory to the end of the block. The \a useful_size may be useful to +/// applications that allocate big blocks then carve them up into smaller +/// structures. +/// +/// Note that any of \a actual_address, \a actual_size and \a useful_size may +/// be passed in as 0 if the caller does not require the information. + +void +byte_pool_block_info(void* memory, + void** actual_address, size_t* actual_size, + size_t* useful_size) +{ + ByteBlock* block; + + if (memory == 0) { + + if (actual_address) *actual_address = 0; + if (actual_size) *actual_size = 0; + if (useful_size) *useful_size = 0; + + } else { + + // This implementation uses the convention that if the \a next pointer + // of the putative ByteBlock == 1, then this is actually an aligned + // allocation and the actual ByteBlock is located at the address + // contained in the \a previous field of the dummy header. + + block = (ByteBlock *)(((unsigned long)memory) - sizeof(ByteBlock)); + if ((int)(block->next) == 1) { + block = block->previous; + } + + if (actual_address) *actual_address = block; + if (actual_size) *actual_size = block_size(block); + if (useful_size) + *useful_size = + (unsigned long)(block->next) - (unsigned long)memory; + } +} + + +// Mark a block as allocated by setting the low-order bit of the \a previous +// pointer. + +static inline ByteBlock * +allocated(ByteBlock *p) +{ + return (ByteBlock *)((unsigned long)p | 1ul); +} + + +static void +mark_allocated(BytePool *pool, ByteBlock *block) +{ + size_t bytes = block_size(block); + + pool->bytes_allocated += bytes; + pool->bytes_free -= bytes; + pool->blocks_allocated += 1; + pool->blocks_free -= 1; + + block->previous = allocated(block->previous); +} + + +// Mark a block as free by clearing the low-order bit of the \a previous +// pointer. + +static inline ByteBlock * +deallocated(ByteBlock *p) +{ + return (ByteBlock *)((unsigned long)p & ~1ul); +} + + +static void +mark_free(BytePool *pool, ByteBlock *block) +{ + size_t bytes = block_size(block); + + pool->bytes_allocated -= bytes; + pool->bytes_free += bytes; + pool->blocks_allocated -= 1; + pool->blocks_free += 1; + + block->previous = deallocated(block->previous); +} + + +// Check for a block being free + +static inline int +block_is_free(ByteBlock *block) +{ + return (((unsigned long)(block->previous)) & 1ul) == 0; +} + + +// Normalize a 'previous' pointer + +static inline ByteBlock * +normalize_previous(ByteBlock *previous) +{ + return (ByteBlock *)((unsigned long)previous & ~1ul); +} + + +// Check for correct linkage. This is such a critical check for application +// memory corruption that it is always done. + +static int +check_linkage(ByteBlock *block) +{ + if (normalize_previous(block->next->previous) != block) { + printk("byte_pool: Forward linkage error\n" + " block : %p\n" + " block->next : %p\n" + " block->next->previous : %p\n", + block, + block->next, + block->next->previous); + SSX_ERROR(BYTE_POOL_REVERSE_LINKAGE); + } else if (normalize_previous(block->previous)->next != block) { + printk("byte_pool: linkage error\n" + " block->previous : %p\n" + " block->pevious->next : %p\n" + " block : %p\n", + block->previous, + block->previous->next, + block); + SSX_ERROR(BYTE_POOL_FORWARD_LINKAGE); + } + return 0; +} + + +// Mark a free list as empty + +static inline void +mark_empty(BytePool *pool, int row, int column) +{ + pool->column_status[row] &= ~UL_BE_MASK(column); + if (pool->column_status[row] == 0) { + pool->row_status &= ~UL_BE_MASK(row); + } +} + + +// Mark a free list as non-empty + +static inline void +mark_non_empty(BytePool *pool, int row, int column) +{ + pool->column_status[row] |= UL_BE_MASK(column); + pool->row_status |= UL_BE_MASK(row); +} + + +// Convert a size into row and column indices + +static inline void +size2rc(BytePool *pool, size_t size, int *row, int *column) +{ + *row = floor_log2(size); + *column = (size >> (*row - pool->log2_columns)) & pool->column_mask; +} + + +// Given a block size, find the free list that contains blocks of that size +// (or greater, up to the next free list). When called during block freeing, +// the block size is known to be valid. When called during allocation, the +// block size may be invalid (too big), in which case 0 is returned. + +static FreeByteBlock ** +find_free_list(BytePool *pool, size_t size, int *row, int *column) +{ + size2rc(pool, size, row, column); + if (*row > pool->last_row) { + return 0; + } + return &((pool->free[*row])[*column]); +} + + +// Remove an arbitrary block from its free list due to a merging operation. + +static void +unlink_free_block(BytePool *pool, ByteBlock *block) +{ + FreeByteBlock **free_list; + FreeByteBlock *free_block; + int row, column; + + free_list = find_free_list(pool, block_size(block), &row, &column); + + if (SSX_ERROR_CHECK_KERNEL) { + if (free_list == 0) { + SSX_PANIC(BYTE_POOL_NULL_FREE_LIST); + } + } + + // Unlink the block from the free list + + free_block = (FreeByteBlock *)block; + *(free_block->previous) = free_block->next; + if (free_block->next) { + free_block->next->previous = free_block->previous; + } + + // If the free list is now 0, mark the free list as empty. + + if (*free_list == 0) { + mark_empty(pool, row, column); + } +} + + +// Link a block into the head of its free list due to freeing memory + +static void +link_free_block(BytePool *pool, ByteBlock *block) +{ + FreeByteBlock **free_list; + FreeByteBlock *free_block; + int row, column; + + free_list = find_free_list(pool, block_size(block), &row, &column); + + if (SSX_ERROR_CHECK_KERNEL) { + if (free_list == 0) { + SSX_PANIC(BYTE_POOL_NULL_FREE_LIST); + } + } + + // Link the block into the free list, and mark the free list as + // non-empty. + + free_block = (FreeByteBlock *)block; + + free_block->next = *free_list; + if (*free_list) { + (*free_list)->previous = &(free_block->next); + } + *free_list = free_block; + free_block->previous = free_list; + + mark_non_empty(pool, row, column); +} + + +// Round up the block size (if required) to the next column. Note that the +// block_size input here is aligned, and remains aligned even after rounding. + +static size_t +round_up_size(BytePool *pool, size_t block_size) +{ + size_t residue, column_span, column_mask; + int row = floor_log2(block_size); + + column_span = 1 << (row - pool->log2_columns); + column_mask = column_span - 1; + residue = block_size & column_mask; + + if (residue == 0) { + return block_size; + } else { + return block_size + (column_span - residue); + } +} + + +// The implemenation of freeing a block of memory. When freed, a block is +// immediately merged with its neighbors if possible, and the final merged +// block is inserted into the proper free list. +// +// The linkage check is done here so that it can also protect internal uses of +// this API (but only if SSX errors lead to panics, the expected default). + +static int +byte_pool_free_block(BytePool *pool, ByteBlock *block) +{ + int rc; + SsxMachineContext ctx; + + rc = check_linkage(block); + if (rc) return rc; + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + pool->free_calls++; + + mark_free(pool, block); + + if (block_is_free(block->next)) { + + // Merge next block into current block + + unlink_free_block(pool, block->next); + + block->next = (ByteBlock *)((unsigned long)(block->next) + + block_size(block->next)); + block->next->previous = allocated(block); + + pool->blocks_free--; + } + + if (block_is_free(block->previous)) { + + // Merge current block into previous block + + unlink_free_block(pool, block->previous); + + block->previous->next = + (ByteBlock *)((unsigned long)(block->previous->next) + + block_size(block)); + block = block->previous; + block->next->previous = allocated(block); + + pool->blocks_free--; + } + + // Finally, insert the block into the proper free list. + + link_free_block(pool, block); + + ssx_critical_section_exit(&ctx); + + return 0; +} + + +/// Free a block of memory back to a byte pool +/// +/// \param pool A pointer to the BytePool structure that allocated the memory. +/// +/// \param memory A pointer to memory returned by byte_pool_alloc() or +/// byte_pool_alloc_aligned() for the pool. This pointer may be NULL (0), in +/// which case the byte_pool_free() request succeeds immediately. +/// +/// The part of this API that manipulates the \a pool runs as an +/// SSX_NONCRITICAL critical section. byte_pool_free() uses a constant-time +/// algorithm. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Success +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The block is not marked as being +/// allocated, or does not appear to have been allocated from this byte_pool. +/// +/// \retval -BYTE_POOL_LINKAGE_ERROR The block being freed is not linked +/// correctly with the other blocks managed by the pool, most likely +/// indicating that the memory being freed was not allocated by +/// byte_pool_alloc(), or that memory corruption has occured. + +// This implementation uses the convention that if the \a next pointer of the +// putative ByteBlock == 1, then this is actually an aligned allocation and +// the actual ByteBlock is located at the address contained in the \a previous +// field of the dummy header. + +int +byte_pool_free(BytePool *pool, void *memory) +{ + ByteBlock *block; + + if (memory == 0) { + return 0; + } + + block = (ByteBlock *)(((unsigned long)memory) - sizeof(ByteBlock)); + if ((int)(block->next) == 1) { + if (0) { + printk("byte_pool_free(%p, %p) [%p] : Aligned\n", + pool, memory, block); + } + block = block->previous; + } + + if (0) { + printk("byte_pool_free(%p, %p) [%p] : %d %d %d\n", + pool, memory, block, + block_is_free(block), + block < pool->first_block, + block > pool->last_block); + } + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); + SSX_ERROR_IF(block_is_free(block) || + (block < pool->first_block) || + (block > pool->last_block), + BYTE_POOL_INVALID_ARGUMENT); + } + + return byte_pool_free_block(pool, block); +} + + +/// Create a BytePool with explicit specification of tuning parameters +/// +/// This routine is the real body of byte_pool_create(), however this +/// underlying interface is provided for testing and experimentation and allows +/// the specification of non-default tuning parameters. +/// +/// There is actually only one tuning parameter for TLSF - the number of +/// columns. The number of columns must be an even power of two no larger +/// than the number of bits in an unsigned long. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool pointer was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT Either the \a memory pointer was NULL +/// (0), the amount of memory was insufficient for the management overhead, or +/// the parameterization was invalid. + +int +byte_pool_create_tuned(BytePool *pool, void *memory, size_t size, + int columns) +{ + size_t overhead, free_list_overhead; + unsigned long memory_ul, aligned_memory; + int i; + FreeByteBlock **free; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); + SSX_ERROR_IF((memory == 0) || + (columns < 1) || + ((columns & (columns - 1)) != 0) || + (floor_log2(columns) > floor_log2(BITS_PER_UNSIGNED_LONG)), + BYTE_POOL_INVALID_ARGUMENT); + } + + // Compute tuning parameters + + compute_tuning(pool, size, columns); + + // Clear free list vector pointers and column status + + for (i = 0; i < BITS_PER_UNSIGNED_LONG; i++) { + pool->free[i] = 0; + pool->column_status[i] = 0; + } + + // Determine the first and last allocated rows. + + pool->first_row = floor_log2(pool->minimum_block_size); + pool->last_row = floor_log2(size); + + // The dynamic overhead consists of aligment overhead, 2 sentinel nodes, + // the vectors of pointers to free lists, plus 2 alignments. There must + // also be enough room for at least 1 block to allocate. + + memory_ul = (unsigned long)memory; + aligned_memory = align(memory_ul, 1); + + free_list_overhead = + (((pool->last_row - pool->first_row + 1) * pool->columns) * + sizeof(FreeByteBlock *)); + + overhead = + (aligned_memory - memory_ul) + + (2 * sizeof(ByteBlock)) + + free_list_overhead + + (2 * ALIGNMENT) + + pool->minimum_block_size; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(overhead >= size, BYTE_POOL_INVALID_ARGUMENT); + } + + // Allocate the overhead items. The free list vectors and column status + // arrays are carved out and zeroed. For good measure we re-align after + // each of these operations. The sentinel blocks are carved off of either + // end of the remaining free space and marked allocated. The remaining + // initial "big block" is also initialized (as if it were allocated). + + size = size - (aligned_memory - memory_ul); + size = align(size, -1); + + pool->row_status = 0; + + free = (FreeByteBlock **)aligned_memory; + memset((void *)free, 0, free_list_overhead); + + aligned_memory += free_list_overhead; + size -= free_list_overhead; + aligned_memory = align(aligned_memory, 1); + size = align(size, -1); + + for (i = pool->first_row; i <= pool->last_row; i++) { + pool->free[i] = free; + free += pool->columns; + } + + pool->first_block = (ByteBlock *)aligned_memory; + aligned_memory += sizeof(ByteBlock); + size -= sizeof(ByteBlock); + + pool->big_block = (ByteBlock *)aligned_memory; + + pool->last_block = + (ByteBlock *)(aligned_memory + size - sizeof(ByteBlock)); + size -= sizeof(ByteBlock); + + pool->first_block->next = pool->big_block; + pool->first_block->previous = 0; + mark_allocated(pool, pool->first_block); + + pool->last_block->next = 0; + pool->last_block->previous = pool->big_block; + mark_allocated(pool, pool->last_block); + + pool->big_block->previous = pool->first_block; + pool->big_block->next = pool->last_block; + + // Initialize statistics + + pool->bytes_allocated = 0; + pool->bytes_free = block_size(pool->big_block); + pool->initial_allocation = pool->bytes_free; + pool->blocks_allocated = 0; + pool->blocks_free = 1; + pool->alloc_calls = 0; + pool->free_calls = 0; + + // Free the big block and we're ready to go. + + mark_allocated(pool, pool->big_block); + byte_pool_free_block(pool, pool->big_block); + + return 0; +} + + +/// Create a BytePool +/// +/// \param pool A pointer to an uninitialized BytePool structure +/// +/// \param memory A pointer to the memory to be managed by the BytePool +/// +/// \param size The size of the managed area in bytes +/// +/// byte_pool_create() sets up the \a memory area to be used as a memory pool +/// for malloc()-style allocation using byte_pool_alloc() and +/// byte_pool_free(). Note that the actual memory area available for +/// allocation will be smaller than \a size due to alignment, and reservation +/// of a portion of the area for management overhead. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool pointer was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT Either the \a memory pointer was NULL +/// (0), the amount of memory was insufficient for the management overhead, or +/// the parameterization was invalid. + +int +byte_pool_create(BytePool *pool, void *memory, size_t size) +{ + return byte_pool_create_tuned(pool, memory, size, BYTE_POOL_TLSF_COLUMNS); +} + + +/// Allocate memory from a byte pool +/// +/// \param pool A pointer to an initialized BytePool +/// +/// \param memory An address to recieve a pointer to the allocated memory. +/// This address will be set to NULL (0) if the allocation request can not be +/// satisfied (or the \a size is 0). +/// +/// \param size The number of bytes to allocate. +/// +/// The part of this API that manipulates the \a pool runs as an +/// SSX_NONCRITICAL critical section. byte_pool_alloc() uses a constant-time +/// algorithm. +/// +/// Return values other than 0 are not necessarily errors; see \ref +/// ssx_errors. +/// +/// The following return codes are not considered errors: +/// +/// \retval 0 Success +/// +/// \retval -BYTE_POOL_NO_MEMORY The allocation request could not be +/// satisfied. The memory pointer will also be NULL (0) in this case. +/// +/// The following return codes are considered errors: +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memory argument is NULL (0). + +int +byte_pool_alloc(BytePool *pool, void **memory, size_t size) +{ + SsxMachineContext ctx; + size_t request_size, actual_size; + int found, row, column; + unsigned long row_status, column_status; + FreeByteBlock **free_list; + FreeByteBlock *free_block; + ByteBlock *block; + ByteBlock *residue_block; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); + SSX_ERROR_IF(memory == 0, BYTE_POOL_INVALID_ARGUMENT); + } + + // Quickly dispense with NULL requests + + if (size == 0) { + *memory = 0; + return 0; + } + + // Compute the requested block size (which includes the header). If the + // size went down we overflowed due to a huge request (which can't be + // filled). Otherwise if the request is small it is boosted up to the + // (aligned) minimum size. To guarantee fast search, the requested size + // must then be rounded up to a size that is represented in the 2-D array + // of free list pointers. + + request_size = align(size + sizeof(ByteBlock), 1); + if (request_size < size) { + *memory = 0; + return -BYTE_POOL_NO_MEMORY; + } + + if (request_size < pool->minimum_block_size) { + request_size = pool->minimum_block_size; + } + + request_size = round_up_size(pool, request_size); + + // Up to this point, all accesses of the memory pool object have been to + // read only constants. Now we get serious. + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + pool->alloc_calls++; + + // See if a block of the correct or larger size exists in the row. The + // search is first via a single bit in the row_status. If that hits then + // we check for columns >= the target column. + + found = 0; + size2rc(pool, request_size, &row, &column); + + if (pool->row_status & UL_BE_MASK(row)) { + + column_status = pool->column_status[row] & + ((UL_BE_MASK(column) << 1) - 1); + + if (column_status != 0) { + column = __builtin_clzl(column_status); + found = 1; + } + } + + // If the block was not found in the 'optimum' row, look in all rows of + // larger size and take the first block that fits. + + if (!found) { + + row_status = pool->row_status & (UL_BE_MASK(row) - 1); + + if (row_status != 0) { + row = __builtin_clzl(row_status); + column = __builtin_clzl(pool->column_status[row]); + found = 1; + } + } + + // Another out of memory case. + + if (!found) { + ssx_critical_section_exit(&ctx); + *memory = 0; + return -BYTE_POOL_NO_MEMORY; + } + + // Now we can get the pointer to the free list and take the block. + + free_list = &((pool->free[row])[column]); + + if (SSX_ERROR_CHECK_KERNEL) { + if ((free_list == 0) || (*free_list == 0)) { + SSX_PANIC(BYTE_POOL_INVALID_FREE_LIST); + } + } + + free_block = *free_list; + *free_list = free_block->next; + if (free_block->next) { + free_block->next->previous = free_list; + } else { + mark_empty(pool, row, column); + } + + // Mark the block as allocated + + block = (ByteBlock *)free_block; + mark_allocated(pool, block); + + // If there is enough residue, split the excess memory off of the end of + // the block. This is a kind of dummy transaction for our statistical + // purposes. + + actual_size = block_size(block); + if ((actual_size - request_size) >= pool->minimum_block_size) { + + residue_block = (ByteBlock *)((unsigned long)block + request_size); + + residue_block->next = block->next; + residue_block->previous = block; + residue_block->previous->next = residue_block; + residue_block->next->previous = allocated(residue_block); + + pool->blocks_allocated++; + byte_pool_free_block(pool, residue_block); + pool->free_calls--; + } + + // Set the memory pointer to the area to be used by the application and + // return. + + *memory = (void *)((unsigned long)block + sizeof(ByteBlock)); + + ssx_critical_section_exit(&ctx); + + if (0) { + ByteBlock* block = + (ByteBlock*)((unsigned long)*memory - sizeof(ByteBlock)); + + printk("byte_pool_alloc(%p, -> %p, %zu)\n" + " request_size = %u, Previous = %p, Next = %p\n", + pool, *memory, size, + request_size, block->previous, block->next); + } + + return 0; +} + + +/// Allocate memory from a byte pool and clear +/// +/// byte_pool_calloc() allocates memory using byte_pool_alloc() then clears +/// the memory area using memset(). The arguments conform to the POSIX +/// standard for calloc(). See byte_pool_alloc() for return codes and usage +/// notes. + +int +byte_pool_calloc(BytePool *pool, void **memory, size_t nmemb, size_t size) +{ + int rc; + + rc = byte_pool_alloc(pool, memory, nmemb * size); + if (rc || (*memory == 0)) { + return rc; + } + + memset(*memory, 0, nmemb * size); + + return 0; +} + + +/// Allocate an aligned memory area +/// +/// \param pool A pointer to an initialized BytePool +/// +/// \param memory An address to recieve a pointer to the allocated memory. +/// This address will be set to NULL (0) if the allocation request can not be +/// satisfied (or the \a size is 0). +/// +/// \param size The size of the memory area required (in bytes). This can be +/// any size - it \e does \e not have to be a multiple of the aligned size (as +/// is required by other common aligned memory allocators). +/// +/// \param alignment The alignment constraint, specified as the base 2 +/// logarithm of the alignment. For example, to align on a 128-byte boundary +/// the \a alignment would be specified as 7. +/// +/// byte_pool_alloc_aligned() is a convenience interface for allocating memory +/// with a guaranteed alignment. The BytePool APIs do not normally do aligned +/// allocation. byte_pool_alloc_aligned() first uses byte_pool_alloc() to +/// allocate a block of memory large enough to satisfy the request and +/// guarantee that a subset of the memory allocation will satisfy the +/// alignment constraint plus the overhead of a dummy block header. Note that +/// it is space-inefficient to allocate many small aligned areas. If possble +/// it would be better to allocate a single aligned area and then have the +/// application partition the memory as required. +/// +/// Memory areas allocated by byte_pool_alloc_aligned() can be freed with +/// byte_pool_free(), just like any other dynamic memory allocation. +/// +/// The part of this API that manipulates the \a pool runs as an +/// SSX_NONCRITICAL critical section. The underlying call of byte_pool_alloc() +/// uses a constant-time algorithm. +/// +/// Return values other than 0 are not necessarily errors; see \ref +/// ssx_errors. +/// +/// The following return codes are not considered errors: +/// +/// \retval 0 Success +/// +/// \retval -BYTE_POOL_NO_MEMORY The allocation request could not be +/// satisfied. The memory pointer will also be NULL (0) in this case. +/// +/// The following return codes are considered errors: +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memory argument is NULL (0), or +/// the \a alignment argument is invalid. + +// The allocation must be big enough for the size requested + the alignment +// amount (to guarantee alignment) + room for a dummy ByteBlock. The dummy +// ByteBlock is marked by setting the \a next pointer to 1 to indicate that +// this is an aligned allocation. In this case the \a previous pointer of the +// dummy ByteBlock points to the ByteBlock of the original allocation. + +int +byte_pool_alloc_aligned(BytePool *pool, void **memory, size_t size, + int alignment) +{ + int rc; + unsigned long pow2_alignment, mask, aligned; + void *unaligned_memory; + ByteBlock *dummy_block, *unaligned_block; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((alignment < 1) || (alignment >= BITS_PER_UNSIGNED_LONG), + BYTE_POOL_INVALID_ARGUMENT); + } + + pow2_alignment = (unsigned long)1 << (unsigned long)alignment; + mask = pow2_alignment - 1; + + rc = byte_pool_alloc(pool, &unaligned_memory, + size + pow2_alignment + sizeof(ByteBlock)); + + if (rc || (unaligned_memory == 0)) { + *memory = 0; + return rc; + } + unaligned_block = (ByteBlock *)(((unsigned long)unaligned_memory) - + sizeof(ByteBlock)); + + aligned = (unsigned long)unaligned_memory + sizeof(ByteBlock); + if (aligned & mask) { + aligned += (pow2_alignment - (aligned & mask)); + } + *memory = (void *)aligned; + + dummy_block = (ByteBlock *)(aligned - sizeof(ByteBlock)); + dummy_block->previous = unaligned_block; + dummy_block->next = (ByteBlock*)1; + + if (0) { + printk("byte_pool_alloc_aligned(%p, -> %p, %zu, %d)\n", + pool, *memory, size, alignment); + } + + return 0; +} + + +/// Allocate aligned memory from a byte pool and clear +/// +/// byte_pool_calloc_alligned() allocates memory using +/// byte_pool_alloc_aligned() then clears the memory area using memset(). The +/// arguments conform to the POSIX standard for calloc(). See +/// byte_pool_alloc_aligned() for return codes and usage notes. In particular +/// note that this memory must be freed with byte_pool_free_aligned(). + +int +byte_pool_calloc_aligned(BytePool *pool, void **memory, + size_t nmemb, size_t size, int alignment) +{ + int rc; + + rc = byte_pool_alloc_aligned(pool, memory, nmemb * size, alignment); + if (rc || (*memory == 0)) return rc; + + memset(*memory, 0, nmemb * size); + + return 0; +} + + +/// malloc() allocates \a size bytes and returns a pointer to the allocated +/// memory. The memory is not cleared. The value returned is a pointer to the +/// allocated memory, which is suitably aligned for any kind of variable, or +/// NULL if the requested \a size is 0 or the request fails. +/// +/// NB: The aplication must create and assign a BytePool object to the +/// library variable _malloc_byte_pool in order for malloc() to work. + +void * +malloc(size_t size) +{ + void *memory; + + if (byte_pool_alloc(_malloc_byte_pool, &memory, size)) { + memory = 0; + } + return memory; +} + + +/// calloc() allocates memory for an array of \a nmemb elements of \a size +/// bytes each and returns a pointer to the allocated memory. The memory is +/// set to zero. The value returned is a pointer to the allocated and cleared +/// memory, which is suitably aligned for any kind of variable, or NULL if the +/// requested \a size is 0 or the request fails. +/// +/// NB: The aplication must create and assign a BytePool object to the +/// library variable _malloc_byte_pool in order for calloc() to work. + +void * +calloc(size_t nmemb, size_t size) +{ + void *memory; + + if (byte_pool_calloc(_malloc_byte_pool, &memory, nmemb, size)) { + return 0; + } + return memory; +} + + +/// free() frees the memory space pointed to by \a ptr, which must have been +/// returned by a previous call to malloc(), posix_memalign, calloc() or +/// realloc(). Otherwise, or if free(ptr) has already been called before, +/// undefined behavior occurs. If \a ptr is NULL, no operation is performed. +/// +/// NB: The aplication must create and assign a BytePool object to the +/// library variable _malloc_byte_pool in order for free() to work. + +void +free(void *ptr) +{ + byte_pool_free(_malloc_byte_pool, ptr); +} + + +/// realloc() changes the size of the memory block pointed to by \a ptr to \a +/// size bytes. The contents will be unchanged to the minimum of the old and +/// new sizes; newly allocated memory will be uninitialized. If \a ptr is +/// NULL, the call is equivalent to malloc(size); if \a size is equal to zero, +/// the call is equivalent to free(ptr). Unless \a ptr is NULL, it must have +/// been returned by an earlier call to malloc(), calloc() or realloc(). If +/// the area pointed to was moved, a free(ptr) is done. +/// +/// realloc() returns a pointer to the newly allocated memory, which is +/// suitably aligned for any kind of variable and may be different from \a +/// ptr, or NULL if the request fails. If \a size was equal to 0, either NULL +/// or a pointer suitable to be passed to free() is returned. If realloc() +/// fails the original block is left untouched; it is not freed or moved. + +void* +realloc(void *ptr, size_t size) +{ + void *memory; + size_t useful_size; + + // Handle simple case + + if (ptr == 0) { + + memory = malloc(size); + + } else if (size == 0) { + + free(ptr); + memory = 0; + + } else { + + // Find out the useful size of the block. If we need more than this we + // need to allocate a new block and memcpy() the old data to the new + // block and free the old block. If we need less then this we also + // need to allocate a new block and move the head of the current + // data. If the new size is the same as the current size we do nothing. + + byte_pool_block_info(ptr, 0, 0, &useful_size); + + if (size == useful_size) { + + memory = ptr; + + } else { + + memory = malloc(size); + if (memory != 0) { + memcpy(memory, ptr, (size > useful_size) ? useful_size : size); + free(ptr); + } + } + } + return memory; +} + + +/// The posix_memalign() function allocates \a size bytes aligned on a +/// boundary specified by \a alignment, and returns a pointer to the allocated +/// memory in \a memptr. The value of \a alignment shall be a multiple of +/// sizeof(void*), that is also a power of two. Upon successful completion, +/// the value pointed to by \a memptr will be a multiple of alignment. +/// +/// Note that memory allocated with posix_memalign() can be freed with +/// free(). +/// +/// In the event of errors, the contents of \a memptr will be returned as 0. +/// +/// The following return codes are mandated by POSIX, and are always returned +/// in the event of the specified condition. +/// +/// \retval 0 Success +/// +/// \retval -EINVAL The value of the \a alignment parameter is not a power of +/// two multiple of sizeof(void*). +/// +/// \retval -ENOMEM There is insufficient memory available with the requested +/// alignment. +/// +/// The following return codes are implementation-specific and may be +/// configured to cause a kernel panic. +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a _malloc_byte_pool is NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memptr argument is NULL (0). + +int +posix_memalign(void** memptr, size_t alignment, size_t size) +{ + int rc; + + if (((alignment & (alignment - 1)) != 0) || + (alignment < sizeof(void*))) { + rc = -EINVAL; + } else { + rc = byte_pool_alloc_aligned(_malloc_byte_pool, memptr, size, + floor_log2(alignment)); + if (!rc && (*memptr == 0)) { + rc = -ENOMEM; + } + } + if (rc && memptr) { + *memptr = 0; + } + return rc; +} + + +/// Print a dump of a byte pool, including the header and allocation report +/// +/// \param stream The stream to receive the dump +/// +/// \param pool The BytePool object to dump +/// +/// \bug This routine is not thread safe. + +void +byte_pool_report(FILE* stream, BytePool* pool) +{ + ByteBlock* block; + uint8_t* p8; + uint32_t* p32; + int i; + + fprintf(stream, ">>> Byte Pool Report for Pool %p <<<\n", pool); + + fprintf(stream, ">>> BytePool Object Dump <<<\n"); + +#define DUMPIT(x, fmt) \ + fprintf(stream, "%20s : " #fmt "\n", #x, pool->x) + + DUMPIT(first_row, %d); + DUMPIT(last_row, %d); + DUMPIT(columns, %d); + DUMPIT(log2_columns, %d); + DUMPIT(column_mask, 0x%08x); + DUMPIT(minimum_block_size, %d); + DUMPIT(free, %p); + DUMPIT(column_status, %p); + DUMPIT(row_status, %lu); + DUMPIT(first_block, %p); + DUMPIT(big_block, %p); + DUMPIT(last_block, %p); + DUMPIT(initial_allocation, %d); + DUMPIT(bytes_allocated, %d); + DUMPIT(bytes_free, %d); + DUMPIT(blocks_allocated, %d); + DUMPIT(blocks_free, %d); + DUMPIT(alloc_calls, %d); + DUMPIT(free_calls, %d); + + fprintf(stream, ">>> Byte Pool Allocation Report <<<\n"); + fprintf(stream, + ">>> status : address : size : binary[0:7] : ASCII[0:7] <<<\n"); + + for (block = pool->first_block->next; + block != pool->last_block; + block = block->next) { + + fprintf(stream, " %c : %p : %6zu : ", + (block_is_free(block) ? 'F' : 'A'), + block, block_size(block)); + + p8 = (uint8_t*)((unsigned long)block + sizeof(ByteBlock)); + p32 = (uint32_t*)p8; + + fprintf(stream, "0x%08x 0x%08x : ", p32[0], p32[1]); + for (i = 0; i < 8; i++) { + if (isprint(p8[i])) { + fputc(p8[i], stream); + } else { + fputc('.', stream); + } + } + fputc('\n', stream); + } + + fprintf(stream, ">>> End Report <<<\n"); +} diff --git a/src/lib/ppc405lib/byte_pool.h b/src/lib/ppc405lib/byte_pool.h new file mode 100644 index 0000000..8766745 --- /dev/null +++ b/src/lib/ppc405lib/byte_pool.h @@ -0,0 +1,166 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/byte_pool.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __BYTE_POOL_H__ +#define __BYTE_POOL_H__ + +// $Id$ + +#ifndef __ASSEMBLER__ + +#include "ssx_io.h" + +struct ByteBlock; +struct FreeByteBlock; + +// A handy constant + +#define BITS_PER_UNSIGNED_LONG (8 * sizeof(unsigned long)) + + +/// A control structure for a byte pool. The application will never modify the +/// structure fields directly, but some applications may be interested in +/// reading the statistics. + +typedef struct { + + // The index of the first allocated row of free list pointers + int first_row; + + // The index of the last allocated row of free list pointers + int last_row; + + // The number of columns in each row of free list pointers + int columns; + + // The log2 of the number of columns in each row of free list pointers + int log2_columns; + + // The shifted block size is ANDed with this mask to extract the column + // number. + size_t column_mask; + + // The minimum block size. + int minimum_block_size; + + // The vectors of free list pointers + struct FreeByteBlock **free[BITS_PER_UNSIGNED_LONG]; + + // The array of column status bit masks + unsigned long column_status[BITS_PER_UNSIGNED_LONG]; + + // The row status bit mask + unsigned long row_status; + + // A sentinel node - the first allocated block. Kept for error checking + // purposes. + struct ByteBlock *first_block; + + // The initial memory allocation. Kept here only for debugging purposes. + struct ByteBlock *big_block; + + // A sentinel node - the last allocated block. Kept here for error + // checking purposes. + struct ByteBlock *last_block; + + // The initial allocation. Kept here for debugging and statistics. + size_t initial_allocation; + + // The total number of bytes currently allocated (excludes overhead) + size_t bytes_allocated; + + // The total number of bytes currently free in the pool + size_t bytes_free; + + // The total number of blocks allocated from the pool + size_t blocks_allocated; + + // The total number of blocks free in the pool + size_t blocks_free; + + // The number of calls to allocate memory + size_t alloc_calls; + + // The number of calls to free memory + size_t free_calls; + +} BytePool; + +extern BytePool *_malloc_byte_pool; + +int +byte_pool_create(BytePool *pool, void *memory, size_t size); + +int +byte_pool_create_tuned(BytePool *pool, void *memory, size_t size, + int columns); + +int +byte_pool_alloc(BytePool *pool, void **memory, size_t size); + +int +byte_pool_calloc(BytePool *pool, void **memory, size_t nmemb, size_t size); + +int +byte_pool_free(BytePool *pool, void *memory); + +void +byte_pool_block_info(void* memory, + void** actual_address, size_t* actual_size, + size_t* useful_size); + +int +byte_pool_alloc_aligned(BytePool *pool, void **memory, size_t size, + int alignment); + +void * +malloc(size_t size); + +void * +calloc(size_t nmemb, size_t size); + +void +free(void *ptr); + +int +posix_memalign(void** memptr, size_t alignment, size_t size); + +void +byte_pool_report(FILE* stream, BytePool* pool); + +#endif /* __ASSEMBLER__ */ + + +// Error/panic codes + +#define BYTE_POOL_INVALID_OBJECT 0x00b98e01 +#define BYTE_POOL_INVALID_ARGUMENT 0x00b98e02 +#define BYTE_POOL_REVERSE_LINKAGE 0x00b98e03 +#define BYTE_POOL_FORWARD_LINKAGE 0x00b98e04 +#define BYTE_POOL_NO_MEMORY 0x00b98e05 + +#define BYTE_POOL_NULL_FREE_LIST 0x00b98e10 +#define BYTE_POOL_INVALID_FREE_LIST 0x00b98e11 + +#endif /* __BYTE_POOL_H__ */ diff --git a/src/lib/ppc405lib/chip_config.h b/src/lib/ppc405lib/chip_config.h new file mode 100644 index 0000000..cc772e2 --- /dev/null +++ b/src/lib/ppc405lib/chip_config.h @@ -0,0 +1,109 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/chip_config.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __CHIP_CONFIG_H__ +#define __CHIP_CONFIG_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file chip_config.h +/// \brief Chip configuration data structures for OCC procedures + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +/// A bitmask defining a chip configuration +/// +/// Since we are using the conventional big-endian notation, any use of these +/// bitmasks requires that the data being tested is of this type - otherwise +/// the masks won't work. +/// +/// Layout: +/// +/// Bits 0:15 - Core chiplet 0..15 is configured +/// Bits 16:23 - MCS 0..7 is configured +/// Bits 24:31 - Centaur 0..7 is configured + +typedef uint64_t ChipConfig; +typedef uint16_t ChipConfigCores; +typedef uint8_t ChipConfigMcs; +typedef uint8_t ChipConfigCentaur; + + +/// Convert a ChipConfig into a mask suitable for use as the 32-bit chiplet +/// mask argument of a PORE wakeup program. +#if 0 +static inline uint32_t +pore_exe_mask(ChipConfig config) +{ + return (uint32_t)((config >> 32) & 0xffff0000); +} +#endif + +/// 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)) + + +#endif /* __CHIP_CONFIG_H__ */ diff --git a/src/lib/ppc405lib/ctype.c b/src/lib/ppc405lib/ctype.c new file mode 100644 index 0000000..5069590 --- /dev/null +++ b/src/lib/ppc405lib/ctype.c @@ -0,0 +1,46 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ctype.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: ctype.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ctype.c +/// \brief Replacement for <ctype.h> functions +/// +/// This file contains entry point equivalents for the "ctype.h" macros. +/// These would only ever be used by assembler programs, therefore it's likely +/// that the object file will never be linked into an image. + +#define __CTYPE_C__ +#include "ctype.h" +#undef __CTYPE_C__ + + + + diff --git a/src/lib/ctype.h b/src/lib/ppc405lib/ctype.h index 9072c41..9e41e45 100755..100644 --- a/src/lib/ctype.h +++ b/src/lib/ppc405lib/ctype.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ctype.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __CTYPE_H__ #define __CTYPE_H__ diff --git a/src/lib/ctype_table.c b/src/lib/ppc405lib/ctype_table.c index 6f3e576..01dd08a 100644 --- a/src/lib/ctype_table.c +++ b/src/lib/ppc405lib/ctype_table.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ctype_table.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ppc405lib/errno.h b/src/lib/ppc405lib/errno.h new file mode 100644 index 0000000..e5ac6a9 --- /dev/null +++ b/src/lib/ppc405lib/errno.h @@ -0,0 +1,49 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/errno.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __ERRNO_H__ +#define __ERRNO_H__ + +// $Id: errno.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/errno.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file errno.h +/// \brief Replacement for <errno.h> +/// +/// SSX does not support a per-thread or global 'errno'. The standard Unix +/// errno values returned by library functions are defined here. The prefix +/// code is the 'telephone code' for "errn". + +#define EINVAL 0x00377601 +#define EBADF 0x00377602 +#define EAGAIN 0x00377603 +#define ENXIO 0x00377604 +#define ENOMEM 0x00377605 + +#endif /* __ERRNO_H__ */ diff --git a/src/lib/fgetc.c b/src/lib/ppc405lib/fgetc.c index e4e4a49..6e6a1f9 100755..100644 --- a/src/lib/fgetc.c +++ b/src/lib/ppc405lib/fgetc.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/fgetc.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ppc405lib/initcall.c b/src/lib/ppc405lib/initcall.c new file mode 100644 index 0000000..664fb6e --- /dev/null +++ b/src/lib/ppc405lib/initcall.c @@ -0,0 +1,70 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/initcall.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file initcall.c +/// \brief An anonymous early initialization facility for SSX applications + +#include "ssx.h" +#include "initcall.h" + +// These linker symbols must be defined if the initcall facility is used. The +// special ELF section .data.initcall contains an array of Initcall structures +// for all declared initcalls. + +extern InitCall _INITCALL_SECTION_BASE[]; +extern SsxLinkerSymbol _INITCALL_SECTION_SIZE; + +void +_initcall_run(InitCall* initcall) +{ + void (*f)(void* arg); + + f = initcall->initcall; + if (f) { + initcall->initcall = 0; + f(initcall->arg); + } +} + + +void +initcall_run_all() +{ + InitCall* initcall; + size_t nCalls; + + initcall = _INITCALL_SECTION_BASE; + nCalls = (size_t)(&_INITCALL_SECTION_SIZE) / sizeof(InitCall); + + for (; nCalls--; initcall++) { + _initcall_run(initcall); + } +} + + + + + diff --git a/src/lib/ppc405lib/initcall.h b/src/lib/ppc405lib/initcall.h new file mode 100644 index 0000000..af8b53c --- /dev/null +++ b/src/lib/ppc405lib/initcall.h @@ -0,0 +1,116 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/initcall.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __INITCALL_H__ +#define __INITCALL_H__ + +// $Id$ + +/// \file initcall.h +/// \brief An early initialization facility for SSX applications +/// +/// The C language standard does not define a generic load-time initialization +/// method, unlike C++ which supports load-time initialization of static +/// objects. The \e initcall facility implements a simple method for SSX +/// applications to declare early initialization functions that are executed +/// prior to or during the invocation of main(). +/// +/// An \e initcall can be any function with the prototype +/// +/// \code +/// +/// void (*initcall)(void* arg) +/// +/// \endcode +/// +/// Initcalls are declared with the INITCALL() macro. An initcall is +/// represented by a named structure, and typically an initcall will be +/// declared static to the compilation unit that implements the initcall: +/// +/// \code +/// +/// void (*init_fn)(void* arg); +/// void* init_data = ...; +/// static INITCALL(init_var, init_fn, init_data); +/// +/// \endcode +/// +/// All INITCALLS loaded in the executable image are executed by the +/// initcall_run_all() API. An SSX application will typically call +/// initcall_run_all() in the function declared as the \a ssx_main_hook, or in +/// the main() routine itself. +/// +/// Initcalls are run in an arbitrary order. However if initcall \a b is +/// dependent on initcall \a a, then initcall \a b can execute +/// initcall_run(&a) to guarantee that initcall \a a runs before \a b. +/// Regardless, every initcall is run exectly once by the initcall facility, +/// even if initcall_run() or initcall_run_all() were to be used multiple +/// times. +/// +/// Behind the scenes, initcalls are implemented by a special ELF section, +/// .data.initcall, that records all declared initcalls. The +/// initcall_run_all() API simply runs all initcalls declared in +/// .data.initcall. + +/// The structure representing an initcall + +typedef struct { + + /// The initialization function + /// + /// Prior to running the initcall, this field is zeroed. This guarantess + /// that each initcall is run at most 1 time. + void (*initcall)(void* arg); + + /// The argument to the initialization function + void* arg; + +} InitCall; + + +/// Declare an initcall +/// +/// This macro generates C code and global data so must be placed at file +/// scope in a C file, not in a header file or inside a C function +/// body. Unless the initcall needs to be referenced by another initcall (to +/// guarantee ordering), this declaration will normally be prepended with +/// 'static'. +#define INITCALL(_var, _initcall, _arg) \ + InitCall _var __attribute__ ((used, section (".data.initcall"))) = \ + {.initcall = _initcall, .arg = _arg}; + + +/// Run the initcall represented by an InitCall structure, assuming it has not +/// already run. +/// +/// \param[in] i_initcall The address of the initcall structure to run +void +initcall_run(InitCall* i_initcall); + + +/// Run all initcalls +void +initcall_run_all(); + +#endif // __INITCALL_H__ diff --git a/src/lib/ppc405lib/lfsr.c b/src/lib/ppc405lib/lfsr.c new file mode 100644 index 0000000..21ddb93 --- /dev/null +++ b/src/lib/ppc405lib/lfsr.c @@ -0,0 +1,50 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/lfsr.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file lfsr.c +/// \brief + +#include <stdint.h> +#include "lfsr.h" + +// Parity for 4-bit numbers +static uint8_t S_parity4[16] = { + 0, 1, 1, 0, + 1, 0, 0, 1, + 1, 0, 0, 1, + 0, 1, 1, 0 +}; + +// 64, 63, 61, 60 LFSR. The routine is coded with the uint8_t casting to help +// the compiler generate more efficient code. + +void +_lfsr64(uint64_t* io_seed) +{ + *io_seed = (*io_seed << 1) | + S_parity4[(uint8_t)((*io_seed >> 59) & 0x3) | + (uint8_t)((*io_seed >> 60) & 0xc)]; +} diff --git a/src/lib/ppc405lib/lfsr.h b/src/lib/ppc405lib/lfsr.h new file mode 100644 index 0000000..951ec45 --- /dev/null +++ b/src/lib/ppc405lib/lfsr.h @@ -0,0 +1,46 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/lfsr.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __LFSR_H__ +#define __LFSR_H__ + +/// \file lfsr.h +/// \brief Linear-Feedback Shift Register Implementations +/// +/// The 32- and 64-bit pseudo-random number generators in this library are of +/// the linear-conguential type. These maximal-length LFSR pseudo-random +/// sequence generators are also provided. + +/// 64-bit LFSR +/// +/// \param[in,out] io_seed The input seed is converted in one step to the +/// output seed. +/// +/// This 64-bit LFSR uses taps 64, 63, 61, and 60. In big-endian numbering +/// these are bits 0, 1, 3 and 4. This LFSR is also implemented for the PORE +/// engines in the file pore_rand.pS. +void +_lfsr64(uint64_t* io_seed); + +#endif // __LFSR_H__ diff --git a/src/lib/ppc405lib/libppc405files.mk b/src/lib/ppc405lib/libppc405files.mk new file mode 100644 index 0000000..59aa7a0 --- /dev/null +++ b/src/lib/ppc405lib/libppc405files.mk @@ -0,0 +1,74 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/ppc405lib/libppc405files.mk $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file libppc405files.mk +# +# @brief mk for libppc405.a object files +# +# @page ChangeLogs Change Logs +# @section libppc405files.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# INCLUDES +########################################################################## + +C-SOURCES = \ + assert.c \ + byte_pool.c \ + ctype.c \ + ctype_table.c \ + fgetc.c \ + initcall.c \ + lfsr.c \ + mutex.c \ + periodic_semaphore.c \ + polling.c \ + printf.c \ + progress.c \ + puts.c \ + rtx_stdio.c \ + simics_stdio.c \ + sprintf.c \ + ssx_dump.c \ + ssx_io.c \ + stdlib.c \ + strcasecmp.c \ + strdup.c \ + string.c \ + string_stream.c \ + strtox.c \ + sxlock.c \ + time.c + +S-SOURCES = + +LIBPPC405_OBJECTS = $(C-SOURCES:.c=.o) $(S-SOURCES:.S=.o) diff --git a/src/lib/ppc405lib/libssx.h b/src/lib/ppc405lib/libssx.h new file mode 100644 index 0000000..601eb00 --- /dev/null +++ b/src/lib/ppc405lib/libssx.h @@ -0,0 +1,44 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/libssx.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#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/ppc405lib/mutex.c b/src/lib/ppc405lib/mutex.c new file mode 100644 index 0000000..db57f54 --- /dev/null +++ b/src/lib/ppc405lib/mutex.c @@ -0,0 +1,129 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/mutex.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file mutex.c +/// \brief A ThreadX-style mutual exclusion object + +#include "mutex.h" + +int +mutex_create(Mutex* i_mutex) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); + } + + rc = ssx_semaphore_create(&(i_mutex->sem), 1, 1); + i_mutex->thread = 0; + i_mutex->count = 0; + + return rc; +} + + +// If the current thread owns the Mutex we simply increment the count, +// otherwise pend for the semaphore. +// +// Note: It's possible this doesn't need to be done in a critical section. The +// fact that ssx_semaphore_pend() is atomic may be sufficient since it locks +// the Mutex. + +int +mutex_pend(Mutex* i_mutex, SsxInterval i_timeout) +{ + int rc; + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); + SSX_ERROR_UNLESS_THREAD_CONTEXT(); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (i_mutex->thread == ssx_current()) { + + i_mutex->count++; + if (i_mutex->count == 0) { + rc = MUTEX_OVERFLOW; + } else { + rc = 0; + } + + } else { + + rc = ssx_semaphore_pend(&(i_mutex->sem), i_timeout); + if (rc == 0) { + i_mutex->thread = ssx_current(); + i_mutex->count = 1; + } + } + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +// If the current thread owns the Mutex we decrement the count and free the +// object when the count goes to 0. +// +// Note: It's possible this doesn't need to be done in a critical section. The +// fact that ssx_semaphore_pend() is atomic may be sufficient since it locks +// the Mutex. + +int +mutex_post(Mutex* i_mutex) +{ + int rc; + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); + SSX_ERROR_UNLESS_THREAD_CONTEXT(); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + SSX_ERROR_IF(i_mutex->thread != ssx_current(), MUTEX_NOT_OWNED); + + if (--i_mutex->count == 0) { + i_mutex->thread = 0; + rc = ssx_semaphore_post(&(i_mutex->sem)); + } else { + rc = 0; + } + + ssx_critical_section_exit(&ctx); + + return rc; +} + + + + diff --git a/src/lib/ppc405lib/mutex.h b/src/lib/ppc405lib/mutex.h new file mode 100644 index 0000000..6d3a352 --- /dev/null +++ b/src/lib/ppc405lib/mutex.h @@ -0,0 +1,164 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/mutex.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __MUTEX_H__ +#define __MUTEX_H__ + +// $Id$ + +/// \file mutex.h +/// \brief A ThreadX-style mutual exclusion object +/// +/// A Mutex is a binary semaphore with the concept of thread ownership. A +/// thread first obtains the Mutex using the mutex_pend() API, which may block +/// if the Mutex is currently owned by another thread. Once a thread owns a +/// Mutex, subsequent calls of mutex_pend() by the same thread simply +/// increment an internal counter, but do not block. Once a thread has +/// executed a matching mutex_post() call for every mutex_pend() call, the +/// Mutex is free for another thread. +/// +/// This type of mutual exclusion object is useful for example to control +/// access to data structures that are manipulated by APIs with several common +/// entry points. Each call of an API in the chain will 'lock' the data +/// structure using mutex_pend()/mutex_post(). The Mutex semantics allows +/// multiple "locks" by the same thread, but requires a corresponding "unlock" +/// for every "lock". +/// +/// The Mutex usage counter is a 32-bit unsigned integer. If a thread makes +/// 2^32 calls to mutex_pend() without an intervening call of mutex_post(), an +/// overflow is signalled. This error should be considered unrecoverable to +/// the application. +/// +/// Like the SSX semaphore, no record is kept in the thread of which Mutex +/// objects are currently owned by the thread. If a thread terminates or is +/// deleted while holding a Mutex it is likely that the application will +/// hang. Unlike the SSX semaphore, it is absolutely illegal to call +/// mutex_pend() and mutex_post() from interrupt contexts. It is also illegal +/// for a thread to call mutex_post() for a mutex it does not own. +/// +/// Mutex objects are easily created with the static initialization macro +/// MUTEX_INITIALIZATION as in the following example. +/// +/// \code +/// +/// Mutex G_mutex = MUTEX_INITIALIZATION; +/// +/// \endcode +/// +/// The API mutex_create() is also provided for run-time initialization. + +#include "ssx.h" + + +// Mutex error/panic codes + +#define MUTEX_OVERFLOW 0x00688901 +#define MUTEX_NOT_OWNED 0x00688902 + + +#ifndef __ASSEMBLER__ + +/// Static initialization of a Mutex +/// +/// For a full description of the Mutex please see the documentation fof the +/// file mutex.h. +#define MUTEX_INITIALIZATION {SSX_SEMAPHORE_INITIALIZATION(1, 1), 0, 0} + + +/// The Mutex object + +typedef struct { + + /// The binary semaphore + SsxSemaphore sem; + + /// A pointer to the owning thread, or NULL (0) + SsxThread* thread; + + /// The count of unmatched mutex_pend() calls made by the owning thread. + uint32_t count; + +} Mutex; + + +/// Create (initialize) a Mutex +/// +/// \param[in] i_mutex A pointer to the Mutex object to initialize. +/// +/// For a full description of the Mutex please see the documentation for the +/// file mutex.h. +/// +/// \retval 0 Success +/// +/// \retval 0 -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). +int +mutex_create(Mutex* i_mutex); + + +/// Pend on a Mutex with optional timeout +/// +/// \param[in] i_mutex A pointer to the Mutex +/// +/// \param[in] i_timeout Either the constant SSX_WAIT_FOREVER, or a timeout +/// interval specification. +/// +/// For a full description of the Mutex please see the documentation for the +/// file mutex.h. +/// +/// \retval 0 Success +/// +/// \retval -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). +/// +/// \retval -SSX_ILLEGAL_CONTEXT The call was not made from a thread context. +/// +/// \retval -SSX_SEMEPHORE_PEND_TIMED_OUT The thread was not able to obtain +/// the Mutex before the timeout. +/// +/// \retval -MUTEX_OVERFLOW The owning thread has made 2^32 unmatched calls of +/// mutex_pend(). +int +mutex_pend(Mutex* i_mutex, SsxInterval i_timeout); + + +/// Post to a Mutex +/// +/// \param[in] i_mutex A pointer to the Mutex +/// +/// For a full description of the Mutex please see the documentation for the +/// file mutex.h. +/// +/// \retval 0 Success +/// +/// \retval -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). +/// +/// \retval -SSX_ILLEGAL_CONTEXT The call was not made from a thread context. +/// +/// \retval -MUTEX_NOT_OWNED The thread calling mutex_post() does not own the +/// Mutex. +int +mutex_post(Mutex* i_mutex); + +#endif // __ASSEMBLER__ + +#endif // __MUTEX_H__ diff --git a/src/lib/ppc405lib/periodic_semaphore.c b/src/lib/ppc405lib/periodic_semaphore.c new file mode 100644 index 0000000..dec9ea2 --- /dev/null +++ b/src/lib/ppc405lib/periodic_semaphore.c @@ -0,0 +1,114 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/periodic_semaphore.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file perodic_semaphore.h +/// \brief Periodic semphores + +#include "ssx.h" +#include "periodic_semaphore.h" + +// The timer callback is created nonpreemptible, so noncritical interrupts are +// disabled at entry. + +static void +_periodic_semaphore_timeout(void* arg) +{ + PeriodicSemaphore* ps; + + ps = (PeriodicSemaphore*)arg; + + if (ps->sem.count != 1) { + ssx_semaphore_post(&(ps->sem)); + } +} + + +int +periodic_semaphore_create(PeriodicSemaphore* sem, SsxInterval period) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); + } + + do { + rc = ssx_semaphore_create(&(sem->sem), 0, 1); + if (rc) break; + + rc = ssx_timer_create_nonpreemptible(&(sem->timer), + _periodic_semaphore_timeout, + sem); + if (rc) break; + + rc = ssx_timer_schedule(&(sem->timer), + period, + period); + if (rc) break; + + } while (0); + + return rc; +} + + +int +periodic_semaphore_pend(PeriodicSemaphore* sem) +{ + int rc; + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (sem->sem.count == 0) { + + rc = ssx_semaphore_pend(&(sem->sem), SSX_WAIT_FOREVER); + + } else { + + sem->sem.count = 0; + rc = -PERIODIC_SEMAPHORE_OVERRUN; + } + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +int +periodic_semaphore_cancel(PeriodicSemaphore* sem) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); + } + + return ssx_timer_cancel(&(sem->timer)); +} diff --git a/src/lib/ppc405lib/periodic_semaphore.h b/src/lib/ppc405lib/periodic_semaphore.h new file mode 100644 index 0000000..c3c2e53 --- /dev/null +++ b/src/lib/ppc405lib/periodic_semaphore.h @@ -0,0 +1,152 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/periodic_semaphore.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PERIODIC_SEMAPHORE_H__ +#define __PERIODIC_SEMAPHORE_H__ + +// $Id$ + +/// \file perodic_semaphore.h +/// \brief Periodic semphores +/// +/// The PeriodicSemphore is a simple abstraction introduced to simplify coding +/// peridic threads. A periodic thread creates the PeriodicSemaphore after +/// thread initialization, but prior to the entry to the periodic infinite +/// loop. This creates the periodicSemaphore with a count of 0. Once thread +/// processing is finished, the thread pends on the PeriodicSemaphore. A +/// periodic timer posts to the PeriodicSemaphore on a fixed, absolute period +/// to reschedule the thread. +/// +/// If the thread pends on the PeriodicSemaphore and the timer has already +/// posted to the semaphore, the call of periodic_semaphore_pend() clears the +/// semaphore and terminates immediately with the return code +/// -PERIODIC_SEMAPHORE_OVERRUN, indicating that the thread has overrun its +/// period. The thread can choose the appropriate action upon obtaining this +/// return code. +/// +/// The PeriodicSemaphore can also be cancelled, which simply cancels the +/// periodic timer posting to the semaphore. If the thread needs to +/// re-initialize the PeriodicSemaphore for any reason (e.g, to resynchronize +/// after an overrun) it must be cancelled first. + +// Error/Panic codes + +#define PERIODIC_SEMAPHORE_OVERRUN 0x0077e601 + + +/// A periodic semaphore + +typedef struct { + + /// The semaphore + SsxSemaphore sem; + + /// The timer + SsxTimer timer; + +} PeriodicSemaphore; + + +/// Create (initialize) a PeriodicSemaphore +/// +/// \param sem A pointer to an uninitialized or inactive +/// PeriodicSemaphore. +/// +/// \param period The semaphore period +/// +/// This API creates the embedded semaphore as a binary semaphore with an +/// initial value of 0, and schedules a periodic timer to post to the +/// semaphore. +/// +/// \retval 0 Success +/// +/// \retval -SSX_INVALID_OBJECT The \a sem was NULL (0) or otherwise invalid. +/// +/// Other return codes are possible from the embedded calls of SSX APIs. +int +periodic_semaphore_create(PeriodicSemaphore* sem, SsxInterval period); + + +/// Pend on a PeriodicSemaphore +/// +/// \param sem A pointer to an initialized PeriodicSemaphore +/// +/// Pend on a PeriodicSemaphore. It is considered a non-fatal error if the +/// semaphore has a non-0 count as this may indicate that a periodic thread +/// has missed a deadline. +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Success. In particular, the semaphore count was 0 at entry. +/// +/// \retval -PERIODIC_SEMAPHORE_OVERRUN This return code indicates that the +/// semaphore count was 1 at entry. This code is always returned (never causes +/// a panic). +/// +/// The following return codes are error codes: +/// +/// \retval -SSX_IVALID_OBJECT The \a sem was NULL () or otherwise invalid at +/// entry. +/// +/// Other error return codes are possible from embedded calls of SSX APIs. +int +periodic_semaphore_pend(PeriodicSemaphore* sem); + + +/// Cancel a periodic semaphore +/// +/// \param sem A pointer to an initialized PeriodicSemaphore +/// +/// Cancel the PeriodicSemaphore timeout. This is a required step if the +/// PeriodicSemaphore is to be reinitialized. This is also required if the +/// PeriodicSemaphore is created on the thread stack and the thread +/// terminates. PeriodicSemaphore can be canceled at any time. It is never +/// an error to call periodic_semaphore_cancel() on a PeriodicSemaphore object +/// after it is created. +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_TIMER_NOT_ACTIVE The embedded timer is not currently +/// scheduled, meaning that the PeriodicSemaphore was previosly +/// cancelled. +/// +/// The following return codes are error codes: +/// +/// \retval -SSX_IVALID_OBJECT The \a sem was NULL () or otherwise invalid at +/// entry. +/// +/// Other error return codes are possible from embedded calls of SSX APIs. +int +periodic_semaphore_cancel(PeriodicSemaphore* sem); + + +#endif // __PERIODIC_SEMAPHORE_H__ diff --git a/src/lib/ppc405lib/polling.c b/src/lib/ppc405lib/polling.c new file mode 100644 index 0000000..a013202 --- /dev/null +++ b/src/lib/ppc405lib/polling.c @@ -0,0 +1,97 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/polling.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $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/ppc405lib/polling.h index b719ac6..99afbe7 100644 --- a/src/lib/polling.h +++ b/src/lib/ppc405lib/polling.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/polling.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __POLLING_H__ #define __POLLING_H__ diff --git a/src/lib/printf.c b/src/lib/ppc405lib/printf.c index 96f08f3..d75cdf3 100755..100644 --- a/src/lib/printf.c +++ b/src/lib/ppc405lib/printf.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/printf.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ppc405lib/progress.c b/src/lib/ppc405lib/progress.c new file mode 100644 index 0000000..8d2cd30 --- /dev/null +++ b/src/lib/ppc405lib/progress.c @@ -0,0 +1,743 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/progress.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id + +/// \file progress.c +/// \brief Programmable progress (hang) checking +/// +/// This is a simple implementation of a progress (hang) checking +/// facility. The application provides an array of \e pass \e counts that are +/// expected to update/count over time. For simplicity and generality all +/// pass counts are defined to be \c uint64_t types. The +/// progress_checker_create() API initializes the checker, and as a +/// convenience clears the pass counts. +/// +/// The checker can be created with an optional \a callback function, which +/// has the prototype +/// +/// \code +/// typedef int (*ProgressCheckerCallback)(struct ProgressChecker *checker, +/// void* arg, +/// size_t failed) +/// \endcode +/// +/// The checker callback is called \e every time a check is made by +/// progress_checker_check(). In addition to a private void* argument, the +/// parameter list of the callback includes a count of the number of counters +/// that failed to make progress - this count will be 0 if the check was +/// successful. The return value of the callback is passed back as the return +/// value of progress_checker_check(). If the callback is specified as NULL +/// (0), then a successful check returns 0, and any failure causes a return +/// code of -PROGRESS_CHECKER_FAILED. +/// +/// The application can dynamically mark counters as either \e exempt or \e +/// required. By default all counts are required to have increased +/// each time a check is made for progress. Counters marked \e exempt when a +/// progress check is made are not checked for progress. +/// +/// The application can also use the progress_checker_schedule() API to +/// schedule either one-shot or periodic checks. The +/// progress_checker_cancel() API can be used to cancel any scheduled +/// checks. It is never an error to call this API, even if no checks are +/// currently scheduled. Note that each call of progress_checker_schedule() +/// also cancels any outstanding scheduled requests before (re-) scheduling +/// the checker. If using the built-in timer mechanism, any calls of +/// progress_checker_check that return a non-0 value will cause a kernel panic. +/// +/// If failures are detected and caught, the ProgressChecker provides a +/// primitive iteration facility for the callback or the applicaton to +/// determine which counters have failed to update. Calling +/// progress_checker_next_failure() returns either the index of the next +/// failing counter, or -1 to indicate no more failures. This iteration +/// facility is reset every time a check is made by progress_checker_check() +/// (including those made implcitly by the timer-based mechanism). There is no +/// API to reset the iteration. +/// +/// The implemetation provides 2 standard callback functions: +/// progess_checker_printk() and progress_checker_printk_dump(). The former +/// callback uses printk() to print a simple report of failed counters, and if +/// there were any failures it then returns its argument as a return code. If +/// the return code is non-zero then the lack of progress will cause a kernel +/// panic (test failure). The later callback first calls +/// progress_checker_printk(). If progress_checker_printk() returns a non-0 +/// value then progress_checker_printk_dump() enters an SSX_CRITICAL crictal +/// section and prints a full kernel state dump that may be useful to help +/// diagnose the hang. +/// +/// \note We do not make the kernel dump the default or only behavior because +/// it could take 1ms or more to produce the large quantity of formatted +/// output required, which could be a significant amount of wall time in a +/// logic simulation environment. +/// +/// The progress_checker_create() API could be used in a couple of ways as +/// illustrated below: +/// +/// \code +/// +/// ProgressChecker progress; +/// uint64_t counter; +/// +/// progress_checker_create(&progress, "progress", counter, 1, +/// progress_checker_printk, +/// (void*)-PROGRESS_CHECKER_FAILED); +/// +/// OR +/// +/// progress_checker_create(&progress, "progress", counter, 1, +/// progress_checker_printk, 0); +/// +/// \endcode +/// +/// The first usage prints a report and panics the test if lack of progress is +/// detected. The second form simply prints a report in the event of a lack +/// or progress. The second form may be useful to report on counters that +/// only have a statistical probability of making progress, however be aware +/// that the report is generated in an interrupt context and all thread +/// activity will be blocked until the formatted I/O is complete. +/// +/// Notes: +/// +/// This implementation requires the \c byte_pool facility and malloc() to be +/// set up as the ProgressChecker allocates dynamic storage during +/// initialization to store the previous pass counts. +/// +/// It is probably not a good idea to use a single ProgressChecker for both +/// manual and timer-based checking, since there is no protection in the +/// implementation for mutiple accesses to the ProgressChecker. + +#include "progress.h" +#include "ssx_dump.h" + +// The built-in timer callback + +static void +progress_callback(void *arg) +{ + ProgressChecker *checker = (ProgressChecker *)arg; + + if (progress_checker_check(checker)){ + if (0) { + progress_checker_dump(checker); + } + SSX_PANIC(PROGRESS_CHECKER_FAILED); + } +} + + +// Bit-vector operations manage the array of bits using little-endian +// protocols + +static inline void +bit_vector_set(uint8_t *vector, size_t bit) +{ + vector[bit / 8] |= (1 << (bit % 8)); +} + + +static inline void +bit_vector_clear(uint8_t *vector, size_t bit) +{ + vector[bit / 8] &= ~(1 << (bit % 8)); +} + + +static inline int +bit_vector_is_set(uint8_t *vector, size_t bit) +{ + return ((vector[bit / 8] & (1 << (bit % 8))) != 0); +} + + +// NB: We don't have a bit-vector object with a size included. For this +// application we can only call this API if we know that there is at least 1 +// bit set in the vector. + +static size_t +bit_vector_find_first_set(uint8_t *vector) +{ + size_t byte = 0; + + while (vector[byte] == 0) { + byte++; + } + + return (8 * byte) + __builtin_ffs(vector[byte]) - 1; +} + + +/// Create a progress checker +/// +/// \param checker A pointer to an uninitialized or idle ProgressChecker +/// +/// \param name An optional character string associated with the checker +/// +/// \param pass_counts An array of pass counters - the array will be cleared +/// by this API. +/// +/// \param counters The number of counters in the array +/// +/// \param callback This function is called \e every time a check is +/// completed. +/// +/// \param arg The private argument of the callback function +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT A null (0) pointer was provided +/// as the \a pass_counts argument, or the number of \a counters can not be +/// represented as a signed integer. +/// +/// \retval -PROGRESS_CHECKER_ALLOCATION_FAILED Memory allocation of dynamic +/// memory failed. This is treated as a fatal error here. +/// +/// This API may also return or signal other errors from its implementation +/// APIs. + +int +progress_checker_create(ProgressChecker *checker, + const char* name, + uint64_t *pass_counts, + size_t counters, + ProgressCheckerCallback callback, + void *arg) +{ + int rc, bytes; + void *memory; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((checker == 0), + PROGRESS_CHECKER_INVALID_OBJECT); + SSX_ERROR_IF((pass_counts == 0) || + (counters != (int)counters), + PROGRESS_CHECKER_INVALID_ARGUMENT); + } + + // Install and clear the counters + + checker->pass_counts = pass_counts; + memset((void *)pass_counts, 0, counters * sizeof(uint64_t)); + checker->counters = counters; + + // Allocate and clear dynamic memory + + memory = calloc(counters, sizeof(uint64_t)); + checker->saved_counts = (uint64_t *)memory; + + bytes = (counters / 8) + (counters % 8 ? 1 : 0); + checker->bit_vector_bytes = bytes; + + memory = calloc(bytes, 1); + checker->exempt = (uint8_t *)memory; + + memory = calloc(bytes, 1); + checker->failure = (uint8_t *)memory; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((counters != 0) && + ((checker->saved_counts == 0) || + (checker->exempt == 0) || + (checker->failure == 0)), + PROGRESS_CHECKER_ALLOCATION_FAILED); + } + + // Initialize other fields + + checker->name = name; + checker->callback = callback; + checker->arg = arg; + checker->failed = 0; + checker->checks = 0; + + // Initialize the timer structure. + + rc = ssx_timer_create(&(checker->timer), + progress_callback, + (void *)checker); + if (rc) return rc; + + return 0; +} + + +/// Exempt a pass count from progress checking +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter The index of the counter to exempt +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not +/// valid. + +int +progress_checker_exempt(ProgressChecker *checker, + size_t counter) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + SSX_ERROR_IF(counter >= checker->counters, + PROGRESS_CHECKER_INVALID_ARGUMENT); + } + + bit_vector_set(checker->exempt, counter); + + return 0; +} + + +/// Exempt all pass counts from progress checking +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// This API is provided to support applications where pass-count-updating +/// processes are added dynamically. This API coule typically be called +/// immediately after progress_checker_create(). Them, as each process was +/// created it would call progress_checker_require() for the pass count. +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +int +progress_checker_exempt_all(ProgressChecker *checker) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + memset(checker->exempt, -1, checker->bit_vector_bytes); + + return 0; +} + + +/// Require a pass count to update for progress checking +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter The index of the counter to require +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not +/// valid. + +int +progress_checker_require(ProgressChecker *checker, + size_t counter) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + SSX_ERROR_IF(counter >= checker->counters, + PROGRESS_CHECKER_INVALID_ARGUMENT); + } + + bit_vector_clear(checker->exempt, counter); + + return 0; +} + + +/// Require a pass count to update for progress checking avoiding races +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter The index of the counter to require +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// If a pass counter is marked "exempt" but then later marked "required", +/// there is a potential race between the update of the pass counter and the +/// next check, particularly when the checker is scheduled periodically. This +/// form of the progress_checker_require() marks the progress checker such +/// that \e all checks are deferred on the next call of +/// progress_checker_check() targeting the object in order to avoid the race. +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not +/// valid. +int +progress_checker_require_defer(ProgressChecker *checker, + size_t counter) +{ + int rc; + SsxMachineContext ctx; + + ssx_critical_section_enter(SSX_CRITICAL, &ctx); + + rc = progress_checker_require(checker, counter); + checker->defer = 1; + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Check for progress in every required pass counter. +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are not necessarily errors; see \ref +/// ssx_errors +/// +/// \retval various Except for the error listed below, +/// progress_checker_check() returns the code returned by the callback +/// function. If no callback was provided when the checker was created, then 0 +/// is returned for success and -PROGRESS_CHECKER_FAILED is returned in the +/// event of a lack of progress. +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. + +int +progress_checker_check(ProgressChecker *checker) +{ + size_t i; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + // Avoid doing this step unless necessary + + if (checker->failed != 0) { + checker->failed = 0; + memset((void *)checker->failure, 0, checker->bit_vector_bytes); + } + + // Check, unless checking has been deferred for 1 time by + // progress_checker_require_defer(). + + if (checker->defer) { + + checker->defer = 0; + + } else { + + SSX_ATOMIC(SSX_CRITICAL, checker->start_check = ssx_timebase_get()); + + for (i = 0; i < checker->counters; i++) { + + if ((checker->pass_counts[i] <= checker->saved_counts[i]) && + !(bit_vector_is_set(checker->exempt, i))) { + + checker->failed++; + bit_vector_set(checker->failure, i); + } + checker->saved_counts[i] = checker->pass_counts[i]; + } + + SSX_ATOMIC(SSX_CRITICAL, checker->end_check = ssx_timebase_get()); + } + + checker->checks++; + + if (checker->callback) { + return checker->callback(checker, checker->arg, checker->failed); + } else if (checker->failed != 0) { + return -PROGRESS_CHECKER_FAILED; + } else { + return 0; + } +} + + +/// Schedule progress checks (periodically) in the future. +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param interval The relative time of the (first) check +/// +/// \param period If non-zero, checks will be made periodically with this +/// period. +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c. See the SSX documentation for a discussion of +/// timer scheduling in SSX. +/// +/// Return values other than 0 errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// This API may also return or signal other errors from its implementation +/// APIs. + + +int +progress_checker_schedule(ProgressChecker *checker, + SsxInterval interval, + SsxInterval period) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + rc = ssx_timer_cancel(&(checker->timer)); + if (rc != -SSX_TIMER_NOT_ACTIVE) return rc; + + rc = ssx_timer_schedule(&(checker->timer), interval, period); + if (rc) return rc; + + return 0; +} + + +/// Cancel all future (periodic) progress checks +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c. See the SSX documentation for a discussion of +/// timer scheduling in SSX. +/// +/// Return values other than 0 errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// This API may also return or signal other errors from its implementation +/// APIs. + +int +progress_checker_cancel(ProgressChecker *checker) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + rc = ssx_timer_cancel(&(checker->timer)); + if (rc) return rc; + + return 0; +} + + +/// Iterate over progress check failures +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter Will return the index of the next failing counter, or -1 to +/// indicate no more failing counters. +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c. +/// +/// Return values other than 0 errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. + +int +progress_checker_next_failure(ProgressChecker *checker, int *counter) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + if (checker->failed == 0) { + *counter = -1; + return 0; + } + + *counter = bit_vector_find_first_set(checker->failure); + bit_vector_clear(checker->failure, *counter); + checker->failed--; + + return 0; +} + + +/// A standard way to print the results of a progress check failure, suitable +/// for use as a ProgressChecker callback. +/// +/// \param checker The checker - which may or may not have failed. If the +/// checker did fail, then a failure report is printed using printk(). +/// +/// \param arg The value to return in case of failure. In case of +/// success, 0 is returned. +/// +/// \param failed - The number of failed checks + +int +progress_checker_printk(ProgressChecker *checker, + void *arg, + size_t failed) +{ + int counter; + + if (!failed) { + return 0; + } + + printk("---------------------------------------------------------------\n"); + printk("-- Progress check failed for \"%s\" (%p).\n", + checker->name, checker); + printk("-- Check %zu over interval 0x%016llx - 0x%016llx\n", + checker->checks, checker->start_check, checker->end_check); + printk("-- %zu failed counter%s listed below\n", + failed, failed > 1 ? "s are" : " is"); + printk("---------------------------------------------------------------\n"); + + do { + progress_checker_next_failure(checker, &counter); + if (counter < 0) { + break; + } + printk("%4d. 0x%016llx\n", counter, checker->pass_counts[counter]); + } while (1); + + printk("---------------------------------------------------------------\n"); + + return (int)arg; +} + + +/// Call progress_checker_printk(), then create a kernel dump on failure +/// +/// \param checker The checker - which may or may not have failed. If the +/// checker did fail, then a failure report is printed using +/// progress_checker_printk(). +/// +/// \param arg The value to return in case of failure. In case of +/// success, 0 is returned. +/// +/// \param failed - The number of failed checks +/// +/// If progress_checker_printk() fails with a non-0 return code then this API +/// prints a full SSX kernel dump after the progress_checker_printk() report. +int +progress_checker_printk_dump(ProgressChecker *checker, + void *arg, + size_t failed) +{ + int rc; + SsxMachineContext ctx; + + rc = progress_checker_printk(checker, arg, failed); + if (rc != 0) { + ssx_critical_section_enter(SSX_CRITICAL, &ctx); + ssx_dump(ssxout, 0); + ssx_critical_section_exit(&ctx); + } + + return rc; +} + + +/// Dump a progress checker structure using printk() + +void +progress_checker_dump(ProgressChecker *checker) +{ + size_t i; + + printk("Dump of progress checker \"%s\" (%p)\n" + " Counters = %zu\n" + " Checks = %zu\n" + " Failed = %zu\n" + " Callback = %p(%p)\n", + checker->name, checker, checker->counters, checker->checks, + checker->failed, checker->callback, checker->arg); + + printk(" Pass Counts (%p) :\n", checker->pass_counts); + for (i = 0; i < checker->counters; i++) { + printk(" %9d%c 0x%016llx\n", + i, + bit_vector_is_set(checker->exempt, i) ? '*' : ' ', + checker->pass_counts[i]); + } + printk(" Saved Counts (%p) :\n", checker->saved_counts); + for (i = 0; i < checker->counters; i++) { + printk(" %9d 0x%016llx\n", + i, checker->saved_counts[i]); + } +} + + + + + + + + diff --git a/src/lib/ppc405lib/progress.h b/src/lib/ppc405lib/progress.h new file mode 100644 index 0000000..3454a0f --- /dev/null +++ b/src/lib/ppc405lib/progress.h @@ -0,0 +1,177 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/progress.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PROGRESS_H__ +#define __PROGRESS_H__ + +// $Id$ + +/// \file progress.h +/// \brief Programmable progress (hang) checking + +#include <time.h> +#include "ssx.h" +#include "byte_pool.h" +#include "ssx_io.h" + +#ifndef __ASSEMBLER__ + +struct ProgressChecker; + +/// ProgressChecker callback type +/// +/// \param checker The checker that has just been checked +/// +/// \param arg The private argument provided when the checker was created +/// +/// \param failed The number of failed pass counts - 0 indicates no failures + +typedef int (*ProgressCheckerCallback)(struct ProgressChecker* checker, + void* arg, + size_t failed); + + +/// A simple progress (hang) checker. For API details see file progress.c + +typedef struct ProgressChecker { + + /// The application provided pass-count array + uint64_t *pass_counts; + + /// The number of pass-count counters in the array. + size_t counters; + + /// The (optional) name of the checker for reporting purposes. + const char *name; + + /// The (optional) checker callback. + ProgressCheckerCallback callback; + + /// The checker callback private argument + void *arg; + + /// The dynamically-allocated saved pass counts. + uint64_t *saved_counts; + + /// The dynamically-allocated exemption bit-vector + /// + /// \todo Get or implement a generic unlimited-precision bit vector + uint8_t *exempt; + + /// The dynamically-allocated failure bit-vector + uint8_t *failure; + + /// The number of bytes in the bit vector + size_t bit_vector_bytes; + + /// Defer all checking the next time progress_checker_check() is called. + /// + /// See progress_checker_require_defer() + int defer; + + /// The number of failures present in the *failure vector + size_t failed; + + /// A timer object to support time-based checking. + SsxTimer timer; + + /// The number of times progress_checker_check() has been called on the + /// object. + size_t checks; + + /// The time the last check started + SsxTimebase start_check; + + /// The time the last check ended + SsxTimebase end_check; + +} ProgressChecker; + + +int +progress_checker_create(ProgressChecker *checker, + const char *name, + uint64_t *pass_counts, + size_t counters, + ProgressCheckerCallback callback, + void *arg); + +int +progress_checker_exempt(ProgressChecker *checker, + size_t counter); + +int +progress_checker_exempt_all(ProgressChecker *checker); + +int +progress_checker_require(ProgressChecker *checker, + size_t counter); + +int +progress_checker_require_defer(ProgressChecker *checker, + size_t counter); + +int +progress_checker_check(ProgressChecker *checker); + +int +progress_checker_schedule(ProgressChecker *checker, + SsxInterval interval, + SsxInterval period); + +int +progress_checker_cancel(ProgressChecker *checker); + +int +progress_checker_next_failure(ProgressChecker *checker, int *counter); + +int +progress_checker_delete(ProgressChecker *checker); + +int +progress_checker_printk(ProgressChecker *checker, + void *arg, + size_t failed); + +int +progress_checker_printk_dump(ProgressChecker *checker, + void *arg, + size_t failed); + +void +progress_checker_dump(ProgressChecker *checker); + + +#endif /* __ASSEMBLER__ */ + +// Error/Panic codes + +#define PROGRESS_CHECKER_INVALID_OBJECT 0x00776001 +#define PROGRESS_CHECKER_INVALID_ARGUMENT 0x00776002 +#define PROGRESS_CHECKER_FAILED 0x00776003 +#define PROGRESS_CHECKER_CALLBACK_PANIC 0x00776004 +#define PROGRESS_CHECKER_INVARIANT 0x00776005 +#define PROGRESS_CHECKER_ALLOCATION_FAILED 0x00776006 + +#endif /* __PROGRESS_H__ */ diff --git a/src/lib/puts.c b/src/lib/ppc405lib/puts.c index ab24d6b..446671a 100755..100644 --- a/src/lib/puts.c +++ b/src/lib/ppc405lib/puts.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/puts.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ppc405lib/rtx_stdio.c b/src/lib/ppc405lib/rtx_stdio.c new file mode 100644 index 0000000..4bb1a18 --- /dev/null +++ b/src/lib/ppc405lib/rtx_stdio.c @@ -0,0 +1,149 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/rtx_stdio.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file rtx_stdio.c +/// \brief SSX I/O drivers for RTX stdio streams +/// +/// The RTX \a stdout and \a stderr components accept 1, 2 and 4-byte +/// transactions on a 32-bit OCI address and write the data to the RTX +/// job's \a stdout or \a stderr respectively. The \a stdin device is not yet +/// implemented. + +#include "ssx.h" +#include "rtx_stdio.h" + +RtxStdio rtx_stdin; +RtxStdio rtx_stdout; +RtxStdio rtx_stderr; + +int +rtx_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read) +{ + SSX_PANIC(ENXIO); + return -ENXIO; +} + + +int +rtx_stdio_swrite(FILE *stream, const void *buf, + size_t count, size_t *written) +{ + RtxStdio *rtx = (RtxStdio *)stream; + size_t n; + + n = count; + while (n) { + if (n >= 4) { + out32(rtx->address, *((uint32_t *)buf)); + buf += 4; + n -= 4; + } else if (n >= 2) { + out16(rtx->address, *((uint16_t *)buf)); + buf += 2; + n -= 2; + } else { + out8(rtx->address, *((uint8_t *)buf)); + buf++; + n--; + } + } + + if (written != 0) { + *written = count; + } + + return 0; +} + + +ssize_t +rtx_stdio_fflush(FILE *stream) +{ + RtxStdio *rtx = (RtxStdio *)stream; + + out8(rtx->flush_address, 0); + return 0; +} + + +int +rtx_stdin_create(RtxStdio *stream) +{ + FILE *base = (FILE *)stream; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, EBADF); + } + + memset((void *)stream, 0, sizeof(RtxStdio)); + base->sread = rtx_stdio_sread; + + stream->address = RTX_STDIN; + + return 0; +} + + +int +rtx_stdout_create(RtxStdio *stream) +{ + FILE *base = (FILE *)stream; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, EBADF); + } + + memset((void *)stream, 0, sizeof(RtxStdio)); + base->swrite = rtx_stdio_swrite; + base->fflush = rtx_stdio_fflush; + + stream->address = RTX_STDOUT; + stream->flush_address = RTX_STDOUT_FLUSH; + + return 0; +} + + +int +rtx_stderr_create(RtxStdio *stream) +{ + FILE *base = (FILE *)stream; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, ENXIO); + } + + memset((void *)stream, 0, sizeof(RtxStdio)); + base->swrite = rtx_stdio_swrite; + base->fflush = rtx_stdio_fflush; + + stream->address = RTX_STDERR; + stream->flush_address = RTX_STDERR_FLUSH; + + return 0; +} + + diff --git a/src/lib/ppc405lib/rtx_stdio.h b/src/lib/ppc405lib/rtx_stdio.h new file mode 100644 index 0000000..cd2a439 --- /dev/null +++ b/src/lib/ppc405lib/rtx_stdio.h @@ -0,0 +1,74 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/rtx_stdio.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __RTX_STDIO_H__ +#define __RTX_STDIO_H__ + +// $Id$ + +/// \file rtx_stdio.h +/// \brief SSX I/O implementations for RTX stdio streams + +#include "ssx_io.h" +#include "rtx_stdio_addresses.h" + +/// A FILE structure for a RTX fake stdio stream + +typedef struct { + + /// The base class + FILE stream; + + /// The MMIO address of the RTX device for the stream + SsxAddress address; + + /// The MMIO address of the RTX device for flushing the stream; + SsxAddress flush_address; + +} RtxStdio; + +extern RtxStdio rtx_stdin; +extern RtxStdio rtx_stdout; +extern RtxStdio rtx_stderr; + +int +rtx_stdin_create(RtxStdio *stream); + +int +rtx_stdout_create(RtxStdio *stream); + +int +rtx_stderr_create(RtxStdio *stream); + +int +rtx_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read); + +int +rtx_stdio_swrite(FILE *stream, const void *buf, + size_t count, size_t *written); + +int +rtx_stdio_sflush(FILE *stream); + +#endif /* __RTX_STDIO_H__ */ diff --git a/src/lib/ppc405lib/rtx_stdio_addresses.h b/src/lib/ppc405lib/rtx_stdio_addresses.h new file mode 100644 index 0000000..5359f7f --- /dev/null +++ b/src/lib/ppc405lib/rtx_stdio_addresses.h @@ -0,0 +1,55 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/rtx_stdio_addresses.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __RTX_STDIO_ADDRESSES_H__ +#define __RTX_STDIO_ADDRESSES_H__ + +// $Id$ + +/// \file rtx_stdio_addresses.h +/// \brief MMIO addresses and offsets of the rtx fake stdio model +/// +/// The RTX stdio module appears as OCI device #7? Reading 1 byte from the +/// stdin offset returns that byte from stdin. Writing 1, 2 or 4 bytes to the +/// stdout or stderr offsets causes output on that stream. Writing any single +/// byte to the 'flush' offsets flush the stdout or stderr streams. +/// +/// -*- This header is maintained as part of the PMX RTX model. -*- +/// -*- Do not edit in the SSX library as your edits will be lost. -*- + +#define RTX_STDIO_BASE 0x40060000 + +#define RTX_STDIN_OFFSET 0x00 +#define RTX_STDOUT_OFFSET 0x04 +#define RTX_STDOUT_FLUSH_OFFSET 0x08 +#define RTX_STDERR_OFFSET 0x0c +#define RTX_STDERR_FLUSH_OFFSET 0x10 + +#define RTX_STDIN (RTX_STDIO_BASE + RTX_STDIN_OFFSET) +#define RTX_STDOUT (RTX_STDIO_BASE + RTX_STDOUT_OFFSET) +#define RTX_STDOUT_FLUSH (RTX_STDIO_BASE + RTX_STDOUT_FLUSH_OFFSET) +#define RTX_STDERR (RTX_STDIO_BASE + RTX_STDERR_OFFSET) +#define RTX_STDERR_FLUSH (RTX_STDIO_BASE + RTX_STDERR_FLUSH_OFFSET) + +#endif /* __RTX_STDIO_ADDRESSES_H__ */ diff --git a/src/lib/simics_stdio.c b/src/lib/ppc405lib/simics_stdio.c index 5c276a2..6ff1fa9 100755..100644 --- a/src/lib/simics_stdio.c +++ b/src/lib/ppc405lib/simics_stdio.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/simics_stdio.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/simics_stdio.h b/src/lib/ppc405lib/simics_stdio.h index abbfe26..b9ebfcf 100755..100644 --- a/src/lib/simics_stdio.h +++ b/src/lib/ppc405lib/simics_stdio.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/simics_stdio.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __SIMICS_STDIO_H__ #define __SIMICS_STDIO_H__ diff --git a/src/lib/simics_stdio_addresses.h b/src/lib/ppc405lib/simics_stdio_addresses.h index 9f7d886..4554bbf 100755..100644 --- a/src/lib/simics_stdio_addresses.h +++ b/src/lib/ppc405lib/simics_stdio_addresses.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/simics_stdio_addresses.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __SIMICS_STDIO_ADDRESSES_H__ #define __SIMICS_STDIO_ADDRESSES_H__ diff --git a/src/lib/sprintf.c b/src/lib/ppc405lib/sprintf.c index 458cd39..0a95416 100755..100644 --- a/src/lib/sprintf.c +++ b/src/lib/ppc405lib/sprintf.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/sprintf.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ssx_dump.c b/src/lib/ppc405lib/ssx_dump.c index 93ce1ea..52334ab 100644 --- a/src/lib/ssx_dump.c +++ b/src/lib/ppc405lib/ssx_dump.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_dump.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ssx_dump.h b/src/lib/ppc405lib/ssx_dump.h index 17455c0..a413214 100644 --- a/src/lib/ssx_dump.h +++ b/src/lib/ppc405lib/ssx_dump.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_dump.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __SSX_DUMP_H__ #define __SSX_DUMP_H__ diff --git a/src/lib/ssx_io.c b/src/lib/ppc405lib/ssx_io.c index e69dcbe..b517aeb 100755..100644 --- a/src/lib/ssx_io.c +++ b/src/lib/ppc405lib/ssx_io.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_io.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ssx_io.h b/src/lib/ppc405lib/ssx_io.h index 794fe5d..b30b32f 100755..100644 --- a/src/lib/ssx_io.h +++ b/src/lib/ppc405lib/ssx_io.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_io.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __SSX_IO_H__ #define __SSX_IO_H__ diff --git a/src/lib/stdlib.c b/src/lib/ppc405lib/stdlib.c index 2dcc009..a15308d 100755..100644 --- a/src/lib/stdlib.c +++ b/src/lib/ppc405lib/stdlib.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/stdlib.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/strcasecmp.c b/src/lib/ppc405lib/strcasecmp.c index 361a387..fc7585c 100644 --- a/src/lib/strcasecmp.c +++ b/src/lib/ppc405lib/strcasecmp.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strcasecmp.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/ppc405lib/strdup.c b/src/lib/ppc405lib/strdup.c new file mode 100644 index 0000000..6d205c7 --- /dev/null +++ b/src/lib/ppc405lib/strdup.c @@ -0,0 +1,63 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strdup.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: strdup.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strdup.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file strdup.c +/// \brief Functions from <string.h> that require malloc() +/// +/// These APIs are split from string.c for the benefit of applications like +/// OCC FW that don't use malloc(). + +#include <stdlib.h> +#include <string.h> + +/// Duplicate a string +/// +/// \param s The string to duplicate +/// +/// The strdup() function returns a pointer to a new string which is a +/// duplicate of the input string \a s. Memory for the new string is obtained +/// with malloc(), and can be freed with free(). +/// +/// \returns The strdup() function returns a pointer to the duplicated string, +/// or NULL (0) if insufficient memory was available. + +char * +strdup(const char* s) +{ + char* dup; + + dup = (char*)malloc(strlen(s) + 1); + if (dup != 0) { + strcpy(dup, s); + } + return dup; +} diff --git a/src/lib/string_stream.c b/src/lib/ppc405lib/string_stream.c index f3b1889..1dfc439 100755..100644 --- a/src/lib/string_stream.c +++ b/src/lib/ppc405lib/string_stream.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/string_stream.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/string_stream.h b/src/lib/ppc405lib/string_stream.h index 939489c..07891f5 100755..100644 --- a/src/lib/string_stream.h +++ b/src/lib/ppc405lib/string_stream.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/string_stream.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __STRING_STREAM_H__ #define __STRING_STREAM_H__ diff --git a/src/lib/strtox.c b/src/lib/ppc405lib/strtox.c index d901158..56493fa 100755..100644 --- a/src/lib/strtox.c +++ b/src/lib/ppc405lib/strtox.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strtox.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/strtox.h b/src/lib/ppc405lib/strtox.h index b005ca9..db2cef2 100755..100644 --- a/src/lib/strtox.h +++ b/src/lib/ppc405lib/strtox.h @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strtox.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __STRTOX_H__ #define __STRTOX_H__ diff --git a/src/lib/ppc405lib/sxlock.c b/src/lib/ppc405lib/sxlock.c new file mode 100644 index 0000000..35f9698 --- /dev/null +++ b/src/lib/ppc405lib/sxlock.c @@ -0,0 +1,494 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/sxlock.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file sxlock.c +/// \brief API for the SharedExclusiveLock +/// +/// The APIs in this file implement a shared-exclusive lock for SSX +/// applications. This type of lock is also called a readers-writer lock. The +/// lock is implemented in terms of SSX semaphores, so its use is limited to +/// threads willing to block for access to a resource. +/// +/// The SharedExclusiveLock allows multiple threads shared access to a +/// resource, while limiting exclusive access to a single thread. There are +/// several ways that this type of lock might be specified. The specification +/// implemented here is an "exclusive-biasing" lock. As long as the lock is +/// held or requested in exclusive mode, all new shared-mode requests will +/// block, and only exclusive accesses will be allowed. If multiple threads +/// are blocked exclusive the requests are honored in priority order (as +/// the underlying implementation is an SSX semaphore). Once the exclusive +/// lock is cleared, any/all threads blocked for shared access are released +/// simultaneously (using an SSX semaphore as a thread barrier). +/// +/// The lock is created (initialized) by sxlock_create(), which allows +/// specification of an initial value of the number of shared or exclusive +/// accesses outstanding. The lock/unlock APIs are as follows: +/// +/// - sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout) +/// - sxlock_unlock_shared(SharedExclusiveLock* sxlock) +/// - sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout) +/// - sxlock_unlock_exclusive(SharedExclusiveLock* sxlock) +/// +/// Threads must always issue *_lock() and *_unlock() requests in matched +/// pairs in order to avoid errors and deadlock. The *_lock() APIs allow +/// specification of a timeout, which may be SSX_WAIT_FOREVER to indicate no +/// timeout. The *_lock() APIs will return the code -SXLOCK_TIMED_OUT if the +/// timeout occurs before the thread acquires the resource. If called from an +/// interrupt context then the only legal timeout specification is +/// SSX_NO_WAIT (0). +/// +/// If a *_lock() request times out then the thread \e has \e not acquired the +/// resource and \e must \e not call *_unlock(). As with semaphores there is +/// no record that a thread holds a lock, so if a thread completes or is +/// deleted while holding a lock it is likely that the application will +/// deadlock. + +#include "ssx.h" +#include "sxlock.h" + +/// Create (initialize) a SharedExclusiveLock +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to +/// initialize +/// +/// \param shared The initial numbers of shared accesses +/// +/// \param exclusive The initial numbers of exclusive accesses +/// +/// Create (initialize) a SharedExclusiveLock and optionally specify an +/// initial state. The initial number of shared or exclusive accesses can be +/// specified, however at most one of \a nshared and \a exclusive can be +/// non-0. If \a shared or \a exclusive are non-0 then eventually a +/// thread(s) will need to issue unmatched *_unlock() call(s) to allow +/// progress for other threads requiring the resource. +/// +/// \retval 0 Success +/// +/// \retval SXLOCK_INVALID_OBJECT The \a sxlock parameter is NULL (0) or +/// otherwise invalid. +/// +/// \retval SXLOCK_INVALID_ARGUMENT Both of the \a shared and \a exclusive +/// parameters are non-0. +/// +/// \retval others sxlock_create() may also return codes from +/// ssx_semaphore_create(), which would indicate a serious bug. + +int +sxlock_create(SharedExclusiveLock* sxlock, + SsxSemaphoreCount shared, + SsxSemaphoreCount exclusive) +{ + int rc; + + rc = 0; + do { + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, + SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF((shared != 0) && (exclusive != 0), + SXLOCK_INVALID_ARGUMENT); + } + + rc = ssx_semaphore_create(&(sxlock->shared_sem), 0, 0); + if (rc) break; + + rc = ssx_semaphore_create(&(sxlock->exclusive_sem), 0, 0); + if (rc) break; + + sxlock->running_shared = shared; + sxlock->running_exclusive = exclusive; + + } while (0); + + return rc; +} + + +/// Lock a SharedExclusiveLock for shared access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to lock +/// +/// \param timeout The maximum amount of time to wait for access, or the +/// constant SSX_WAIT_FOREVER to wait forever. +/// +/// Acquire a SharedExclusiveLock for shared access, potentially blocking +/// forever or until a specified timeout if access is not immediately +/// granted. Access will be blocked as long as one or more threads request or +/// control exclusive acesss to the resource. Once the access is granted, the +/// thread maintains shared access to the resource until a subsequent call of +/// sxlock_unlock_shared(). +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SXLOCK_TIMED_OUT Shared access was not acquired before the +/// timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), shared access was +/// not immediately available and a non-zero timeout was specified. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + + +int +sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout) +{ + SsxMachineContext ctx; + int rc, pending_exclusive; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + rc = 0; + do { + + // NB: This is the only way to correctly compute the number of threads + // pending exclusive, given that threads could be removed from the + // exclusive_sem by timeout. + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if ((sxlock->running_exclusive == 0) && (pending_exclusive == 0)) { + + // If no other thread has or is requesting exclusive access, the + // current thread gets immediate access. + + sxlock->running_shared++; + + } else { + + // If threads are running or pending exclusive, this thread must + // pend shared. The thread will be unblocked by an exclusive + // unlock, which is responsible for adjusting + // sxlock->running_shared in this case. + + rc = ssx_semaphore_pend(&(sxlock->shared_sem), timeout); + if (rc == -SSX_SEMAPHORE_PEND_TIMED_OUT) { + rc = -SXLOCK_TIMED_OUT; + } + } + + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Relase a SharedExclusiveLock from shared access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to unlock +/// +/// Release a SharedExclusiveLock from shared access, signalling that the +/// thread no longer requires or expects shared access to the resource. It is +/// an error for a thread to use this API if it has not previously locked +/// shared access by a call of sxlock_pend_shared() (or the thread is +/// unlocking a lock initialized in the shared-locked state). +/// +/// Return values other than SSX_OK (0) are errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SXLOCK_SHARED_UNDERFLOW There was apparently no matched call of +/// sxlock_lock_shared() prior to this call. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + +int +sxlock_unlock_shared(SharedExclusiveLock* sxlock) +{ + SsxMachineContext ctx; + int rc, pending_exclusive; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF(sxlock->running_shared == 0, SXLOCK_SHARED_UNDERFLOW); + } + + if (SSX_ERROR_CHECK_KERNEL) { + SSX_PANIC_IF(sxlock->running_exclusive != 0, + SXLOCK_SHARED_EXCLUSIVE_INVARIANT); + } + + rc = 0; + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + do { + + // If this is the last shared thread running, and a thread wants + // exclusive access, grant it. Note that there may be shared requests + // pending on the shared_sem but we always give preference to + // exclusive requests. + + sxlock->running_shared--; + if (sxlock->running_shared == 0) { + + // Wake any single thread pending exclusive + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive != 0) { + + sxlock->running_exclusive = 1; + rc = ssx_semaphore_post(&(sxlock->exclusive_sem)); + if (rc) break; + } + } + + } while(0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Lock a SharedExclusiveLock for exclusive access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to lock +/// +/// \param timeout The maximum amount of time to wait for access, or the +/// constant SSX_WAIT_FOREVER to wait forever. +/// +/// Acquire a SharedExclusiveLock for exclusive access, potentially blocking +/// forever or until a specified timeout if access is not immediately +/// granted. Access will be blocked as long as one or more threads control +/// shared acesss to the resource, however once the thread requests exclusive +/// access all new shared access requests will block. Once the access is +/// granted, the thread maintains exclusive access to the resource until a +/// subsequent call of sxlock_unlock_exclusive(). +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SXLOCK_TIMED_OUT Exclusive access was not acquired before the +/// timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), exclusive access +/// was not immediately available and a non-zero timeout was specified. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which shoudl be considered as non-recoverable errors. + + +int +sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout) +{ + SsxMachineContext ctx; + int rc, pending_exclusive, pending_shared; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + rc = 0; + do { + + if ((sxlock->running_shared == 0) && + (sxlock->running_exclusive == 0)) { + + // If no other thread has acquired the lock, this thread gets + // immediate access. + + sxlock->running_exclusive = 1; + + } else { + + // Some other thread has acquired the lock. This thread must pend + // exclusive. In this case the sxlock->running_exclusive must be + // set by the *_unlock() operation that unblocks the thread. + + rc = ssx_semaphore_pend(&(sxlock->exclusive_sem), timeout); + if (rc == -SSX_SEMAPHORE_PEND_TIMED_OUT) { + + // This exclusive request timed out. Since the request may + // have blocked shared requests, then if this is the only + // exclusive request or thread we need to unblock any pending + // shared requests. + + if (sxlock->running_exclusive == 0) { + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive == 0) { + + rc = ssx_semaphore_info_get(&(sxlock->shared_sem), + 0, &pending_shared); + if (rc) break; + + if (pending_shared != 0) { + + sxlock->running_shared += pending_shared; + rc = ssx_semaphore_release_all(&(sxlock->shared_sem)); + if (rc) break; + } + } + } + + rc = -SXLOCK_TIMED_OUT; + } + } + + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Release a SharedExclusiveLock from exclusive access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to unlock +/// +/// Release a SharedExclusiveLock from exclusive access, signalling that the +/// thread no longer requires or expects exclusive access to the resource. It +/// is an error for a thread to use this API if it has not previously locked +/// exclusive access by a call of sxlock_lock_exclusive() (or the thread is +/// unlocking a lock initialized in the exclusive-locked state). +/// +/// Return values other than SSX_OK (0) are errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SXLOCK_EXCLUSIVE_UNDERFLOW There was apparently no matched call of +/// sxlock_lock_exclusive() prior to this call. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + +int +sxlock_unlock_exclusive(SharedExclusiveLock* sxlock) +{ + SsxMachineContext ctx; + int rc, pending_exclusive, pending_shared; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF(sxlock->running_exclusive != 1, SXLOCK_SHARED_UNDERFLOW); + } + + if (SSX_ERROR_CHECK_KERNEL) { + SSX_PANIC_IF(sxlock->running_shared != 0, + SXLOCK_SHARED_EXCLUSIVE_INVARIANT); + } + + rc = 0; + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + do { + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive != 0) { + + // If there are other threads pending exclusive, make the + // highest-priority one of them + // runnable. sxlock->running_exclusive remains equal to 1. + + rc = ssx_semaphore_post(&(sxlock->exclusive_sem)); + if (rc) break; + + } else { + + // Otherwise unblock any/all threads pending shared + + sxlock->running_exclusive = 0; + + rc = ssx_semaphore_info_get(&(sxlock->shared_sem), + 0, &pending_shared); + if (rc) break; + + if (pending_shared != 0) { + + sxlock->running_shared = pending_shared; + rc = ssx_semaphore_release_all(&(sxlock->shared_sem)); + if (rc) break; + } + } + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} diff --git a/src/lib/ppc405lib/sxlock.h b/src/lib/ppc405lib/sxlock.h new file mode 100644 index 0000000..e0fa196 --- /dev/null +++ b/src/lib/ppc405lib/sxlock.h @@ -0,0 +1,108 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/sxlock.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SXLOCK_H__ +#define __SXLOCK_H__ + +// $Id$ + +/// \file sxlock.h +/// \brief The implementation of a SharedExclusiveLock +/// +/// The SharedExclusiveLock is documented in the comments for the file +/// sxlock.c + +// Error/panic codes + +#define SXLOCK_INVALID_OBJECT 0x00795501 +#define SXLOCK_INVALID_ARGUMENT 0x00795502 +#define SXLOCK_TIMED_OUT 0x00795503 +#define SXLOCK_SHARED_UNDERFLOW 0x00795504 +#define SXLOCK_EXCLUSIVE_UNDERFLOW 0x00795505 +#define SXLOCK_SHARED_EXCLUSIVE_INVARIANT 0x00795506 + +/// A shared-exclusive lock object (also called a readers-write lock) +/// +/// This facility is documented in the file sxlock.c + +typedef struct { + + /// A semaphore for threads requesting shared access + SsxSemaphore shared_sem; + + /// A semaphore for threads requesting exclusive access + SsxSemaphore exclusive_sem; + + /// The number of threads running shared + SsxSemaphoreCount running_shared; + + /// The number of threads running exclusive + SsxSemaphoreCount running_exclusive; + +} SharedExclusiveLock; + + +/// Static initialization of a shared-exclusive lock object +/// +/// \param[in] shared The number of threads running shared at static +/// initialization +/// +/// \param[in] exclusive The number of threads running exclusive at static +/// initialization. +/// +/// Note that it is an error to specify both \a shared and \a exclusive as +/// non-0. + +#define SXLOCK_INITIALIZATION(shared, exclusive) \ + { \ + SSX_SEMAPHORE_INITIALIZATION(0, 0), \ + SSX_SEMAPHORE_INITIALIZATION(0, 0), \ + (shared), (exclusive) \ + } + +/// Declare and initialize a shared-exclusive lock + +#define SXLOCK(sxlock, shared, exclusive) \ + SharedExclusiveLock sxlock = SXLOCK_INITIALIZATION(shared, exclusive) + + + +int +sxlock_create(SharedExclusiveLock* sxlock, + SsxSemaphoreCount shared, + SsxSemaphoreCount exclusive); + +int +sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout); + +int +sxlock_unlock_shared(SharedExclusiveLock* sxlock); + +int +sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout); + +int +sxlock_unlock_exclusive(SharedExclusiveLock* sxlock); + +#endif // __SXLOCK_H__ diff --git a/src/lib/time.c b/src/lib/ppc405lib/time.c index bc1f2e7..39d005c 100755..100644 --- a/src/lib/time.c +++ b/src/lib/ppc405lib/time.c @@ -1,3 +1,27 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/time.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // $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 $ //----------------------------------------------------------------------------- diff --git a/src/lib/pstates.c b/src/lib/pstates.c deleted file mode 100755 index 958b9e1..0000000 --- a/src/lib/pstates.c +++ /dev/null @@ -1,410 +0,0 @@ -// $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 deleted file mode 100755 index 2aeb52c..0000000 --- a/src/lib/pstates.h +++ /dev/null @@ -1,835 +0,0 @@ -#ifndef __PSTATES_H__ -#define __PSTATES_H__ - -// $Id: pstates.h,v 1.5 2015/05/18 15:56:07 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 - -/// VPD #V Operating Points -#define VPD_PV_POINTS 4 -#define VPD_PV_ORDER_STR {"PowerSave", "Nominal ", "Turbo ", "UltraTurbo"} -#define POWERSAVE 0 -#define NOMINAL 1 -#define TURBO 2 -#define ULTRA 3 - -/// IDDQ readings -#define CORE_IDDQ_MEASUREMENTS 6 -#define CHIP_IDDQ_MEASUREMENTS 1 - -#define CORE_IDDQ_ARRAY_VOLTAGES {0.80, 0.90, 1.00, 1.10, 1.20, 1.25} -#define CHIP_IDDQ_ARRAY_VOLTAGES {1.10} - -/// Iddq LRPx and CRPx elements -#define LRP_IDDQ_RECORDS CORE_IDDQ_MEASUREMENTS -#define CRP_IDDQ_RECORDS CHIP_IDDQ_MEASUREMENTS -#define IDDQ_READINGS_PER_IQ 2 - -/// LRPx mapping to Core measurements 1 2 3 4 5 6 -/// Index 0 1 2 3 4 5 -#define CORE_IDDQ_MEASUREMENTS_ORDER { 1, 2, 3, 4, 5, 0} -#define CORE_IDDQ_MEASUREMENT_VOLTAGES {"0.90", "1.00", "1.10", "1.20", "1.25", "0.80"} -#define CORE_IDDQ_VALIDITY_CHECK { 1, 1, 1, 1, 1, 0} -#define CORE_IDDQ_VALID_SECOND { 1, 1, 1, 1, 1, 0} - -// CRPx mapping to Chip measurements 0 -#define CHIP_IDDQ_MEASUREMENTS_ORDER { 0 } -#define CHIP_IDDQ_MEASUREMENT_VOLTAGES {"1.10"} -#define CHIP_IDDQ_VALID_SECOND { 0 } - -/// WOF Items -#define NUM_ACTIVE_CORES 12 -#define MAX_UT_PSTATES 64 // Oversized - -// 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 0x5053544154453034ull /* PSTATE04 */ -#define PSTATE_SUPERSTRUCTURE_GOOD1 0x5053544154453031ull /* PSTATE01 */ -#define PSTATE_SUPERSTRUCTURE_GOOD2 0x5053544154453032ull /* PSTATE02 */ -#define PSTATE_SUPERSTRUCTURE_GOOD3 0x5053544154453033ull /* PSTATE03 */ -#define PSTATE_SUPERSTRUCTURE_GOOD4 0x5053544154453034ull /* 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 - -/// Flag to indicated that the 0.8V readings in the IDDQ Table are valid -#define PSTATE_IDDQ_0P80V_VALID 0x20 -#define PSTATE_IDDQ_0P80V_INVALID ~PSTATE_IDDQ_0P80V_VALID - -/// @} - -#ifndef __ASSEMBLER__ - -#include <stdint.h> - -/// A Global Pstate Table Entry, in the form of a packed 'firmware register' -/// -/// Global Pstate table entries are referenced by OCC firmware, for example -/// in procedures that do 'manual' Pstate manipulation. - -typedef union gpst_entry { - - uint64_t value; - struct { -#ifdef _BIG_ENDIAN - uint32_t high_order; - uint32_t low_order; -#else - uint32_t low_order; - uint32_t high_order; -#endif // _BIG_ENDIAN - } words; - struct { -#ifdef _BIG_ENDIAN - uint64_t evid_vdd : 8; - uint64_t evid_vcs : 8; - uint64_t reserved16 : 1; - uint64_t evid_vdd_eff : 7; - uint64_t reserved24 : 1; - uint64_t evid_vcs_eff : 7; - uint64_t reserved32 : 1; - uint64_t maxreg_vdd : 7; - uint64_t reserved40 : 1; - uint64_t maxreg_vcs : 7; - uint64_t reserved48 : 8; - uint64_t ecc : 8; -#else - uint64_t ecc : 8; - uint64_t reserved48 : 8; - uint64_t maxreg_vcs : 7; - uint64_t reserved40 : 1; - uint64_t maxreg_vdd : 7; - uint64_t reserved32 : 1; - uint64_t evid_vcs_eff : 7; - uint64_t reserved24 : 1; - uint64_t evid_vdd_eff : 7; - uint64_t reserved16 : 1; - uint64_t evid_vcs : 8; - uint64_t evid_vdd : 8; -#endif // _BIG_ENDIAN - } fields; - -} gpst_entry_t; - - -/// A Local Pstate Table Entry, in the form of a packed 'firmware register' -/// -/// This structure is provided for reference only; Currently the OCC firmware -/// does not manupulate Local Pstate table entries, however it is possible -/// that future lab applications will require this. - -typedef union lpst_entry { - - uint64_t value; - struct { -#ifdef _BIG_ENDIAN - uint32_t high_order; - uint32_t low_order; -#else - uint32_t low_order; - uint32_t high_order; -#endif // _BIG_ENDIAN - } words; - struct { -#ifdef _BIG_ENDIAN - uint64_t ivid_vdd : 7; - uint64_t ivid_vcs : 7; - uint64_t vdd_core_pwrratio : 6; - uint64_t vcs_core_pwrratio : 6; - uint64_t vdd_eco_pwrratio : 6; - uint64_t vcs_eco_pwrratio : 6; - uint64_t ps1_vid_incr : 3; - uint64_t ps2_vid_incr : 3; - uint64_t ps3_vid_incr : 3; - uint64_t reserved47 : 7; - uint64_t inc_step : 3; - uint64_t dec_step : 3; - uint64_t reserved60 : 4; -#else - uint64_t reserved60 : 4; - uint64_t dec_step : 3; - uint64_t inc_step : 3; - uint64_t reserved47 : 7; - uint64_t ps3_vid_incr : 3; - uint64_t ps2_vid_incr : 3; - uint64_t ps1_vid_incr : 3; - uint64_t vcs_eco_pwrratio : 6; - uint64_t vdd_eco_pwrratio : 6; - uint64_t vcs_core_pwrratio : 6; - uint64_t vdd_core_pwrratio : 6; - uint64_t ivid_vcs : 7; - uint64_t ivid_vdd : 7; -#endif // _BIG_ENDIAN - } fields; - -} lpst_entry_t; - - -/// A VDS/VIN table Entry - -typedef union vdsvin_entry { - uint64_t value; - struct { -#ifdef _BIG_ENDIAN - uint32_t high_order; - uint32_t low_order; -#else - uint32_t low_order; - uint32_t high_order; -#endif // _BIG_ENDIAN - } words; - struct { -#ifdef _BIG_ENDIAN - uint64_t ivid0 : 7; - uint64_t ivid1 : 7; - uint64_t reserved14 : 2; - uint64_t pfet0 : 5; - uint64_t pfet1 : 5; - uint64_t pfet2 : 5; - uint64_t pfet3 : 5; - uint64_t pfet4 : 5; - uint64_t pfet5 : 5; - uint64_t pfet6 : 5; - uint64_t pfet7 : 5; - uint64_t reserved_56 : 8; -#else - uint64_t reserved_56 : 8; - uint64_t pfet7 : 5; - uint64_t pfet6 : 5; - uint64_t pfet5 : 5; - uint64_t pfet4 : 5; - uint64_t pfet3 : 5; - uint64_t pfet2 : 5; - uint64_t pfet1 : 5; - uint64_t pfet0 : 5; - uint64_t reserved14 : 2; - uint64_t ivid1 : 7; - uint64_t ivid0 : 7; -#endif // _BIG_ENDIAN - } fields; -} vdsvin_entry_t; - -/// Standard options controlling Pstate setup and GPSM procedures - -typedef struct { - - /// Option flags; See \ref pstate_options - uint32_t options; - - /// Pad structure to 8 bytes. Could also be used for other options later. - uint32_t pad; - -} PstateOptions; - - -/// An abstract Global Pstate table -/// -/// The GlobalPstateTable is an abstraction of a set of voltage/frequency -/// operating points along with hardware limits. Besides the hardware global -/// Pstate table, the abstract table contains enough extra information to make -/// it the self-contained source for setting up and managing voltage and -/// frequency in either Hardware or Firmware Pstate mode. -/// -/// When installed in PMC, Global Pstate table indices are adjusted such that -/// the defined Pstates begin with table entry 0. The table need not be full - -/// the \a pmin and \a entries fields define the minimum and maximum Pstates -/// represented in the table. However at least 1 entry must be defined to -/// create a legal table. -/// -/// Note that Global Pstate table structures to be mapped into PMC hardware -/// must be 1KB-aligned. This requirement is fullfilled by ensuring that -/// instances of this structure are 1KB-aligned. - -typedef struct { - - /// The Pstate table - gpst_entry_t pstate[GLOBAL_PSTATE_TABLE_ENTRIES]; - - /// Pstate options - /// - /// The options are included as part of the GlobalPstateTable so that they - /// are available to all procedures after gpsm_initialize(). - PstateOptions options; - - /// The frequency associated with Pstate[0] in KHz - uint32_t pstate0_frequency_khz; - - /// The frequency step in KHz - uint32_t frequency_step_khz; - - /// The DPLL frequency code corresponding to Pstate 0 - /// - /// This frequency code is installed in the PCB Slave as the DPLL Fnom - /// when the Pstate table is activated. Normally this frequency code is - /// computed as - /// - /// pstate0_frequency_khz / frequency_step_khz - /// - /// however it may be replaced by any other code as a way to - /// transparently bias frequency on a per-core basis. - DpllCode pstate0_frequency_code[PGP_NCORES]; - - /// The DPLL Fmax bias - /// - /// This bias value (default 0, range -8 to +7 frequency ticks) is - /// installed when the Pstate table is installed. The value is allowed to - /// vary per core. This bias value will usually be set to a small - /// positive number to provide a small amount of frequency headroom for - /// the CPM-DPLL voltage control algorithm. - /// - /// \bug Hardware currently specifies this field as unsigned for the - /// computation of frequency stability in - /// dpll_freqout_mode_en. (HW217404). This issue will be fixed in - /// Venice. Since we never plan to use this mode no workaround or - /// mitigation is provided by GPSM procedures. - - int8_t dpll_fmax_bias[PGP_NCORES]; - - /// The number of entries defined in the table. - uint8_t entries; - - /// The minimum Pstate in the table - /// - /// Note that gpsi_min = pmin - PSTATE_MIN, gpsi_max = pmin + entries - 1. - Pstate pmin; - - /// The "Safe" Global Pstate - /// - /// This Pstate is installed in the PMC and represents the safe-mode - /// voltage. - Pstate pvsafe; - - /// The "Safe" Local Pstate - /// - /// This Pstate is installed in the PCB Slaves and represents the - /// safe-mode frequency. - Pstate psafe; - - /// Step size of Global Pstate steps - uint8_t pstate_stepsize; - - /// The exponent of the exponential encoding of Pstate stepping delay - uint8_t vrm_stepdelay_range; - - /// The significand of the exponential encoding of Pstate stepping delay - uint8_t vrm_stepdelay_value; - - /// The Pstate for minimum core frequency in the system, defined by MRW - uint8_t pfloor; - - /// The Pstate representing the Turbo VPD point - Pstate turbo_ps; - - /// The Pstate representing the Nominal VPD point - Pstate nominal_ps; - - /// The Pstate representing the PowerSave VPD point - Pstate powersave_ps; - - /// The Pstate within the GPST which is the maximum for which iVRMs are - /// defined. This allows WOF Pstate and iVRM Pstates to be non-overlapping - /// to simplify characterization. - Pstate ivrm_max_ps; - - /// The number of entries over which iVRM enablement is possible. The - /// starting entry is PMin. - uint8_t ivrm_entries; - -} 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; - - -/// A VPD operating point -/// -/// VPD operating points are stored without load-line correction. Frequencies -/// are in MHz, voltages are specified in units of 5mV, and characterization -/// currents are specified in units of 500mA. -/// -/// \bug The assumption is that the 'maxreg' points for the iVRM will also be -/// supplied in the VPD in units of 5mv. If they are supplied in some other -/// form then chip_characterization_create() will need to be modified. - -typedef struct { - - uint32_t vdd_5mv; - uint32_t vcs_5mv; - uint32_t vdd_maxreg_5mv; - uint32_t vcs_maxreg_5mv; - uint32_t idd_500ma; - uint32_t ics_500ma; - uint32_t frequency_mhz; - -} VpdOperatingPoint; - -/// System Power Distribution Paramenters -/// -/// Parameters set by system design that influence the power distribution -/// for a rail to the processor module. This values are typically set in the -/// system machine readable workbook and are used in the generation of the -/// Global Pstate Table. This values are carried in the Pstate SuperStructure -/// for use and/or reference by OCC firmware (eg the WOF algorithm) - -typedef struct { - - /// Loadline - /// Impedance (binary microOhms) of the load line from a processor VDD VRM - /// to the Processor Module pins. - uint32_t loadline_uohm; - - /// Distribution Loss - /// Impedance (binary in microOhms) of the VDD distribution loss sense point - /// to the circuit. - uint32_t distloss_uohm; - - /// Distribution Offset - /// Offset voltage (binary in microvolts) to apply to the rail VRM - /// distribution to the processor module. - uint32_t distoffset_uv; - -} SysPowerDistParms; - - - -/// IDDQ Reading Type -/// Each entry is 2 bytes. The values are in 10mA units; this allow for a -/// maximum value of 655.36A represented. -/// -typedef uint16_t iddq_entry_t; - -/// IDDQ Reading -/// -/// Structure with "raw" and "temperature corrected" values. See VPD -/// documentation for the correction function that is applied to the raw -/// value to load the corrected value. -/// -typedef union { - uint32_t value; - struct { -#ifdef _BIG_ENDIAN - iddq_entry_t iddq_raw_value; - iddq_entry_t iddq_corrected_value; -#else - iddq_entry_t iddq_corrected_value; - iddq_entry_t iddq_raw_value; -#endif // _BIG_ENDIAN - } fields; - -} IddqReading; - -/// Iddq Table -/// -/// A set of arrays of leakage values (Iddq) collected at various voltage -/// conditions during manufacturing test that will feed into the Workload -/// Optimized Frequency algorithms on the OCC. These values are not installed -/// in any hardware facilities. -/// -typedef struct { - - /// IDDQ version - uint8_t iddq_version; - - /// VDD IDDQ readings - IddqReading iddq_vdd[CORE_IDDQ_MEASUREMENTS]; - - /// VCS IDDQ readings - IddqReading iddq_vcs[CORE_IDDQ_MEASUREMENTS]; - - /// VIO IDDQ readings - IddqReading iddq_vio[CHIP_IDDQ_MEASUREMENTS]; - -} IddqTable; - - - -/// UltraTurbo Segment VIDs by Core Count -typedef struct { - - /// Number of Segment Pstates - uint8_t ut_segment_pstates; - - /// Maximum number of core possibly active - uint8_t ut_max_cores; - - /// VDD VID modification - /// 1 core active = offset 0 - /// 2 cores active = offset 1 - /// ... - /// 12 cores active = offset 11 - uint8_t ut_segment_vdd_vid[MAX_UT_PSTATES][NUM_ACTIVE_CORES]; - - /// VCS VID modification - /// 1 core active = offset 0 - /// 2 cores active = offset 1 - /// ... - /// 12 cores active = offset 11 - uint8_t ut_segment_vcs_vid[MAX_UT_PSTATES][NUM_ACTIVE_CORES]; - -} VIDModificationTable; - -/// Workload Optimized Frequency (WOF) Elements -/// -/// Structure defining various control elements needed by the WOF algorithm -/// firmware running on the OCC. -/// -typedef struct { - - /// WOF Enablement - uint8_t wof_enabled; - - /// Operating points - /// - /// VPD operating points are stored without load-line correction. Frequencies - /// are in MHz, voltages are specified in units of 5mV, and currents are - /// in units of 500mA. - VpdOperatingPoint operating_points[VPD_PV_POINTS]; - - /// Loadlines and Distribution values for the VDD rail - SysPowerDistParms vdd_sysparm; - - /// Loadlines and Distribution values for the VCS rail - SysPowerDistParms vcs_sysparm; - - /// TDP<>RDP Current Factor - /// Value read from ??? VPD - /// Defines the scaling factor that converts current (amperage) value from - /// the Thermal Design Point to the Regulator Design Point (RDP) as input - /// to the Workload Optimization Frequency (WOF) OCC algorithm. - /// - /// This is a ratio value and has a granularity of 0.01 decimal. Data - /// is held in hexidecimal (eg 1.22 is represented as 122 and then converted - /// to hex 0x7A). - uint32_t tdp_rdp_factor; - - /// UltraTurbo Segment VIDs by Core Count - VIDModificationTable ut_vid_mod; - - uint8_t pad[4]; - -} WOFElements; - - -/// 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; - - /// Iddq Table - IddqTable iddq; - - /// WOF Controls - WOFElements wof; - -} 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/special_wakeup.c b/src/lib/special_wakeup.c deleted file mode 100644 index 59e3274..0000000 --- a/src/lib/special_wakeup.c +++ /dev/null @@ -1,149 +0,0 @@ -// $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 deleted file mode 100644 index 88b8ec0..0000000 --- a/src/lib/special_wakeup.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __SPECIAL_WAKEUP_H__ -#define __SPECIAL_WAKEUP_H__ - -// $Id: special_wakeup.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/special_wakeup.h,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file special_wakeup.h -/// \brief Container for special wakeup related procedures - -#ifndef __ASSEMBLER__ - -#include <stdint.h> -#include "pgp_config.h" -#include "ssx.h" - -#define SPWU_INVALID_ARGUMENT 0x00779801 - -extern uint32_t G_special_wakeup_count[PGP_NCORES]; - -int -occ_special_wakeup(int set, - ChipConfigCores cores, - int timeout_ms, - ChipConfigCores *o_timeouts); - -#endif /* __ASEMBLER__ */ -#endif /* __SPECIAL_WAKEUP_H__ */ diff --git a/src/lib/strdup.c b/src/lib/strdup.c deleted file mode 100755 index c6ac04c..0000000 --- a/src/lib/strdup.c +++ /dev/null @@ -1,39 +0,0 @@ -// $Id: strdup.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strdup.c,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file strdup.c -/// \brief Functions from <string.h> that require malloc() -/// -/// These APIs are split from string.c for the benefit of applications like -/// OCC FW that don't use malloc(). - -#include <stdlib.h> -#include <string.h> - -/// Duplicate a string -/// -/// \param s The string to duplicate -/// -/// The strdup() function returns a pointer to a new string which is a -/// duplicate of the input string \a s. Memory for the new string is obtained -/// with malloc(), and can be freed with free(). -/// -/// \returns The strdup() function returns a pointer to the duplicated string, -/// or NULL (0) if insufficient memory was available. - -char * -strdup(const char* s) -{ - char* dup; - - dup = (char*)malloc(strlen(s) + 1); - if (dup != 0) { - strcpy(dup, s); - } - return dup; -} diff --git a/src/lib/string.h b/src/lib/string.h deleted file mode 100755 index 2376463..0000000 --- a/src/lib/string.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __STRING_H__ -#define __STRING_H__ - -// $Id: string.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ -// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string.h,v $ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2013 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file string.h -/// \brief Replacement for <string.h> -/// -/// The SSX library does not implement the entire <string.h> function. -/// However the real reason for this header was the finding that under certain -/// optimization modes, we were geting errors from the default <string.h> -/// supplied with the MPC environment. So we created this replacement that -/// only calls out what is implemented, exactly as it is implemented for SSX. - -#ifndef __ASSEMBLER__ - -#include <stddef.h> - -// APIs inmplemented by string.c - -size_t -strlen(const char *s); - -int -strcmp(const char* s1, const char* s2); - -int -strncmp(const char* s1, const char* s2, size_t n); - -int -strcasecmp(const char* s1, const char* s2); - -int -strncasecmp(const char* s1, const char* s2, size_t n); - -char * -strcpy(char *dest, const char *src); - -char * -strncpy(char *dest, const char *src, size_t n); - -void * -memcpy(void *dest, const void *src, size_t n); - -void * -memset(void *s, int c, size_t n); - -int -memcmp(const void* s1, const void* s2, size_t n); - -// APIs implemented by strdup.c - -char * -strdup(const char* s); - -#endif /* __ASSEMBLER__ */ - -#endif /* __STRING_H__ */ diff --git a/src/lib/vrm.c b/src/lib/vrm.c deleted file mode 100755 index 57f19fa..0000000 --- a/src/lib/vrm.c +++ /dev/null @@ -1,394 +0,0 @@ -// $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 deleted file mode 100755 index 2efea4d..0000000 --- a/src/lib/vrm.h +++ /dev/null @@ -1,59 +0,0 @@ -#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__ */ |