diff options
author | Michael Meissner <gnu@the-meissners.org> | 1996-12-10 16:12:48 +0000 |
---|---|---|
committer | Michael Meissner <gnu@the-meissners.org> | 1996-12-10 16:12:48 +0000 |
commit | 3fbe064171ca48e2fafad0a454f561c07aceef7a (patch) | |
tree | 202691d6e02f2d932b55f0c85055be6d696ecebe /sim/ppc | |
parent | a7dd20e8396ab9dd76ea22e0aed0bcda42b9bc54 (diff) | |
download | gdb-3fbe064171ca48e2fafad0a454f561c07aceef7a.zip gdb-3fbe064171ca48e2fafad0a454f561c07aceef7a.tar.gz gdb-3fbe064171ca48e2fafad0a454f561c07aceef7a.tar.bz2 |
New revision from Andrew
Diffstat (limited to 'sim/ppc')
-rw-r--r-- | sim/ppc/.Sanitize | 8 | ||||
-rw-r--r-- | sim/ppc/configure.in | 592 | ||||
-rw-r--r-- | sim/ppc/hw_glue.c | 371 | ||||
-rw-r--r-- | sim/ppc/hw_ide.c | 869 | ||||
-rw-r--r-- | sim/ppc/hw_opic.c | 1827 | ||||
-rw-r--r-- | sim/ppc/hw_pic.c | 98 | ||||
-rw-r--r-- | sim/ppc/hw_trace.c | 108 | ||||
-rw-r--r-- | sim/ppc/tree.h | 139 |
8 files changed, 3635 insertions, 377 deletions
diff --git a/sim/ppc/.Sanitize b/sim/ppc/.Sanitize index 59e646f..c9239bf 100644 --- a/sim/ppc/.Sanitize +++ b/sim/ppc/.Sanitize @@ -96,13 +96,17 @@ hw_cpu.c hw_cpu.h hw_disk.c hw_eeprom.c +hw_glue.c hw_htab.c +hw_ide.c hw_init.c hw_iobus.c hw_memory.c hw_nvram.c +hw_opic.c hw_pal.c -hw_pic.c +hw_phb.c +hw_phb.h hw_register.c hw_trace.c hw_vm.c @@ -158,6 +162,8 @@ sim_calls.c std-config.h table.c table.h +tree.c +tree.h vm.c vm.h vm_n.h diff --git a/sim/ppc/configure.in b/sim/ppc/configure.in index 7662740..fa6ae13 100644 --- a/sim/ppc/configure.in +++ b/sim/ppc/configure.in @@ -13,6 +13,55 @@ else CC_FOR_BUILD=gcc fi + +AC_ARG_ENABLE(sim-alignment, +[ --enable-sim-alignment=align Specify strict or nonstrict alignment.], +[case "${enableval}" in + yes | strict | STRICT) sim_alignment="-DWITH_ALIGNMENT=STRICT_ALIGNMENT";; + no | nonstrict | NONSTRICT) sim_alignment="-DWITH_ALIGNMENT=NONSTRICT_ALIGNMENT";; + 0 | default | DEFAULT) sim_alignment="-DWITH_ALIGNMENT=0";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-alignment"); sim_alignment="";; +esac +if test x"$silent" != x"yes" && test x"$sim_alignment" != x""; then + echo "Setting alignment flags = $sim_alignment" 6>&1 +fi],[sim_alignment=""])dnl + + +AC_ARG_ENABLE(sim-assert, +[ --enable-sim-assert Specify whether to perform random assertions.], +[case "${enableval}" in + yes) sim_assert="-DWITH_ASSERT=1";; + no) sim_assert="-DWITH_ASSERT=0";; + *) AC_MSG_ERROR("--enable-sim-assert does not take a value"); sim_assert="";; +esac +if test x"$silent" != x"yes" && test x"$sim_assert" != x""; then + echo "Setting assert flags = $sim_assert" 6>&1 +fi],[sim_assert=""])dnl + + +AC_ARG_ENABLE(sim-bitsize, +[ --enable-sim-bitsize=n Specify target bitsize (32 or 64).], +[case "${enableval}" in + 32|64) sim_bitsize="-DWITH_TARGET_WORD_BITSIZE=$enableval";; + *) AC_MSG_ERROR("--enable-sim-bitsize was given $enableval. Expected 32 or 64"); sim_bitsize="";; +esac +if test x"$silent" != x"yes" && test x"$sim_bitsize" != x""; then + echo "Setting bitsize flags = $sim_bitsize" 6>&1 +fi],[sim_bitsize=""])dnl + + +AC_ARG_ENABLE(sim-bswap, +[ --enable-sim-bswap Use the BSWAP instruction on Intel 486s and Pentiums.], +[case "${enableval}" in + yes) sim_bswap="-DWITH_BSWAP=1";; + no) sim_bswap="-DWITH_BSWAP=0";; + *) AC_MSG_ERROR("--enable-sim-bswap does not take a value"); sim_bswap="";; +esac +if test x"$silent" != x"yes" && test x"$sim_bswap" != x""; then + echo "Setting bswap flags = $sim_bswap" 6>&1 +fi],[sim_bswap=""])dnl + + AC_ARG_ENABLE(sim-cflags, [ --enable-sim-cflags=opts Extra CFLAGS for use in building simulator], [case "${enableval}" in @@ -24,27 +73,6 @@ if test x"$silent" != x"yes" && test x"$sim_cflags" != x""; then echo "Setting sim cflags = $sim_cflags" 6>&1 fi],[sim_cflags=""])dnl -AC_ARG_ENABLE(sim-warnings, -[ --enable-sim-warnings=opts Extra CFLAGS for turning on compiler warnings except for idecode.o, semantics.o and psim.o], -[case "${enableval}" in - yes) sim_warnings="-Werror -Wall -Wpointer-arith -Wmissing-prototypes";; - no) sim_warnings="-w";; - *) sim_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; -esac -if test x"$silent" != x"yes" && test x"$sim_warnings" != x""; then - echo "Setting warning flags = $sim_warnings" 6>&1 -fi],[sim_warnings=""])dnl - -AC_ARG_ENABLE(sim-line-nr, -[ --enable-sim-line-nr=opts Generate extra CPP code that references source rather than generated code], -[case "${enableval}" in - yes) sim_line_nr="";; - no) sim_line_nr="-L";; - *) AC_MSG_ERROR("--enable-sim-line-nr does not take a value"); sim_line_nr="";; -esac -if test x"$silent" != x"yes" && test x"$sim_line_nr" != x""; then - echo "Setting warning flags = $sim_line_nr" 6>&1 -fi],[sim_line_nr=""])dnl AC_ARG_ENABLE(sim-config, [ --enable-sim-config=file Override default config file], @@ -66,39 +94,17 @@ if test x"$silent" != x"yes"; then echo "Setting config flags = $sim_config" 6>&1 fi])dnl -AC_ARG_ENABLE(sim-opcode, -[ --enable-sim-opcode=which Override default opcode lookup.], -[case "${enableval}" in - yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-opcode=file");; - *) if test -f "${srcdir}/${enableval}"; then - sim_opcode="${enableval}" - elif test -f "${srcdir}/ppc-opcode-${enableval}"; then - sim_opcode="ppc-opcode-${enableval}" - else - AC_MSG_ERROR("File $enableval is not an opcode rules file"); - sim_opcode="ppc-opcode-complex" - fi;; -esac -if test x"$silent" != x"yes" && test x"$sim_opcode" != x""; then - echo "Setting opcode flags = $sim_opcode" 6>&1 -fi],[sim_opcode="ppc-opcode-complex" -if test x"$silent" != x"yes"; then - echo "Setting opcode flags = $sim_opcode" -fi])dnl -AC_ARG_ENABLE(sim-switch, -[ --enable-sim-switch Use a switch instead of a table for instruction call.], +AC_ARG_ENABLE(sim-default-model, +[ --enable-sim-default-model=which Specify default PowerPC to model.], [case "${enableval}" in - yes) sim_switch="-s";; - no) sim_switch="";; - *) AC_MSG_ERROR("--enable-sim-switch does not take a value"); sim_switch="";; + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-default-model=model");; + *) sim_default_model="-DWITH_DEFAULT_MODEL=${enableval}";; esac -if test x"$silent" != x"yes" && test x"$sim_switch" != x""; then - echo "Setting switch flags = $sim_switch" 6>&1 -fi],[sim_switch=""; -if test x"$silent" != x"yes"; then - echo "Setting switch flags = $sim_switch" 6>&1 -fi])dnl +if test x"$silent" != x"yes" && test x"$sim_default_model" != x""; then + echo "Setting default-model flags = $sim_default_model" 6>&1 +fi],[sim_model=""])dnl + AC_ARG_ENABLE(sim-duplicate, [ --enable-sim-duplicate Expand (duplicate) semantic functions.], @@ -114,19 +120,38 @@ if test x"$silent" != x"yes"; then echo "Setting duplicate flags = $sim_dup" 6>&1 fi])dnl -AC_ARG_ENABLE(sim-jump, -[ --enable-sim-jump Jump between semantic code (instead of call/return).], + +AC_ARG_ENABLE(sim-endian, +[ --enable-sim-endian=endian Specify target byte endian orientation.], [case "${enableval}" in - yes) sim_jump="-J";; - no) sim_jump="";; - *) AC_MSG_ERROR("--enable-sim-jump does not take a value"); sim_jump="";; + yes) case "$target" in + *powerpc-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; + *powerpcle-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; + *) echo "Unknown target $target" 1>&6; sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; + esac;; + no) sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; + b*|B*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; + l*|L*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; + *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-endian"); sim_endian="";; esac -if test x"$silent" != x"yes" && test x"$sim_jump" != x""; then - echo "Setting jump flag = $sim_jump" 6>&1 -fi],[sim_jump="-E" -if test x"$silent" != x"yes"; then - echo "Setting jump flag = $sim_jump" 6>&1 -fi])dnl +if test x"$silent" != x"yes" && test x"$sim_endian" != x""; then + echo "Setting endian flags = $sim_endian" 6>&1 +fi],[sim_endian=""])dnl + + +AC_ARG_ENABLE(sim-env, +[ --enable-sim-env=env Specify target environment (operating, virtual, user).], +[case "${enableval}" in + operating | os | oea) sim_env="-DWITH_ENVIRONMENT=OPERATING_ENVIRONMENT";; + virtual | vea) sim_env="-DWITH_ENVIRONMENT=VIRTUAL_ENVIRONMENT";; + user | uea) sim_env="-DWITH_ENVIRONMENT=USER_ENVIRONMENT";; + no) sim_env="-DWITH_ENVIRONMENT=0";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-env"); sim_env="";; +esac +if test x"$silent" != x"yes" && test x"$sim_env" != x""; then + echo "Setting env flags = $sim_env" 6>&1 +fi],[sim_env=""])dnl + AC_ARG_ENABLE(sim-filter, [ --enable-sim-filter=rule Specify filter rules.], @@ -142,6 +167,75 @@ if test x"$silent" != x"yes"; then echo "Setting filter flags = $sim_filter" 6>&1 fi])dnl + +AC_ARG_ENABLE(sim-float, +[ --enable-sim-float Specify whether to use host floating point or simulate.], +[case "${enableval}" in + yes | hard) sim_float="-DWITH_FLOATING_POINT=HARD_FLOATING_POINT";; + no | soft) sim_float="-DWITH_FLOATING_POINT=SOFT_FLOATING_POINT";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-float"); sim_float="";; +esac +if test x"$silent" != x"yes" && test x"$sim_float" != x""; then + echo "Setting float flags = $sim_float" 6>&1 +fi],[sim_float=""])dnl + + +AC_ARG_ENABLE(sim-hardware, +[ --enable-sim-hardware=list Specify the hardware to be included in the build.], +[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide" +case "${enableval}" in + yes) ;; + no) AC_MSG_ERROR("List of hardware must be specified for --enable-sim-hardware"); hardware="";; + ,*) hardware="${hardware}${enableval}";; + *,) hardware="${enableval}${hardware}";; + *) hardware="${enableval}"'';; +esac +sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` +sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes" && test x"$hardware" != x""; then + echo "Setting hardware to $sim_hw_src, $sim_hw_obj" +fi],[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide" +sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` +sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes"; then + echo "Setting hardware to $sim_hw_src, $sim_hw_obj" +fi])dnl + + +AC_ARG_ENABLE(sim-hostbitsize, +[ --enable-sim-hostbitsize=32|64 Specify host bitsize (32 or 64).], +[case "${enableval}" in + 32|64) sim_hostbitsize="-DWITH_HOST_WORD_BITSIZE=$enableval";; + *) AC_MSG_ERROR("--enable-sim-hostbitsize was given $enableval. Expected 32 or 64"); sim_hostbitsize="";; +esac +if test x"$silent" != x"yes" && test x"$sim_hostbitsize" != x""; then + echo "Setting hostbitsize flags = $sim_hostbitsize" 6>&1 +fi],[sim_hostbitsize=""])dnl + + +AC_ARG_ENABLE(sim-hostendian, +[ --enable-sim-hostendain=end Specify host byte endian orientation.], +[case "${enableval}" in + no) sim_hostendian="-DWITH_HOST_BYTE_ORDER=0";; + b*|B*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN";; + l*|L*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN";; + *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-hostendian"); sim_hostendian="";; +esac +if test x"$silent" != x"yes" && test x"$sim_hostendian" != x""; then + echo "Setting hostendian flags = $sim_hostendian" 6>&1 +fi],[ +if test "x$cross_compiling" = "xno"; then + AC_C_BIGENDIAN + if test $ac_cv_c_bigendian = yes; then + sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN" + else + sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN" + fi +else + sim_hostendian="-DWITH_HOST_BYTE_ORDER=0" +fi])dnl + + AC_ARG_ENABLE(sim-icache, [ --enable-sim-icache=size Specify instruction cache size.], [icache="" @@ -168,46 +262,6 @@ if test x"$silent" != x"yes"; then echo "Setting instruction cache size to 1024 ($sim_icache)" fi])dnl -AC_ARG_ENABLE(sim-hardware, -[ --enable-sim-hardware=list Specify the hardware to be included in the build.], -[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com" -case "${enableval}" in - yes) ;; - no) AC_MSG_ERROR("List of hardware must be specified for --enable-sim-hardware"); hardware="";; - ,*) hardware="${hardware}${enableval}";; - *) hardware="${enableval}";; -esac -sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` -sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` -if test x"$silent" != x"yes" && test x"$hardware" != x""; then - echo "Setting hardware to $sim_hw_src, $sim_hw_obj" -fi],[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com" -sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` -sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` -if test x"$silent" != x"yes"; then - echo "Setting hardware to $sim_hw_src, $sim_hw_obj" -fi])dnl - - -AC_ARG_ENABLE(sim-packages, -[ --enable-sim-packages=list Specify the packages to be included in the build.], -[packages=disklabel -case "${enableval}" in - yes) ;; - no) AC_MSG_ERROR("List of packages must be specified for --enable-sim-packages"); packages="";; - ,*) packages="${packages}${enableval}";; - *) packages="${enableval}"'' -esac -sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` -sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` -if test x"$silent" != x"yes" && test x"$packages" != x""; then - echo "Setting packages to $sim_pk_src, $sim_pk_obj" -fi],[packages=disklabel -sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` -sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` -if test x"$silent" != x"yes"; then - echo "Setting packages to $sim_pk_src, $sim_pk_obj" -fi])dnl AC_ARG_ENABLE(sim-inline, [ --enable-sim-inline=inlines Specify which functions should be inlined.], @@ -243,33 +297,113 @@ else sim_inline="" fi])dnl -AC_ARG_ENABLE(sim-bswap, -[ --enable-sim-bswap Use the BSWAP instruction on Intel 486s and Pentiums.], + +AC_ARG_ENABLE(sim-jump, +[ --enable-sim-jump Jump between semantic code (instead of call/return).], [case "${enableval}" in - yes) sim_bswap="-DWITH_BSWAP=1";; - no) sim_bswap="-DWITH_BSWAP=0";; - *) AC_MSG_ERROR("--enable-sim-bswap does not take a value"); sim_bswap="";; + yes) sim_jump="-J";; + no) sim_jump="";; + *) AC_MSG_ERROR("--enable-sim-jump does not take a value"); sim_jump="";; esac -if test x"$silent" != x"yes" && test x"$sim_bswap" != x""; then - echo "Setting bswap flags = $sim_bswap" 6>&1 -fi],[sim_bswap=""])dnl +if test x"$silent" != x"yes" && test x"$sim_jump" != x""; then + echo "Setting jump flag = $sim_jump" 6>&1 +fi],[sim_jump="-E" +if test x"$silent" != x"yes"; then + echo "Setting jump flag = $sim_jump" 6>&1 +fi])dnl -AC_ARG_ENABLE(sim-endian, -[ --enable-sim-endian=endian Specify target byte endian orientation.], + +AC_ARG_ENABLE(sim-line-nr, +[ --enable-sim-line-nr=opts Generate extra CPP code that references source rather than generated code], [case "${enableval}" in - yes) case "$target" in - *powerpc-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; - *powerpcle-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; - *) echo "Unknown target $target" 1>&6; sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; - esac;; - no) sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; - b*|B*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; - l*|L*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; - *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-endian"); sim_endian="";; + yes) sim_line_nr="";; + no) sim_line_nr="-L";; + *) AC_MSG_ERROR("--enable-sim-line-nr does not take a value"); sim_line_nr="";; esac -if test x"$silent" != x"yes" && test x"$sim_endian" != x""; then - echo "Setting endian flags = $sim_endian" 6>&1 -fi],[sim_endian=""])dnl +if test x"$silent" != x"yes" && test x"$sim_line_nr" != x""; then + echo "Setting warning flags = $sim_line_nr" 6>&1 +fi],[sim_line_nr=""])dnl + + +AC_ARG_ENABLE(sim-model, +[ --enable-sim-model=which Specify PowerPC to model.], +[case "${enableval}" in + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-model=model");; + *) sim_model="-DWITH_MODEL=${enableval}";; +esac +if test x"$silent" != x"yes" && test x"$sim_model" != x""; then + echo "Setting model flags = $sim_model" 6>&1 +fi],[sim_model=""])dnl + + +AC_ARG_ENABLE(sim-model-issue, +[ --enable-sim-model-issue Specify whether to simulate model specific actions], +[case "${enableval}" in + yes) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_PROCESS";; + no) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_IGNORE";; + *) AC_MSG_ERROR("--enable-sim-model-issue does not take a value"); sim_model_issue="";; +esac +if test x"$silent" != x"yes"; then + echo "Setting model-issue flags = $sim_model_issue" 6>&1 +fi],[sim_model_issue=""])dnl + + +AC_ARG_ENABLE(sim-monitor, +[ --enable-sim-monitor=mon Specify whether to enable monitoring events.], +[case "${enableval}" in + yes) sim_monitor="-DWITH_MON='MONITOR_INSTRUCTION_ISSUE | MONITOR_LOAD_STORE_UNIT'";; + no) sim_monitor="-DWITH_MON=0";; + instruction) sim_monitor="-DWITH_MON=MONITOR_INSTRUCTION_ISSUE";; + memory) sim_monitor="-DWITH_MON=MONITOR_LOAD_STORE_UNIT";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-mon"); sim_env="";; +esac +if test x"$silent" != x"yes" && test x"$sim_monitor" != x""; then + echo "Setting monitor flags = $sim_monitor" 6>&1 +fi],[sim_monitor=""])dnl + + +AC_ARG_ENABLE(sim-opcode, +[ --enable-sim-opcode=which Override default opcode lookup.], +[case "${enableval}" in + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-opcode=file");; + *) if test -f "${srcdir}/${enableval}"; then + sim_opcode="${enableval}" + elif test -f "${srcdir}/ppc-opcode-${enableval}"; then + sim_opcode="ppc-opcode-${enableval}" + else + AC_MSG_ERROR("File $enableval is not an opcode rules file"); + sim_opcode="ppc-opcode-complex" + fi;; +esac +if test x"$silent" != x"yes" && test x"$sim_opcode" != x""; then + echo "Setting opcode flags = $sim_opcode" 6>&1 +fi],[sim_opcode="ppc-opcode-complex" +if test x"$silent" != x"yes"; then + echo "Setting opcode flags = $sim_opcode" +fi])dnl + + +AC_ARG_ENABLE(sim-packages, +[ --enable-sim-packages=list Specify the packages to be included in the build.], +[packages=disklabel +case "${enableval}" in + yes) ;; + no) AC_MSG_ERROR("List of packages must be specified for --enable-sim-packages"); packages="";; + ,*) packages="${packages}${enableval}";; + *,) packages="${enableval}${packages}";; + *) packages="${enableval}"'';; +esac +sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` +sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes" && test x"$packages" != x""; then + echo "Setting packages to $sim_pk_src, $sim_pk_obj" +fi],[packages=disklabel +sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` +sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes"; then + echo "Setting packages to $sim_pk_src, $sim_pk_obj" +fi])dnl + AC_ARG_ENABLE(sim-regparm, [ --enable-sim-regparm=nr-parm Pass parameters in registers instead of on the stack - x86/GCC specific.], @@ -283,39 +417,18 @@ if test x"$silent" != x"yes" && test x"$sim_regparm" != x""; then echo "Setting regparm flags = $sim_regparm" 6>&1 fi],[sim_regparm=""])dnl -AC_ARG_ENABLE(sim-stdcall, -[ --enable-sim-stdcall=type Use an alternative function call/return mechanism - x86/GCC specific.], -[case "${enableval}" in - no) sim_stdcall="" ;; - std*) sim_stdcall="-DWITH_STDCALL=1";; - yes) sim_stdcall="-DWITH_STDCALL=1";; - *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-stdcall"); sim_stdcall="";; -esac -if test x"$silent" != x"yes" && test x"$sim_stdcall" != x""; then - echo "Setting function call flags = $sim_stdcall" 6>&1 -fi],[sim_stdcall=""])dnl -AC_ARG_ENABLE(sim-hostendian, -[ --enable-sim-hostendain=end Specify host byte endian orientation.], +AC_ARG_ENABLE(sim-reserved-bits, +[ --enable-sim-reserved-bits Specify whether to check reserved bits in instruction.], [case "${enableval}" in - no) sim_hostendian="-DWITH_HOST_BYTE_ORDER=0";; - b*|B*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN";; - l*|L*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN";; - *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-hostendian"); sim_hostendian="";; + yes) sim_reserved="-DWITH_RESERVED_BITS=1";; + no) sim_reserved="-DWITH_RESERVED_BITS=0";; + *) AC_MSG_ERROR("--enable-sim-reserved-bits does not take a value"); sim_reserved="";; esac -if test x"$silent" != x"yes" && test x"$sim_hostendian" != x""; then - echo "Setting hostendian flags = $sim_hostendian" 6>&1 -fi],[ -if test "x$cross_compiling" = "xno"; then - AC_C_BIGENDIAN - if test $ac_cv_c_bigendian = yes; then - sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN" - else - sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN" - fi -else - sim_hostendian="-DWITH_HOST_BYTE_ORDER=0" -fi])dnl +if test x"$silent" != x"yes" && test x"$sim_reserved" != x""; then + echo "Setting reserved flags = $sim_reserved" 6>&1 +fi],[sim_reserved=""])dnl + AC_ARG_ENABLE(sim-smp, [ --enable-sim-smp=n Specify number of processors to configure for.], @@ -331,49 +444,46 @@ if test x"$silent" != x"yes"; then echo "Setting smp flags = $sim_smp" 6>&1 fi])dnl -AC_ARG_ENABLE(sim-xor-endian, -[ --enable-sim-xor-endian=n Specify number bytes involved in PowerPC XOR bi-endian mode (default 8).], -[case "${enableval}" in - yes) sim_xor_endian="-DWITH_XOR_ENDIAN=8";; - no) sim_xor_endian="-DWITH_XOR_ENDIAN=0";; - *) sim_xor_endian="-DWITH_XOR_ENDIAN=$enableval";; -esac -if test x"$silent" != x"yes" && test x"$sim_xor_endian" != x""; then - echo "Setting xor-endian flag = $sim_xor_endian" 6>&1 -fi],[sim_xor_endian=""])dnl -AC_ARG_ENABLE(sim-bitsize, -[ --enable-sim-bitsize=n Specify target bitsize (32 or 64).], +AC_ARG_ENABLE(sim-stdcall, +[ --enable-sim-stdcall=type Use an alternative function call/return mechanism - x86/GCC specific.], [case "${enableval}" in - 32|64) sim_bitsize="-DWITH_TARGET_WORD_BITSIZE=$enableval";; - *) AC_MSG_ERROR("--enable-sim-bitsize was given $enableval, expected 32 or 64"); sim_bitsize="";; + no) sim_stdcall="" ;; + std*) sim_stdcall="-DWITH_STDCALL=1";; + yes) sim_stdcall="-DWITH_STDCALL=1";; + *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-stdcall"); sim_stdcall="";; esac -if test x"$silent" != x"yes" && test x"$sim_bitsize" != x""; then - echo "Setting bitsize flags = $sim_bitsize" 6>&1 -fi],[sim_bitsize=""])dnl +if test x"$silent" != x"yes" && test x"$sim_stdcall" != x""; then + echo "Setting function call flags = $sim_stdcall" 6>&1 +fi],[sim_stdcall=""])dnl -AC_ARG_ENABLE(sim-hostbitsize, -[ --enable-sim-hostbitsize=32|64 Specify host bitsize (32 or 64).], + +AC_ARG_ENABLE(sim-stdio, +[ --enable-sim-stdio Specify whether to use stdio for console input/output.], [case "${enableval}" in - 32|64) sim_hostbitsize="-DWITH_HOST_WORD_BITSIZE=$enableval";; - *) AC_MSG_ERROR("--enable-sim-hostbitsize was given $enableval, expected 32 or 64"); sim_hostbitsize="";; + yes) sim_stdio="-DWITH_STDIO=DO_USE_STDIO";; + no) sim_stdio="-DWITH_STDIO=DONT_USE_STDIO";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-stdio"); sim_stdio="";; esac -if test x"$silent" != x"yes" && test x"$sim_hostbitsize" != x""; then - echo "Setting hostbitsize flags = $sim_hostbitsize" 6>&1 -fi],[sim_hostbitsize=""])dnl +if test x"$silent" != x"yes" && test x"$sim_stdio" != x""; then + echo "Setting stdio flags = $sim_stdio" 6>&1 +fi],[sim_stdio=""])dnl -AC_ARG_ENABLE(sim-env, -[ --enable-sim-env=env Specify target environment (operating, virtual, user).], + +AC_ARG_ENABLE(sim-switch, +[ --enable-sim-switch Use a switch instead of a table for instruction call.], [case "${enableval}" in - operating | os | oea) sim_env="-DWITH_ENVIRONMENT=OPERATING_ENVIRONMENT";; - virtual | vea) sim_env="-DWITH_ENVIRONMENT=VIRTUAL_ENVIRONMENT";; - user | uea) sim_env="-DWITH_ENVIRONMENT=USER_ENVIRONMENT";; - no) sim_env="-DWITH_ENVIRONMENT=0";; - *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-env"); sim_env="";; + yes) sim_switch="-s";; + no) sim_switch="";; + *) AC_MSG_ERROR("--enable-sim-switch does not take a value"); sim_switch="";; esac -if test x"$silent" != x"yes" && test x"$sim_env" != x""; then - echo "Setting env flags = $sim_env" 6>&1 -fi],[sim_env=""])dnl +if test x"$silent" != x"yes" && test x"$sim_switch" != x""; then + echo "Setting switch flags = $sim_switch" 6>&1 +fi],[sim_switch=""; +if test x"$silent" != x"yes"; then + echo "Setting switch flags = $sim_switch" 6>&1 +fi])dnl + AC_ARG_ENABLE(sim-timebase, [ --enable-sim-timebase Specify whether the PPC timebase is supported.], @@ -386,17 +496,6 @@ if test x"$silent" != x"yes" && test x"$sim_timebase" != x""; then echo "Setting timebase flags = $sim_timebase" 6>&1 fi],[sim_timebase=""])dnl -AC_ARG_ENABLE(sim-alignment, -[ --enable-sim-alignment=align Specify strict or nonstrict alignment.], -[case "${enableval}" in - yes | strict | STRICT) sim_alignment="-DWITH_ALIGNMENT=STRICT_ALIGNMENT";; - no | nonstrict | NONSTRICT) sim_alignment="-DWITH_ALIGNMENT=NONSTRICT_ALIGNMENT";; - 0 | default | DEFAULT) sim_alignment="-DWITH_ALIGNMENT=0";; - *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-alignment"); sim_alignment="";; -esac -if test x"$silent" != x"yes" && test x"$sim_alignment" != x""; then - echo "Setting alignment flags = $sim_alignment" 6>&1 -fi],[sim_alignment=""])dnl AC_ARG_ENABLE(sim-trace, [ --enable-sim-trace Specify whether tracing is supported.], @@ -409,93 +508,30 @@ if test x"$silent" != x"yes" && test x"$sim_trace" != x""; then echo "Setting trace flags = $sim_trace" 6>&1 fi],[sim_trace=""])dnl -AC_ARG_ENABLE(sim-assert, -[ --enable-sim-assert Specify whether to perform random assertions.], -[case "${enableval}" in - yes) sim_assert="-DWITH_ASSERT=1";; - no) sim_assert="-DWITH_ASSERT=0";; - *) AC_MSG_ERROR("--enable-sim-assert does not take a value"); sim_assert="";; -esac -if test x"$silent" != x"yes" && test x"$sim_assert" != x""; then - echo "Setting assert flags = $sim_assert" 6>&1 -fi],[sim_assert=""])dnl - -AC_ARG_ENABLE(sim-reserved-bits, -[ --enable-sim-reserved-bits Specify whether to check reserved bits in instruction.], -[case "${enableval}" in - yes) sim_reserved="-DWITH_RESERVED_BITS=1";; - no) sim_reserved="-DWITH_RESERVED_BITS=0";; - *) AC_MSG_ERROR("--enable-sim-reserved-bits does not take a value"); sim_reserved="";; -esac -if test x"$silent" != x"yes" && test x"$sim_reserved" != x""; then - echo "Setting reserved flags = $sim_reserved" 6>&1 -fi],[sim_reserved=""])dnl - -AC_ARG_ENABLE(sim-float, -[ --enable-sim-float Specify whether to use host floating point or simulate.], -[case "${enableval}" in - yes | hard) sim_float="-DWITH_FLOATING_POINT=HARD_FLOATING_POINT";; - no | soft) sim_float="-DWITH_FLOATING_POINT=SOFT_FLOATING_POINT";; - *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-float"); sim_float="";; -esac -if test x"$silent" != x"yes" && test x"$sim_float" != x""; then - echo "Setting float flags = $sim_float" 6>&1 -fi],[sim_float=""])dnl - -AC_ARG_ENABLE(sim-monitor, -[ --enable-sim-monitor=mon Specify whether to enable monitoring events.], -[case "${enableval}" in - yes) sim_monitor="-DWITH_MON='MONITOR_INSTRUCTION_ISSUE | MONITOR_LOAD_STORE_UNIT'";; - no) sim_monitor="-DWITH_MON=0";; - instruction) sim_monitor="-DWITH_MON=MONITOR_INSTRUCTION_ISSUE";; - memory) sim_monitor="-DWITH_MON=MONITOR_LOAD_STORE_UNIT";; - *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-mon"); sim_env="";; -esac -if test x"$silent" != x"yes" && test x"$sim_monitor" != x""; then - echo "Setting monitor flags = $sim_monitor" 6>&1 -fi],[sim_monitor=""])dnl -AC_ARG_ENABLE(sim-model, -[ --enable-sim-model=which Specify PowerPC to model.], +AC_ARG_ENABLE(sim-warnings, +[ --enable-sim-warnings=opts Extra CFLAGS for turning on compiler warnings except for idecode.o, semantics.o and psim.o], [case "${enableval}" in - yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-model=model");; - *) sim_model="-DWITH_MODEL=${enableval}";; + yes) sim_warnings="-Werror -Wall -Wpointer-arith -Wmissing-prototypes -Wmissing-declarations ";; + no) sim_warnings="-w";; + *) sim_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; esac -if test x"$silent" != x"yes" && test x"$sim_model" != x""; then - echo "Setting model flags = $sim_model" 6>&1 -fi],[sim_model=""])dnl +if test x"$silent" != x"yes" && test x"$sim_warnings" != x""; then + echo "Setting warning flags = $sim_warnings" 6>&1 +fi],[sim_warnings=""])dnl -AC_ARG_ENABLE(sim-default-model, -[ --enable-sim-default-model=which Specify default PowerPC to model.], -[case "${enableval}" in - yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-default-model=model");; - *) sim_default_model="-DWITH_DEFAULT_MODEL=${enableval}";; -esac -if test x"$silent" != x"yes" && test x"$sim_default_model" != x""; then - echo "Setting default-model flags = $sim_default_model" 6>&1 -fi],[sim_model=""])dnl -AC_ARG_ENABLE(sim-model-issue, -[ --enable-sim-model-issue Specify whether to simulate model specific actions], +AC_ARG_ENABLE(sim-xor-endian, +[ --enable-sim-xor-endian=n Specify number bytes involved in PowerPC XOR bi-endian mode (default 8).], [case "${enableval}" in - yes) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_PROCESS";; - no) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_IGNORE";; - *) AC_MSG_ERROR("--enable-sim-model-issue does not take a value"); sim_model_issue="";; + yes) sim_xor_endian="-DWITH_XOR_ENDIAN=8";; + no) sim_xor_endian="-DWITH_XOR_ENDIAN=0";; + *) sim_xor_endian="-DWITH_XOR_ENDIAN=$enableval";; esac -if test x"$silent" != x"yes"; then - echo "Setting model-issue flags = $sim_model_issue" 6>&1 -fi],[sim_model_issue=""])dnl +if test x"$silent" != x"yes" && test x"$sim_xor_endian" != x""; then + echo "Setting xor-endian flag = $sim_xor_endian" 6>&1 +fi],[sim_xor_endian=""])dnl -AC_ARG_ENABLE(sim-stdio, -[ --enable-sim-stdio Specify whether to use stdio for console input/output.], -[case "${enableval}" in - yes) sim_stdio="-DWITH_STDIO=DO_USE_STDIO";; - no) sim_stdio="-DWITH_STDIO=DONT_USE_STDIO";; - *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-stdio"); sim_stdio="";; -esac -if test x"$silent" != x"yes" && test x"$sim_stdio" != x""; then - echo "Setting stdio flags = $sim_stdio" 6>&1 -fi],[sim_stdio=""])dnl AC_CONFIG_AUX_DIR(`cd $srcdir;pwd`/../..) AC_CANONICAL_SYSTEM diff --git a/sim/ppc/hw_glue.c b/sim/ppc/hw_glue.c new file mode 100644 index 0000000..d7ad929 --- /dev/null +++ b/sim/ppc/hw_glue.c @@ -0,0 +1,371 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _HW_GLUE_C_ +#define _HW_GLUE_C_ + +#include "device_table.h" + + +/* DEVICE + + + glue - glue to interconnect and test interrupts + + + DESCRIPTION + + + The glue device provides two functions. Firstly, it provides a + mechanism for inspecting and driving the interrupt net. Secondly, + it provides a set of boolean primitives that can be used add + combinatorial operations to the interrupt network. + + Glue devices have a variable number of big endian <<output>> + registers. Each host-word size. The registers can be both read + and written. + + Writing a value to an output register causes an interrupt (of the + specified level) to be driven on the devices corresponding output + interrupt port. + + Reading an <<output>> register returns either the last value + written or the most recently computed value (for that register) as + a result of an interrupt ariving (which ever was computed last). + + At present the following sub device types are available: + + <<glue>>: In addition to driving its output interrupt port with any + value written to an interrupt input port is stored in the + corresponding <<output>> register. Such input interrupts, however, + are not propogated to an output interrupt port. + + <<glue-and>>: The bit-wise AND of the interrupt inputs is computed + and then both stored in <<output>> register zero and propogated to + output interrupt output port zero. + + + PROPERTIES + + + reg = <address> <size> (required) + + Specify the address (within the parent bus) that this device is to + live. The address must be 2048 * sizeof(word) (8k in a 32bit + simulation) aligned. + + + interrupt-ranges = <int-number> <range> (optional) + + If present, this specifies the number of valid interrupt inputs (up + to the maximum of 2048). By default, <<int-number>> is zero and + range is determined by the <<reg>> size. + + + EXAMPLES + + + Enable tracing of the device: + + | -t glue-device \ + + + Create source, bitwize-and, and sink glue devices. Since the + device at address <<0x10000>> is of size <<8>> it will have two + output interrupt ports. + + | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \ + | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \ + | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \ + | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \ + + + Wire the two source interrupts to the AND device: + + | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \ + | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \ + + + Wire the AND device up to the sink so that the and's output is not + left open. + + | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \ + + + With the above configuration. The client program is able to + compute a two bit AND. For instance the <<C>> stub below prints 1 + AND 0. + + | unsigned *input = (void*)0xf0010000; + | unsigned *output = (void*)0xf0030000; + | unsigned ans; + | input[0] = htonl(1); + | input[1] = htonl(0); + | ans = ntohl(*output); + | write_string("AND is "); + | write_int(ans); + | write_line(); + + + BUGS + + + A future implementation of this device may support multiple + interrupt ranges. + + Some of the devices listed may not yet be fully implemented. + + Additional devices such as a dff, an inverter or a latch may be + useful. + + */ + + +enum { + max_nr_interrupts = 2048, +}; + +typedef enum _hw_glue_type { + glue_undefined = 0, + glue_io, + glue_and, + glue_nand, + glue_or, + glue_xor, + glue_nor, + glue_not, +} hw_glue_type; + +typedef struct _hw_glue_device { + hw_glue_type type; + int int_number; + int *input; + int nr_inputs; + unsigned sizeof_input; + /* our output registers */ + int space; + unsigned_word address; + unsigned sizeof_output; + int *output; + int nr_outputs; +} hw_glue_device; + + +static void +hw_glue_init_address(device *me) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + + /* attach to my parent */ + generic_device_init_address(me); + + /* establish the output registers */ + if (glue->output != NULL) { + memset(glue->output, 0, glue->sizeof_output); + } + else { + reg_property_spec unit; + int reg_nr; + /* find a relevant reg entry */ + reg_nr = 0; + while (device_find_reg_array_property(me, "reg", reg_nr, &unit) + && !device_size_to_attach_size(device_parent(me), &unit.size, + &glue->sizeof_output, me)) + reg_nr++; + /* check out the size */ + if (glue->sizeof_output == 0) + device_error(me, "at least one reg property size must be nonzero"); + if (glue->sizeof_output % sizeof(unsigned_word) != 0) + device_error(me, "reg property size must be %d aligned", sizeof(unsigned_word)); + /* and the address */ + device_address_to_attach_address(device_parent(me), + &unit.address, &glue->space, &glue->address, + me); + if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0) + device_error(me, "reg property address must be %d aligned", + sizeof(unsigned_word) * max_nr_interrupts); + glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word); + glue->output = zalloc(glue->sizeof_output); + } + + /* establish the input interrupt ports */ + if (glue->input != NULL) { + memset(glue->input, 0, glue->sizeof_input); + } + else { + const device_property *ranges = device_find_property(me, "interrupt-ranges"); + if (ranges == NULL) { + glue->int_number = 0; + glue->nr_inputs = glue->nr_outputs; + } + else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) { + device_error(me, "invalid interrupt-ranges property (incorrect size)"); + } + else { + const unsigned_cell *int_range = ranges->array; + glue->int_number = BE2H_cell(int_range[0]); + glue->nr_inputs = BE2H_cell(int_range[1]); + } + glue->sizeof_input = glue->nr_inputs * sizeof(unsigned); + glue->input = zalloc(glue->sizeof_input); + } + + /* determine our type */ + if (glue->type == glue_undefined) { + const char *name = device_name(me); + if (strcmp(name, "glue") == 0) + glue->type = glue_io; + else if (strcmp(name, "glue-and") == 0) + glue->type = glue_and; + else + device_error(me, "unimplemented glue type"); + } + + DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n", + glue->int_number, glue->nr_inputs, glue->nr_outputs)); +} + +static unsigned +hw_glue_io_read_buffer_callback(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs; + if (nr_bytes != sizeof(unsigned_word) + || (addr % sizeof(unsigned_word)) != 0) + device_error(me, "missaligned read access (%d:0x%lx:%d) not supported", + space, (unsigned long)addr, nr_bytes); + *(unsigned_word*)dest = H2BE_4(glue->output[reg]); + DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n", + reg, (unsigned long) addr, glue->output[reg])); + return nr_bytes; +} + + +static unsigned +hw_glue_io_write_buffer_callback(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts; + if (nr_bytes != sizeof(unsigned_word) + || (addr % sizeof(unsigned_word)) != 0) + device_error(me, "missaligned write access (%d:0x%lx:%d) not supported", + space, (unsigned long)addr, nr_bytes); + glue->output[reg] = H2BE_4(*(unsigned_word*)source); + DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n", + reg, (unsigned long) addr, glue->output[reg])); + device_interrupt_event(me, reg, glue->output[reg], processor, cia); + return nr_bytes; +} + +static void +hw_glue_interrupt_event(device *me, + int my_port, + device *source, + int source_port, + int level, + cpu *processor, + unsigned_word cia) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + int i; + if (my_port < glue->int_number + || my_port >= glue->int_number + glue->nr_inputs) + device_error(me, "interrupt %d outside of valid range", my_port); + glue->input[my_port - glue->int_number] = level; + switch (glue->type) { + case glue_io: + { + int port = my_port % glue->nr_outputs; + glue->output[port] = level; + DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n", + my_port, + (unsigned long)glue->address + port * sizeof(unsigned_word), + level)); + break; + } + case glue_and: + glue->output[0] = glue->input[0]; + for (i = 1; i < glue->nr_inputs; i++) + glue->output[0] &= glue->input[i]; + DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n", + my_port, level, glue->output[0])); + device_interrupt_event(me, 0, glue->output[0], processor, cia); + break; + default: + device_error(me, "operator not implemented"); + break; + } +} + + +static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = { + { "int", 0, max_nr_interrupts }, + { NULL } +}; + + +static device_callbacks const hw_glue_callbacks = { + { hw_glue_init_address, NULL }, + { NULL, }, /* address */ + { hw_glue_io_read_buffer_callback, + hw_glue_io_write_buffer_callback, }, + { NULL, }, /* DMA */ + { hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */ + { NULL, }, /* unit */ + NULL, /* instance */ +}; + + +static void * +hw_glue_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + /* create the descriptor */ + hw_glue_device *glue = ZALLOC(hw_glue_device); + return glue; +} + + +const device_descriptor hw_glue_device_descriptor[] = { + { "glue", hw_glue_create, &hw_glue_callbacks }, + { "glue-and", hw_glue_create, &hw_glue_callbacks }, + { "glue-nand", hw_glue_create, &hw_glue_callbacks }, + { "glue-or", hw_glue_create, &hw_glue_callbacks }, + { "glue-xor", hw_glue_create, &hw_glue_callbacks }, + { "glue-nor", hw_glue_create, &hw_glue_callbacks }, + { "glue-not", hw_glue_create, &hw_glue_callbacks }, + { NULL }, +}; + +#endif /* _HW_GLUE_C_ */ diff --git a/sim/ppc/hw_ide.c b/sim/ppc/hw_ide.c new file mode 100644 index 0000000..00d54b3 --- /dev/null +++ b/sim/ppc/hw_ide.c @@ -0,0 +1,869 @@ +/* This file is part of the program psim. + + Copyright (C) 1996, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _HW_IDE_C_ +#define _HW_IDE_C_ + +#include "device_table.h" + + + +/* DEVICE + + + ide - Integrated Disk Electronics + + + DESCRIPTION + + + This device models the primary/secondary <<ide>> controller + described in the [CHRPIO] document. + + The controller has separate independant interrupt outputs for each + <<ide>> bus. + + + PROPERTIES + + + reg = ... (required) + + The <<reg>> property is described in the document [CHRPIO]. + + + ready-delay = <integer> (optional) + + If present, this specifies the time that the <<ide>> device takes + to complete an I/O operation. + + + disk@?/ide-byte-count = <integer> (optional) + + disk@?/ide-sector-count = <integer> (optional) + + disk@?/ide-head-count = <integer> (optional) + + The <<ide>> device checks each child (disk device) node to see if + it has the above properties. If present, these values will be used + to compute the <<LBA>> address in <<CHS>> addressing mode. + + + EXAMPLES + + + Enable tracing: + + | -t ide-device \ + + + Attach the <<ide>> device to the <<pci>> bus at slot one. Specify + legacy I/O addresses: + + | -o '/phb/ide@1/assigned-addresses \ + | ni0,0,10,1f0 8 \ + | ni0,0,14,3f8 8 \ + | ni0,0,18,170 8 \ + | ni0,0,1c,378 8 \ + | ni0,0,20,200 8' \ + | -o '/phb@0x80000000/ide@1/reg \ + | 1 0 \ + | i0,0,10,0 8 \ + | i0,0,18,0 8 \ + | i0,0,14,6 1 \ + | i0,0,1c,6 1 \ + | i0,0,20,0 8' \ + + Note: the fouth and fifth reg entries specify that the register is + at an offset into the address specified by the base register + (<<assigned-addresses>>); Apart from restrictions placed by the + <<pci>> specification, no restrictions are placed on the number of + base registers specified by the <<assigned-addresses>> property. + + Attach a <<disk>> to the primary and a <<cdrom>> to the secondary + <<ide>> controller. + + | -o '/phb@0x80000000/ide@1/disk@0/file "zero' \ + | -o '/phb@0x80000000/ide@1/cdrom@2/file "/dev/cdrom"' \ + + Connect the two interrupt outputs (a and b) to a <<glue>> device to + allow testing of the interrupt port. In a real simulation they + would be wired to the interrupt controller. + + | -o '/phb@0x80000000/glue@2/reg 2 0 ni0,0,0,0 8' \ + | -o '/phb@0x80000000/ide@1 > a 0 /phb@0x80000000/glue@2' \ + | -o '/phb@0x80000000/ide@1 > b 1 /phb@0x80000000/glue@2' + + + BUGS + + + While the DMA registers are present, DMA support has not yet been + implemented. + + The number of supported commands is very limited. + + The standards documents appear to be vague on how to specify the + <<unit-address>> of disk devices devices being attached to the + <<ide>> controller. I've chosen to use integers with devices zero + and one going to the primary controller while two and three are + connected to the secondary controller. + + + REFERENCES + + + [CHRPIO] PowerPC(tm) Microprocessor Common Hardware Reference + Platform: I/O Device Reference. http://chrp.apple.com/???. + + [SCHMIDT] The SCSI Bus and IDE Interface - Protocols, Applications + and Programming. Friedhelm Schmidt (translated by Michael + Schultz). ISBN 0-201-42284-0. Addison-Wesley Publishing Company. + + + */ + + + +typedef enum _io_direction { + is_read, + is_write, +} io_direction; + + +enum { + nr_ide_controllers = 2, + nr_ide_drives_per_controller = 2, + nr_fifo_entries = 8192, +}; + +enum { + /* command register block - read */ + ide_data_reg, + ide_error_reg, /*ide_feature_reg*/ + ide_sector_count_reg, + ide_sector_number_reg, + ide_cylinder_reg0, + ide_cylinder_reg1, + ide_drive_head_reg, + ide_status_reg, /*ide_command_reg*/ + /* command register block - write */ + ide_feature_reg, /*ide_error_reg*/ + ide_command_reg, /*ide_status_reg*/ + /* control register block - read */ + ide_alternate_status_reg, /*ide_control_reg*/ + ide_control_reg, /*ide_alternate_status_reg*/ + /* dma register block */ + ide_dma_command_reg, + ide_dma_unused_1_reg, + ide_dma_status_reg, + ide_dma_unused_3_reg, + ide_dma_prd_table_address_reg0, + ide_dma_prd_table_address_reg1, + ide_dma_prd_table_address_reg2, + ide_dma_prd_table_address_reg3, + nr_ide_registers, +}; + + +typedef enum _ide_states { + idle_state, + busy_loaded_state, + busy_drained_state, + busy_dma_state, + busy_command_state, + loading_state, + draining_state, +} ide_states; + +static const char * +ide_state_name(ide_states state) +{ + switch (state) { + case idle_state: return "idle"; + case busy_loaded_state: return "busy_loaded_state"; + case busy_drained_state: return "busy_drained_state"; + case busy_dma_state: return "busy_dma_state"; + case busy_command_state: return "busy_command_state"; + case loading_state: return "loading_state"; + case draining_state: return "draining_state"; + default: return "illegal-state"; + } +} + +typedef struct _ide_geometry { + int head; + int sector; + int byte; +} ide_geometry; + +typedef struct _ide_drive { + int nr; + device *device; + ide_geometry geometry; + ide_geometry default_geometry; +} ide_drive; + +typedef struct _ide_controller { + int nr; + ide_states state; + unsigned8 reg[nr_ide_registers]; + unsigned8 fifo[nr_fifo_entries]; + int fifo_pos; + int fifo_size; + ide_drive *current_drive; + int current_byte; + int current_transfer; + ide_drive drive[nr_ide_drives_per_controller]; + device *me; + event_entry_tag event_tag; + int is_interrupting; + signed64 ready_delay; +} ide_controller; + + + +static void +set_interrupt(device *me, + ide_controller *controller) +{ + if ((controller->reg[ide_control_reg] & 0x2) == 0) { + DTRACE(ide, ("controller %d - interrupt set\n", controller->nr)); + device_interrupt_event(me, controller->nr, 1, NULL, 0); + controller->is_interrupting = 1; + } +} + + +static void +clear_interrupt(device *me, + ide_controller *controller) +{ + if (controller->is_interrupting) { + DTRACE(ide, ("controller %d - interrupt clear\n", controller->nr)); + device_interrupt_event(me, controller->nr, 0, NULL, 0); + controller->is_interrupting = 0; + } +} + + +static void +do_event(void *data) +{ + ide_controller *controller = data; + device *me = controller->me; + controller->event_tag = 0; + switch (controller->state) { + case busy_loaded_state: + case busy_drained_state: + if (controller->current_transfer > 0) { + controller->state = (controller->state == busy_loaded_state + ? loading_state : draining_state); + } + else { + controller->state = idle_state; + } + set_interrupt(me, controller); + break; + default: + device_error(me, "controller %d - unexpected event", controller->nr); + break; + } +} + + +static void +schedule_ready_event(device *me, + ide_controller *controller) +{ + if (controller->event_tag != 0) + device_error(me, "controller %d - attempting to schedule multiple events", + controller->nr); + controller->event_tag = + device_event_queue_schedule(me, controller->ready_delay, + do_event, controller); +} + + +static void +do_fifo_read(device *me, + ide_controller *controller, + void *dest, + int nr_bytes) +{ + if (controller->state != draining_state) + device_error(me, "controller %d - reading fifo when not ready (%s)", + controller->nr, + ide_state_name(controller->state)); + if (controller->fifo_pos + nr_bytes > controller->fifo_size) + device_error(me, "controller %d - fifo underflow", controller->nr); + if (nr_bytes > 0) { + memcpy(dest, &controller->fifo[controller->fifo_pos], nr_bytes); + controller->fifo_pos += nr_bytes; + } + if (controller->fifo_pos == controller->fifo_size) { + controller->current_transfer -= 1; + if (controller->current_transfer > 0 + && controller->current_drive != NULL) { + DTRACE(ide, ("controller %d:%d - reading %d byte block at 0x%x\n", + controller->nr, + controller->current_drive->nr, + controller->fifo_size, + controller->current_byte)); + if (device_io_read_buffer(controller->current_drive->device, + controller->fifo, + 0, controller->current_byte, + controller->fifo_size, + NULL, 0) + != controller->fifo_size) + device_error(me, "controller %d - disk %s io read error", + controller->nr, + device_path(controller->current_drive->device)); + } + controller->state = busy_drained_state; + controller->fifo_pos = 0; + controller->current_byte += controller->fifo_size; + schedule_ready_event(me, controller); + } +} + + +static void +do_fifo_write(device *me, + ide_controller *controller, + const void *source, + int nr_bytes) +{ + if (controller->state != loading_state) + device_error(me, "controller %d - writing fifo when not ready (%s)", + controller->nr, + ide_state_name(controller->state)); + if (controller->fifo_pos + nr_bytes > controller->fifo_size) + device_error(me, "controller %d - fifo overflow", controller->nr); + if (nr_bytes > 0) { + memcpy(&controller->fifo[controller->fifo_pos], source, nr_bytes); + controller->fifo_pos += nr_bytes; + } + if (controller->fifo_pos == controller->fifo_size) { + if (controller->current_transfer > 0 + && controller->current_drive != NULL) { + DTRACE(ide, ("controller %d:%d - writing %d byte block at 0x%x\n", + controller->nr, + controller->current_drive->nr, + controller->fifo_size, + controller->current_byte)); + if (device_io_write_buffer(controller->current_drive->device, + controller->fifo, + 0, controller->current_byte, + controller->fifo_size, + NULL, 0) + != controller->fifo_size) + device_error(me, "controller %d - disk %s io write error", + controller->nr, + device_path(controller->current_drive->device)); + } + controller->current_transfer -= 1; + controller->fifo_pos = 0; + controller->current_byte += controller->fifo_size; + controller->state = busy_loaded_state; + schedule_ready_event(me, controller); + } +} + + +static void +setup_fifo(device *me, + ide_controller *controller, + int is_simple, + int is_with_disk, + io_direction direction) +{ + /* find the disk */ + if (is_with_disk) { + int drive_nr = (controller->reg[ide_drive_head_reg] & 0x10) != 0; + controller->current_drive = &controller->drive[drive_nr]; + } + else { + controller->current_drive = NULL; + } + + /* number of transfers */ + if (is_simple) + controller->current_transfer = 1; + else { + int sector_count = controller->reg[ide_sector_count_reg]; + if (sector_count == 0) + controller->current_transfer = 256; + else + controller->current_transfer = sector_count; + } + + /* the transfer size */ + if (controller->current_drive == NULL) + controller->fifo_size = 512; + else + controller->fifo_size = controller->current_drive->geometry.byte; + + /* empty the fifo */ + controller->fifo_pos = 0; + + /* the starting address */ + if (controller->current_drive == NULL) + controller->current_byte = 0; + else if (controller->reg[ide_drive_head_reg] & 0x40) { + /* LBA addressing mode */ + controller->current_byte = controller->fifo_size + * (((controller->reg[ide_drive_head_reg] & 0xf) << 24) + | (controller->reg[ide_cylinder_reg1] << 16) + | (controller->reg[ide_cylinder_reg0] << 8) + | (controller->reg[ide_sector_number_reg])); + } + else if (controller->current_drive->geometry.head != 0 + && controller->current_drive->geometry.sector != 0) { + /* CHS addressing mode */ + int head_nr = controller->reg[ide_drive_head_reg] & 0xf; + int cylinder_nr = ((controller->reg[ide_cylinder_reg1] << 8) + | controller->reg[ide_cylinder_reg0]); + int sector_nr = controller->reg[ide_sector_number_reg]; + controller->current_byte = controller->fifo_size + * ((cylinder_nr * controller->current_drive->geometry.head + head_nr) + * controller->current_drive->geometry.sector + sector_nr - 1); + } + else + device_error(me, "controller %d:%d - CHS addressing disabled", + controller->nr, controller->current_drive->nr); + DTRACE(ide, ("controller %ld:%ld - transfer (%s) %ld blocks of %ld bytes from 0x%lx\n", + (long)controller->nr, + controller->current_drive == NULL ? -1L : (long)controller->current_drive->nr, + direction == is_read ? "read" : "write", + (long)controller->current_transfer, + (long)controller->fifo_size, + (unsigned long)controller->current_byte)); + switch (direction) { + case is_read: + /* force a primeing read */ + controller->current_transfer += 1; + controller->state = draining_state; + controller->fifo_pos = controller->fifo_size; + do_fifo_read(me, controller, NULL, 0); + break; + case is_write: + controller->state = loading_state; + break; + } +} + + +static void +do_command(device *me, + ide_controller *controller, + int command) +{ + if (controller->state != idle_state) + device_error(me, "controller %d - command when not idle", controller->nr); + switch (command) { + case 0x20: case 0x21: /* read-sectors */ + setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_read); + break; + case 0x30: case 0x31: /* write */ + setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_write); + break; + } +} + +static unsigned8 +get_status(device *me, + ide_controller *controller) +{ + switch (controller->state) { + case loading_state: + case draining_state: + return 0x08; /* data req */ + case busy_loaded_state: + case busy_drained_state: + return 0x80; /* busy */ + case idle_state: + return 0x40; /* drive ready */ + default: + device_error(me, "internal error"); + return 0; + } +} + + +/* The address presented to the IDE controler is decoded and then + mapped onto a controller:reg pair */ + +enum { + nr_address_blocks = 6, +}; + +typedef struct _address_block { + int space; + unsigned_word base_addr; + unsigned_word bound_addr; + int controller; + int base_reg; +} address_block; + +typedef struct _address_decoder { + address_block block[nr_address_blocks]; +} address_decoder; + +static void +decode_address(device *me, + address_decoder *decoder, + int space, + unsigned_word address, + int *controller, + int *reg, + io_direction direction) +{ + int i; + for (i = 0; i < nr_address_blocks; i++) { + if (space == decoder->block[i].space + && address >= decoder->block[i].base_addr + && address <= decoder->block[i].bound_addr) { + *controller = decoder->block[i].controller; + *reg = (address + - decoder->block[i].base_addr + + decoder->block[i].base_reg); + if (direction == is_write) { + switch (*reg) { + case ide_error_reg: *reg = ide_feature_reg; break; + case ide_status_reg: *reg = ide_command_reg; break; + case ide_alternate_status_reg: *reg = ide_control_reg; break; + default: break; + } + } + return; + } + } + device_error(me, "address %d:0x%lx invalid", + space, (unsigned long)address); +} + + +static void +build_address_decoder(device *me, + address_decoder *decoder) +{ + int reg; + for (reg = 1; reg < 6; reg++) { + reg_property_spec unit; + int space; + unsigned_word address; + unsigned size; + /* find and decode the reg property */ + if (!device_find_reg_array_property(me, "reg", reg, &unit)) + device_error(me, "missing or invalid reg entry %d", reg); + device_address_to_attach_address(device_parent(me), &unit.address, + &space, &address, me); + device_size_to_attach_size(device_parent(me), &unit.size, &size, me); + /* insert it into the address decoder */ + switch (reg) { + case 1: + case 2: + /* command register block */ + if (size != 8) + device_error(me, "reg entry %d must have a size of 8", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + size - 1; + decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; + decoder->block[reg-1].base_reg = ide_data_reg; + DTRACE(ide, ("controller %d command register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + break; + case 3: + case 4: + /* control register block */ + if (size != 1) + device_error(me, "reg entry %d must have a size of 1", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + size - 1; + decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; + decoder->block[reg-1].base_reg = ide_alternate_status_reg; + DTRACE(ide, ("controller %d control register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + break; + case 5: + /* dma register block */ + if (size != 8) + device_error(me, "reg entry %d must have a size of 8", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + 4 - 1; + decoder->block[reg-1].base_reg = ide_dma_command_reg; + decoder->block[reg-1].controller = 0; + DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + decoder->block[reg].space = space; + decoder->block[reg].base_addr = address + 4; + decoder->block[reg].bound_addr = address + 8 - 1; + decoder->block[reg].controller = 1; + decoder->block[reg].base_reg = ide_dma_command_reg; + DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", + decoder->block[reg].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg].base_addr, + (unsigned long)decoder->block[reg].bound_addr)); + break; + default: + device_error(me, "internal error - bad switch"); + break; + } + } +} + + + +typedef struct _hw_ide_device { + ide_controller controller[nr_ide_controllers]; + address_decoder decoder; +} hw_ide_device; + + +static void +hw_ide_init_address(device *me) +{ + hw_ide_device *ide = device_data(me); + int controller; + int drive; + + /* zero some things */ + for (controller = 0; controller < nr_ide_controllers; controller++) { + memset(&ide->controller[controller], 0, sizeof(ide_controller)); + for (drive = 0; drive < nr_ide_drives_per_controller; drive++) { + ide->controller[controller].drive[drive].nr = drive; + } + ide->controller[controller].me = me; + if (device_find_property(me, "ready-delay") != NULL) + ide->controller[controller].ready_delay = + device_find_integer_property(me, "ready-delay"); + } + + /* attach this device to its parent */ + generic_device_init_address(me); + + /* determine our own address map */ + build_address_decoder(me, &ide->decoder); + +} + + +static void +hw_ide_attach_address(device *me, + attach_type type, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + hw_ide_device *ide = (hw_ide_device*)device_data(me); + int controller_nr = addr / nr_ide_drives_per_controller; + int drive_nr = addr % nr_ide_drives_per_controller; + ide_controller *controller; + ide_drive *drive; + if (controller_nr >= nr_ide_controllers) + device_error(me, "no controller for disk %s", + device_path(client)); + + controller = &ide->controller[controller_nr]; + drive = &controller->drive[drive_nr]; + drive->device = client; + if (device_find_property(client, "ide-byte-count") != NULL) + drive->geometry.byte = device_find_integer_property(client, "ide-byte-count"); + else + drive->geometry.byte = 512; + if (device_find_property(client, "ide-sector-count") != NULL) + drive->geometry.sector = device_find_integer_property(client, "ide-sector-count"); + if (device_find_property(client, "ide-head-count") != NULL) + drive->geometry.head = device_find_integer_property(client, "ide-head-count"); + drive->default_geometry = drive->geometry; + DTRACE(ide, ("controller %d:%d %s byte-count %d, sector-count %d, head-count %d\n", + controller_nr, + drive->nr, + device_path(client), + drive->geometry.byte, + drive->geometry.sector, + drive->geometry.head)); +} + + +static unsigned +hw_ide_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_ide_device *ide = (hw_ide_device *)device_data(me); + int control_nr; + int reg; + ide_controller *controller; + + /* find the interface */ + decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_read); + controller = & ide->controller[control_nr]; + + /* process the transfer */ + memset(dest, 0, nr_bytes); + switch (reg) { + case ide_data_reg: + do_fifo_read(me, controller, dest, nr_bytes); + break; + case ide_status_reg: + *(unsigned8*)dest = get_status(me, controller); + clear_interrupt(me, controller); + break; + case ide_alternate_status_reg: + *(unsigned8*)dest = get_status(me, controller); + break; + case ide_error_reg: + case ide_sector_count_reg: + case ide_sector_number_reg: + case ide_cylinder_reg0: + case ide_cylinder_reg1: + case ide_drive_head_reg: + case ide_control_reg: + case ide_dma_command_reg: + case ide_dma_status_reg: + case ide_dma_prd_table_address_reg0: + case ide_dma_prd_table_address_reg1: + case ide_dma_prd_table_address_reg2: + case ide_dma_prd_table_address_reg3: + *(unsigned8*)dest = controller->reg[reg]; + break; + default: + device_error(me, "bus-error at address 0x%lx", addr); + break; + } + return nr_bytes; +} + + +static unsigned +hw_ide_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_ide_device *ide = (hw_ide_device *)device_data(me); + int control_nr; + int reg; + ide_controller *controller; + + /* find the interface */ + decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_write); + controller = &ide->controller[control_nr]; + + /* process the access */ + switch (reg) { + case ide_data_reg: + do_fifo_write(me, controller, source, nr_bytes); + break; + case ide_command_reg: + do_command(me, controller, *(unsigned8*)source); + break; + case ide_control_reg: + controller->reg[reg] = *(unsigned8*)source; + /* possibly cancel interrupts */ + if ((controller->reg[reg] & 0x02) == 0x02) + clear_interrupt(me, controller); + break; + case ide_feature_reg: + case ide_sector_count_reg: + case ide_sector_number_reg: + case ide_cylinder_reg0: + case ide_cylinder_reg1: + case ide_drive_head_reg: + case ide_dma_command_reg: + case ide_dma_status_reg: + case ide_dma_prd_table_address_reg0: + case ide_dma_prd_table_address_reg1: + case ide_dma_prd_table_address_reg2: + case ide_dma_prd_table_address_reg3: + controller->reg[reg] = *(unsigned8*)source; + break; + default: + device_error(me, "bus-error at 0x%lx", addr); + break; + } + return nr_bytes; +} + + +static const device_interrupt_port_descriptor hw_ide_interrupt_ports[] = { + { "a", 0, 0 }, + { "b", 1, 0 }, + { "c", 2, 0 }, + { "d", 3, 0 }, + { NULL } +}; + + + +static device_callbacks const hw_ide_callbacks = { + { hw_ide_init_address, }, + { hw_ide_attach_address, }, /* attach */ + { hw_ide_io_read_buffer, hw_ide_io_write_buffer, }, + { NULL, }, /* DMA */ + { NULL, NULL, hw_ide_interrupt_ports }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, + generic_device_address_to_attach_address, + generic_device_size_to_attach_size }, +}; + + +static void * +hw_ide_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_ide_device *ide = ZALLOC(hw_ide_device); + return ide; +} + + +const device_descriptor hw_ide_device_descriptor[] = { + { "ide", hw_ide_create, &hw_ide_callbacks }, + { NULL, }, +}; + +#endif /* _HW_IDE_ */ diff --git a/sim/ppc/hw_opic.c b/sim/ppc/hw_opic.c new file mode 100644 index 0000000..c314347 --- /dev/null +++ b/sim/ppc/hw_opic.c @@ -0,0 +1,1827 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _HW_OPIC_C_ +#define _HW_OPIC_C_ + +#include "device_table.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + + +/* DEVICE + + + opic - Open Programmable Interrupt Controller (OpenPIC) + + + DESCRIPTION + + + This device implements the core of the OpenPIC interrupt controller + as described in the OpenPIC specification 1.2 and other related + documents. + + The model includes: + + o Up to 2048 external interrupt sources + + o The four count down timers + + o The four interprocessor multicast interrupts + + o multiprocessor support + + o Full tracing to assist help debugging + + o Support for all variations of edge/level x high/low polarity. + + + + PROPERTIES + + + reg = <address> <size> ... (required) + + Determine where the device lives in the parents address space. The + first <<address>> <<size>> pair specifies the address of the + interrupt destination unit (which might contain an interrupt source + unit) while successive reg entries specify additional interrupt + source units. + + Note that for an <<opic>> device attached to a <<pci>> bus, the + first <<reg>> entry may need to be ignored it will be the address + of the devices configuration registers. + + + interrupt-ranges = <int-number> <range> ... (required) + + A list of pairs. Each pair corresponds to a block of interrupt + source units (the address of which being specified by the + corresponding reg tupple). <<int-number>> is the number of the + first interrupt in the block while <<range>> is the number of + interrupts in the block. + + + timer-frequency = <integer> (optional) + + If present, specifies the default value of the timer frequency + reporting register. By default a value of 1 HZ is used. The value + is arbitrary, the timers are always updated once per machine cycle. + + + vendor-identification = <integer> (optional) + + If present, specifies the value to be returned when the vendor + identification register is read. + + + EXAMPLES + + + See the test suite directory: + + | psim-test/hw-opic + + + BUGS + + For an OPIC controller attached to a PCI bus, it is not clear what + the value of the <<reg>> and <<interrupt-ranges>> properties should + be. In particular, the PCI firmware bindings require the first + value of the <<reg>> property to specify the devices configuration + address while the OpenPIC bindings require that same entry to + specify the address of the Interrupt Delivery Unit. This + implementation checks for and, if present, ignores any + configuration address (and its corresponding <<interrupt-ranges>> + entry). + + The OpenPIC specification requires the controller to be fair when + distributing interrupts between processors. At present the + algorithm used isn't fair. It is biased towards processor zero. + + The OpenPIC specification includes a 8259 pass through mode. This + is not supported. + + + REFERENCES + + + PowerPC Multiprocessor Interrupt Controller (MPIC), January 19, + 1996. Available from IBM. + + + The Open Programmable Interrupt Controller (PIC) Register Interface + Specification Revision 1.2. Issue Date: Opctober 1995. Available + somewhere on AMD's web page (http://www.amd.com/) + + + PowerPC Microprocessor Common Hardware Reference Platform (CHRP) + System bindings to: IEEE Std 1275-1994 Standard for Boot + (Initialization, Configuration) Firmware. Revision 1.2b (INTERIM + DRAFT). April 22, 1996. Available on the Open Firmware web site + http://playground.sun.com/p1275/. + + + */ + + +/* forward types */ + +typedef struct _hw_opic_device hw_opic_device; + + +/* bounds */ + +enum { + max_nr_interrupt_sources = 2048, + max_nr_interrupt_destinations = 32, + max_nr_task_priorities = 16, +}; + + +enum { + opic_alignment = 16, +}; + + +/* global configuration register */ + +enum { + gcr0_8259_bit = 0x20000000, + gcr0_reset_bit = 0x80000000, +}; + + +/* offsets and sizes */ + +enum { + idu_isu_base = 0x10000, + sizeof_isu_register_block = 32, + idu_per_processor_register_base = 0x20000, + sizeof_idu_per_processor_register_block = 0x1000, + idu_timer_base = 0x01100, + sizeof_timer_register_block = 0x00040, +}; + + +/* Interrupt sources */ + +enum { + isu_mask_bit = 0x80000000, + isu_active_bit = 0x40000000, + isu_multicast_bit = 0x20000000, + isu_positive_polarity_bit = 0x00800000, + isu_level_triggered_bit = 0x00400000, + isu_priority_shift = 16, + isu_vector_bits = 0x000000ff, +}; + + +typedef struct _opic_interrupt_source { + unsigned is_masked; /* left in place */ + unsigned is_multicast; /* left in place */ + unsigned is_positive_polarity; /* left in place */ + unsigned is_level_triggered; /* left in place */ + unsigned priority; + unsigned vector; + /* misc */ + int nr; + unsigned destination; + unsigned pending; + unsigned in_service; +} opic_interrupt_source; + + +/* interrupt destinations (normally processors) */ + +typedef struct _opic_interrupt_destination { + int nr; + unsigned base_priority; + opic_interrupt_source *current_pending; + opic_interrupt_source *current_in_service; + unsigned bit; + int init_port; + int intr_port; +} opic_interrupt_destination; + + +/* address map descriptors */ + +typedef struct _opic_isu_block { /* interrupt source unit block */ + int space; + unsigned_word address; + unsigned size; + unsigned_cell int_number; + unsigned_cell range; + int reg; +} opic_isu_block; + + +typedef struct _opic_idu { /* interrupt delivery unit */ + int reg; + int space; + unsigned_word address; + unsigned size; +} opic_idu; + +typedef enum { + /* bad */ + invalid_opic_register, + /* interrupt source */ + interrupt_source_N_destination_register, + interrupt_source_N_vector_priority_register, + /* timers */ + timer_N_destination_register, + timer_N_vector_priority_register, + timer_N_base_count_register, + timer_N_current_count_register, + timer_frequency_reporting_register, + /* inter-processor interrupts */ + ipi_N_vector_priority_register, + ipi_N_dispatch_register, + /* global configuration */ + spurious_vector_register, + processor_init_register, + vendor_identification_register, + global_configuration_register_N, + feature_reporting_register_N, + /* per processor */ + end_of_interrupt_register_N, + interrupt_acknowledge_register_N, + current_task_priority_register_N, +} opic_register; + +static const char * +opic_register_name(opic_register type) +{ + switch (type) { + case invalid_opic_register: return "invalid_opic_register"; + case interrupt_source_N_destination_register: return "interrupt_source_N_destination_register"; + case interrupt_source_N_vector_priority_register: return "interrupt_source_N_vector_priority_register"; + case timer_N_destination_register: return "timer_N_destination_register"; + case timer_N_vector_priority_register: return "timer_N_vector_priority_register"; + case timer_N_base_count_register: return "timer_N_base_count_register"; + case timer_N_current_count_register: return "timer_N_current_count_register"; + case timer_frequency_reporting_register: return "timer_frequency_reporting_register"; + case ipi_N_vector_priority_register: return "ipi_N_vector_priority_register"; + case ipi_N_dispatch_register: return "ipi_N_dispatch_register"; + case spurious_vector_register: return "spurious_vector_register"; + case processor_init_register: return "processor_init_register"; + case vendor_identification_register: return "vendor_identification_register"; + case global_configuration_register_N: return "global_configuration_register_N"; + case feature_reporting_register_N: return "feature_reporting_register_N"; + case end_of_interrupt_register_N: return "end_of_interrupt_register_N"; + case interrupt_acknowledge_register_N: return "interrupt_acknowledge_register_N"; + case current_task_priority_register_N: return "current_task_priority_register_N"; + } + return NULL; +} + + + +/* timers */ + +typedef struct _opic_timer { + int nr; + device *me; /* find my way home */ + hw_opic_device *opic; /* ditto */ + unsigned base_count; + int inhibited; + signed64 count; /* *ONLY* if inhibited */ + event_entry_tag timeout_event; + opic_interrupt_source *interrupt_source; +} opic_timer; + + +/* the OPIC */ + +struct _hw_opic_device { + + /* vendor id */ + unsigned vendor_identification; + + /* interrupt destinations - processors */ + int nr_interrupt_destinations; + opic_interrupt_destination *interrupt_destination; + unsigned sizeof_interrupt_destination; + + /* bogus interrupts */ + int spurious_vector; + + /* interrupt sources - external interrupt source units + extra internal ones */ + int nr_interrupt_sources; + opic_interrupt_source *interrupt_source; + unsigned sizeof_interrupt_source; + + /* external interrupts */ + int nr_external_interrupts; + opic_interrupt_source *external_interrupt_source; + + /* inter-processor-interrupts */ + int nr_interprocessor_interrupts; + opic_interrupt_source *interprocessor_interrupt_source; + + /* timers */ + int nr_timer_interrupts; + opic_timer *timer; + unsigned sizeof_timer; + opic_interrupt_source *timer_interrupt_source; + unsigned timer_frequency; + + /* init register */ + unsigned32 init; + + /* address maps */ + opic_idu idu; + int nr_isu_blocks; + opic_isu_block *isu_block; +}; + + +static void +hw_opic_init_data(device *me) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + int isb; + int idu_reg; + int nr_isu_blocks; + int i; + + /* determine the first valid reg property entry (there could be + leading reg entries with invalid (zero) size fields) and the + number of isu entries found in the reg property. */ + idu_reg = 0; + nr_isu_blocks = 0; + while (1) { + reg_property_spec unit; + int attach_space; + unsigned_word attach_address; + unsigned attach_size; + if (!device_find_reg_array_property(me, "reg", idu_reg + nr_isu_blocks, + &unit)) + break; + if (nr_isu_blocks > 0 + || (device_address_to_attach_address(device_parent(me), &unit.address, + &attach_space, &attach_address, + me) + && device_size_to_attach_size(device_parent(me), &unit.size, + &attach_size, + me))) { + /* we count any thing once we've found one valid address/size pair */ + nr_isu_blocks += 1; + } + else { + idu_reg += 1; + } + } + + /* determine the number and location of the multiple interrupt + source units and the single interrupt delivery unit */ + if (opic->isu_block == NULL) { + int reg_nr; + opic->nr_isu_blocks = nr_isu_blocks; + opic->isu_block = zalloc(sizeof(opic_isu_block) * opic->nr_isu_blocks); + isb = 0; + reg_nr = idu_reg; + while (isb < opic->nr_isu_blocks) { + reg_property_spec reg; + if (!device_find_reg_array_property(me, "reg", reg_nr, ®)) + device_error(me, "reg property missing entry number %d", reg_nr); + opic->isu_block[isb].reg = reg_nr; + if (!device_address_to_attach_address(device_parent(me), ®.address, + &opic->isu_block[isb].space, + &opic->isu_block[isb].address, + me) + || !device_size_to_attach_size(device_parent(me), ®.size, + &opic->isu_block[isb].size, + me)) { + device_error(me, "reg property entry %d invalid", reg_nr); + } + if (!device_find_integer_array_property(me, "interrupt-ranges", + reg_nr * 2, + &opic->isu_block[isb].int_number) + || !device_find_integer_array_property(me, "interrupt-ranges", + reg_nr * 2 + 1, + &opic->isu_block[isb].range)) + device_error(me, "missing or invalid interrupt-ranges property entry %d", reg_nr); + /* first reg entry specifies the address of both the IDU and the + first set of ISU registers, adjust things accordingly */ + if (reg_nr == idu_reg) { + opic->idu.reg = opic->isu_block[isb].reg; + opic->idu.space = opic->isu_block[isb].space; + opic->idu.address = opic->isu_block[isb].address; + opic->idu.size = opic->isu_block[isb].size; + opic->isu_block[isb].address += idu_isu_base; + opic->isu_block[isb].size = opic->isu_block[isb].range * (16 + 16); + } + /* was this a valid reg entry? */ + if (opic->isu_block[isb].range == 0) { + opic->nr_isu_blocks -= 1; + } + else { + opic->nr_external_interrupts += opic->isu_block[isb].range; + isb++; + } + reg_nr++; + } + } + DTRACE(opic, ("interrupt source unit block - effective number of blocks %d\n", + (int)opic->nr_isu_blocks)); + + + /* the number of other interrupts */ + opic->nr_interprocessor_interrupts = 4; + opic->nr_timer_interrupts = 4; + + + /* create space for the interrupt source registers */ + if (opic->interrupt_source != NULL) { + memset(opic->interrupt_source, 0, opic->sizeof_interrupt_source); + } + else { + opic->nr_interrupt_sources = (opic->nr_external_interrupts + + opic->nr_interprocessor_interrupts + + opic->nr_timer_interrupts); + if (opic->nr_interrupt_sources > max_nr_interrupt_sources) + device_error(me, "number of interrupt sources exceeded"); + opic->sizeof_interrupt_source = (sizeof(opic_interrupt_source) + * opic->nr_interrupt_sources); + opic->interrupt_source = zalloc(opic->sizeof_interrupt_source); + opic->external_interrupt_source = opic->interrupt_source; + opic->interprocessor_interrupt_source = (opic->external_interrupt_source + + opic->nr_external_interrupts); + opic->timer_interrupt_source = (opic->interprocessor_interrupt_source + + opic->nr_interprocessor_interrupts); + } + for (i = 0; i < opic->nr_interrupt_sources; i++) { + opic_interrupt_source *source = &opic->interrupt_source[i]; + source->nr = i; + source->is_masked = isu_mask_bit; + } + DTRACE(opic, ("interrupt sources - external %d, timer %d, ipi %d, total %d\n", + opic->nr_external_interrupts, + opic->nr_timer_interrupts, + opic->nr_interprocessor_interrupts, + opic->nr_interrupt_sources)); + + + /* timers or interprocessor interrupts */ + if (opic->timer != NULL) + memset(opic->timer, 0, opic->sizeof_timer); + else { + opic->nr_timer_interrupts = 4; + opic->sizeof_timer = sizeof(opic_timer) * opic->nr_timer_interrupts; + opic->timer = zalloc(opic->sizeof_timer); + } + for (i = 0; i < opic->nr_timer_interrupts; i++) { + opic_timer *timer = &opic->timer[i]; + timer->nr = i; + timer->me = me; + timer->opic = opic; + timer->inhibited = 1; + timer->interrupt_source = &opic->timer_interrupt_source[i]; + } + if (device_find_property(me, "timer-frequency")) + opic->timer_frequency = device_find_integer_property(me, "timer-frequency"); + else + opic->timer_frequency = 1; + + + /* create space for the interrupt destination registers */ + if (opic->interrupt_destination != NULL) { + memset(opic->interrupt_destination, 0, opic->sizeof_interrupt_destination); + } + else { + opic->nr_interrupt_destinations = tree_find_integer_property(me, "/openprom/options/smp"); + opic->sizeof_interrupt_destination = (sizeof(opic_interrupt_destination) + * opic->nr_interrupt_destinations); + opic->interrupt_destination = zalloc(opic->sizeof_interrupt_destination); + if (opic->nr_interrupt_destinations > max_nr_interrupt_destinations) + device_error(me, "number of interrupt destinations exceeded"); + } + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + dest->bit = (1 << i); + dest->nr = i; + dest->init_port = (device_interrupt_decode(me, "init0", output_port) + + i); + dest->intr_port = (device_interrupt_decode(me, "intr0", output_port) + + i); + dest->base_priority = max_nr_task_priorities - 1; + } + DTRACE(opic, ("interrupt destinations - total %d\n", + (int)opic->nr_interrupt_destinations)); + + + /* verify and print out the ISU's */ + for (isb = 0; isb < opic->nr_isu_blocks; isb++) { + unsigned correct_size; + if ((opic->isu_block[isb].address % opic_alignment) != 0) + device_error(me, "interrupt source unit %d address not aligned to %d byte boundary", + isb, opic_alignment); + correct_size = opic->isu_block[isb].range * sizeof_isu_register_block; + if (opic->isu_block[isb].size != correct_size) + device_error(me, "interrupt source unit %d (reg %d) has an incorrect size, should be 0x%x", + isb, opic->isu_block[isb].reg, correct_size); + DTRACE(opic, ("interrupt source unit block %ld - address %d:0x%lx, size 0x%lx, int-number %ld, range %ld\n", + (long)isb, + (int)opic->isu_block[isb].space, + (unsigned long)opic->isu_block[isb].address, + (unsigned long)opic->isu_block[isb].size, + (long)opic->isu_block[isb].int_number, + (long)opic->isu_block[isb].range)); + } + + + /* verify and print out the IDU */ + { + unsigned correct_size; + unsigned alternate_size; + if ((opic->idu.address % opic_alignment) != 0) + device_error(me, "interrupt delivery unit not aligned to %d byte boundary", + opic_alignment); + correct_size = (idu_per_processor_register_base + + (sizeof_idu_per_processor_register_block + * opic->nr_interrupt_destinations)); + alternate_size = (idu_per_processor_register_base + + (sizeof_idu_per_processor_register_block + * max_nr_interrupt_destinations)); + if (opic->idu.size != correct_size + && opic->idu.size != alternate_size) + device_error(me, "interrupt delivery unit has incorrect size, should be 0x%x or 0x%x", + correct_size, alternate_size); + DTRACE(opic, ("interrupt delivery unit - address %d:0x%lx, size 0x%lx\n", + (int)opic->idu.space, + (unsigned long)opic->idu.address, + (unsigned long)opic->idu.size)); + } + + /* initialize the init interrupts */ + opic->init = 0; + + + /* vendor ident */ + if (device_find_property(me, "vendor-identification") != NULL) + opic->vendor_identification = device_find_integer_property(me, "vendor-identification"); + else + opic->vendor_identification = 0; + + /* misc registers */ + opic->spurious_vector = 0xff; + +} + + +/* interrupt related actions */ + +static void +assert_interrupt(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest) +{ + ASSERT(dest >= opic->interrupt_destination); + ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations); + DTRACE(opic, ("assert interrupt - intr port %d\n", dest->intr_port)); + device_interrupt_event(me, dest->intr_port, 1, NULL, 0); +} + + +static void +negate_interrupt(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest) +{ + ASSERT(dest >= opic->interrupt_destination); + ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations); + DTRACE(opic, ("negate interrupt - intr port %d\n", dest->intr_port)); + device_interrupt_event(me, dest->intr_port, 0, NULL, 0); +} + + +static int +can_deliver(device *me, + opic_interrupt_source *source, + opic_interrupt_destination *dest) +{ + return (source != NULL && dest != NULL + && source->priority > dest->base_priority + && (dest->current_in_service == NULL + || source->priority > dest->current_in_service->priority)); +} + + +static unsigned +deliver_pending(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest) +{ + ASSERT(can_deliver(me, dest->current_pending, dest)); + dest->current_in_service = dest->current_pending; + dest->current_in_service->in_service |= dest->bit; + if (!dest->current_pending->is_level_triggered) { + if (dest->current_pending->is_multicast) + dest->current_pending->pending &= ~dest->bit; + else + dest->current_pending->pending = 0; + } + dest->current_pending = NULL; + negate_interrupt(me, opic, dest); + return dest->current_in_service->vector; +} + + +typedef enum { + pending_interrupt, + in_service_interrupt, +} interrupt_class; + +static opic_interrupt_source * +find_interrupt_for_dest(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest, + interrupt_class class) +{ + int i; + opic_interrupt_source *pending = NULL; + for (i = 0; i < opic->nr_interrupt_sources; i++) { + opic_interrupt_source *src = &opic->interrupt_source[i]; + /* is this a potential hit? */ + switch (class) { + case in_service_interrupt: + if ((src->in_service & dest->bit) == 0) + continue; + break; + case pending_interrupt: + if ((src->pending & dest->bit) == 0) + continue; + break; + } + /* see if it is the highest priority */ + if (pending == NULL) + pending = src; + else if (src->priority > pending->priority) + pending = src; + } + return pending; +} + + +static opic_interrupt_destination * +find_lowest_dest(device *me, + hw_opic_device *opic, + opic_interrupt_source *src) +{ + int i; + opic_interrupt_destination *lowest = NULL; + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + if (src->destination & dest->bit) { + if (dest->base_priority < src->priority) { + if (lowest == NULL) + lowest = dest; + else if (lowest->base_priority > dest->base_priority) + lowest = dest; + else if (lowest->current_in_service != NULL + && dest->current_in_service == NULL) + lowest = dest; /* not doing anything */ + else if (lowest->current_in_service != NULL + && dest->current_in_service != NULL + && (lowest->current_in_service->priority + > dest->current_in_service->priority)) + lowest = dest; /* less urgent */ + /* FIXME - need to be more fair */ + } + } + } + return lowest; +} + + +static void +handle_interrupt(device *me, + hw_opic_device *opic, + opic_interrupt_source *src, + int asserted) +{ + if (src->is_masked) { + DTRACE(opic, ("interrupt %d - ignore masked\n", src->nr)); + } + else if (src->is_multicast) { + /* always try to deliver multicast interrupts - just easier */ + int i; + ASSERT(!src->is_level_triggered); + ASSERT(src->is_positive_polarity); + ASSERT(asserted); + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + if (src->destination & dest->bit) { + if (src->pending & dest->bit) { + DTRACE(opic, ("interrupt %d - multicast still pending to %d\n", + src->nr, dest->nr)); + } + else if (can_deliver(me, src, dest)) { + dest->current_pending = src; + src->pending |= dest->bit; + assert_interrupt(me, opic, dest); + DTRACE(opic, ("interrupt %d - multicast to %d\n", + src->nr, dest->nr)); + } + else { + src->pending |= dest->bit; + DTRACE(opic, ("interrupt %d - multicast pending to %d\n", + src->nr, dest->nr)); + } + } + } + } + else if (src->is_level_triggered + && src->is_positive_polarity + && !asserted) { + if (src->pending) + DTRACE(opic, ("interrupt %d - ignore withdrawn (active high)\n", + src->nr)); + else + DTRACE(opic, ("interrupt %d - ignore low level (active high)\n", + src->nr)); + ASSERT(!src->is_multicast); + src->pending = 0; + } + else if (src->is_level_triggered + && !src->is_positive_polarity + && asserted) { + if (src->pending) + DTRACE(opic, ("interrupt %d - ignore withdrawn (active low)\n", + src->nr)); + else + DTRACE(opic, ("interrupt %d - ignore high level (active low)\n", + src->nr)); + + ASSERT(!src->is_multicast); + src->pending = 0; + } + else if (!src->is_level_triggered + && src->is_positive_polarity + && !asserted) { + DTRACE(opic, ("interrupt %d - ignore falling edge (positive edge trigered)\n", + src->nr)); + } + else if (!src->is_level_triggered + && !src->is_positive_polarity + && asserted) { + DTRACE(opic, ("interrupt %d - ignore rising edge (negative edge trigered)\n", + src->nr)); + } + else if (src->in_service != 0) { + /* leave the interrupt where it is */ + ASSERT(!src->is_multicast); + ASSERT(src->pending == 0 || src->pending == src->in_service); + src->pending = src->in_service; + DTRACE(opic, ("interrupt %ld - ignore already in service to 0x%lx\n", + (long)src->nr, (long)src->in_service)); + } + else if (src->pending != 0) { + DTRACE(opic, ("interrupt %ld - ignore still pending to 0x%lx\n", + (long)src->nr, (long)src->pending)); + } + else { + /* delivery is needed */ + opic_interrupt_destination *dest = find_lowest_dest(me, opic, src); + if (can_deliver(me, src, dest)) { + dest->current_pending = src; + src->pending = dest->bit; + DTRACE(opic, ("interrupt %d - delivered to %d\n", src->nr, dest->nr)); + assert_interrupt(me, opic, dest); + } + else { + src->pending = src->destination; /* any can take this */ + DTRACE(opic, ("interrupt %ld - pending to 0x%lx\n", + (long)src->nr, (long)src->pending)); + } + } +} + +static unsigned +do_interrupt_acknowledge_register_N_read(device *me, + hw_opic_device *opic, + int dest_nr) +{ + opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr]; + unsigned vector; + + ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations); + ASSERT(dest_nr == dest->nr); + + /* try the current pending */ + if (can_deliver(me, dest->current_pending, dest)) { + ASSERT(dest->current_pending->pending & dest->bit); + vector = deliver_pending(me, opic, dest); + DTRACE(opic, ("interrupt ack %d - entering %d (pending) - vector %d (%d), priority %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->vector, vector, + dest->current_in_service->priority)); + } + else { + /* try for something else */ + dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt); + if (can_deliver(me, dest->current_pending, dest)) { + vector = deliver_pending(me, opic, dest); + DTRACE(opic, ("interrupt ack %d - entering %d (not pending) - vector %d (%d), priority %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->vector, vector, + dest->current_in_service->priority)); + } + else { + dest->current_pending = NULL; + vector = opic->spurious_vector; + DTRACE(opic, ("interrupt ack %d - spurious interrupt %d\n", + dest->nr, vector)); + } + } + return vector; +} + + +static void +do_end_of_interrupt_register_N_write(device *me, + hw_opic_device *opic, + int dest_nr, + unsigned reg) +{ + opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr]; + + ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations); + ASSERT(dest_nr == dest->nr); + + /* check the value written is zero */ + if (reg != 0) { + DTRACE(opic, ("eoi %d - ignoring nonzero value\n", dest->nr)); + } + + /* user doing wierd things? */ + if (dest->current_in_service == NULL) { + DTRACE(opic, ("eoi %d - strange, no current interrupt\n", dest->nr)); + return; + } + + /* an internal stuff up? */ + if (!(dest->current_in_service->in_service & dest->bit)) { + device_error(me, "eoi %d - current interrupt not in service", dest->nr); + } + + /* find what was probably the previous in service interrupt */ + dest->current_in_service->in_service &= ~dest->bit; + DTRACE(opic, ("eoi %d - ending %d - priority %d, vector %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->priority, + dest->current_in_service->vector)); + dest->current_in_service = find_interrupt_for_dest(me, opic, dest, in_service_interrupt); + if (dest->current_in_service != NULL) + DTRACE(opic, ("eoi %d - resuming %d - priority %d, vector %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->priority, + dest->current_in_service->vector)); + else + DTRACE(opic, ("eoi %d - resuming none\n", dest->nr)); + + /* check to see if that shouldn't be interrupted */ + dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt); + if (can_deliver(me, dest->current_pending, dest)) { + ASSERT(dest->current_pending->pending & dest->bit); + assert_interrupt(me, opic, dest); + } + else { + dest->current_pending = NULL; + } +} + + +static void +decode_opic_address(device *me, + hw_opic_device *opic, + int space, + unsigned_word address, + unsigned nr_bytes, + opic_register *type, + int *index) +{ + int isb = 0; + + /* is the size valid? */ + if (nr_bytes != 4) { + *type = invalid_opic_register; + *index = -1; + return; + } + + /* try for a per-processor register within the interrupt delivery + unit */ + if (space == opic->idu.space + && address >= (opic->idu.address + idu_per_processor_register_base) + && address < (opic->idu.address + idu_per_processor_register_base + + (sizeof_idu_per_processor_register_block + * opic->nr_interrupt_destinations))) { + unsigned_word block_offset = (address + - opic->idu.address + - idu_per_processor_register_base); + unsigned_word offset = block_offset % sizeof_idu_per_processor_register_block; + *index = block_offset / sizeof_idu_per_processor_register_block; + switch (offset) { + case 0x040: + *type = ipi_N_dispatch_register; + *index = 0; + break; + case 0x050: + *type = ipi_N_dispatch_register; + *index = 1; + break; + case 0x060: + *type = ipi_N_dispatch_register; + *index = 2; + break; + case 0x070: + *type = ipi_N_dispatch_register; + *index = 3; + break; + case 0x080: + *type = current_task_priority_register_N; + break; + case 0x0a0: + *type = interrupt_acknowledge_register_N; + break; + case 0x0b0: + *type = end_of_interrupt_register_N; + break; + default: + *type = invalid_opic_register; + break; + } + DTRACE(opic, ("per-processor register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + + /* try for an interrupt source unit */ + for (isb = 0; isb < opic->nr_isu_blocks; isb++) { + if (opic->isu_block[isb].space == space + && address >= opic->isu_block[isb].address + && address < (opic->isu_block[isb].address + opic->isu_block[isb].size)) { + unsigned_word block_offset = address - opic->isu_block[isb].address; + unsigned_word offset = block_offset % sizeof_isu_register_block; + *index = (opic->isu_block[isb].int_number + + (block_offset / sizeof_isu_register_block)); + switch (offset) { + case 0x00: + *type = interrupt_source_N_vector_priority_register; + break; + case 0x10: + *type = interrupt_source_N_destination_register; + break; + default: + *type = invalid_opic_register; + break; + } + DTRACE(opic, ("isu register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + } + + /* try for a timer */ + if (space == opic->idu.space + && address >= (opic->idu.address + idu_timer_base) + && address < (opic->idu.address + idu_timer_base + + opic->nr_timer_interrupts * sizeof_timer_register_block)) { + unsigned_word offset = address % sizeof_timer_register_block; + *index = ((address - opic->idu.address - idu_timer_base) + / sizeof_timer_register_block); + switch (offset) { + case 0x00: + *type = timer_N_current_count_register; + break; + case 0x10: + *type = timer_N_base_count_register; + break; + case 0x20: + *type = timer_N_vector_priority_register; + break; + case 0x30: + *type = timer_N_destination_register; + break; + default: + *type = invalid_opic_register; + break; + } + DTRACE(opic, ("timer register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + + /* finally some other misc global register */ + if (space == opic->idu.space + && address >= opic->idu.address + && address < opic->idu.address + opic->idu.size) { + unsigned_word block_offset = address - opic->idu.address; + switch (block_offset) { + case 0x010f0: + *type = timer_frequency_reporting_register; + *index = -1; + break; + case 0x010e0: + *type = spurious_vector_register; + *index = -1; + break; + case 0x010d0: + case 0x010c0: + case 0x010b0: + case 0x010a0: + *type = ipi_N_vector_priority_register; + *index = (block_offset - 0x010a0) / 16; + break; + case 0x01090: + *type = processor_init_register; + *index = -1; + break; + case 0x01080: + *type = vendor_identification_register; + *index = -1; + break; + case 0x01020: + *type = global_configuration_register_N; + *index = 0; + break; + case 0x01000: + *type = feature_reporting_register_N; + *index = 0; + break; + default: + *type = invalid_opic_register; + *index = -1; + break; + } + DTRACE(opic, ("global register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + + /* nothing matched */ + *type = invalid_opic_register; + DTRACE(opic, ("invalid register %d:0x%lx\n", + space, (unsigned long)address)); + return; +} + + +/* Processor init register: + + The bits in this register (one per processor) are directly wired to + output "init" interrupt ports. */ + +static unsigned +do_processor_init_register_read(device *me, + hw_opic_device *opic) +{ + unsigned reg = opic->init; + DTRACE(opic, ("processor init register - read 0x%lx\n", + (long)reg)); + return reg; +} + +static void +do_processor_init_register_write(device *me, + hw_opic_device *opic, + unsigned reg) +{ + int i; + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + if ((reg & dest->bit) != (opic->init & dest->bit)) { + if (reg & dest->bit) { + DTRACE(opic, ("processor init register - write 0x%lx - asserting init%d\n", + (long)reg, i)); + opic->init |= dest->bit; + device_interrupt_event(me, dest->init_port, 1, NULL, 0); + } + else { + DTRACE(opic, ("processor init register - write 0x%lx - negating init%d\n", + (long)reg, i)); + opic->init &= ~dest->bit; + device_interrupt_event(me, dest->init_port, 0, NULL, 0); + } + } + } +} + + + +/* Interrupt Source Vector/Priority Register: */ + +static unsigned +read_vector_priority_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + const char *reg_name, + int reg_index) +{ + unsigned reg; + reg = 0; + reg |= interrupt->is_masked; + reg |= (interrupt->in_service || interrupt->pending + ? isu_active_bit : 0); /* active */ + reg |= interrupt->is_multicast; + reg |= interrupt->is_positive_polarity; + reg |= interrupt->is_level_triggered; /* sense? */ + reg |= interrupt->priority << isu_priority_shift; + reg |= interrupt->vector; + DTRACE(opic, ("%s %d vector/priority register - read 0x%lx\n", + reg_name, reg_index, (unsigned long)reg)); + return reg; +} + +static unsigned +do_interrupt_source_N_vector_priority_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index < opic->nr_external_interrupts); + reg = read_vector_priority_register(me, opic, + &opic->interrupt_source[index], + "interrupt source", index); + return reg; +} + +static void +write_vector_priority_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + unsigned reg, + const char *reg_name, + int reg_index) +{ + interrupt->is_masked = (reg & isu_mask_bit); + interrupt->is_multicast = (reg & isu_multicast_bit); + interrupt->is_positive_polarity = (reg & isu_positive_polarity_bit); + interrupt->is_level_triggered = (reg & isu_level_triggered_bit); + interrupt->priority = ((reg >> isu_priority_shift) + % max_nr_task_priorities); + interrupt->vector = (reg & isu_vector_bits); + DTRACE(opic, ("%s %d vector/priority register - write 0x%lx - %s%s%s-polarity, %s-triggered, priority %ld vector %ld\n", + reg_name, + reg_index, + (unsigned long)reg, + interrupt->is_masked ? "masked, " : "", + interrupt->is_multicast ? "multicast, " : "", + interrupt->is_positive_polarity ? "positive" : "negative", + interrupt->is_level_triggered ? "level" : "edge", + (long)interrupt->priority, + (long)interrupt->vector)); +} + +static void +do_interrupt_source_N_vector_priority_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index < opic->nr_external_interrupts); + reg &= ~isu_multicast_bit; /* disable multicast */ + write_vector_priority_register(me, opic, + &opic->interrupt_source[index], + reg, "interrupt source", index); +} + + + +/* Interrupt Source Destination Register: */ + +static unsigned +read_destination_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + const char *reg_name, + int reg_index) +{ + unsigned long reg; + reg = interrupt->destination; + DTRACE(opic, ("%s %d destination register - read 0x%lx\n", + reg_name, reg_index, reg)); + return reg; +} + +static unsigned +do_interrupt_source_N_destination_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index < opic->nr_external_interrupts); + reg = read_destination_register(me, opic, &opic->external_interrupt_source[index], + "interrupt source", index); + return reg; +} + +static void +write_destination_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + unsigned reg, + const char *reg_name, + int reg_index) +{ + reg &= (1 << opic->nr_interrupt_destinations) - 1; /* mask out invalid */ + DTRACE(opic, ("%s %d destination register - write 0x%x\n", + reg_name, reg_index, reg)); + interrupt->destination = reg; +} + +static void +do_interrupt_source_N_destination_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index < opic->nr_external_interrupts); + write_destination_register(me, opic, &opic->external_interrupt_source[index], + reg, "interrupt source", index); +} + + + +/* Spurious vector register: */ + +static unsigned +do_spurious_vector_register_read(device *me, + hw_opic_device *opic) +{ + unsigned long reg = opic->spurious_vector; + DTRACE(opic, ("spurious vector register - read 0x%lx\n", reg)); + return reg; +} + +static void +do_spurious_vector_register_write(device *me, + hw_opic_device *opic, + unsigned reg) +{ + reg &= 0xff; /* mask off invalid */ + DTRACE(opic, ("spurious vector register - write 0x%x\n", reg)); + opic->spurious_vector = reg; +} + + + +/* current task priority register: */ + +static unsigned +do_current_task_priority_register_N_read(device *me, + hw_opic_device *opic, + int index) +{ + opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index]; + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_interrupt_destinations); + reg = interrupt_destination->base_priority; + DTRACE(opic, ("current task priority register %d - read 0x%x\n", index, reg)); + return reg; +} + +static void +do_current_task_priority_register_N_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index]; + ASSERT(index >= 0 && index < opic->nr_interrupt_destinations); + reg %= max_nr_task_priorities; + DTRACE(opic, ("current task priority register %d - write 0x%x\n", index, reg)); + interrupt_destination->base_priority = reg; +} + + + +/* Timer Frequency Reporting Register: */ + +static unsigned +do_timer_frequency_reporting_register_read(device *me, + hw_opic_device *opic) +{ + unsigned reg; + reg = opic->timer_frequency; + DTRACE(opic, ("timer frequency reporting register - read 0x%x\n", reg)); + return reg; +} + +static void +do_timer_frequency_reporting_register_write(device *me, + hw_opic_device *opic, + unsigned reg) +{ + DTRACE(opic, ("timer frequency reporting register - write 0x%x\n", reg)); + opic->timer_frequency = reg; +} + + +/* timer registers: */ + +static unsigned +do_timer_N_current_count_register_read(device *me, + hw_opic_device *opic, + int index) +{ + opic_timer *timer = &opic->timer[index]; + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + if (timer->inhibited) + reg = timer->count; /* stalled value */ + else + reg = timer->count - device_event_queue_time(me); /* time remaining */ + DTRACE(opic, ("timer %d current count register - read 0x%x\n", index, reg)); + return reg; +} + + +static unsigned +do_timer_N_base_count_register_read(device *me, + hw_opic_device *opic, + int index) +{ + opic_timer *timer = &opic->timer[index]; + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg = timer->base_count; + DTRACE(opic, ("timer %d base count register - read 0x%x\n", index, reg)); + return reg; +} + + +static void +timer_event(void *data) +{ + opic_timer *timer = data; + device *me = timer->me; + if (timer->inhibited) + device_error(timer->me, "internal-error - timer event occured when timer %d inhibited", + timer->nr); + handle_interrupt(timer->me, timer->opic, timer->interrupt_source, 1); + timer->timeout_event = device_event_queue_schedule(me, timer->base_count, + timer_event, timer); + DTRACE(opic, ("timer %d - interrupt at %ld, next at %d\n", + timer->nr, (long)device_event_queue_time(me), timer->base_count)); +} + + +static void +do_timer_N_base_count_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + opic_timer *timer = &opic->timer[index]; + int inhibit; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + inhibit = reg & 0x80000000; + if (timer->inhibited && !inhibit) { + timer->inhibited = 0; + if (timer->timeout_event != NULL) + device_event_queue_deschedule(me, timer->timeout_event); + timer->count = device_event_queue_time(me) + reg; + timer->base_count = reg; + timer->timeout_event = device_event_queue_schedule(me, timer->base_count, + timer_event, (void*)timer); + DTRACE(opic, ("timer %d base count register - write 0x%x - timer started\n", + index, reg)); + } + else if (!timer->inhibited && inhibit) { + if (timer->timeout_event != NULL) + device_event_queue_deschedule(me, timer->timeout_event); + timer->count = timer->count - device_event_queue_time(me); + timer->inhibited = 1; + timer->base_count = reg; + DTRACE(opic, ("timer %d base count register - write 0x%x - timer stopped\n", + index, reg)); + } + else { + ASSERT((timer->inhibited && inhibit) || (!timer->inhibited && !inhibit)); + DTRACE(opic, ("timer %d base count register - write 0x%x\n", index, reg)); + timer->base_count = reg; + } +} + + +static unsigned +do_timer_N_vector_priority_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg = read_vector_priority_register(me, opic, + &opic->timer_interrupt_source[index], + "timer", index); + return reg; +} + +static void +do_timer_N_vector_priority_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg &= ~isu_level_triggered_bit; /* force edge trigger */ + reg |= isu_positive_polarity_bit; /* force rising (positive) edge */ + reg |= isu_multicast_bit; /* force multicast */ + write_vector_priority_register(me, opic, + &opic->timer_interrupt_source[index], + reg, "timer", index); +} + + +static unsigned +do_timer_N_destination_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg = read_destination_register(me, opic, &opic->timer_interrupt_source[index], + "timer", index); + return reg; +} + +static void +do_timer_N_destination_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + write_destination_register(me, opic, &opic->timer_interrupt_source[index], + reg, "timer", index); +} + + +/* IPI registers */ + +static unsigned +do_ipi_N_vector_priority_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts); + reg = read_vector_priority_register(me, opic, + &opic->interprocessor_interrupt_source[index], + "ipi", index); + return reg; +} + +static void +do_ipi_N_vector_priority_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts); + reg &= ~isu_level_triggered_bit; /* force edge trigger */ + reg |= isu_positive_polarity_bit; /* force rising (positive) edge */ + reg |= isu_multicast_bit; /* force a multicast source */ + write_vector_priority_register(me, opic, + &opic->interprocessor_interrupt_source[index], + reg, "ipi", index); +} + +static void +do_ipi_N_dispatch_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + opic_interrupt_source *source = &opic->interprocessor_interrupt_source[index]; + ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts); + DTRACE(opic, ("ipi %d interrupt dispatch register - write 0x%x\n", index, reg)); + source->destination = reg; + handle_interrupt(me, opic, source, 1); +} + + +/* vendor and other global registers */ + +static unsigned +do_vendor_identification_register_read(device *me, + hw_opic_device *opic) +{ + unsigned reg; + reg = opic->vendor_identification; + DTRACE(opic, ("vendor identification register - read 0x%x\n", reg)); + return reg; +} + +static unsigned +do_feature_reporting_register_N_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg = 0; + ASSERT(index == 0); + switch (index) { + case 0: + reg |= (opic->nr_external_interrupts << 16); + reg |= (opic->nr_interrupt_destinations << 8); + reg |= (2/*version 1.2*/); + break; + } + DTRACE(opic, ("feature reporting register %d - read 0x%x\n", index, reg)); + return reg; +} + +static unsigned +do_global_configuration_register_N_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg = 0; + ASSERT(index == 0); + switch (index) { + case 0: + reg |= gcr0_8259_bit; /* hardwire 8259 disabled */ + break; + } + DTRACE(opic, ("global configuration register %d - read 0x%x\n", index, reg)); + return reg; +} + +static void +do_global_configuration_register_N_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index == 0); + if (reg & gcr0_reset_bit) { + DTRACE(opic, ("global configuration register %d - write 0x%x - reseting opic\n", index, reg)); + hw_opic_init_data(me); + } + if (!(reg & gcr0_8259_bit)) { + DTRACE(opic, ("global configuration register %d - write 0x%x - ignoring 8259 enable\n", index, reg)); + } +} + + + +/* register read-write */ + +static unsigned +hw_opic_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + opic_register type; + int index; + decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index); + if (type == invalid_opic_register) { + device_error(me, "invalid opic read access to %d:0x%lx (%d bytes)", + space, (unsigned long)addr, nr_bytes); + } + else { + unsigned reg; + switch (type) { + case processor_init_register: + reg = do_processor_init_register_read(me, opic); + break; + case interrupt_source_N_vector_priority_register: + reg = do_interrupt_source_N_vector_priority_register_read(me, opic, index); + break; + case interrupt_source_N_destination_register: + reg = do_interrupt_source_N_destination_register_read(me, opic, index); + break; + case interrupt_acknowledge_register_N: + reg = do_interrupt_acknowledge_register_N_read(me, opic, index); + break; + case spurious_vector_register: + reg = do_spurious_vector_register_read(me, opic); + break; + case current_task_priority_register_N: + reg = do_current_task_priority_register_N_read(me, opic, index); + break; + case timer_frequency_reporting_register: + reg = do_timer_frequency_reporting_register_read(me, opic); + break; + case timer_N_current_count_register: + reg = do_timer_N_current_count_register_read(me, opic, index); + break; + case timer_N_base_count_register: + reg = do_timer_N_base_count_register_read(me, opic, index); + break; + case timer_N_vector_priority_register: + reg = do_timer_N_vector_priority_register_read(me, opic, index); + break; + case timer_N_destination_register: + reg = do_timer_N_destination_register_read(me, opic, index); + break; + case ipi_N_vector_priority_register: + reg = do_ipi_N_vector_priority_register_read(me, opic, index); + break; + case feature_reporting_register_N: + reg = do_feature_reporting_register_N_read(me, opic, index); + break; + case global_configuration_register_N: + reg = do_global_configuration_register_N_read(me, opic, index); + break; + case vendor_identification_register: + reg = do_vendor_identification_register_read(me, opic); + break; + default: + reg = 0; + device_error(me, "unimplemented read of register %s[%d]", + opic_register_name(type), index); + } + *(unsigned_4*)dest = H2LE_4(reg); + } + return nr_bytes; +} + + +static unsigned +hw_opic_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + opic_register type; + int index; + decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index); + if (type == invalid_opic_register) { + device_error(me, "invalid opic write access to %d:0x%lx (%d bytes)", + space, (unsigned long)addr, nr_bytes); + } + else { + unsigned reg = LE2H_4(*(unsigned_4*)source); + switch (type) { + case processor_init_register: + do_processor_init_register_write(me, opic, reg); + break; + case interrupt_source_N_vector_priority_register: + do_interrupt_source_N_vector_priority_register_write(me, opic, index, reg); + break; + case interrupt_source_N_destination_register: + do_interrupt_source_N_destination_register_write(me, opic, index, reg); + break; + case end_of_interrupt_register_N: + do_end_of_interrupt_register_N_write(me, opic, index, reg); + break; + case spurious_vector_register: + do_spurious_vector_register_write(me, opic, reg); + break; + case current_task_priority_register_N: + do_current_task_priority_register_N_write(me, opic, index, reg); + break; + case timer_frequency_reporting_register: + do_timer_frequency_reporting_register_write(me, opic, reg); + break; + case timer_N_base_count_register: + do_timer_N_base_count_register_write(me, opic, index, reg); + break; + case timer_N_vector_priority_register: + do_timer_N_vector_priority_register_write(me, opic, index, reg); + break; + case timer_N_destination_register: + do_timer_N_destination_register_write(me, opic, index, reg); + break; + case ipi_N_dispatch_register: + do_ipi_N_dispatch_register_write(me, opic, index, reg); + break; + case ipi_N_vector_priority_register: + do_ipi_N_vector_priority_register_write(me, opic, index, reg); + break; + case global_configuration_register_N: + do_global_configuration_register_N_write(me, opic, index, reg); + break; + default: + device_error(me, "unimplemented write to register %s[%d]", + opic_register_name(type), index); + } + } + return nr_bytes; +} + + +static void +hw_opic_interrupt_event(device *me, + int my_port, + device *source, + int source_port, + int level, + cpu *processor, + unsigned_word cia) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + + int isb; + int src_nr = 0; + + /* find the corresponding internal input port */ + for (isb = 0; isb < opic->nr_isu_blocks; isb++) { + if (my_port >= opic->isu_block[isb].int_number + && my_port < opic->isu_block[isb].int_number + opic->isu_block[isb].range) { + src_nr += my_port - opic->isu_block[isb].int_number; + break; + } + else + src_nr += opic->isu_block[isb].range; + } + if (isb == opic->nr_isu_blocks) + device_error(me, "interrupt %d out of range", my_port); + DTRACE(opic, ("external-interrupt %d, internal %d, level %d\n", + my_port, src_nr, level)); + + /* pass it on */ + ASSERT(src_nr >= 0 && src_nr < opic->nr_external_interrupts); + handle_interrupt(me, opic, &opic->external_interrupt_source[src_nr], level); +} + + +static const device_interrupt_port_descriptor hw_opic_interrupt_ports[] = { + { "irq", 0, max_nr_interrupt_sources, input_port, }, + { "intr", 0, max_nr_interrupt_destinations, output_port, }, + { "init", max_nr_interrupt_destinations, max_nr_interrupt_destinations, output_port, }, + { NULL } +}; + + +static device_callbacks const hw_opic_callbacks = { + { generic_device_init_address, + hw_opic_init_data }, + { NULL, }, /* address */ + { hw_opic_io_read_buffer, + hw_opic_io_write_buffer }, /* IO */ + { NULL, }, /* DMA */ + { hw_opic_interrupt_event, NULL, hw_opic_interrupt_ports }, /* interrupt */ + { NULL, }, /* unit */ + NULL, /* instance */ +}; + +static void * +hw_opic_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_opic_device *opic = ZALLOC(hw_opic_device); + return opic; +} + + + +const device_descriptor hw_opic_device_descriptor[] = { + { "opic", hw_opic_create, &hw_opic_callbacks }, + { NULL }, +}; + +#endif /* _HW_OPIC_C_ */ diff --git a/sim/ppc/hw_pic.c b/sim/ppc/hw_pic.c deleted file mode 100644 index 3203f8c..0000000 --- a/sim/ppc/hw_pic.c +++ /dev/null @@ -1,98 +0,0 @@ -/* ICU device: icu@<address> - - <address> : read - processor nr - <address> : write - interrupt processor nr - <address> + 4 : read - nr processors - - Single byte registers that control a simple ICU. - - Illustrates passing of events to parent device. Passing of - interrupts to an interrupt destination. */ - - -static unsigned -icu_io_read_buffer_callback(device *me, - void *dest, - int space, - unsigned_word addr, - unsigned nr_bytes, - cpu *processor, - unsigned_word cia) -{ - memset(dest, 0, nr_bytes); - switch (addr & 4) { - case 0: - *(unsigned_1*)dest = cpu_nr(processor); - break; - case 4: - *(unsigned_1*)dest = - device_find_integer_property(me, "/openprom/options/smp"); - break; - } - return nr_bytes; -} - - -static unsigned -icu_io_write_buffer_callback(device *me, - const void *source, - int space, - unsigned_word addr, - unsigned nr_bytes, - cpu *processor, - unsigned_word cia) -{ - unsigned_1 val = H2T_1(*(unsigned_1*)source); - /* tell the parent device that the interrupt lines have changed. - For this fake ICU. The interrupt lines just indicate the cpu to - interrupt next */ - device_interrupt_event(me, - val, /*my_port*/ - val, /*val*/ - processor, cia); - return nr_bytes; -} - -static void -icu_do_interrupt(event_queue *queue, - void *data) -{ - cpu *target = (cpu*)data; - /* try to interrupt the processor. If the attempt fails, try again - on the next tick */ - if (!external_interrupt(target)) - event_queue_schedule(queue, 1, icu_do_interrupt, target); -} - - -static void -icu_interrupt_event_callback(device *me, - int my_port, - device *source, - int source_port, - int level, - cpu *processor, - unsigned_word cia) -{ - /* the interrupt controller can't interrupt a cpu at any time. - Rather it must synchronize with the system clock before - performing an interrupt on the given processor */ - psim *system = cpu_system(processor); - cpu *target = psim_cpu(system, my_port); - if (target != NULL) { - event_queue *events = cpu_event_queue(target); - event_queue_schedule(events, 1, icu_do_interrupt, target); - } -} - -static device_callbacks const icu_callbacks = { - { generic_device_init_address, }, - { NULL, }, /* address */ - { icu_io_read_buffer_callback, - icu_io_write_buffer_callback, }, - { NULL, }, /* DMA */ - { icu_interrupt_event_callback, }, - { NULL, }, /* unit */ -}; - - diff --git a/sim/ppc/hw_trace.c b/sim/ppc/hw_trace.c new file mode 100644 index 0000000..be2c3c4 --- /dev/null +++ b/sim/ppc/hw_trace.c @@ -0,0 +1,108 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _HW_TRACE_C_ +#define _HW_TRACE_C_ + +#include "device_table.h" +#include <stdarg.h> + +/* DEVICE + + trace - the properties of this dummy device specify trace options + + DESCRIPTION + + The properties of this device are used, during initialization, to + specify the value various simulation trace options. The + initialization can occure implicitly (during device tree init) or + explicitly using this devices ioctl method. + + The actual options and their default values (for a given target) + are defined in the file debug. + + This device is normally a child of the /openprom node. + + EXAMPLE + + The trace option dump-device-tree can be enabled by specifying the + option: + + | -o '/openprom/trace/dump-device-tree 0x1' + + Alternativly the shorthand version: + + | -t dump-device-tree + + can be used. */ + +static void +hw_trace_init_data(device *me) +{ + const device_property *prop = device_find_property(me, NULL); + while (prop != NULL) { + const char *name = prop->name; + unsigned32 value = device_find_integer_property(me, name); + trace_option(name, value); + prop = device_next_property(prop); + } +} + + +/* Hook to allow the (re) initialization of the trace options at any + time */ + +static int +hw_trace_ioctl(device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + va_list ap) +{ + switch (request) { + case device_ioctl_set_trace: + hw_trace_init_data(me); + break; + default: + device_error(me, "insupported ioctl request"); + break; + } + return 0; +} + + +static device_callbacks const hw_trace_callbacks = { + { NULL, hw_trace_init_data, }, /* init */ + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ + NULL, /* instance-create */ + hw_trace_ioctl, +}; + +const device_descriptor hw_trace_device_descriptor[] = { + { "trace", NULL, &hw_trace_callbacks }, + { NULL }, +}; + +#endif _HW_TRACE_C_ diff --git a/sim/ppc/tree.h b/sim/ppc/tree.h new file mode 100644 index 0000000..46590cb --- /dev/null +++ b/sim/ppc/tree.h @@ -0,0 +1,139 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _TREE_H_ +#define _TREE_H_ + +#ifndef INLINE_TREE +#define INLINE_TREE +#endif + +/* Constructing the device tree: + + The initial device tree populated with devices and basic properties + is created using the function <<device_tree_add_parsed()>>. This + function parses a PSIM device specification and uses it to populate + the tree accordingly. + + This function accepts a printf style formatted string as the + argument that describes the entry. Any properties or interrupt + connections added to a device tree using this function are marked + as having a permenant disposition. When the tree is (re) + initialized they will be restored to their initial value. + + */ + +EXTERN_TREE\ +(device *) tree_parse +(device *root, + const char *fmt, + ...) __attribute__ ((format (printf, 2, 3))); + + +INLINE_TREE\ +(void) tree_usage +(int verbose); + +INLINE_TREE\ +(void) tree_print +(device *root); + +INLINE_TREE\ +(device_instance*) tree_instance +(device *root, + const char *device_specifier); + + +/* Tree traversal:: + + The entire device tree can be traversed using the + <<device_tree_traverse()>> function. The traversal can be in + either pre- or postfix order. + + */ + +typedef void (tree_traverse_function) + (device *device, + void *data); + +INLINE_DEVICE\ +(void) tree_traverse +(device *root, + tree_traverse_function *prefix, + tree_traverse_function *postfix, + void *data); + + +/* Tree lookup:: + + The function <<tree_find_device()>> will attempt to locate + the specified device within the tree. If the device is not found a + NULL device is returned. + + */ + +INLINE_TREE\ +(device *) tree_find_device +(device *root, + const char *path); + + +INLINE_TREE\ +(const device_property *) tree_find_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(int) tree_find_boolean_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(signed_cell) tree_find_integer_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(device_instance *) tree_find_ihandle_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(const char *) tree_find_string_property +(device *root, + const char *path_to_property); + + +/* Initializing the created tree: + + Once a device tree has been created the <<device_tree_init()>> + function is used to initialize it. The exact sequence of events + that occure during initialization are described separatly. + + */ + +INLINE_TREE\ +(void) tree_init +(device *root, + psim *system); + + +#endif /* _TREE_H_ */ |