diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index af0c6b2790..375ad907d3 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -43,7 +43,7 @@ config ARCH_CHIP_S698PM select ARCH_HAVE_TESTSET select ARCH_HAVE_SERIAL_TERMIOS ---help--- - Microchip S698PM (ARCH_SPARC_V8) + ORBITA Sailing S698PM (ARCH_SPARC_V8) endchoice diff --git a/arch/sparc/include/s698pm/irq.h b/arch/sparc/include/s698pm/irq.h new file mode 100644 index 0000000000..56a81f8cf5 --- /dev/null +++ b/arch/sparc/include/s698pm/irq.h @@ -0,0 +1,206 @@ +/**************************************************************************** + * arch/sparc/include/s698pm/irq.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* This file should never be included directed but, rather, only indirectly + * through nuttx/irq.h + */ + +#ifndef __ARCH_SPARC_INCLUDE_S698PM_IRQ_H +#define __ARCH_SPARC_INCLUDE_S698PM_IRQ_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Interrupt Sources The interrupt source numbers directly map to the trap + * type and to the bits used in the Interrupt Clear, Interrupt Force, + * Interrupt Mask, and the Interrupt Pending Registers. + */ + +#if defined(CONFIG_ARCH_CHIP_S698PM) + +#define S698PM_IRQREG_BASE 0x80000200 + +#define S698PM_IRQREG_ILEVEL (S698PM_IRQREG_BASE + 0) +#define S698PM_IRQREG_IPEND (S698PM_IRQREG_BASE + 0x4) +#define S698PM_IRQREG_IFORCE (S698PM_IRQREG_BASE + 0x8) +#define S698PM_IRQREG_ICLEAR (S698PM_IRQREG_BASE + 0xc) +#define S698PM_IRQREG_MPSTATUS (S698PM_IRQREG_BASE + 0x10) +#define S698PM_IRQREG_BROADCAST (S698PM_IRQREG_BASE + 0x14) + +#define S698PM_IRQREG_P0_MASK (S698PM_IRQREG_BASE + 0x40) +#define S698PM_IRQREG_P1_MASK (S698PM_IRQREG_BASE + 0x44) +#define S698PM_IRQREG_P2_MASK (S698PM_IRQREG_BASE + 0x48) +#define S698PM_IRQREG_P3_MASK (S698PM_IRQREG_BASE + 0x4c) + +#define S698PM_IRQREG_P0_FORCE (S698PM_IRQREG_BASE + 0x80) +#define S698PM_IRQREG_P1_FORCE (S698PM_IRQREG_BASE + 0x84) +#define S698PM_IRQREG_P2_FORCE (S698PM_IRQREG_BASE + 0x88) +#define S698PM_IRQREG_P3_FORCE (S698PM_IRQREG_BASE + 0x8c) + +#define S698PM_IRQREG_P0_EXTACK (S698PM_IRQREG_BASE + 0xc0) +#define S698PM_IRQREG_P1_EXTACK (S698PM_IRQREG_BASE + 0xc4) +#define S698PM_IRQREG_P2_EXTACK (S698PM_IRQREG_BASE + 0xc8) +#define S698PM_IRQREG_P3_EXTACK (S698PM_IRQREG_BASE + 0xcc) + +#define S698PM_IRQ_FIRST 0x00 + +#define S698PM_IRQ_RESET 0x00 +#define S698PM_IRQ_INST_ACC_EXCEPTION 0x01 +#define S698PM_IRQ_ILL_INST 0x02 +#define S698PM_IRQ_PRIVELEGE_INST 0x03 +#define S698PM_IRQ_FP_DISABLED 0x04 +#define S698PM_IRQ_WINDOW_OVERFLOW 0x05 +#define S698PM_IRQ_WINDOW_UNDERFLOW 0x06 +#define S698PM_IRQ_ADD_NOT_ALIGNED 0x07 +#define S698PM_IRQ_ADD_FP_EXCEPTION 0x08 +#define S698PM_IRQ_DATA_ACC_EXCEPTION 0x09 +#define S698PM_IRQ_TAG_OVERFLOW 0x0A +#define S698PM_IRQ_HW_UNDEFINED_0B 0x0B +#define S698PM_IRQ_HW_UNDEFINED_0C 0x0C +#define S698PM_IRQ_HW_UNDEFINED_0D 0x0D +#define S698PM_IRQ_HW_UNDEFINED_0E 0x0E +#define S698PM_IRQ_HW_UNDEFINED_0F 0x0F +#define S698PM_IRQ_HW_UNDEFINED_10 0x10 + +#define S698PM_IRQ_FIRST_INT 0x11 + +#define S698PM_IRQ_AHB_ERROR 0x11 +#define S698PM_IRQ_UART_1_RX_TX 0x12 +#define S698PM_IRQ_UART_2_RX_TX 0x13 +#define S698PM_IRQ_LOCKTIMER12 0x14 +#define S698PM_IRQ_ETHERNET 0x15 +#define S698PM_IRQ_TIMER1 0x16 +#define S698PM_IRQ_TIMER2 0x17 +#define S698PM_IRQ_TIMER3 0x18 +#define S698PM_IRQ_TIMER4 0x19 +#define S698PM_IRQ_1553B 0x1A +#define S698PM_IRQ_EXTENDED 0x1B +#define S698PM_IRQ_EXTERNAL_12 0x1C +#define S698PM_IRQ_EXTERNAL_13 0x1D +#define S698PM_IRQ_EXTERNAL_14 0x1E +#define S698PM_IRQ_EXTERNAL_15 0x1F + +#define S698PM_IRQ_LAST_INT 0x1F + +#define S698PM_IRQ_HW_UNDEFINED_20 0x20 +#define S698PM_IRQ_HW_UNDEFINED_21 0x21 +#define S698PM_IRQ_HW_UNDEFINED_22 0x22 +#define S698PM_IRQ_HW_UNDEFINED_23 0x23 + +#define S698PM_IRQ_CP_DISABLED 0x24 + +#define S698PM_IRQ_HW_UNDEFINED_25 0x25 +#define S698PM_IRQ_HW_UNDEFINED_26 0x26 +#define S698PM_IRQ_HW_UNDEFINED_27 0x27 + +#define S698PM_IRQ_CP_EXCEPTION 0x28 + +#define S698PM_IRQ_HW_UNDEFINED_29 0x29 +#define S698PM_IRQ_HW_UNDEFINED_7F 0x7F + +#define S698PM_IRQ_SW_SYSCALL_TA0 0x80 +#define S698PM_IRQ_SW_UNDEFINED_81 0x81 +#define S698PM_IRQ_SW_UNDEFINED_82 0x82 +#define S698PM_IRQ_SW_FLUSH_WINDOWS 0x83 + +#define S698PM_IRQ_SW_UNDEFINED_84 0x84 +#define S698PM_IRQ_SW_UNDEFINED_85 0x85 +#define S698PM_IRQ_SW_UNDEFINED_86 0x86 +#define S698PM_IRQ_SW_UNDEFINED_87 0x87 +#define S698PM_IRQ_SW_SYSCALL_TA8 0x88 + +#define S698PM_IRQ_SW_SYSCALL_IRQDIS 0x89 +#define S698PM_IRQ_SW_SYSCALL_IRQEN 0x8A + +#define S698PM_IRQ_SW_UNDEFINED_8B 0x8B +#define S698PM_IRQ_SW_UNDEFINED_FF 0xFF + +#define S698PM_IRQ_LAST 0xFF + +#define S698PM_IRQ_SPW1 0x100 +#define S698PM_IRQ_SPW2 0x101 +#define S698PM_IRQ_SPW3 0x102 +#define S698PM_IRQ_SPW4 0x103 +#define S698PM_IRQ_CCSDS 0x104 +#define S698PM_IRQ_EXTERNAL 0x105 +#define S698PM_IRQ_USBHOST 0x106 +#define S698PM_IRQ_UART_3_RX_TX 0x107 +#define S698PM_IRQ_CAN1 0x108 +#define S698PM_IRQ_CAN2 0x109 +#define S698PM_IRQ_CCSDS_CODE 0x10A +#define S698PM_IRQ_CCSDS_DECODE 0x10B +#define S698PM_IRQ_I2C 0x10C +#define S698PM_IRQ_SPI 0x10D +#define S698PM_IRQ_UART_4_RX_TX 0x10E +#define S698PM_IRQ_L2CACHE 0x10F + +#define NR_IRQS 272 +#define S698PM_EXTENDED_IRQ 11 +#define S698PM_IPI_VECTOR 14 +#define S698PM_IPI_IRQ S698PM_IRQ_EXTERNAL_14 +#define S698PM_EXTENDED_START 16 +#define S698PM_CPUINT_MAX 32 + +#else + #error "Unrecognized chip" +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Inline functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ +#endif /* __ASSEMBLY__ */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __ARCH_SPARC_INCLUDE_S698PM_IRQ_H */ diff --git a/arch/sparc/src/common/up_internal.h b/arch/sparc/src/common/up_internal.h index 81d42b65ce..873ba7781b 100644 --- a/arch/sparc/src/common/up_internal.h +++ b/arch/sparc/src/common/up_internal.h @@ -210,7 +210,6 @@ uintptr_t up_intstack_alloc(void); uintptr_t up_intstack_top(void); #endif - /* Chip-specific functions **************************************************/ /* Chip specific functions defined in arch/sparc/src/ */ diff --git a/arch/sparc/src/s698pm/Kconfig b/arch/sparc/src/s698pm/Kconfig new file mode 100644 index 0000000000..129308efa7 --- /dev/null +++ b/arch/sparc/src/s698pm/Kconfig @@ -0,0 +1,135 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +if ARCH_CHIP_S698PM +comment "S698PM Configuration Options" + +menu "S698PM Peripheral Support" + +config S698PM_WDG + bool "Watchdog timer (WDT)" + default n + select WATCHDOG + +config S698PM_TIM1 + bool "Timer 1 (T1)" + default n + +config S698PM_TIM2 + bool "Timer 2 (T2)" + default n + +config S698PM_TIM3 + bool "Timer 3 (T3)" + default n + +config S698PM_TIM4 + bool "Timer 4 (T4)" + default n + +config S698PM_UART1 + bool "UART1" + default n + select UART1_SERIALDRIVER + select ARCH_HAVE_SERIAL_TERMIOS + +config S698PM_UART2 + bool "UART2" + default n + select UART2_SERIALDRIVER + select ARCH_HAVE_SERIAL_TERMIOS + +config S698PM_UART3 + bool "UART3" + default n + select UART3_SERIALDRIVER + select ARCH_HAVE_SERIAL_TERMIOS + +config S698PM_UART4 + bool "UART4" + default n + select UART4_SERIALDRIVER + select ARCH_HAVE_SERIAL_TERMIOS + +endmenu # S698PM Peripheral Selections + +menuconfig S698PM_GPIOIRQ + bool "GPIO Interrupt Support" + default n + ---help--- + Build in support for interrupts based on GPIO inputs from IOPorts + +if S698PM_GPIOIRQ + +config S698PM_GPIOIRQ_PORT4 + bool "I/O PORT4 Interrupt Support" + default n + +config S698PM_GPIOIRQ_PORT5 + bool "I/O PORT5 Interrupt Support" + default n + +config S698PM_GPIOIRQ_PORT6 + bool "I/O PORT6 Interrupt Support" + default n +config S698PM_GPIOIRQ_PORT7 + bool "I/O PORT7 Interrupt Support" + default n + +endif # S698PM_GPIOIRQ + +menu "Timer Configuration" + +if SCHED_TICKLESS + +config S698PM_ONESHOT + bool + default y + +config S698PM_FREERUN + bool + default y + +config S698PM_TICKLESS_ONESHOT + int "Tickless one-shot timer channel" + default 1 + range 1 2 + depends on S698PM_ONESHOT + ---help--- + If the Tickless OS feature is enabled, then one clock must be + assigned to provide the one-shot timer needed by the OS. + +config S698PM_TICKLESS_FREERUN + int "Tickless free-running timer channel" + default 2 + range 1 2 + depends on S698PM_FREERUN + ---help--- + If the Tickless OS feature is enabled, then one clock must be + assigned to provide the free-running timer needed by the OS. + +endif # SCHED_TICKLESS + +if !SCHED_TICKLESS + +config S698PM_ONESHOT + bool "TIM one-shot wrapper" + default n + ---help--- + Enable a wrapper around the low level timer/counter functions to + support one-shot timer. + +config S698PM_FREERUN + bool "TIM free-running wrapper" + default n + ---help--- + Enable a wrapper around the low level timer/counter functions to + support a free-running timer. + +endif # !SCHED_TICKLESS + +endmenu # Timer Configuration + +endif diff --git a/arch/sparc/src/s698pm/Make.defs b/arch/sparc/src/s698pm/Make.defs new file mode 100644 index 0000000000..be95c64ee5 --- /dev/null +++ b/arch/sparc/src/s698pm/Make.defs @@ -0,0 +1,58 @@ +############################################################################ +# arch/sparc/src/s698pm/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include common/Make.defs + +# The start-up, "head", file + +HEAD_ASRC = s698pm_head.S + +# Required S698PM files + +CHIP_ASRCS = s698pm_exceptions.S +CHIP_CSRCS = s698pm-lowconsole.c s698pm-lowinit.c s698pm-serial.c s698pm-irq.c s698pm_tim.c + +ifeq ($(CONFIG_TIMER),y) +CHIP_CSRCS += s698pm_tim_lowerhalf.c +endif + +ifeq ($(CONFIG_S698PM_WDG),y) +CHIP_CSRCS += s698pm_wdg.c +endif + +ifneq ($(CONFIG_SCHED_TICKLESS),y) +CHIP_CSRCS += s698pm-timerisr.c +else +CHIP_CSRCS += s698pm_tickless.c +endif + +ifeq ($(CONFIG_S698PM_ONESHOT),y) +CHIP_CSRCS += s698pm_oneshot.c s698pm_oneshot_lowerhalf.c +endif + +ifeq ($(CONFIG_S698PM_FREERUN),y) +CHIP_CSRCS += s698pm_freerun.c +endif + +# Configuration-dependent files + +ifeq ($(CONFIG_SMP),y) +CHIP_CSRCS += s698pm_cpuindex.c s698pm_cpustart.c s698pm_cpupause.c s698pm_cpuidlestack.c +endif diff --git a/arch/sparc/src/s698pm/chip.h b/arch/sparc/src/s698pm/chip.h new file mode 100644 index 0000000000..026d3cedf3 --- /dev/null +++ b/arch/sparc/src/s698pm/chip.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/chip.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_SPARC_SRC_S698PM_CHIP_H +#define __ARCH_SPARC_SRC_S698PM_CHIP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* Include only the memory map. Other chip hardware files should then + * include this file for the proper setup + */ + +#include "s698pm-memorymap.h" + +#endif /* __ARCH_SPARC_SRC_S698PM_CHIP_H */ + diff --git a/arch/sparc/src/s698pm/s698pm-config.h b/arch/sparc/src/s698pm/s698pm-config.h new file mode 100644 index 0000000000..c9f22489bf --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-config.h @@ -0,0 +1,100 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-config.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_SPARC_SRC_S698PM_CONFIG_H +#define __ARCH_SPARC_SRC_S698PM_CONFIG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "chip.h" + +#include "s698pm-memorymap.h" +#include "s698pm-uart.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* UARTs ********************************************************************/ + +/* Don't enable UARTs not supported by the chip. */ + +#if CHIP_NUARTS < 1 +# undef CONFIG_S698PM_UART1 +# undef CONFIG_S698PM_UART2 +# undef CONFIG_S698PM_UART3 +# undef CONFIG_S698PM_UART4 +#elif CHIP_NUARTS < 2 +# undef CONFIG_S698PM_UART2 +# undef CONFIG_S698PM_UART3 +# undef CONFIG_S698PM_UART4 +#elif CHIP_NUARTS < 3 +# undef CONFIG_S698PM_UART3 +# undef CONFIG_S698PM_UART4 +#elif CHIP_NUARTS < 4 +# undef CONFIG_S698PM_UART4 +#endif + +/* Are any UARTs enabled? */ + +#undef HAVE_UART_DEVICE +#if defined(CONFIG_S698PM_UART1) || defined(CONFIG_S698PM_UART2) || \ + defined(CONFIG_S698PM_UART3) || defined(CONFIG_S698PM_UART4) +# define HAVE_UART_DEVICE 1 +#endif + +/* Is there a serial console? There should be no more than one defined. It + * could be on any UARTn, n=1,.. CHIP_NUARTS + */ + +#if defined(CONFIG_UART1_SERIAL_CONSOLE) && defined(CONFIG_S698PM_UART1) +# undef CONFIG_UART2_SERIAL_CONSOLE +# undef CONFIG_UART3_SERIAL_CONSOLE +# undef CONFIG_UART4_SERIAL_CONSOLE +# define HAVE_SERIAL_CONSOLE 1 +#elif defined(CONFIG_UART2_SERIAL_CONSOLE) && defined(CONFIG_S698PM_UART2) +# undef CONFIG_UART1_SERIAL_CONSOLE +# undef CONFIG_UART3_SERIAL_CONSOLE +# undef CONFIG_UART4_SERIAL_CONSOLE +# define HAVE_SERIAL_CONSOLE 1 +#elif defined(CONFIG_UART3_SERIAL_CONSOLE) && defined(CONFIG_S698PM_UART3) +# undef CONFIG_UART1_SERIAL_CONSOLE +# undef CONFIG_UART2_SERIAL_CONSOLE +# undef CONFIG_UART4_SERIAL_CONSOLE +# define HAVE_SERIAL_CONSOLE 1 +#elif defined(CONFIG_UART4_SERIAL_CONSOLE) && defined(CONFIG_S698PM_UART4) +# undef CONFIG_UART1_SERIAL_CONSOLE +# undef CONFIG_UART2_SERIAL_CONSOLE +# undef CONFIG_UART3_SERIAL_CONSOLE +# define HAVE_SERIAL_CONSOLE 1 +#else +# undef CONFIG_UART1_SERIAL_CONSOLE +# undef CONFIG_UART2_SERIAL_CONSOLE +# undef CONFIG_UART3_SERIAL_CONSOLE +# undef CONFIG_UART4_SERIAL_CONSOLE +# undef HAVE_SERIAL_CONSOLE +#endif + +#endif /* __ARCH_SPARC_SRC_S698PM_CONFIG_H */ diff --git a/arch/sparc/src/s698pm/s698pm-irq.c b/arch/sparc/src/s698pm/s698pm-irq.c new file mode 100644 index 0000000000..03e49907f3 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-irq.c @@ -0,0 +1,541 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-irq.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "up_internal.h" +#include "s698pm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if CONFIG_ARCH_INTERRUPTSTACK > 7 + +#if defined(CONFIG_SMP) +# define INTSTACK_ALLOC (CONFIG_SMP_NCPUS * INTSTACK_SIZE) +#else +# define INTSTACK_ALLOC (INTSTACK_SIZE) +#endif + +#endif + +/* IRQ to CPU and CPU interrupts mapping: + * + * Encoding: CCCIIIII + * C: CPU that enabled the interrupt (0 ~ 3). + * I: Associated CPU interrupt (0 ~ 31). + */ + +#define IRQ_UNMAPPED 0xff +#define IRQ_GETCPU(m) (((m) & 0xe0) >> 0x05) +#define IRQ_GETCPUINT(m) ((m) & 0x1f) +#define IRQ_MKMAP(c, i) (((c) << 0x05) | (i)) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static volatile uint8_t g_irqmap[NR_IRQS]; + +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +/* In the SMP configuration, we will need custom interrupt stacks. + * These definitions provide the aligned stack allocations. + */ + +static uint64_t g_intstack_alloc[INTSTACK_ALLOC >> 3]; + +/* These definitions provide the "top" of the push-down stacks. */ + +uintptr_t g_cpu_intstack_top[CONFIG_SMP_NCPUS] = +{ + (uintptr_t)g_intstack_alloc + INTSTACK_SIZE, +#if defined(CONFIG_SMP) + +#if CONFIG_SMP_NCPUS > 1 + (uintptr_t)g_intstack_alloc + (2 * INTSTACK_SIZE), +#if CONFIG_SMP_NCPUS > 2 + (uintptr_t)g_intstack_alloc + (3 * INTSTACK_SIZE), +#if CONFIG_SMP_NCPUS > 3 + (uintptr_t)g_intstack_alloc + (4 * INTSTACK_SIZE), +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +#endif /* CONFIG_SMP_NCPUS > 1 */ + +#endif /* defined(CONFIG_SMP) */ +}; +#endif /* if CONFIG_ARCH_INTERRUPTSTACK > 7 */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifndef CONFIG_ARCH_IRQPRIO +static int up_prioritize_irq(int irq, int priority); +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_irqinitialize + ****************************************************************************/ + +void up_irqinitialize(void) +{ + int irq = 0; + + for (irq = 0; irq < NR_IRQS; irq++) + { + g_irqmap[irq] = IRQ_UNMAPPED; + } + + /* Initialize CPU interrupts */ + + s698pm_cpuint_initialize(); + + /* Set interrupts priority */ + + for (irq = S698PM_IRQ_FIRST_INT; irq <= S698PM_IRQ_LAST_INT; irq++) + { + /* Set all interrupts to the default (low) priority */ + + (void)up_prioritize_irq(irq, 0); + } + + /* Attach software interrupts */ + + irq_attach(S698PM_IRQ_SW_SYSCALL_TA0, up_swint0, NULL); + irq_attach(S698PM_IRQ_SW_SYSCALL_TA8, up_swint1, NULL); + + /* And finally, enable cpu interrupts */ + +#ifndef CONFIG_SUPPRESS_INTERRUPTS + up_irq_enable(); +#endif +} + +/**************************************************************************** + * Name: s698pm_cpuint_initialize + * + * Description: + * Initialize CPU interrupts + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int s698pm_cpuint_initialize(void) +{ + uintptr_t regaddr; + int cpu = 0; + +#ifdef CONFIG_SMP + /* Which CPU are we initializing */ + + cpu = up_cpu_index(); + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS); +#endif + + /* get Interrupt_Mask reg address of cpu */ + + regaddr = S698PM_IRQREG_P0_MASK + 4 * cpu; + + /* Disable all CPU interrupts on this CPU */ + + putreg32(0, regaddr); + +#if defined CONFIG_SMP + /* Attach IPI interrupts */ + + irq_attach(S698PM_IPI_IRQ, s698pm_pause_handler, NULL); + + (void)s698pm_setup_irq(cpu, S698PM_IPI_IRQ, 0); + + /* And enable the IPI interrupt */ + + up_enable_irq(S698PM_IPI_IRQ); +#endif + + return OK; +} + +/**************************************************************************** + * Name: s698pm_setup_irq + * + * Description: + * This function sets up the IRQ. It allocates a CPU interrupt of the given + * priority andattaches it to the given irq. + * + * Input Parameters: + * cpu - The CPU to receive the interrupt 0~3 + * irq - The irq number from irq.h to be assigned to a EXT interrupt. + * priority - Interrupt's priority (0~1). + * + * Returned Value: + * The allocated CPU interrupt on success, a negated errno value on + * failure. + * + ****************************************************************************/ + +int s698pm_setup_irq(int cpu, int irq, int priority) +{ + irqstate_t irqstate; + int cpuint; + + irqstate = enter_critical_section(); + + if (irq >= S698PM_IRQ_FIRST_INT && irq <= S698PM_IRQ_LAST_INT) + { + cpuint = irq - S698PM_IRQ_FIRST_INT + 1; + } + else if (irq > S698PM_IRQ_LAST && irq < NR_IRQS) + { + /* Second level interrupt share a IRQ and connnect to a interrupt 14 + * of first level interrupt(that is IRQ 0x1E), We extend Second level + * interrupt to IRQ 0x100~0x10F. because first level interrupt in the + * interrupt reister is bit 0~15 and second level interrupt is bit 16 + * ~31, so cpuint of second level interrupt is irq - 256 + + * S698PM_EXTENDED_START + */ + + cpuint = irq - 256 + S698PM_EXTENDED_START; + } + else + { + cpuint = -1; + } + + DEBUGASSERT(cpuint >= 0 && cpuint < S698PM_CPUINT_MAX); + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS); + + g_irqmap[irq] = IRQ_MKMAP(cpu, cpuint); + (void)up_prioritize_irq(irq, priority); + + leave_critical_section(irqstate); + + return cpuint; +} + +/**************************************************************************** + * Name: s698pm_teardown_irq + * + * Description: + * This function undoes the operations done by s698pm_setup_irq. + * It detaches a ext interrupt from a CPU irq. + * + * Input Parameters: + * irq - The irq number from irq.h to be assigned to a EXT interrupt. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void s698pm_teardown_irq(int irq) +{ + irqstate_t irqstate; + + irqstate = enter_critical_section(); + + g_irqmap[irq] = IRQ_UNMAPPED; + + leave_critical_section(irqstate); +} + +/**************************************************************************** + * Name: up_disable_irq + * + * Description: + * Disable the IRQ specified by 'irq' + * + ****************************************************************************/ + +void up_disable_irq(int irq) +{ + uintptr_t regaddr; + int cpu = IRQ_GETCPU(g_irqmap[irq]); + int cpuint = IRQ_GETCPUINT(g_irqmap[irq]); + + if (g_irqmap[irq] == IRQ_UNMAPPED) + { + /* This interrupt is already disabled. */ + + return; + } + + DEBUGASSERT(cpuint >= 0 && cpuint < S698PM_CPUINT_MAX); + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS); + + if ((irq >= S698PM_IRQ_FIRST_INT && irq <= S698PM_IRQ_LAST_INT) || + (irq > S698PM_IRQ_LAST && irq < NR_IRQS)) + { + /* get Interrupt_Mask reg address of cpu */ + + regaddr = S698PM_IRQREG_P0_MASK + 4 * cpu; + + /* Disable the interrupt by clearing the associated bit in the + * Interrupt_Mask + */ + + modifyreg32(regaddr, 1 << cpuint, 0); + } +} + +/**************************************************************************** + * Name: up_enable_irq + * + * Description: + * Enable the IRQ specified by 'irq' + * + ****************************************************************************/ + +void up_enable_irq(int irq) +{ + uintptr_t regaddr; + int cpu = IRQ_GETCPU(g_irqmap[irq]); + int cpuint = IRQ_GETCPUINT(g_irqmap[irq]); + + if (g_irqmap[irq] == IRQ_UNMAPPED) + { + /* This interrupt is already disabled. */ + + return; + } + + DEBUGASSERT(cpuint >= 0 && cpuint < S698PM_CPUINT_MAX); + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS); + + if ((irq >= S698PM_IRQ_FIRST_INT && irq <= S698PM_IRQ_LAST_INT) || + (irq > S698PM_IRQ_LAST && irq < NR_IRQS)) + { + /* get Interrupt_Mask reg address of cpu */ + + regaddr = S698PM_IRQREG_P0_MASK + 4 * cpu; + + /* Disable the interrupt by clearing the associated bit in the + * Interrupt_Mask + */ + + modifyreg32(regaddr, 1 << cpuint, 1 << cpuint); + } +} + +/**************************************************************************** + * Name: up_pending_irq + * + * Description: + * Return true if the interrupt is pending and unmasked. + * + ****************************************************************************/ + +bool up_pending_irq(int irq) +{ + uintptr_t regaddr; + uint16_t regval; + uint16_t regval1; + uint16_t regval2; + int cpu = IRQ_GETCPU(g_irqmap[irq]); + int cpuint = IRQ_GETCPUINT(g_irqmap[irq]); + + /* Test if the interrupt is pending by reading both the MASK and ACK + * register. Return true if the bit associated with the irq is both + * pending the ACK and enabled in the MASK. + */ + + if (g_irqmap[irq] == IRQ_UNMAPPED) + { + /* This interrupt is already disabled. */ + + return false; + } + + DEBUGASSERT(cpuint >= 0 && cpuint < S698PM_CPUINT_MAX); + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS); + + if ((irq >= S698PM_IRQ_FIRST_INT && irq <= S698PM_IRQ_LAST_INT) || + (irq > S698PM_IRQ_LAST && irq < NR_IRQS)) + { + /* get interrupt mask register address of cpu */ + + regaddr = S698PM_IRQREG_P0_MASK + 4 * cpu; + regval1 = getreg32(regaddr); + + /* get interrupt pend register address of cpu */ + + regaddr = S698PM_IRQREG_IPEND; + regval2 = getreg32(regaddr); + + /* Get the set of unmasked, pending interrupts. */ + + regval = regval1 & regval2; + + /* Return true if the interrupt is pending and unmask. */ + + return (regval & (1 << cpuint)) != 0; + } + + return false; +} + +/**************************************************************************** + * Name: up_clrpend_irq + * + * Description: + * Clear any pending interrupt + * + ****************************************************************************/ + +void up_clrpend_irq(int irq) +{ + int cpuint = IRQ_GETCPUINT(g_irqmap[irq]); + + /* Test if the interrupt is pending by reading both the MASK and ACK + * register. Return true if the bit associated with the irq is both + * pending the ACK and enabled in the MASK. + */ + + if (g_irqmap[irq] == IRQ_UNMAPPED) + { + /* This interrupt is already disabled. */ + + return; + } + + DEBUGASSERT(cpuint >= 0 && cpuint < S698PM_CPUINT_MAX); + + /* Acknowledge the interrupt by clearing the associated bit in the ITP + * register. It is necessary to do this BEFORE lowering the interrupt + * priority level otherwise recursive interrupts would occur. + */ + + if ((irq >= S698PM_IRQ_FIRST_INT && irq <= S698PM_IRQ_LAST_INT) || + (irq > S698PM_IRQ_LAST && irq < NR_IRQS)) + { + /* written with a ‘1’, in Interrupt Clear Register + * will clear the corresponding bit(s) in the interrupt pending + * register + */ + + putreg32(1 << cpuint, S698PM_IRQREG_ICLEAR); + } + + return; +} + +/**************************************************************************** + * Name: up_prioritize_irq + * + * Description: + * It is possible to change the priority level of an interrupt using the two + * priority levels from the interrupt mask and priority register (ITMP). + * Each interrupt can be assigned to one of two levels as programmed in the + * Interrupt mask and priority register. Level 1 has higher priority than + * level 0. Within each level the interrupts are prioritised + ****************************************************************************/ + +#ifndef CONFIG_ARCH_IRQPRIO +static +#endif +int up_prioritize_irq(int irq, int priority) +{ + int shift; + + /* Don't allow this function to be used for disabling interrupts. */ + + DEBUGASSERT((unsigned)irq < NR_IRQS && (unsigned)(priority) < 2); + + if (irq >= S698PM_IRQ_FIRST_INT && irq <= S698PM_IRQ_LAST_INT) + { + shift = irq - S698PM_IRQ_FIRST_INT + 1; + + /* Set the new interrupt priority */ + + putreg32(1 << shift, S698PM_IRQREG_ILEVEL); + } + else + { + /* Value out of range.. just ignore */ + + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: sparc_intstack_top + * + * Description: + * Return a pointer to the top the correct interrupt stack allocation + * for the current CPU. + * + ****************************************************************************/ + +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +uintptr_t up_intstack_top(void) +{ +#if defined(CONFIG_SMP) + return g_cpu_intstack_top[up_cpu_index()]; +#else + return g_cpu_intstack_top[0]; +#endif +} +#endif + +/**************************************************************************** + * Name: sparc_intstack_alloc + * + * Description: + * Return a pointer to the "alloc" the correct interrupt stack allocation + * for the current CPU. + * + ****************************************************************************/ + +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +uintptr_t up_intstack_alloc(void) +{ +#if defined(CONFIG_SMP) + return g_cpu_intstack_top[up_cpu_index()] - INTSTACK_SIZE; +#else + return g_cpu_intstack_top[0] - INTSTACK_SIZE; +#endif +} +#endif diff --git a/arch/sparc/src/s698pm/s698pm-lowconsole.c b/arch/sparc/src/s698pm/s698pm-lowconsole.c new file mode 100644 index 0000000000..30ec44af0c --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-lowconsole.c @@ -0,0 +1,266 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-lowconsole.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "s698pm-config.h" + +#include +#include + +#include + +#include "up_internal.h" +#include "s698pm-uart.h" +#include "s698pm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Select UART parameters for the selected console */ + +#ifdef HAVE_SERIAL_CONSOLE +# if defined(CONFIG_UART1_SERIAL_CONSOLE) +# define S698PM_CONSOLE_BASE S698PM_UART1_BASE +# define S698PM_CONSOLE_BAUD CONFIG_UART1_BAUD +# define S698PM_CONSOLE_BITS CONFIG_UART1_BITS +# define S698PM_CONSOLE_PARITY CONFIG_UART1_PARITY +# define S698PM_CONSOLE_2STOP CONFIG_UART1_2STOP +# elif defined(CONFIG_UART2_SERIAL_CONSOLE) +# define S698PM_CONSOLE_BASE S698PM_UART2_BASE +# define S698PM_CONSOLE_BAUD CONFIG_UART2_BAUD +# define S698PM_CONSOLE_BITS CONFIG_UART2_BITS +# define S698PM_CONSOLE_PARITY CONFIG_UART2_PARITY +# define S698PM_CONSOLE_2STOP CONFIG_UART2_2STOP +# elif defined(CONFIG_UART3_SERIAL_CONSOLE) +# define S698PM_CONSOLE_BASE S698PM_UART3_BASE +# define S698PM_CONSOLE_BAUD CONFIG_UART3_BAUD +# define S698PM_CONSOLE_BITS CONFIG_UART3_BITS +# define S698PM_CONSOLE_PARITY CONFIG_UART3_PARITY +# define S698PM_CONSOLE_2STOP CONFIG_UART3_2STOP +# elif defined(CONFIG_UART4_SERIAL_CONSOLE) +# define S698PM_CONSOLE_BASE S698PM_UART4_BASE +# define S698PM_CONSOLE_BAUD CONFIG_UART4_BAUD +# define S698PM_CONSOLE_BITS CONFIG_UART4_BITS +# define S698PM_CONSOLE_PARITY CONFIG_UART4_PARITY +# define S698PM_CONSOLE_2STOP CONFIG_UART4_2STOP +# else +# error "No CONFIG_UARTn_SERIAL_CONSOLE Setting" +# endif +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s698pm_putreg + * + * Description: + * Write a value to a UART register + * + ****************************************************************************/ + +#ifdef HAVE_UART_DEVICE +static inline void s698pm_putreg(uintptr_t uart_base, unsigned int offset, + uint32_t value) +{ + putreg32(value, uart_base + offset); +} +#endif + +/**************************************************************************** + * Name: s698pm_getreg + * + * Description: + * Get a value from a UART register + * + ****************************************************************************/ + +#ifdef HAVE_UART_DEVICE +static inline uint32_t s698pm_getreg(uintptr_t uart_base, + unsigned int offset) +{ + return getreg32(uart_base + offset); +} +#endif + +/**************************************************************************** + * Name: s698pm_uartreset + * + * Description: + * Reset UART. + * + ****************************************************************************/ + +#ifdef HAVE_UART_DEVICE +void s698pm_uartreset(uintptr_t uart_base) +{ + uint32_t reg; + + /* Clear USART configuration */ + + reg = s698pm_getreg(uart_base, S698PM_UART_CTRLREG_OFFSET); + uart_disable(reg); + uart_parity_config(reg, NONE); + uart_flow_ctrl_config(reg, OFF); + uart_loopback_config(reg, OFF); + s698pm_putreg(uart_base, S698PM_UART_CTRLREG_OFFSET, reg); +} +#endif + +/**************************************************************************** + * Name: s698pm_uartconfigure + * + * Description: + * Configure a UART as a console. + * + ****************************************************************************/ + +#ifdef HAVE_UART_DEVICE +void s698pm_uartconfigure(uintptr_t uart_base, uint32_t baudrate, + unsigned int parity, unsigned int nbits, + bool stop2) +{ + uint32_t reg; + + /* Select baud. */ + + s698pm_putreg(S698PM_CONSOLE_BASE, S698PM_UART_SCALREG_OFFSET, + uart_set_baudrate(baudrate)); + + reg = s698pm_getreg(S698PM_CONSOLE_BASE, S698PM_UART_CTRLREG_OFFSET); + + /* Select parity */ + + if (parity == 1) + { + uart_parity_config(reg, ODD); /* Odd parity */ + } + else if (parity == 2) + { + uart_parity_config(reg, EVEN); /* Even parity */ + } + else + { + uart_parity_config(reg, NONE); /* Even none */ + } + + uart_flow_ctrl_config(reg, OFF); + uart_loopback_config(reg, OFF); + + uart_enable(reg); + + s698pm_putreg(S698PM_CONSOLE_BASE, S698PM_UART_CTRLREG_OFFSET, reg); +} +#endif + +/**************************************************************************** + * Name: s698pm_consoleinit + * + * Description: + * Initialize a console for debug output. This function is called very + * early in the initialization sequence to configure the serial console + * uart + * (only). + * + ****************************************************************************/ + +void s698pm_consoleinit(void) +{ + uint32_t gpreg; +#ifdef HAVE_UART_DEVICE + + /* Setup up pin selection registers for all configured UARTs. The board.h + * header file must provide these definitions to select the correct pin + * configuration for each enabled UARt. + */ + + gpreg = getreg32(S698PM_GPREG_BASE); + +#ifdef CONFIG_S698PM_UART3 + /* Configure UART3 RX (input) and TX (output) pins */ + + gpreg |= 0x1; + putreg32(gpreg, S698PM_GPREG_BASE); + +#endif /* CONFIG_S698PM_UART3 */ + +#ifdef CONFIG_S698PM_UART4 + /* Configure UART4 RX (input) and TX (output) pins */ + + gpreg |= 0x2; + putreg32(gpreg, S698PM_GPREG_BASE); + +#endif /* CONFIG_S698PM_UART4 */ + +#ifdef HAVE_SERIAL_CONSOLE + /* Configure the console uart */ + + s698pm_uartconfigure(S698PM_CONSOLE_BASE, S698PM_CONSOLE_BAUD, + S698PM_CONSOLE_PARITY, S698PM_CONSOLE_BITS, + S698PM_CONSOLE_2STOP); + +#endif /* HAVE_SERIAL_CONSOLE */ +#endif /* HAVE_UART_DEVICE */ +} + +/**************************************************************************** + * Name: up_lowputc + * + * Description: + * Output one byte on the serial console + * + ****************************************************************************/ + +void up_lowputc(char ch) +{ +#ifdef HAVE_SERIAL_CONSOLE + while ((s698pm_getreg(S698PM_CONSOLE_BASE, S698PM_UART_STATREG_OFFSET) & + UART_STA_TE) == 0); + + /* Then write the character to the TX data register */ + + s698pm_putreg(S698PM_CONSOLE_BASE, S698PM_UART_TXREG_OFFSET, + (uint32_t)ch); +#endif +} diff --git a/arch/sparc/src/s698pm/s698pm-lowinit.c b/arch/sparc/src/s698pm/s698pm-lowinit.c new file mode 100644 index 0000000000..7e7b0f5ef1 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-lowinit.c @@ -0,0 +1,119 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-lowinit.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "s698pm-config.h" +#include "up_internal.h" +#include "s698pm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: showprogress + * + * Description: + * Print a character on the UART to show boot status. + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_FEATURES +# define showprogress(c) up_lowputc(c) +#else +# define showprogress(c) +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_lowinit + * + * Description: + * This performs basic initialization of the USART used for the serial + * console. Its purpose is to get the console output available as soon + * as possible. + * + ****************************************************************************/ + +void up_lowinit(void) +{ + uint32_t *dest; + + /* Initialize a console (probably a serial console) */ + + s698pm_consoleinit(); + + showprogress('A'); + + /* Clear .bss. We'll do this inline (vs. calling memset) just to be + * certain that there are no issues with the state of global variables. + */ + + for (dest = &_bss_start; dest < &_end; ) + { + *dest++ = 0; + } + + showprogress('B'); + /* Perform early serial initialization (so that we will have debug output + * available as soon as possible). + */ + +#ifdef USE_EARLYSERIALINIT + up_earlyserialinit(); +#endif + + /* Perform board-level initialization */ + + s698pm_boardinitialize(); + + /* Then start NuttX */ + + showprogress('\r'); + showprogress('\n'); +} diff --git a/arch/sparc/src/s698pm/s698pm-memorymap.h b/arch/sparc/src/s698pm/s698pm-memorymap.h new file mode 100644 index 0000000000..b267e47c95 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-memorymap.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-memorymap.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_SPARC_SRC_S698PM_S698PM_MEMORYMAP_H +#define __ARCH_SPARC_SRC_S698PM_S698PM_MEMORYMAP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Base Addresses **************************************************/ + +/* UART 1-4 Register Base Addresses */ + +# define S698PM_UART1_BASE (0x80000100) +# define S698PM_UART2_BASE (0x80000900) +# define S698PM_UART3_BASE (0x80100100) +# define S698PM_UART4_BASE (0x80100200) + +/* GPIO metux Register Base Addresses */ + +# define S698PM_GPREG_BASE (0x80100500) + +#define S698PM_DSU_BASE (0x90000000) +#define S698PM_DSU_CPU0_BASE (0x90000000) +#define S698PM_DSU_CPU1_BASE (0x91000000) +#define S698PM_DSU_CPU2_BASE (0x92000000) +#define S698PM_DSU_CPU3_BASE (0x93000000) + +#define S698PM_DSU_PC_OFFSET 0x400010 /* PC register */ +#define S698PM_DSU_NPC_OFFSET 0x400014 /* nPC register */ + +#endif /* __ARCH_SPARC_SRC_S698PM_S698PM_MEMORYMAP_H */ + diff --git a/arch/sparc/src/s698pm/s698pm-serial.c b/arch/sparc/src/s698pm/s698pm-serial.c new file mode 100644 index 0000000000..0486884eb1 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-serial.c @@ -0,0 +1,1031 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-serial.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SERIAL_TERMIOS +# include +#endif + +#include +#include +#include +#include +#include + +#include "up_internal.h" +#include "s698pm-config.h" +#include "chip.h" +#include "s698pm-uart.h" +#include "s698pm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* If we are not using the serial driver for the console, then we still must + * provide some minimal implementation of up_putc. + */ + +#ifdef USE_SERIALDRIVER + +/* Which UART with be tty0/console and which tty1? The console will always + * be ttyS0. If there is no console then will use the lowest numbered UART. + */ + +/* First pick the console and ttys0. This could be any of UART1-4 */ + +#if defined(CONFIG_UART1_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart1port /* UART1 is console */ +# define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ +# define UART1_ASSIGNED 1 +#elif defined(CONFIG_UART2_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart2port /* UART2 is console */ +# define TTYS0_DEV g_uart2port /* UART2 is ttyS0 */ +# define UART2_ASSIGNED 1 +#elif defined(CONFIG_UART3_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart3port /* UART3 is console */ +# define TTYS0_DEV g_uart3port /* UART3 is ttyS0 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_UART4_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart4port /* UART4 is console */ +# define TTYS0_DEV g_uart4port /* UART4 is ttyS0 */ +# define UART4_ASSIGNED 1 +#else +# undef CONSOLE_DEV /* No console */ +# if defined(CONFIG_S698PM_UART1) +# define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ +# define UART1_ASSIGNED 1 +# elif defined(CONFIG_S698PM_UART2) +# define TTYS0_DEV g_uart2port /* UART2 is ttyS0 */ +# define UART2_ASSIGNED 1 +# elif defined(CONFIG_S698PM_UART3) +# define TTYS0_DEV g_uart3port /* UART3 is ttyS0 */ +# define UART3_ASSIGNED 1 +# elif defined(CONFIG_S698PM_UART4) +# define TTYS0_DEV g_uart4port /* UART4 is ttyS0 */ +# define UART4_ASSIGNED 1 +# endif +#endif + +/* Pick ttys1. This could be any of UART1-4 excluding the console UART. */ + +#if defined(CONFIG_S698PM_UART1) && !defined(UART1_ASSIGNED) +# define TTYS1_DEV g_uart1port /* UART1 is ttyS1 */ +# define UART1_ASSIGNED 1 +#elif defined(CONFIG_S698PM_UART2) && !defined(UART2_ASSIGNED) +# define TTYS1_DEV g_uart2port /* UART2 is ttyS1 */ +# define UART2_ASSIGNED 1 +#elif defined(CONFIG_S698PM_UART3) && !defined(UART3_ASSIGNED) +# define TTYS1_DEV g_uart3port /* UART3 is ttyS1 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_S698PM_UART4) && !defined(UART4_ASSIGNED) +# define TTYS1_DEV g_uart4port /* UART4 is ttyS1 */ +# define UART4_ASSIGNED 1 +#endif + +/* Pick ttys2. This could be one of UART2-4. It can't be UART1 because that + * was either assigned as ttyS0 or ttys1. One of UART 1-4 could also be the + * console. + */ + +#if defined(CONFIG_S698PM_UART2) && !defined(UART2_ASSIGNED) +# define TTYS2_DEV g_uart2port /* UART2 is ttyS2 */ +# define UART2_ASSIGNED 1 +#elif defined(CONFIG_S698PM_UART3) && !defined(UART3_ASSIGNED) +# define TTYS2_DEV g_uart3port /* UART3 is ttyS2 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_S698PM_UART4) && !defined(UART4_ASSIGNED) +# define TTYS2_DEV g_uart4port /* UART4 is ttyS2 */ +# define UART4_ASSIGNED 1 +#endif + +/* Pick ttys3. This could be one of UART3-4. It can't be UART1-2 because + * those have already been assigned to ttsyS0, 1, or 2. One of + * UART 2-4 could also be the console. + */ + +#if defined(CONFIG_S698PM_UART3) && !defined(UART3_ASSIGNED) +# define TTYS3_DEV g_uart3port /* UART3 is ttyS3 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_S698PM_UART4) && !defined(UART4_ASSIGNED) +# define TTYS3_DEV g_uart4port /* UART4 is ttyS3 */ +# define UART4_ASSIGNED 1 +#endif + +/* Common initialization logic will not not know that the all of the UARTs + * have been disabled. So, as a result, we may still have to provide + * stub implementations of up_earlyserialinit(), up_serialinit(), and + * up_putc(). + */ + +#ifdef HAVE_UART_DEVICE + +/* These values describe the set of enabled interrupts */ + +#define RX_ENABLED(im) (((im) & MSK_UART_ENABLE_RX) != 0) +#define TX_ENABLED(im) (((im) & MSK_UART_ENABLE_TX) != 0) + +#define ENABLE_RX(im) do { (im) |= MSK_UART_ENABLE_RX; } while (0) +#define ENABLE_TX(im) do { (im) |= MSK_UART_ENABLE_TX; } while (0) + +#define DISABLE_RX(im) do { (im) &= ~MSK_UART_ENABLE_RX; } while (0) +#define DISABLE_TX(im) do { (im) &= ~MSK_UART_ENABLE_TX; } while (0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct up_dev_s +{ + uintptr_t uartbase; /* Base address of UART registers */ + uint32_t baud; /* Configured baud */ + uint8_t irq; /* IRQ associated with this UART (for attachment) */ + uint8_t im; /* Interrupt mask state */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (5, 6, 7 or 8) */ + bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ + spinlock_t lock; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Low-level helpers */ + +static inline uint32_t up_serialin(struct up_dev_s *priv, int offset); +static inline void up_serialout(struct up_dev_s *priv, int offset, + uint32_t value); +static void up_restoreuartint(struct uart_dev_s *dev, uint8_t im); +static void up_disableuartint(struct uart_dev_s *dev, uint8_t *im); + +/* Serial driver methods */ + +static int up_setup(struct uart_dev_s *dev); +static void up_shutdown(struct uart_dev_s *dev); +static int up_attach(struct uart_dev_s *dev); +static void up_detach(struct uart_dev_s *dev); +static int up_interrupt(int irq, void *context, void *arg); +static int up_ioctl(struct file *filep, int cmd, unsigned long arg); +static int up_receive(struct uart_dev_s *dev, uint32_t *status); +static void up_rxint(struct uart_dev_s *dev, bool enable); +static bool up_rxavailable(struct uart_dev_s *dev); +static void up_send(struct uart_dev_s *dev, int ch); +static void up_txint(struct uart_dev_s *dev, bool enable); +static bool up_txready(struct uart_dev_s *dev); +static bool up_txempty(struct uart_dev_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct uart_ops_s g_uart_ops = +{ + .setup = up_setup, + .shutdown = up_shutdown, + .attach = up_attach, + .detach = up_detach, + .ioctl = up_ioctl, + .receive = up_receive, + .rxint = up_rxint, + .rxavailable = up_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = NULL, +#endif + .send = up_send, + .txint = up_txint, + .txready = up_txready, + .txempty = up_txempty, +}; + +/* I/O buffers */ + +#ifdef CONFIG_S698PM_UART1 +static char g_uart1rxbuffer[CONFIG_UART1_RXBUFSIZE]; +static char g_uart1txbuffer[CONFIG_UART1_TXBUFSIZE]; +#endif +#ifdef CONFIG_S698PM_UART2 +static char g_uart2rxbuffer[CONFIG_UART2_RXBUFSIZE]; +static char g_uart2txbuffer[CONFIG_UART2_TXBUFSIZE]; +#endif +#ifdef CONFIG_S698PM_UART3 +static char g_uart3rxbuffer[CONFIG_UART3_RXBUFSIZE]; +static char g_uart3txbuffer[CONFIG_UART3_TXBUFSIZE]; +#endif +#ifdef CONFIG_S698PM_UART4 +static char g_uart3rxbuffer[CONFIG_UART4_RXBUFSIZE]; +static char g_uart3txbuffer[CONFIG_UART4_TXBUFSIZE]; +#endif + +/* This describes the state of the S698PM UART1 port. */ + +#ifdef CONFIG_S698PM_UART1 +static struct up_dev_s g_uart1priv = +{ + .uartbase = S698PM_UART1_BASE, + .baud = CONFIG_UART1_BAUD, + .irq = S698PM_IRQ_UART_1_RX_TX, + .parity = CONFIG_UART1_PARITY, + .bits = CONFIG_UART1_BITS, + .stopbits2 = CONFIG_UART1_2STOP, +}; + +static uart_dev_t g_uart1port = +{ + .recv = + { + .size = CONFIG_UART1_RXBUFSIZE, + .buffer = g_uart1rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART1_TXBUFSIZE, + .buffer = g_uart1txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart1priv, +}; +#endif + +/* This describes the state of the S698PM UART2 port. */ + +#ifdef CONFIG_S698PM_UART2 +static struct up_dev_s g_uart2priv = +{ + .uartbase = S698PM_UART2_BASE, + .baud = CONFIG_UART2_BAUD, + .irq = S698PM_IRQ_UART_2_RX_TX, + .parity = CONFIG_UART2_PARITY, + .bits = CONFIG_UART2_BITS, + .stopbits2 = CONFIG_UART2_2STOP, +}; + +static uart_dev_t g_uart2port = +{ + .recv = + { + .size = CONFIG_UART2_RXBUFSIZE, + .buffer = g_uart2rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART2_TXBUFSIZE, + .buffer = g_uart2txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart2priv, +}; +#endif + +/* This describes the state of the S698PM UART3 port. */ + +#ifdef CONFIG_S698PM_UART3 +static struct up_dev_s g_uart3priv = +{ + .uartbase = S698PM_UART3_BASE, + .baud = CONFIG_UART3_BAUD, + .irq = S698PM_IRQ_UART_3_RX_TX, + .parity = CONFIG_UART3_PARITY, + .bits = CONFIG_UART3_BITS, + .stopbits2 = CONFIG_UART3_2STOP, +}; + +static uart_dev_t g_uart3port = +{ + .recv = + { + .size = CONFIG_UART3_RXBUFSIZE, + .buffer = g_uart3rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART3_TXBUFSIZE, + .buffer = g_uart3txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart3priv, +}; +#endif + +/* This describes the state of the S698PM UART4 port. */ + +#ifdef CONFIG_S698PM_UART4 +static struct up_dev_s g_uart4priv = +{ + .uartbase = S698PM_UART4_BASE, + .baud = CONFIG_UART4_BAUD, + .irq = S698PM_IRQ_UART_4_RX_TX, + .parity = CONFIG_UART4_PARITY, + .bits = CONFIG_UART4_BITS, + .stopbits2 = CONFIG_UART4_2STOP, +}; + +static uart_dev_t g_uart4port = +{ + .recv = + { + .size = CONFIG_UART4_RXBUFSIZE, + .buffer = g_uart4rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART4_TXBUFSIZE, + .buffer = g_uart4txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart4priv, +}; +#endif +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_serialin + ****************************************************************************/ + +static inline uint32_t up_serialin(struct up_dev_s *priv, int offset) +{ + return getreg32(priv->uartbase + offset); +} + +/**************************************************************************** + * Name: up_serialout + ****************************************************************************/ + +static inline void up_serialout(struct up_dev_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->uartbase + offset); +} + +/**************************************************************************** + * Name: up_setuartint + ****************************************************************************/ + +static inline void up_setuartint(struct up_dev_s *priv) +{ + uint8_t regval; + + regval = up_serialin(priv, S698PM_UART_CTRLREG_OFFSET); + regval &= ~MSK_UART_ALLINTS; + regval |= priv->im; + up_serialout(priv, S698PM_UART_CTRLREG_OFFSET, regval); +} + +/**************************************************************************** + * Name: up_restoreuartint + ****************************************************************************/ + +static void up_restoreuartint(struct uart_dev_s *dev, uint8_t im) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + irqstate_t flags; + + /* Re-enable/re-disable interrupts corresponding to the state of bits + * in im + */ + + flags = spin_lock_irqsave(&priv->lock); + priv->im &= ~MSK_UART_ALLINTS; + priv->im |= im; + up_setuartint(priv); + spin_unlock_irqrestore(&priv->lock, flags); +} + +/**************************************************************************** + * Name: up_disableuartint + ****************************************************************************/ + +static void up_disableuartint(struct uart_dev_s *dev, uint8_t *im) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + if (im) + { + *im = priv->im; + } + + /* Disable all interrupts */ + + DISABLE_RX(priv->im); + DISABLE_TX(priv->im); + up_setuartint(priv); + spin_unlock_irqrestore(&priv->lock, flags); +} + +/**************************************************************************** + * Name: up_setup + * + * Description: + * Configure the UART baud, bits, parity, etc. This method is called the + * first time that the serial port is opened. + * + ****************************************************************************/ + +static int up_setup(struct uart_dev_s *dev) +{ +#ifndef CONFIG_SUPPRESS_UART_CONFIG + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Configure the UART as an RS-232 UART */ + + s698pm_uartconfigure(priv->uartbase, priv->baud, priv->parity, + priv->bits, priv->stopbits2); +#endif + +#ifdef CONFIG_ARCH_IRQPRIO + /* Set up the interrupt priority */ + + up_prioritize_irq(priv->irq, priv->irqprio); +#endif + + return OK; +} + +/**************************************************************************** + * Name: up_shutdown + * + * Description: + * Disable the UART. This method is called when the serial + * port is closed + * + ****************************************************************************/ + +static void up_shutdown(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Disable interrupts */ + + up_disableuartint(dev, NULL); + + /* Reset hardware and disable Rx and Tx */ + + s698pm_uartreset(priv->uartbase); +} + +/**************************************************************************** + * Name: up_attach + * + * Description: + * Configure the UART to operation in interrupt driven mode. This method is + * called when the serial port is opened. Normally, this is just after the + * the setup() method is called, however, the serial console may operate in + * a non-interrupt driven mode during the boot phase. + * + * RX and TX interrupts are not enabled by the attach method (unless the + * hardware supports multiple levels of interrupt enabling). The RX and TX + * interrupts are not enabled until the txint() and rxint() methods are + * called. + * + ****************************************************************************/ + +static int up_attach(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + int ret; + + /* Attach and enable the IRQ */ + + ret = irq_attach(priv->irq, up_interrupt, dev); + if (ret == OK) + { + /* Set up to uart interrupts on the current CPU */ + + (void)s698pm_setup_irq(0, priv->irq, 0); + + /* Enable the interrupt (RX and TX interrupts are still disabled + * in the USART + */ + + up_enable_irq(priv->irq); + } + + return ret; +} + +/**************************************************************************** + * Name: up_detach + * + * Description: + * Detach UART interrupts. This method is called when the serial port is + * closed normally just before the shutdown method is called. The exception + * is the serial console which is never shutdown. + * + ****************************************************************************/ + +static void up_detach(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Disable interrupts */ + + up_disableuartint(dev, NULL); + + /* Detach from the interrupt */ + + irq_detach(priv->irq); +} + +/**************************************************************************** + * Name: up_interrupt + * + * Description: + * This is the UART interrupt handler. It will be invoked when an + * interrupt received on the 'irq' It should call uart_transmitchars or + * uart_receivechar to perform the appropriate data transfers. The + * interrupt handling logic must be able to map the 'irq' number into the + * approprite uart_dev_s structure in order to call these functions. + * + ****************************************************************************/ + +static int up_interrupt(int irq, void *context, void *arg) +{ + struct uart_dev_s *dev = (struct uart_dev_s *)arg; + struct up_dev_s *priv; + uint32_t mis; + int passes; + bool handled; + + DEBUGASSERT(dev != NULL && dev->priv != NULL); + priv = (struct up_dev_s *)dev->priv; + + /* Loop until there are no characters to be transferred or, + * until we have been looping for a long time. + */ + + handled = true; + for (passes = 0; passes < 256 && handled; passes++) + { + handled = false; + + /* Get the masked UART status and clear the pending interrupts. */ + + mis = up_serialin(priv, S698PM_UART_STATREG_OFFSET); + + /* Handle incoming, receive bytes */ + + if ((mis & UART_STA_DR) != 0) + { + /* Rx buffer not empty ... process incoming bytes */ + + uart_recvchars(dev); + handled = true; + } + + /* Handle outgoing, transmit bytes */ + + if ((mis & UART_STA_TF) != UART_STA_TF) + { + /* Tx FIFO not full ... process outgoing bytes */ + + uart_xmitchars(dev); + handled = true; + } + } + + return OK; +} + +/**************************************************************************** + * Name: up_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + ****************************************************************************/ + +static int up_ioctl(struct file *filep, int cmd, unsigned long arg) +{ +#ifdef CONFIG_SERIAL_TERMIOS + struct inode *inode; + struct uart_dev_s *dev; + struct up_dev_s *priv; + int ret = OK; + + DEBUGASSERT(filep, filep->f_inode); + inode = filep->f_inode; + dev = inode->i_private; + + DEBUGASSERT(dev, dev->priv); + priv = (struct up_dev_s *)dev->priv; + + switch (cmd) + { + case xxx: /* Add commands here */ + break; + + case TCGETS: + { + struct termios *termiosp = (struct termios *)arg; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* TODO: Other termios fields are not yet returned. + * Note that only cfsetospeed is not necessary because we have + * knowledge that only one speed is supported. + */ + + cfsetispeed(termiosp, priv->baud); + } + break; + + case TCSETS: + { + struct termios *termiosp = (struct termios *)arg; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* TODO: Handle other termios settings. + * Note that only cfgetispeed is used besued we have knowledge + * that only one speed is supported. + */ + + priv->baud = cfgetispeed(termiosp); + s698pm_uartconfigure(priv->uartbase, priv->baud, priv->parity, + priv->bits, priv->stopbits2); + } + break; + + default: + ret = -ENOTTY; + break; + } + + return ret; +#else + return -ENOTTY; +#endif +} + +/**************************************************************************** + * Name: up_receive + * + * Description: + * Called (usually) from the interrupt level to receive one + * character from the UART. Error bits associated with the + * receipt are provided in the return 'status'. + * + ****************************************************************************/ + +static int up_receive(struct uart_dev_s *dev, uint32_t *status) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Return status information */ + + if (status) + { + *status = 0; /* We are not yet tracking serial errors */ + } + + /* Then return the actual received byte */ + + return (int)(up_serialin(priv, S698PM_UART_RXREG_OFFSET) & + UART_RXREG_MASK); +} + +/**************************************************************************** + * Name: up_rxint + * + * Description: + * Call to enable or disable RX interrupts + * + ****************************************************************************/ + +static void up_rxint(struct uart_dev_s *dev, bool enable) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + if (enable) + { + /* Receive an interrupt when their is anything in the Rx FIFO (or an Rx + * timeout occurs. + */ + +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + priv->im |= MSK_UART_ENABLE_RXIT; +#endif + } + else + { + priv->im &= ~MSK_UART_ENABLE_RXIT; + } + + up_setuartint(priv); + spin_unlock_irqrestore(&priv->lock, flags); +} + +/**************************************************************************** + * Name: up_rxavailable + * + * Description: + * Return true if the receive register is not empty + * + ****************************************************************************/ + +static bool up_rxavailable(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Return true is data is available in the receive data buffer */ + + return (up_serialin(priv, S698PM_UART_STATREG_OFFSET) & + UART_STA_DR) != 0; +} + +/**************************************************************************** + * Name: up_send + * + * Description: + * This method will send one byte on the UART. + * + ****************************************************************************/ + +static void up_send(struct uart_dev_s *dev, int ch) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + up_serialout(priv, S698PM_UART_TXREG_OFFSET, (uint32_t)ch); +} + +/**************************************************************************** + * Name: up_txint + * + * Description: + * Call to enable or disable TX interrupts + * + ****************************************************************************/ + +static void up_txint(struct uart_dev_s *dev, bool enable) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + if (enable) + { + /* Set to receive an interrupt when the TX data register is empty */ + +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + priv->im |= MSK_UART_ENABLE_TXIT; + up_setuartint(priv); + + /* Fake a TX interrupt */ + +# ifdef CONFIG_SMP + spin_unlock_irqrestore(&priv->lock, flags); +# endif + uart_xmitchars(dev); +# ifdef CONFIG_SMP + flags = spin_lock_irqsave(&priv->lock); +# endif +#endif + } + else + { + /* Disable the TX interrupt */ + + priv->im &= ~MSK_UART_ENABLE_TXIT; + up_setuartint(priv); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +/**************************************************************************** + * Name: up_txready + * + * Description: + * Return true if the tranmsit data register is empty + * + ****************************************************************************/ + +static bool up_txready(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Return TRUE if the Transmit buffer register is not full */ + + return (up_serialin(priv, S698PM_UART_STATREG_OFFSET) & + UART_STA_TF) != UART_STA_TF; +} + +/**************************************************************************** + * Name: up_txempty + * + * Description: + * Return true if the tranmsit data register is empty + * + ****************************************************************************/ + +static bool up_txempty(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Return TRUE if the Transmit shift register is empty */ + + return (up_serialin(priv, S698PM_UART_STATREG_OFFSET) & + UART_STA_TS) != 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_earlyserialinit + * + * Description: + * Performs the low level UART initialization early in debug so that the + * serial console will be available during bootup. This must be called + * before up_serialinit. NOTE: This function depends on GPIO pin + * configuration performed in up_consoleinit() and main clock iniialization + * performed in up_clkinitialize(). + * + ****************************************************************************/ + +void up_earlyserialinit(void) +{ + /* Disable interrupts from all UARTS. The console is enabled in + * s698pm_consoleinit(). + */ + + up_disableuartint(&TTYS0_DEV, NULL); +#ifdef TTYS1_DEV + up_disableuartint(&TTYS1_DEV, NULL); +#endif +#ifdef TTYS2_DEV + up_disableuartint(&TTYS2_DEV, NULL); +#endif +#ifdef TTYS3_DEV + up_disableuartint(&TTYS3_DEV, NULL); +#endif + /* Configuration whichever one is the console */ + +#ifdef HAVE_SERIAL_CONSOLE + CONSOLE_DEV.isconsole = true; + up_setup(&CONSOLE_DEV); +#endif +} + +/**************************************************************************** + * Name: up_serialinit + * + * Description: + * Register serial console and serial ports. This assumes + * that up_earlyserialinit was called previously. + * + ****************************************************************************/ + +void up_serialinit(void) +{ + /* Register the console */ + +#ifdef HAVE_SERIAL_CONSOLE + uart_register("/dev/console", &CONSOLE_DEV); +#endif + + /* Register all UARTs */ + + uart_register("/dev/ttyS0", &TTYS0_DEV); +#ifdef TTYS1_DEV + uart_register("/dev/ttyS1", &TTYS1_DEV); +#endif +#ifdef TTYS2_DEV + uart_register("/dev/ttyS2", &TTYS2_DEV); +#endif +#ifdef TTYS3_DEV + uart_register("/dev/ttyS3", &TTYS3_DEV); +#endif +} + +/**************************************************************************** + * Name: up_putc + * + * Description: + * Provide priority, low-level access to support OS debug writes + * + ****************************************************************************/ + +int up_putc(int ch) +{ +#ifdef HAVE_SERIAL_CONSOLE + struct uart_dev_s *dev = (struct uart_dev_s *)&CONSOLE_DEV; + uint8_t imr = 0; + + up_disableuartint(dev, &imr); + + /* Check for LF */ + + if (ch == '\n') + { + /* Add CR */ + + up_lowputc('\r'); + } + + up_lowputc(ch); + up_restoreuartint(dev, imr); +#endif + return ch; +} + +/**************************************************************************** + * Name: up_earlyserialinit, up_serialinit, and up_putc + * + * Description: + * stubs that may be needed. These stubs would be used if all UARTs are + * disabled. In that case, the logic in common/up_initialize() is not + * smart enough to know that there are not UARTs and will still expect + * these interfaces to be provided. + * + ****************************************************************************/ +#else /* HAVE_UART_DEVICE */ +void up_earlyserialinit(void) +{ +} + +void up_serialinit(void) +{ +} + +int up_putc(int ch) +{ + return ch; +} + +#endif /* HAVE_UART_DEVICE */ +#else /* USE_SERIALDRIVER */ + +/**************************************************************************** + * Name: up_putc + * + * Description: + * Provide priority, low-level access to support OS debug writes + * + ****************************************************************************/ + +int up_putc(int ch) +{ +#ifdef HAVE_SERIAL_CONSOLE + /* Check for LF */ + + if (ch == '\n') + { + /* Add CR */ + + up_lowputc('\r'); + } + + up_lowputc(ch); +#endif + return ch; +} + +#endif /* USE_SERIALDRIVER */ + diff --git a/arch/sparc/src/s698pm/s698pm-timerisr.c b/arch/sparc/src/s698pm/s698pm-timerisr.c new file mode 100644 index 0000000000..1f37f47e99 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-timerisr.c @@ -0,0 +1,156 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-timerisr.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "up_internal.h" +#include "s698pm.h" +#include "s698pm_tim.h" +#include "s698pm_irq.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The CPU frequency is given by BOARD_CPU_CLOCK (defined in board.h). The + * desired interrupt frequency is given by CONFIG_USEC_PER_TICK. An unscaled + * ideal match is given by: + * + * CLOCK = CPU_CLOCK / DIVISOR # CPU clocks/sec + * MATCH = CLOCK / CLOCKS_PER_SEC # CPU clocks/timer tick + * MATCH = CPU_CLOCK / DIVISOR / CLOCKS_PER_SEC # CPU clocks/timer tick + * + * But we only have 16-bits of accuracy so we need to pick the smallest + * divisor using the following brute force calculation: + */ + +#define S698PM_TIMER_CLOCK 1000000 +#define MATCH1 (( 1000000 / CLOCKS_PER_SEC) - 1) + +/* Bit 0: enables the timer when set */ + +#define TIMCTR_ENABLE_COUNTER (1 << 0) + +/* Bit 1: automatically reloaded with the reload value after each underflow */ + +#define TIMCTR_AUTO_RELOAD (1 << 1) + +/* Bit 2: Set 1, will load the timer reload register into the timer counter + * register + */ + +#define TIMCTR_LOAD_COUNTER (1 << 2) + +/* Bit 3: Set 1, will triger underflow interrupt each underflow */ + +#define TIMCTR_ENABLE_INT (1 << 3) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: s698pm_timerisr + * + * Description: + * The timer ISR will perform a variety of services for various portions + * of the systems. + * + ****************************************************************************/ + +static int s698pm_timerisr(int irq, uint32_t *regs, void *arg) +{ + /* Clear the pending timer interrupt */ + + up_clrpend_irq(S698PM_IRQ_TIMER1); + + /* Process timer interrupt */ + + nxsched_process_timer(); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: up_timer_initialize + * + * Description: + * This function is called during start-up to initialize the timer + * interrupt. NOTE: This function depends on setup of OSC32 by + * up_clkinitialize(). + * + ****************************************************************************/ + +void up_timer_initialize(void) +{ + uint32_t regval; + + regval = (BOARD_CPU_CLOCK / S698PM_TIMER_CLOCK) - 1; + + putreg16(regval, S698PM_TIMPRE_BASE + S698PM_TIM_PSCLOAD_OFFSET); + + putreg16(regval, S698PM_TIMPRE_BASE + S698PM_TIM_PSCCONT_OFFSET); + + /* Setup timer 1 compare match A to generate a tick interrupt. + * + * First, setup the match value for compare match A. + */ + + putreg32(MATCH1, S698PM_TIM1_BASE + S698PM_TIM_CNT_OFFSET); + putreg32(MATCH1, S698PM_TIM1_BASE + S698PM_TIM_ARR_OFFSET); + + regval = (TIMCTR_ENABLE_COUNTER | TIMCTR_AUTO_RELOAD | + TIMCTR_LOAD_COUNTER | TIMCTR_ENABLE_INT); + putreg32(regval, S698PM_TIM1_BASE + S698PM_TIM_CR_OFFSET); + + /* Configure the timer interrupt */ + + up_clrpend_irq(S698PM_IRQ_TIMER1); + + /* Attach the timer interrupt vector */ + + irq_attach(S698PM_IRQ_TIMER1, (xcpt_t)s698pm_timerisr, NULL); + + /* Set up to timer1 interrupts on the current CPU */ + +#ifdef CONFIG_ARCH_IRQPRIO + (void)s698pm_setup_irq(0, S698PM_IRQ_TIMER1, CONFIG_S698PM_TIMER1PRIO); +#else + (void)s698pm_setup_irq(0, S698PM_IRQ_TIMER1, 0); +#endif + + /* And enable the timer interrupt */ + + up_enable_irq(S698PM_IRQ_TIMER1); +} diff --git a/arch/sparc/src/s698pm/s698pm-uart.h b/arch/sparc/src/s698pm/s698pm-uart.h new file mode 100644 index 0000000000..29a181a77d --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm-uart.h @@ -0,0 +1,195 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm-uart.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_SPARC_SRC_S698PM_S698PM_UART_H +#define __ARCH_SPARC_SRC_S698PM_S698PM_UART_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *************************************************^*******/ +#define S698PM_UART_TXREG_OFFSET 0x0000 /* UARTx transmit register */ +#define S698PM_UART_RXREG_OFFSET 0x0000 /* UARTx receive register */ +#define S698PM_UART_STATREG_OFFSET 0x0004 /* UARTx status register */ +#define S698PM_UART_CTRLREG_OFFSET 0x0008 /* UARTx control register */ +#define S698PM_UART_SCALREG_OFFSET 0x000c /* UARTx scaler register */ + +/* Register Addresses *******************************************************/ +#if CHIP_NUARTS > 0 +#define S698PM_UART1_TXREG (S698PM_UART1_BASE+S698PM_UART_TXREG_OFFSET) +#define S698PM_UART1_RXREG (S698PM_UART1_BASE+S698PM_UART_RXREG_OFFSET) +#define S698PM_UART1_STATREG (S698PM_UART1_BASE+S698PM_UART_STATREG_OFFSET) +#define S698PM_UART1_CTRLREG (S698PM_UART1_BASE+S698PM_UART_CTRLREG_OFFSET) +#define S698PM_UART1_SCALREG (S698PM_UART1_BASE+S698PM_UART_SCALREG_OFFSET) +#endif + +#if CHIP_NUARTS > 1 +#define S698PM_UART2_TXREG (S698PM_UART2_BASE+S698PM_UART_TXREG_OFFSET) +#define S698PM_UART2_RXREG (S698PM_UART2_BASE+S698PM_UART_RXREG_OFFSET) +#define S698PM_UART2_STATREG (S698PM_UART2_BASE+S698PM_UART_STATREG_OFFSET) +#define S698PM_UART2_CTRLREG (S698PM_UART2_BASE+S698PM_UART_CTRLREG_OFFSET) +#define S698PM_UART2_SCALREG (S698PM_UART2_BASE+S698PM_UART_SCALREG_OFFSET) +#endif + +#if CHIP_NUARTS > 2 +#define S698PM_UART3_TXREG (S698PM_UART3_BASE+S698PM_UART_TXREG_OFFSET) +#define S698PM_UART3_RXREG (S698PM_UART3_BASE+S698PM_UART_RXREG_OFFSET) +#define S698PM_UART3_STATREG (S698PM_UART3_BASE+S698PM_UART_STATREG_OFFSET) +#define S698PM_UART3_CTRLREG (S698PM_UART3_BASE+S698PM_UART_CTRLREG_OFFSET) +#define S698PM_UART3_SCALREG (S698PM_UART3_BASE+S698PM_UART_SCALREG_OFFSET) +#endif + +#if CHIP_NUARTS > 3 +#define S698PM_UART4_TXREG (S698PM_UART4_BASE+S698PM_UART_TXREG_OFFSET) +#define S698PM_UART4_RXREG (S698PM_UART4_BASE+S698PM_UART_RXREG_OFFSET) +#define S698PM_UART4_STATREG (S698PM_UART4_BASE+S698PM_UART_STATREG_OFFSET) +#define S698PM_UART4_CTRLREG (S698PM_UART4_BASE+S698PM_UART_CTRLREG_OFFSET) +#define S698PM_UART4_SCALREG (S698PM_UART4_BASE+S698PM_UART_SCALREG_OFFSET) +#endif + +/* Register Bit-Field Definitions *******************************************/ + +#define ODD 1 +#define EVEN 0 +#define ON 1 +#define OFF 0 +#define NONE 2 +#define RX 0 +#define TX 1 +#define RXTX 3 + +/** Uart control list - Mask */ + +#define MSK_UART_ENABLE_RX 0x01 +#define MSK_UART_ENABLE_TX 0x02 +#define MSK_UART_ENABLE_RXIT 0x04 +#define MSK_UART_ENABLE_TXIT 0x08 +#define MSK_UART_PAR 0x10 +#define MSK_UART_ENABLE_PAR 0x20 +#define MSK_UART_ENABLE_FLOW 0x40 +#define MSK_UART_LOOPBACK 0x80 +#define MSK_UART_CLOCK 0x100 +#define MSK_UART_ALLINTS 0x0C + +/* UARTx status and control register */ + +#define UART_STA_DR (1 << 0) /* Bit 0: Receive buffer data available */ +#define UART_STA_TS (1 << 1) /* Bit 1: Transmit shift register is empty */ +#define UART_STA_TE (1 << 2) /* Bit 2: TX buffer empty */ +#define UART_STA_BR (1 << 3) /* Bit 3: Transmit break */ +#define UART_STA_OV (1 << 4) /* Bit 4: overflow error status */ +#define UART_STA_PE (1 << 5) /* Bit 5: Parity error status */ +#define UART_STA_FE (1 << 6) /* Bit 6: Framing error status */ +#define UART_STA_TH (1 << 7) /* Bit 7: TX buffer 1/2 full */ +#define UART_STA_RH (1 << 8) /* Bit 8: RX buffer 1/2 full */ +#define UART_STA_TF (1 << 9) /* Bit 9: Transmit buffer full status */ +#define UART_STA_RF (1 << 10) /* Bit 10: Receive buffer full status */ +#define REG_STAT_TX_CNT (0x3f << 20) +#define REG_STAT_RX_CNT (0x3f << 26) + +/* UARTx transmit register */ + +#define UART_TXREG_MASK 0xff + +/* UARTx receive register */ + +#define UART_RXREG_MASK 0xff + +/* UARTx baud rate register */ + +#define UART_BRG_MASK 0xfff + +#define uart_set_baudrate(baudrate) ((uint32_t)((((BOARD_CPU_CLOCK*10)/(baudrate * 8))-5)/10)) + +#define uart_parity_config(reg, uart_parity) ((uart_parity == ODD) ? \ + (reg = ((reg | MSK_UART_PAR) | MSK_UART_ENABLE_PAR)) : \ + ((uart_parity == EVEN) ? \ + (reg = ((reg & ~MSK_UART_PAR) | MSK_UART_ENABLE_PAR)) : \ + (reg &= ~MSK_UART_ENABLE_PAR ) \ + ) \ + ) + +#define Uart_interrupt_config(reg, uart_its) ((uart_its == RXTX) ? \ + (reg |= (MSK_UART_ENABLE_RXIT | MSK_UART_ENABLE_TXIT)) : \ + ((uart_its == RX ) ? \ + (reg |= (MSK_UART_ENABLE_RXIT & ~MSK_UART_ENABLE_TXIT)) : \ + ((uart_its == TX) ? \ + (reg |= (MSK_UART_ENABLE_TXIT & ~MSK_UART_ENABLE_RXIT)) : \ + (reg &= ~(MSK_UART_ENABLE_RXIT | MSK_UART_ENABLE_TXIT)) \ + ) \ + ) \ + ) + +#define uart_flow_ctrl_config(reg, uart_flow) ((uart_flow == ON) ? \ + (reg |= MSK_UART_ENABLE_FLOW) : \ + (reg &= ~MSK_UART_ENABLE_FLOW) \ + ) + +#define uart_loopback_config(reg, uart_loopb) ((uart_loopb == ON) ? \ + (reg |= MSK_UART_LOOPBACK) : \ + (reg &= ~MSK_UART_LOOPBACK) \ + ) + +#define uart_enable(reg) (reg |= (MSK_UART_ENABLE_RX | MSK_UART_ENABLE_TX)) +#define uart_disable(reg) (reg &= ~(MSK_UART_ENABLE_RX | MSK_UART_ENABLE_TX)) + +#define uart_tx_ready() ((S698PM_REG.uart_status1 & UART_STA_TF) != UART_STA_TF) +#define uart_rx_ready() ((S698PM_REG.uart_status1 & UART_STA_DR) == UART_STA_DR) + +#define uart_send_byte(ch) (S698PM_REG.uart_data1 = ch) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_SPARC_SRC_S698PM_S698PM_UART_H */ diff --git a/arch/sparc/src/s698pm/s698pm.h b/arch/sparc/src/s698pm/s698pm.h new file mode 100644 index 0000000000..6182b23855 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm.h @@ -0,0 +1,442 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_SPARC_SRC_S698PM_S698PM_H +#define __ARCH_SARRC_SRC_S698PM_S698PM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "s698pm-config.h" +#include +#include +#include "s698pm_irq.h" +#ifdef CONFIG_S698PM_GPIOIRQ +#include +#include "s698pm_exti.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The following defines the bits in Memory Configuration Register 1. */ + +#define S698PM_MEMORY_CONFIGURATION_PROM_SIZE_MASK 0x0003C000 + +/* The following defines the bits in Memory Configuration Register 1. */ + +#define S698PM_MEMORY_CONFIGURATION_RAM_SIZE_MASK 0x00001E00 + +/* The following defines the bits in the Timer Control Register. */ + +/* 1 = enable counting + * 0 = hold scalar and counter + */ + +#define S698PM_REG_TIMER_CONTROL_EN 0x00000001 + +/* 1 = reload at 0 + * 0 = stop at 0 + */ + +#define S698PM_REG_TIMER_CONTROL_RL 0x00000002 + +/* 1 = load counter + * 0 = no function + */ + +#define S698PM_REG_TIMER_CONTROL_LD 0x00000004 + +/* The following defines the bits in the UART Control Registers. */ + +#define S698PM_REG_UartCtrlRTD 0x000000FF /* RX/TX data */ + +/* The following defines the bits in the S698PM UART Status Registers. */ + +#define S698PM_REG_UART_STATUS_CLR 0x00000000 /* Clear all status bits */ +#define S698PM_REG_UART_STATUS_DR 0x00000001 /* Data Ready */ +#define S698PM_REG_UART_STATUS_TSE 0x00000002 /* TX Send Register Empty */ +#define S698PM_REG_UART_STATUS_THE 0x00000004 /* TX Hold Register Empty */ +#define S698PM_REG_UART_STATUS_BR 0x00000008 /* Break Error */ +#define S698PM_REG_UART_STATUS_OE 0x00000010 /* RX Overrun Error */ +#define S698PM_REG_UART_STATUS_PE 0x00000020 /* RX Parity Error */ +#define S698PM_REG_UART_STATUS_FE 0x00000040 /* RX Framing Error */ +#define S698PM_REG_UART_STATUS_ERR 0x00000078 /* Error Mask */ + +/* The following defines the bits in the S698PM UART Status Registers. */ + +#define S698PM_REG_UART_CTRL_RE 0x00000001 /* Receiver enable */ +#define S698PM_REG_UART_CTRL_TE 0x00000002 /* Transmitter enable */ +#define S698PM_REG_UART_CTRL_RI 0x00000004 /* Receiver interrupt enable */ +#define S698PM_REG_UART_CTRL_TI 0x00000008 /* Transmitter interrupt enable */ +#define S698PM_REG_UART_CTRL_PS 0x00000010 /* Parity select */ +#define S698PM_REG_UART_CTRL_PE 0x00000020 /* Parity enable */ +#define S698PM_REG_UART_CTRL_FL 0x00000040 /* Flow control enable */ +#define S698PM_REG_UART_CTRL_LB 0x00000080 /* Loop Back enable */ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_SMP +/* SPARC requires at least a 8-byte stack alignment */ + +#define SMP_STACK_ALIGNMENT 8 +#define SMP_STACK_MASK 7 +#define SMP_STACK_SIZE ((CONFIG_IDLETHREAD_STACKSIZE + 7) & ~7) +#define SMP_STACK_WORDS (SMP_STACK_SIZE >> 2) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern uint32_t g_cpu0_idlestack[SMP_STACK_WORDS]; +#if CONFIG_SMP_NCPUS > 1 +extern uint32_t g_cpu1_idlestack[SMP_STACK_WORDS]; +#if CONFIG_SMP_NCPUS > 2 +extern uint32_t g_cpu2_idlestack[SMP_STACK_WORDS]; +#if CONFIG_SMP_NCPUS > 3 +extern uint32_t g_cpu3_idlestack[SMP_STACK_WORDS]; +#if CONFIG_SMP_NCPUS > 4 +# error This logic needs to extended for CONFIG_SMP_NCPUS > 4 +#endif /* CONFIG_SMP_NCPUS > 4 */ +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +#endif /* CONFIG_SMP_NCPUS > 1 */ +#endif + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Read CPU ID(LEON3) + ****************************************************************************/ + +#define LEON3_READ_CPUID(reg) \ + RD %asr17 , reg; \ + SRL reg , 28 , reg; + +/**************************************************************************** + * Read CPU ID (Now only support LEON3, LEON4!) + ****************************************************************************/ + +#ifdef CONFIG_SMP +#define READ_CPUID(reg) LEON3_READ_CPUID(reg) +#else +#define READ_CPUID(reg) MOV %g0 , reg +#endif + +/* Trap Types for on-chip peripherals + * + * Source: Table 8 - Interrupt Trap Type and Default Priority Assignments + * + * NOTE: The priority level for each source corresponds to the least + * significant nibble of the trap type. + */ + +#define S698PM_TRAP_TYPE( _source ) SPARC_ASYNCHRONOUS_TRAP((_source) + 0x10) + +#define S698PM_TRAP_SOURCE( _trap ) ((_trap) - 0x10) + +#define S698PM_INT_TRAP( _trap ) \ + ( (_trap) >= S698PM_TRAP_TYPE( S698PM_IRQ_CORRECTABLE_MEMORY_ERROR ) && \ + (_trap) <= S698PM_TRAP_TYPE( S698PM_IRQ_EMPTY6 ) ) + +static __inline__ int bsp_irq_fixup(int irq) +{ + return irq; +} + +/* Macros to manipulate the Interrupt Clear, Interrupt Force, Interrupt Mask + * and the Interrupt Pending Registers. + * + * NOTE: For operations which are not atomic, this code disables interrupts + * to guarantee there are no intervening accesses to the same register + * The operations which read the register, modify the value and then + * store the result back are vulnerable. + */ + +#define S698PM_Clear_interrupt( _source ) \ + do { \ + S698PM_REG.Interrupt_Clear = (1 << (_source)); \ + } while (0) + +#define S698PM_Force_interrupt( _source ) \ + do { \ + S698PM_REG.Interrupt_Force = (1 << (_source)); \ + } while (0) + +#define S698PM_Is_interrupt_pending( _source ) \ + (S698PM_REG.Interrupt_Pending & (1 << (_source))) + +#define S698PM_Is_interrupt_masked( _source ) \ + (!(S698PM_REG.Interrupt_Mask & (1 << (_source)))) + +#define S698PM_Mask_interrupt( _source ) \ + do { \ + uint32_t _level; \ + \ + _level = sparc_disable_interrupts(); \ + S698PM_REG.Interrupt_Mask &= ~(1 << (_source)); \ + sparc_enable_interrupts( _level ); \ + } while (0) + +#define S698PM_Unmask_interrupt( _source ) \ + do { \ + uint32_t _level; \ + \ + _level = sparc_disable_interrupts(); \ + S698PM_REG.Interrupt_Mask |= (1 << (_source)); \ + sparc_enable_interrupts( _level ); \ + } while (0) + +#define S698PM_Disable_interrupt( _source, _previous ) \ + do { \ + uint32_t _level; \ + uint32_t _mask = 1 << (_source); \ + \ + _level = sparc_disable_interrupts(); \ + (_previous) = S698PM_REG.Interrupt_Mask; \ + S698PM_REG.Interrupt_Mask = _previous & ~_mask; \ + sparc_enable_interrupts( _level ); \ + (_previous) &= _mask; \ + } while (0) + +#define S698PM_Restore_interrupt( _source, _previous ) \ + do { \ + uint32_t _level; \ + uint32_t _mask = 1 << (_source); \ + \ + _level = sparc_disable_interrupts(); \ + S698PM_REG.Interrupt_Mask = \ + (S698PM_REG.Interrupt_Mask & ~_mask) | (_previous); \ + sparc_enable_interrupts( _level ); \ + } while (0) + +/* Each timer control register is organized as follows: + * + * D0 - Enable + * 1 = enable counting + * 0 = hold scaler and counter + * + * D1 - Counter Reload + * 1 = reload counter at zero and restart + * 0 = stop counter at zero + * + * D2 - Counter Load + * 1 = load counter with preset value + * 0 = no function + * + */ + +#define S698PM_REG_TIMER_COUNTER_RELOAD_AT_ZERO 0x00000002 +#define S698PM_REG_TIMER_COUNTER_STOP_AT_ZERO 0x00000000 + +#define S698PM_REG_TIMER_COUNTER_LOAD_COUNTER 0x00000004 + +#define S698PM_REG_TIMER_COUNTER_ENABLE_COUNTING 0x00000001 +#define S698PM_REG_TIMER_COUNTER_DISABLE_COUNTING 0x00000000 + +#define S698PM_REG_TIMER_COUNTER_RELOAD_MASK 0x00000002 +#define S698PM_REG_TIMER_COUNTER_ENABLE_MASK 0x00000001 + +#define S698PM_REG_TIMER_COUNTER_DEFINED_MASK 0x00000003 +#define S698PM_REG_TIMER_COUNTER_CURRENT_MODE_MASK 0x00000003 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Load 32-bit word by forcing a cache-miss */ + +static inline unsigned int s698pm_r32_no_cache(uintptr_t addr) +{ + unsigned int tmp; + __asm__ volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(addr)); + return tmp; +} + +/**************************************************************************** + * Name: up_clkinit + * + * Description: + * Initialiaze clock/PLL settings per the definitions in the board.h file. + * + ****************************************************************************/ + +void up_clkinitialize(void); + +/**************************************************************************** + * Name: s698pm_consoleinit + * + * Description: + * Performs low level initialization of the console UART. This UART done + * early so that the serial console is available for debugging very early + * in the boot + * sequence. + * + ****************************************************************************/ + +#ifdef HAVE_SERIAL_CONSOLE +void s698pm_consoleinit(void); +#else +# define s698pm_consoleinit() +#endif + +/**************************************************************************** + * Name: s698pm_uartreset + * + * Description: + * Reset a UART. + * + ****************************************************************************/ + +#ifdef HAVE_UART_DEVICE +void s698pm_uartreset(uintptr_t uart_base); +#endif + +/**************************************************************************** + * Name: s698pm_uartconfigure + * + * Description: + * Configure a UART as a RS-232 UART. + * + ****************************************************************************/ + +#ifdef HAVE_UART_DEVICE +void s698pm_uartconfigure(uintptr_t uart_base, uint32_t baudrate, + unsigned int parity, unsigned int nbits, bool stop2); +#endif + +/**************************************************************************** + * Name: s698pm_boardinitialize + * + * Description: + * This function must be provided by the board-specific logic in the + * directory boards/sparc/s698pm//src. + * + ****************************************************************************/ + +void s698pm_boardinitialize(void); + +/**************************************************************************** + * Name: gpio_irqinitialize + * + * Description: + * Initialize all vectors to the unexpected interrupt handler + * + * Configuration Notes: + * Configuration CONFIG_AVR_GPIOIRQ must be selected to enable the + * overall GPIO IRQ feature. + * + * Assumptions: + * Called during the early boot sequence before global interrupts have + * been enabled. + * + ****************************************************************************/ + +#ifdef CONFIG_SPARC_GPIOIRQ +void weak_function gpio_irqinitialize(void); +#endif + +/**************************************************************************** + * Name: gpio_irqattach + * + * Description: + * Attach in GPIO interrupt to the provide 'isr' + * + * Configuration Notes: + * Configuration CONFIG_AVR_GPIOIRQ must be selected to enable the + * overall GPIO IRQ feature. + * + ****************************************************************************/ + +#ifdef CONFIG_SPARC_GPIOIRQ +int gpio_irqattach(int irq, xcpt_t newisr, xcpt_t *oldisr); +#endif + +/**************************************************************************** + * Name: gpio_irqenable + * + * Description: + * Enable the GPIO IRQ specified by 'irq' + * + * Configuration Notes: + * Configuration CONFIG_AVR_GPIOIRQ must be selected to enable the + * overall GPIO IRQ feature. + * + ****************************************************************************/ + +#ifdef CONFIG_SPARC_GPIOIRQ +void gpio_irqenable(int irq); +#endif + +/**************************************************************************** + * Name: gpio_irqdisable + * + * Description: + * Disable the GPIO IRQ specified by 'irq' + * + * Configuration Notes: + * Configuration CONFIG_AVR_GPIOIRQ must be selected to enable the + * overall GPIO IRQ feature. + * + ****************************************************************************/ + +#ifdef CONFIG_SPARC_GPIOIRQ +void gpio_irqdisable(int irq); +#endif + +/**************************************************************************** + * Name: s698pm_pause_handler + * + * Description: + * Inter-CPU interrupt handler + * + * Input Parameters: + * Standard interrupt handler inputs + * + * Returned Value: + * Should always return OK + * + ****************************************************************************/ + +#ifdef CONFIG_SMP +int s698pm_pause_handler(int irq, void *c, void *arg); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_SPARC_SRC_S698PM_S698PM_H */ diff --git a/arch/sparc/src/s698pm/s698pm_cpuidlestack.c b/arch/sparc/src/s698pm/s698pm_cpuidlestack.c new file mode 100644 index 0000000000..a0558b30de --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_cpuidlestack.c @@ -0,0 +1,132 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_cpuidlestack.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "up_internal.h" +#include "s698pm.h" + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Stack alignment macros */ + +#define STACK_ISALIGNED(a) ((uintptr_t)(a) & ~SMP_STACK_MASK) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if CONFIG_SMP_NCPUS > 1 +static const uint32_t *g_cpu_stackalloc[CONFIG_SMP_NCPUS] = +{ + g_cpu0_idlestack + , g_cpu1_idlestack +#if CONFIG_SMP_NCPUS > 2 + , g_cpu2_idlestack +#if CONFIG_SMP_NCPUS > 3 + , g_cpu3_idlestack +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +}; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_cpu_idlestack + * + * Description: + * Allocate a stack for the CPU[n] IDLE task (n > 0) if appropriate and + * setup up stack-related information in the IDLE task's TCB. This + * function is always called before up_cpu_start(). This function is + * only called for the CPU's initial IDLE task; up_create_task is used for + * all normal tasks, pthreads, and kernel threads for all CPUs. + * + * The initial IDLE task is a special case because the CPUs can be started + * in different wans in different environments: + * + * 1. The CPU may already have been started and waiting in a low power + * state for up_cpu_start(). In this case, the IDLE thread's stack + * has already been allocated and is already in use. Here + * up_cpu_idlestack() only has to provide information about the + * already allocated stack. + * + * 2. The CPU may be disabled but started when up_cpu_start() is called. + * In this case, a new stack will need to be created for the IDLE + * thread and this function is then equivalent to: + * + * return up_create_stack(tcb, stack_size, TCB_FLAG_TTYPE_KERNEL); + * + * The following TCB fields must be initialized by this function: + * + * - adj_stack_size: Stack size after adjustment for hardware, processor, + * etc. This value is retained only for debug purposes. + * - stack_alloc_ptr: Pointer to allocated stack + * - stack_base_ptr: Adjusted stack base pointer after the TLS Data and + * Arguments has been removed from the stack allocation. + * + * Input Parameters: + * - cpu: CPU index that indicates which CPU the IDLE task is + * being created for. + * - tcb: The TCB of new CPU IDLE task + * - stack_size: The requested stack size for the IDLE task. At least + * this much must be allocated. This should be + * CONFIG_SMP_STACK_SIZE. + * + ****************************************************************************/ + +int up_cpu_idlestack(int cpu, struct tcb_s *tcb, size_t stack_size) +{ +#if CONFIG_SMP_NCPUS > 1 + uintptr_t stack_alloc; + + DEBUGASSERT(cpu > 0 && cpu < CONFIG_SMP_NCPUS && tcb != NULL && + stack_size <= SMP_STACK_SIZE); + + /* Get the top of the stack */ + + stack_alloc = (uintptr_t)g_cpu_stackalloc[cpu]; + DEBUGASSERT(stack_alloc != 0 && STACK_ISALIGNED(stack_alloc)); + + tcb->adj_stack_size = SMP_STACK_SIZE; + tcb->stack_alloc_ptr = (void *)stack_alloc; + tcb->stack_base_ptr = tcb->stack_alloc_ptr; +#endif + + return OK; +} + +#endif /* CONFIG_SMP */ diff --git a/arch/sparc/src/s698pm/s698pm_cpuindex.c b/arch/sparc/src/s698pm/s698pm_cpuindex.c new file mode 100644 index 0000000000..d8018fd1a0 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_cpuindex.c @@ -0,0 +1,68 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_cpuindex.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include "up_internal.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_cpu_index + * + * Description: + * Return an index in the range of 0 through (CONFIG_SMP_NCPUS-1) that + * corresponds to the currently executing CPU. + * + * Input Parameters: + * None + * + * Returned Value: + * An integer index in the range of 0 through (CONFIG_SMP_NCPUS-1) that + * corresponds to the currently executing CPU. + * + ****************************************************************************/ + +#ifdef CONFIG_SMP +int up_cpu_index(void) +{ + int cpu = 0; + + __asm__ __volatile__ + ( + "rd %%asr17, %0\n" + "srl %0, 28, %0\n" + : "=r" (cpu) : "0" (cpu) + ); + + return cpu; +} +#endif diff --git a/arch/sparc/src/s698pm/s698pm_cpupause.c b/arch/sparc/src/s698pm/s698pm_cpupause.c new file mode 100644 index 0000000000..cd07c5a148 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_cpupause.c @@ -0,0 +1,324 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_cpupause.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sched/sched.h" +#include "up_internal.h" +#include "chip.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* These spinlocks are used in the SMP configuration in order to implement + * up_cpu_pause(). The protocol for CPUn to pause CPUm is as follows + * + * 1. The up_cpu_pause() implementation on CPUn locks both g_cpu_wait[m] + * and g_cpu_paused[m]. CPUn then waits spinning on g_cpu_paused[m]. + * 2. CPUm receives the interrupt it (1) unlocks g_cpu_paused[m] and + * (2) locks g_cpu_wait[m]. The first unblocks CPUn and the second + * blocks CPUm in the interrupt handler. + * + * When CPUm resumes, CPUn unlocks g_cpu_wait[m] and the interrupt handler + * on CPUm continues. CPUm must, of course, also then unlock g_cpu_wait[m] + * so that it will be ready for the next pause operation. + */ + +volatile spinlock_t g_cpu_wait[CONFIG_SMP_NCPUS]; +volatile spinlock_t g_cpu_paused[CONFIG_SMP_NCPUS]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_cpu_pausereq + * + * Description: + * Return true if a pause request is pending for this CPU. + * + * Input Parameters: + * cpu - The index of the CPU to be queried + * + * Returned Value: + * true = a pause request is pending. + * false = no pasue request is pending. + * + ****************************************************************************/ + +bool up_cpu_pausereq(int cpu) +{ + return spin_islocked(&g_cpu_paused[cpu]); +} + +/**************************************************************************** + * Name: up_cpu_paused + * + * Description: + * Handle a pause request from another CPU. Normally, this logic is + * executed from interrupt handling logic within the architecture-specific + * However, it is sometimes necessary necessary to perform the pending + * pause operation in other contexts where the interrupt cannot be taken + * in order to avoid deadlocks. + * + * This function performs the following operations: + * + * 1. It saves the current task state at the head of the current assigned + * task list. + * 2. It waits on a spinlock, then + * 3. Returns from interrupt, restoring the state of the new task at the + * head of the ready to run list. + * + * Input Parameters: + * cpu - The index of the CPU to be paused + * + * Returned Value: + * On success, OK is returned. Otherwise, a negated errno value indicating + * the nature of the failure is returned. + * + ****************************************************************************/ + +int up_cpu_paused(int cpu) +{ + struct tcb_s *tcb = this_task(); + + /* Update scheduler parameters */ + + nxsched_suspend_scheduler(tcb); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify that we are paused */ + + sched_note_cpu_paused(tcb); +#endif + + /* Save the current context at CURRENT_REGS into the TCB at the head + * of the assigned task list for this CPU. + */ + + up_savestate(tcb->xcp.regs); + + /* Wait for the spinlock to be released */ + + spin_unlock(&g_cpu_paused[cpu]); + spin_lock(&g_cpu_wait[cpu]); + + /* Restore the exception context of the tcb at the (new) head of the + * assigned task list. + */ + + tcb = this_task(); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify that we have resumed */ + + sched_note_cpu_resumed(tcb); +#endif + + /* Reset scheduler parameters */ + + nxsched_resume_scheduler(tcb); + + /* Then switch contexts. Any necessary address environment changes + * will be made when the interrupt returns. + */ + + up_restorestate(tcb->xcp.regs); + + spin_unlock(&g_cpu_wait[cpu]); + + return OK; +} + +/**************************************************************************** + * Name: s698pm_pause_handler + * + * Description: + * Inter-CPU interrupt handler + * + * Input Parameters: + * Standard interrupt handler inputs + * + * Returned Value: + * Should always return OK + * + ****************************************************************************/ + +int s698pm_pause_handler(int irq, void *c, void *arg) +{ + int cpu = up_cpu_index(); + + /* Clear IPI (Inter-Processor-Interrupt) */ + + putreg32(1 << S698PM_IPI_VECTOR, S698PM_IRQREG_ICLEAR); + + /* Check for false alarms. Such false could occur as a consequence of + * some deadlock breaking logic that might have already serviced the SG2 + * interrupt by calling up_cpu_paused. + */ + + if (up_cpu_pausereq(cpu)) + { + /* NOTE: The following enter_critical_section() will call + * up_cpu_paused() to process a pause request to break a deadlock + * because the caller held a critical section. Once up_cpu_paused() + * finished, the caller will proceed and release the g_cpu_irqlock. + * Then this CPU will acquire g_cpu_irqlock in the function. + */ + + irqstate_t flags = enter_critical_section(); + + /* NOTE: the pause request should not exist here */ + + DEBUGVERIFY(!up_cpu_pausereq(cpu)); + + leave_critical_section(flags); + } + + return OK; +} + +/**************************************************************************** + * Name: up_cpu_pause + * + * Description: + * Save the state of the current task at the head of the + * g_assignedtasks[cpu] task list and then pause task execution on the + * CPU. + * + * This function is called by the OS when the logic executing on one CPU + * needs to modify the state of the g_assignedtasks[cpu] list for another + * CPU. + * + * Input Parameters: + * cpu - The index of the CPU to be stopped/ + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_cpu_pause(int cpu) +{ + uintptr_t regaddr; + + sinfo("cpu=%d\n", cpu); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify of the pause event */ + + sched_note_cpu_pause(this_task(), cpu); +#endif + + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu()); + + /* Take the both spinlocks. The g_cpu_wait spinlock will prevent the SGI2 + * handler from returning until up_cpu_resume() is called; g_cpu_paused + * is a handshake that will prefent this function from returning until + * the CPU is actually paused. + * Note that we might spin before getting g_cpu_wait, this just means that + * the other CPU still hasn't finished responding to the previous resume + * request. + */ + + DEBUGASSERT(!spin_islocked(&g_cpu_paused[cpu])); + + spin_lock(&g_cpu_wait[cpu]); + spin_lock(&g_cpu_paused[cpu]); + + /* Execute Pause IRQ to CPU(cpu) */ + + regaddr = (uintptr_t)S698PM_IRQREG_P0_FORCE + (4 * cpu); + putreg32(1 << S698PM_IPI_VECTOR, regaddr); + + /* Wait for the other CPU to unlock g_cpu_paused meaning that + * it is fully paused and ready for up_cpu_resume(); + */ + + spin_lock(&g_cpu_paused[cpu]); + + spin_unlock(&g_cpu_paused[cpu]); + + /* On successful return g_cpu_wait will be locked, the other CPU will be + * spinning on g_cpu_wait and will not continue until g_cpu_resume() is + * called. g_cpu_paused will be unlocked in any case. + */ + + return 0; +} + +/**************************************************************************** + * Name: up_cpu_resume + * + * Description: + * Restart the cpu after it was paused via up_cpu_pause(), restoring the + * state of the task at the head of the g_assignedtasks[cpu] list, and + * resume normal tasking. + * + * This function is called after up_cpu_pause in order resume operation of + * the CPU after modifying its g_assignedtasks[cpu] list. + * + * Input Parameters: + * cpu - The index of the CPU being re-started. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_cpu_resume(int cpu) +{ + sinfo("cpu=%d\n", cpu); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify of the resume event */ + + sched_note_cpu_resume(this_task(), cpu); +#endif + + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu()); + + /* Release the spinlock. Releasing the spinlock will cause the SGI2 + * handler on 'cpu' to continue and return from interrupt to the newly + * established thread. + */ + + DEBUGASSERT(spin_islocked(&g_cpu_wait[cpu]) && + !spin_islocked(&g_cpu_paused[cpu])); + + spin_unlock(&g_cpu_wait[cpu]); + + return 0; +} diff --git a/arch/sparc/src/s698pm/s698pm_cpustart.c b/arch/sparc/src/s698pm/s698pm_cpustart.c new file mode 100644 index 0000000000..eb98358cc3 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_cpustart.c @@ -0,0 +1,165 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_cpustart.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sched/sched.h" +#include "up_internal.h" + +#ifdef CONFIG_BUILD_KERNEL +# include "s698pm_mmu.h" +#endif + +#include "s698pm.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +volatile static spinlock_t g_cpu_boot; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s698pm_cpu_boot + * + * Description: + * Boot handler for cpu[x] + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void s698pm_cpu_boot(void) +{ + struct tcb_s *tcb = this_task(); + + _info("CPU%d Started\n", this_cpu()); + + /* Initialize CPU interrupts */ + + s698pm_cpuint_initialize(); + + spin_unlock(&g_cpu_boot); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify that this CPU has started */ + + sched_note_cpu_started(tcb); +#endif + + /* Reset scheduler parameters */ + + nxsched_resume_scheduler(tcb); + + /* And finally, enable cpu interrupts */ + +#ifndef CONFIG_SUPPRESS_INTERRUPTS + up_irq_enable(); +#endif + + /* Then transfer control to the IDLE task */ + + nx_idle_trampoline(); +} + +/**************************************************************************** + * Name: up_cpu_start + * + * Description: + * In an SMP configuration, only one CPU is initially active (CPU 0). + * System initialization occurs on that single thread. At the completion of + * the initialization of the OS, just before beginning normal multitasking, + * the additional CPUs would be started by calling this function. + * + * Each CPU is provided the entry point to its IDLE task when started. A + * TCB for each CPU's IDLE task has been initialized and placed in the + * CPU's g_assignedtasks[cpu] list. No stack has been allocated or + * initialized. + * + * The OS initialization logic calls this function repeatedly until each + * CPU has been started, 1 through (CONFIG_SMP_NCPUS-1). + * + * Input Parameters: + * cpu - The index of the CPU being started. This will be a numeric + * value in the range of one to (CONFIG_SMP_NCPUS-1). + * (CPU 0 is already active) + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_cpu_start(int cpu) +{ + uintptr_t regaddr; + + _info("CPU=%d\n", cpu); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify of the start event */ + + sched_note_cpu_start(this_task(), cpu); +#endif + + /* Set the start up address */ + + regaddr = S698PM_DSU_BASE + (0x1000000 * cpu) + S698PM_DSU_PC_OFFSET; + putreg32(CONFIG_RAM_START, regaddr); + putreg32(0x40001000, regaddr); + + regaddr = S698PM_DSU_BASE + (0x1000000 * cpu) + S698PM_DSU_NPC_OFFSET; + putreg32(0x40001004, regaddr); + + spin_lock(&g_cpu_boot); + + /* set 1 to bit n of multiprocessor status register to active cpu n */ + + putreg32(1 << cpu, S698PM_IRQREG_MPSTATUS); + + spin_lock(&g_cpu_boot); + + /* prev cpu boot done */ + + spin_unlock(&g_cpu_boot); + + return 0; +} diff --git a/arch/sparc/src/s698pm/s698pm_exceptions.S b/arch/sparc/src/s698pm/s698pm_exceptions.S new file mode 100644 index 0000000000..3423c52fce --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_exceptions.S @@ -0,0 +1,482 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/up_exceptions.S + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + + .file "s698pm_exceptions.S" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + + .text + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + + .global _ISR_Handler +#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 7 + .global g_cpu_intstack_top +#endif /* CONFIG_SMP && CONFIG_ARCH_INTERRUPTSTACK > 7 */ + .global up_doirq /* Dispatch an IRQ */ + .align 8 +/* + * void _ISR_Handler() + * + * This routine provides the RTEMS interrupt management. + * + * We enter this handler from the 4 instructions in the trap table with + * the following registers assumed to be set as shown: + * + * l0 = PSR + * l1 = PC + * l2 = nPC + * l3 = trap type + * + * NOTE: By an executive defined convention, trap type is between 0 and 255 if + * it is an asynchonous trap and 256 and 511 if it is synchronous. + */ + +_ISR_Handler: + + /* + * Fix the return address for synchronous traps. + */ + + and %l3, 0xF0, %l6 + cmp %l6, 0x10 ! Is this a synchronous trap? + be,a win_ovflow ! No, then skip the adjustment + nop ! DELAY + mov %l2, %l1 ! do not return to the instruction + add %l2, 4, %l2 ! indicated + +win_ovflow: + /* + * Save the globals this block uses. + * + * These registers are not restored from the locals. Their contents + * are saved directly from the locals into the ISF below. + */ + + mov %g4, %l4 ! save the globals this block uses + mov %g5, %l5 + /* + * When at a "window overflow" trap, (wim == (1 << cwp)). + * If we get here like that, then process a window overflow. + */ + rd %wim, %g4 + srl %g4, %l0, %g5 ! g5 = win >> cwp ; shift count and CWP + ! are LS 5 bits ; how convenient :) + cmp %g5, 1 ! Is this an invalid window? + bne dont_do_the_window ! No, then skip all this stuff + nop + ! we are using the delay slot + + /* + * The following is same as a 1 position right rotate of WIM + */ + srl %g4, 1, %g5 ! g5 = WIM >> 1 + sll %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS-1 , %g4 + ! g4 = WIM << (Number Windows - 1) + or %g4, %g5, %g4 ! g4 = (WIM >> 1) | + ! (WIM << (Number Windows - 1)) + + /* + * At this point: + * + * g4 = the new WIM + * g5 is free + */ + + /* + * Since we are tinkering with the register windows, we need to + * make sure that all the required information is in global registers. + */ + save ! Save into the window + wr %g4, 0, %wim ! WIM = new WIM + nop ! delay slots + nop + nop + + /* + * Now save the window just as if we overflowed to it. + */ + +SAVE_WINDOW: + std %l0, [%sp + CPU_STACK_FRAME_L0_OFFSET] + std %l2, [%sp + CPU_STACK_FRAME_L2_OFFSET] + std %l4, [%sp + CPU_STACK_FRAME_L4_OFFSET] + std %l6, [%sp + CPU_STACK_FRAME_L6_OFFSET] + + std %i0, [%sp + CPU_STACK_FRAME_I0_OFFSET] + std %i2, [%sp + CPU_STACK_FRAME_I2_OFFSET] + std %i4, [%sp + CPU_STACK_FRAME_I4_OFFSET] + std %i6, [%sp + CPU_STACK_FRAME_I6_FP_OFFSET] + + restore + nop + +dont_do_the_window: + /* + * Global registers %g4 and %g5 are saved directly from %l4 and + * %l5 directly into the ISF below. + */ + +save_isf: + + /* + * Save the state of the interrupted task -- especially the global + * registers -- in the Interrupt Stack Frame. Note that the ISF + * includes a regular minimum stack frame which will be used if + * needed by register window overflow and underflow handlers. + * + * REGISTERS SAME AS AT _ISR_Handler + */ + +#if CONFIG_ARCH_INTERRUPTSTACK > 7 + +#ifdef CONFIG_SMP + rd %asr17 , %g5 + srl %g5 , 28 , %g5 ! Bits 0-1=CPU ID +#else + mov %g0, %g5 ! CPU ID = 0 +#endif + sll %g5, 2 , %g5 ! %g5 = CPUID * 4 + set g_cpu_intstack_top, %g4 ! %g4 = Array of stack pointers + add %g4, %g5, %g4 ! %g4 = g_cpu_intstack_top + CPUID * 4 + ld [%g4], %g5 ! restore %sp + sub %g5, CONTEXT_CONTROL_INTERRUPT_FRAME_SIZE, %sp + +#else + sub %fp, CONTEXT_CONTROL_INTERRUPT_FRAME_SIZE, %sp +#endif + + std %l0, [%sp + ISF_PSR_OFFSET] ! save psr, PC + st %l2, [%sp + ISF_NPC_OFFSET] ! save nPC + st %g1, [%sp + ISF_G1_OFFSET] ! save g1 + std %g2, [%sp + ISF_G2_OFFSET] ! save g2, g3 + std %l4, [%sp + ISF_G4_OFFSET] ! save g4, g5 -- see above + std %g6, [%sp + ISF_G6_OFFSET] ! save g6, g7 + + std %i0, [%sp + ISF_I0_OFFSET] ! save i0, i1 + std %i2, [%sp + ISF_I2_OFFSET] ! save i2, i3 + std %i4, [%sp + ISF_I4_OFFSET] ! save i4, i5 + std %i6, [%sp + ISF_I6_FP_OFFSET] ! save i6/fp, i7 + + rd %y, %g1 + st %g1, [%sp + ISF_Y_OFFSET] ! save y + + mov %sp, %o1 ! 2nd arg to ISR Handler + + st %fsr, [%sp + ISF_FSR_OFFSET] + std %f0, [%sp + ISF_F0_OFFSET] + std %f2, [%sp + ISF_F2_OFFSET] + std %f4, [%sp + ISF_F4_OFFSET] + std %f6, [%sp + ISF_F6_OFFSET] + std %f8, [%sp + ISF_F8_OFFSET] + std %f10, [%sp + ISF_F10_OFFSET] + std %f12, [%sp + ISF_F12_OFFSET] + std %f14, [%sp + ISF_F14_OFFSET] + std %f16, [%sp + ISF_F16_OFFSET] + std %f18, [%sp + ISF_F18_OFFSET] + std %f20, [%sp + ISF_F20_OFFSET] + std %f22, [%sp + ISF_F22_OFFSET] + std %f24, [%sp + ISF_F24_OFFSET] + std %f26, [%sp + ISF_F26_OFFSET] + std %f28, [%sp + ISF_F28_OFFSET] + std %f30, [%sp + ISF_F30_OFFSET] ! total 32 word + +fix_pil: + mov %l0, %g5 + or %g5, SPARC_PSR_PIL_MASK, %g5 /* 0x00000F00 */ + wr %g5, SPARC_PSR_ET_MASK, %psr ! **** ENABLE TRAPS **** /* 0x00000020 */ + nop + nop + nop +/*==========================================================================*/ + cmp %l3, 11 ! l3 = vector number + bne do_irq + nop + +#ifdef CONFIG_SMP + rd %asr17 , %l4 + srl %l4 , 28 , %l4 +#else + mov %g0 , %l4 +#endif + set S698PM_IRQREG_P0_EXTACK, %l5 + sll %l4, 2, %l4 ! l4 = CPUID * 4 + add %l5, %l4, %l5 ! l5 = S698PM_IRQREG_P(k)_EXTACK + ld [%l5], %l4 ! l4 = EXTENDED_ACK + cmp %l4, %g0 + be do_irq + nop + add %l4, 240, %l3 ! l3 = extended vector number +do_irq: + ! o1 = 2nd arg = address of the ISF + ! WAS LOADED WHEN ISF WAS SAVED!!! + mov %l3, %o0 ! o0 = 1st arg = vector number + call up_doirq /* call ISR dispatcher */ + nop +/*==========================================================================*/ + mov %l0, %psr ! **** DISABLE TRAPS **** + nop; + nop; + nop; + /* If a context switch occurred while processing the interrupt then + * %o0 may have change value. If we return any value different + * from the input regs %o1, then the lower level will know that a context + * switch occurred during interrupt processing. + */ + mov %o0, %g6 ! g6 = %o0 + cmp %g6, %sp ! Is this a context switch occurred? + be,a simple_return ! No, then skip the window save + nop ! DELAY + + /* + * Flush all windows with valid contents except the current one. + * In examining the set register windows, one may logically divide + * the windows into sets (some of which may be empty) based on their + * current status: + * + * + current (i.e. in use), + * + used (i.e. a restore would not trap) + * + invalid (i.e. 1 in corresponding bit in WIM) + * + unused + * + * Either the used or unused set of windows may be empty. + * + * NOTE: We assume only one bit is set in the WIM at a time. + * + * Given a CWP of 5 and a WIM of 0x1, the registers are divided + * into sets as follows: + * + * + 0 - invalid + * + 1-4 - unused + * + 5 - current + * + 6-7 - used + * + * In this case, we only would save the used windows -- 6 and 7. + * + * Traps are disabled for the same logical period as in a + * flush all windows trap handler. + * + * Register Usage while saving the windows: + * g1 = current PSR + * g2 = current wim + * g3 = CWP + * g4 = wim scratch + * g5 = scratch + */ + + and %l0, SPARC_PSR_CWP_MASK, %g3 ! g3 = CWP + andn %l0, SPARC_PSR_ET_MASK, %g1 ! g1 = psr with traps disabled + ! mov %g1, %psr ! **** DISABLE TRAPS **** + mov %wim, %g2 ! g2 = wim + mov 1, %g4 + sll %g4, %g3, %g4 ! g4 = WIM mask for CW invalid + +save_frame_loop: + sll %g4, 1, %g5 ! rotate the "wim" left 1 + srl %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS - 1, %g4 + or %g4, %g5, %g4 ! g4 = wim if we do one restore + + /* + * If a restore would not underflow, then continue. + */ + + andcc %g4, %g2, %g0 ! Any windows to flush? + bnz done_flushing ! No, then continue + nop + + restore ! back one window + + /* + * Now save the window just as if we overflowed to it. + */ + + std %l0, [%sp + CPU_STACK_FRAME_L0_OFFSET] + std %l2, [%sp + CPU_STACK_FRAME_L2_OFFSET] + std %l4, [%sp + CPU_STACK_FRAME_L4_OFFSET] + std %l6, [%sp + CPU_STACK_FRAME_L6_OFFSET] + + std %i0, [%sp + CPU_STACK_FRAME_I0_OFFSET] + std %i2, [%sp + CPU_STACK_FRAME_I2_OFFSET] + std %i4, [%sp + CPU_STACK_FRAME_I4_OFFSET] + std %i6, [%sp + CPU_STACK_FRAME_I6_FP_OFFSET] + + ba save_frame_loop + nop + +done_flushing: + + ! Wait three instructions after the write to PSR before using + ! non-global registers or instructions affecting the CWP + ! g1 = psr with traps disabled + ! g3 = CWP (interrupt regs window) + mov %g1, %psr ! restore cwp + add %g3, 1, %g2 ! calculate desired WIM + and %g2, SPARC_NUMBER_OF_REGISTER_WINDOWS - 1, %g2 + mov 1, %g4 + sll %g4, %g2, %g4 ! g4 = new WIM + mov %g4, %wim + + mov %g6, %o0 ! %o0 = sp of context switch to + +simple_return: + ldd [%o0 + ISF_I6_FP_OFFSET], %i6 ! restore i6/fp, i7 + ! sub %fp, CONTEXT_CONTROL_INTERRUPT_FRAME_SIZE, %sp + + ld [%o0 + ISF_Y_OFFSET], %l5 ! restore y + wr %l5, 0, %y + + ldd [%o0 + ISF_PSR_OFFSET], %l0 ! restore psr, PC + + ld [%o0 + ISF_NPC_OFFSET], %l2 ! restore nPC + rd %psr, %l3 + and %l3, SPARC_PSR_CWP_MASK, %l3 ! want "current" CWP + andn %l0, SPARC_PSR_CWP_MASK, %l0 ! want rest from task + or %l3, %l0, %l0 ! install it later... + andn %l0, SPARC_PSR_ET_MASK, %l0 ! **** DISABLE TRAPS **** + + /* + * Restore tasks global and out registers + */ + ld [%o0 + ISF_G1_OFFSET], %g1 ! restore g1 ! g1 is restored later + ldd [%o0 + ISF_G2_OFFSET], %g2 ! restore g2, g3 + ldd [%o0 + ISF_G4_OFFSET], %g4 ! restore g4, g5 + ldd [%o0 + ISF_G6_OFFSET], %g6 ! restore g6, g7 + + ldd [%o0 + ISF_I0_OFFSET], %i0 ! restore i0, i1 + ldd [%o0 + ISF_I2_OFFSET], %i2 ! restore i2, i3 + ldd [%o0 + ISF_I4_OFFSET], %i4 ! restore i4, i5 + + ldd [%o0 + ISF_F0_OFFSET] ,%f0 + ldd [%o0 + ISF_F2_OFFSET] ,%f2 + ldd [%o0 + ISF_F4_OFFSET] ,%f4 + ldd [%o0 + ISF_F6_OFFSET] ,%f6 + ldd [%o0 + ISF_F8_OFFSET] ,%f8 + ldd [%o0 + ISF_F10_OFFSET],%f10 + ldd [%o0 + ISF_F12_OFFSET],%f12 + ldd [%o0 + ISF_F14_OFFSET],%f14 + ldd [%o0 + ISF_F16_OFFSET],%f16 + ldd [%o0 + ISF_F18_OFFSET],%f18 + ldd [%o0 + ISF_F20_OFFSET],%f20 + ldd [%o0 + ISF_F22_OFFSET],%f22 + ldd [%o0 + ISF_F24_OFFSET],%f24 + ldd [%o0 + ISF_F26_OFFSET],%f26 + ldd [%o0 + ISF_F28_OFFSET],%f28 + ldd [%o0 + ISF_F30_OFFSET],%f30 + ld [%o0 + ISF_FSR_OFFSET],%fsr + nop + nop + nop + + /* + * Registers: + * + * ALL global registers EXCEPT G1 and the input registers have + * already been restored and thuse off limits. + * + * The following is the contents of the local registers: + * + * %l0 = original psr + * %l1 = return address (i.e. PC) + * %l2 = nPC + * %l3 = %tbr + */ + + /* + * if (CWP + 1) is an invalid window then we need to reload it. + * + * WARNING: Traps should now be disabled + */ + + mov %l0, %psr ! **** DISABLE TRAPS **** + nop + nop + nop + rd %wim, %l4 + add %l0, 1, %l6 ! l6 = cwp + 1 + and %l6, SPARC_PSR_CWP_MASK, %l6 ! do the modulo on it + srl %l4, %l6, %l5 ! l5 = win >> cwp + 1 ; shift count + ! and CWP are conveniently LS 5 bits + cmp %l5, 1 ! Is tasks window invalid? + bne good_task_window + + /* + * The following code is the same as a 1 position left rotate of WIM. + */ + sll %l4, 1, %l5 ! l5 = WIM << 1 + srl %l4, SPARC_NUMBER_OF_REGISTER_WINDOWS-1 , %l4 + ! l4 = WIM >> (Number Windows - 1) + or %l4, %l5, %l4 ! l4 = (WIM << 1) | + ! (WIM >> (Number Windows - 1)) + /* + * Now restore the window just as if we underflowed to it. + */ + wr %l4, 0, %wim ! WIM = new WIM + nop ! must delay after writing WIM + nop + nop + + restore ! now into the tasks window + + ldd [%sp + CPU_STACK_FRAME_L0_OFFSET], %l0 + ldd [%sp + CPU_STACK_FRAME_L2_OFFSET], %l2 + ldd [%sp + CPU_STACK_FRAME_L4_OFFSET], %l4 + ldd [%sp + CPU_STACK_FRAME_L6_OFFSET], %l6 + ldd [%sp + CPU_STACK_FRAME_I0_OFFSET], %i0 + ldd [%sp + CPU_STACK_FRAME_I2_OFFSET], %i2 + ldd [%sp + CPU_STACK_FRAME_I4_OFFSET], %i4 + ldd [%sp + CPU_STACK_FRAME_I6_FP_OFFSET],%i6 + ! reload of sp clobbers ISF + save ! Back to ISR dispatch window + +good_task_window: + + mov %l0, %psr ! **** DISABLE TRAPS **** + nop; + nop; + nop + ! and restore condition codes. + + jmp %l1 ! transfer control and + rett %l2 ! go back to tasks window + nop + /* isr end */ + + /* trap handler end*/ + + .end diff --git a/arch/sparc/src/s698pm/s698pm_head.S b/arch/sparc/src/s698pm/s698pm_head.S new file mode 100644 index 0000000000..9ec060d71e --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_head.S @@ -0,0 +1,678 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_head.S + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#define RTRAP(_vector, _handler) mov %g0, %l0 ; sethi %hi(_handler), %l4 ; jmp %l4+%lo(_handler); mov _vector, %l3 +#define TRAP(_vector, _handler) mov %psr, %l0; sethi %hi(_handler), %l4 ; jmp %l4+%lo(_handler); mov _vector, %l3 + +/* Unexcpected trap will halt the processor by forcing it to error state */ +#define BAD_TRAP ta 0; nop; nop; nop; +#define SOFT_TRAP BAD_TRAP /* Software trap. Treat as BAD_TRAP */ + +/* + * System call optimized trap table entry + */ +#define IRQDIS_TRAP(_handler) \ + mov %psr, %l0 ; \ + sethi %hi(_handler), %l4 ; \ + jmp %l4+%lo(_handler); \ + or %l0, 0x0f00, %l3; ! Set PIL=0xf to disable IRQ + +/* + * System call optimized trap table entry + */ +#define IRQEN_TRAP(_handler) \ + mov %psr, %l0 ; \ + sethi %hi(_handler), %l4 ; \ + jmp %l4+%lo(_handler); \ + andn %l0, 0xf00, %l3; ! Set PIL=0 to Enable IRQ + +/* + * Window Overflow optimized trap table entry + */ +#define WOTRAP(_vector, _handler) \ + sethi %hi(_handler), %l4; \ + jmp %l4+%lo(_handler); \ + save; \ + nop + +/* + * Window Underflow optimized trap table entry + */ +#define WUTRAP(_vector, _handler) \ + mov %wim, %l3 ; \ + sethi %hi(_handler), %l4 ; \ + jmp %l4+%lo(_handler); \ + sll %l3, 1, %l4 ! l4 = WIM << 1 + +#define S698PM_STACK_TOP _end + (CONFIG_IDLETHREAD_STACKSIZE * CONFIG_SMP_NCPUS) + + .text + .global _trap_table, _hardreset + .global __start + .global up_lowinit /* Perform low level initialization */ + .global nx_start /* NuttX entry point */ + .global _userinit, _end + .global _window_overflow, _window_underflow, _flush_windows, _fpdis_enable + .global syscall_irqdis, syscall_irqen + .global _ISR_Handler +#ifdef CONFIG_SMP + .global s698pm_cpu_boot +#endif + + /* Hardware traps */ +_trap_table: +_hardreset: + RTRAP(0,__start); ! 00 reset trap + BAD_TRAP; ! 01 instruction_access_exception + BAD_TRAP; ! 02 illegal_instruction + BAD_TRAP; ! 03 priveleged_instruction + TRAP(4,_fpdis_enable); ! 04 fp_disabled + WOTRAP(5,_window_overflow); ! 05 window_overflow + WUTRAP(6,_window_underflow); ! 06 window_underflow + BAD_TRAP; ! 07 memory_add0ress_not_aligned + BAD_TRAP; ! 08 fp_exception + BAD_TRAP; ! 09 data_access_exception + BAD_TRAP; ! 0A tag_overflow + BAD_TRAP; ! 0B undefined + BAD_TRAP; ! 0C undefined + BAD_TRAP; ! 0D undefined + BAD_TRAP; ! 0E undefined + BAD_TRAP; ! 0F undefined + BAD_TRAP; ! 10 undefined + + /* Interrupt entries */ + TRAP(0x11,_ISR_Handler) ! 11 interrupt level 1 + TRAP(0x12,_ISR_Handler) ! 12 interrupt level 2 + TRAP(0x13,_ISR_Handler) ! 13 interrupt level 3 + TRAP(0x14,_ISR_Handler) ! 14 interrupt level 4 + TRAP(0x15,_ISR_Handler) ! 15 interrupt level 5 + TRAP(0x16,_ISR_Handler) ! 16 interrupt level 6 + TRAP(0x17,_ISR_Handler) ! 17 interrupt level 7 + TRAP(0x18,_ISR_Handler) ! 18 interrupt level 8 + TRAP(0x19,_ISR_Handler) ! 19 interrupt level 9 + TRAP(0x1A,_ISR_Handler) ! 1A interrupt level 1 + TRAP(0x1B,_ISR_Handler) ! 1B interrupt level 11 + TRAP(0x1C,_ISR_Handler) ! 1C interrupt level 12 + TRAP(0x1D,_ISR_Handler) ! 1D interrupt level 13 + TRAP(0x1E,_ISR_Handler) ! 1E interrupt level 14 + TRAP(0x1F,_ISR_Handler) ! 1F interrupt level 15 + + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 20 - 23 undefined + BAD_TRAP; ! 24 cp_disabled + BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 25 - 27 undefined + BAD_TRAP; ! 28 cp_exception + BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 29 - 2B undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 2C - 2F undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 30 - 33 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 34 - 37 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 38 - 3B undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 3C - 3F undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 40 - 43 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 44 - 47 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 48 - 4B undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 4C - 4F undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 50 - 53 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 54 - 57 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 58 - 5B undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 5C - 5F undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 60 - 63 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 64 - 67 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 68 - 6B undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 6C - 6F undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 70 - 73 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 74 - 77 undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 78 - 7B undefined + BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 7C - 7F undefined + + /* + * Software traps + * + * NOTE: At the risk of being redundant... this is not a full + * table. The setjmp on the SPARC requires a window flush trap + * handler and RTEMS will preserve the entries that were + * installed before. + */ + + TRAP( 0x80, _ISR_Handler ); ! 80 halt syscall SW trap + SOFT_TRAP; SOFT_TRAP; ! 81 - 82 + TRAP( 0x83, _flush_windows); ! 83 flush windows SW trap + + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! 84 - 87 + TRAP( 0x88, _ISR_Handler ); ! 88 + + /* + * SW Trap 9-15 Reserved for Operating System + * + * SPARC_SWTRAP_IRQDIS + * SPARC_SWTRAP_IRQEN + */ + IRQDIS_TRAP(syscall_irqdis); ! 89 IRQ Disable syscall trap + IRQEN_TRAP(syscall_irqen); ! 8A IRQ Enable syscall trap + + SOFT_TRAP; ! 8B + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! 8C - 8F + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! 90 - 93 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! 94 - 97 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! 98 - 9B + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! 9C - 9F + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! A0 - A3 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! A4 - A7 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! A8 - AB + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! AC - AF + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! B0 - B3 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! B4 - B7 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! B8 - BB + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! BC - BF + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! C0 - C3 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! C4 - C7 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! C8 - CB + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! CC - CF + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! D0 - D3 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! D4 - D7 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! D8 - DB + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! DC - DF + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! E0 - E3 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! E4 - E7 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! E8 - EB + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! EC - EF + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! F0 - F3 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! F4 - F7 + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! F8 - FB + SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; ! FC - FF + +/* trap table end*/ + +/* + * Init for SPARC-CPU + */ +#define PSR_INIT 0x10c0 /* Disable traps, set s and ps */ +__start: + ! initial the IU register !disable trap + set 0x10c0, %g1 + mov %g1,%psr + nop + nop + nop + + mov %g0,%wim + nop + nop + nop + + mov %g0,%g1 + mov %g0,%g2 + mov %g0,%g3 + mov %g0,%g4 + mov %g0,%g5 + mov %g0,%g6 + mov %g0,%g7 + + /*Fill local and in/out register*/ + mov 0x8,%g1 +1: mov %g0,%l0 + mov %g0,%l1 + mov %g0,%l2 + mov %g0,%l3 + mov %g0,%l4 + mov %g0,%l5 + mov %g0,%l6 + mov %g0,%l7 + mov %g0,%i0 + mov %g0,%i1 + mov %g0,%i2 + mov %g0,%i3 + mov %g0,%i4 + mov %g0,%i5 + mov %g0,%i6 + mov %g0,%i7 + subcc %g1,1,%g1 + save + bne 1b + flush + nop + + ! initial the WIM register = 2 + set 2, %g1 + mov %g1, %wim + nop + nop + nop + + ! initial the TBR table + sethi %hi(_trap_table), %g1 + mov %g1, %tbr + nop + nop + nop + + ! enable traps,fpu in psr + set 0x10e0, %g1 + mov %g1,%psr + nop + nop + nop + + ! initial the FSR register + set _fsrinit,%g1 + ld [%g1] , %fsr + nop + nop + nop + + ! initial the FPU %f register + set _fpdata,%g1 + ldd [%g1], %f0 + ldd [%g1], %f2 + ldd [%g1], %f4 + ldd [%g1], %f6 + ldd [%g1], %f8 + ldd [%g1], %f10 + ldd [%g1], %f12 + ldd [%g1], %f14 + ldd [%g1], %f16 + ldd [%g1], %f18 + ldd [%g1], %f20 + ldd [%g1], %f22 + ldd [%g1], %f24 + ldd [%g1], %f26 + ldd [%g1], %f28 + ldd [%g1], %f30 + nop + +#ifdef CONFIG_SMP + set CONFIG_IDLETHREAD_STACKSIZE , %l2 + rd %asr17 , %l1 + srl %l1 , 28 , %l1 +#else + mov %g0 , %l1 +#endif + mov %l1 , %g2 + + ! initial the stack point + sethi %hi(.Lcpu0_stacktop), %g1 + or %g1, %lo(.Lcpu0_stacktop), %g1 +1: + cmp %l1 , 0 + beq 2f + nop + add %g1 , %l2 , %g1 + sub %l1 , 1 , %l1 + b 1b + nop +2: + subcc %g1, CPU_MINIMUM_STACK_FRAME_SIZE, %g1 + mov %g1, %sp + nop + +#ifdef CONFIG_SMP + cmp %g2 , 0 + bne s698pm_cpu_boot + nop +#endif + call _userinit + nop + + call up_lowinit ! s698pm_lowinit.c + nop + + call nx_start ! sched/init/nx_start.c + nop + +_exit: + ta 0 + nop + nop + +_userinit: /* clear the bss */ + save %sp, -64, %sp + sethi %hi(__bss_start),%g2 + or %g2,%lo(__bss_start),%g2 ! g2 = start of bss + sethi %hi(__stack),%g3 + or %g3,%lo(__stack),%g3 ! g3 = end of bss + !set 0x40200000, %g3 + mov %g0,%g1 ! so std has two zeros + sub %g3, %g2, %g3 +zerobss: + subcc %g3, 8, %g3 + bge,a zerobss + std %g0,[%g2+%g3] + set _end, %o0 + st %g0,[%o0] + nop + + ret + restore + +/* + * Init END + */ + +/* Number of register windows */ +#define NWINDOWS 8 + + /* + * Window overflow trap handler. + * + * On entry: + * + * prev regwin l1 = pc + * prev regwin l2 = npc + */ + .global _window_overflow +_window_overflow: + + /* + * Calculate new WIM by "rotating" the valid bits in the WIM right + * by one position. The following shows how the bits move for a SPARC + * cpu implementation where SPARC_NUMBER_OF_REGISTER_WINDOWS is 8. + * + * OLD WIM = 76543210 + * NEW WIM = 07654321 + * + * NOTE: New WIM must be stored in a global register since the + * "save" instruction just prior to the load of the wim + * register will result in the local register set changing. + */ + + std %l0, [%sp + 0x00] ! save local register set + std %l2, [%sp + 0x08] + mov %wim, %l3 + sll %l3, NWINDOWS-1 , %l2 + ! l2 = WIM << (Number Windows - 1) + std %l4, [%sp + 0x10] + std %l6, [%sp + 0x18] + srl %l3, 1, %l3 ! l3 = WIM >> 1 + wr %l3, %l2, %wim ! WIM = (WIM >> 1) ^ + ! (WIM << (Number Windows - 1)) + ! 3 instruction delay not needed here + std %i0, [%sp + 0x20] ! save input register set + std %i2, [%sp + 0x28] + std %i4, [%sp + 0x30] + std %i6, [%sp + 0x38] + restore ! Go back to trap window. + jmp %l1 ! Re-execute save. + rett %l2 + + /* + * Window underflow trap handler. + * + * On entry: + * + * l1 = pc + * l2 = npc + * l3 = wim (from trap vector) + * l4 = wim << 1 (from trap vector) + */ + .global _window_underflow +_window_underflow: + + /* + * Calculate new WIM by "rotating" the valid bits in the WIM left + * by one position. The following shows how the bits move for a SPARC + * cpu implementation where SPARC_NUMBER_OF_REGISTER_WINDOWS is 8. + * + * OLD WIM = 76543210 + * NEW WIM = 07654321 + * + * NOTE: New WIM must be stored in a global register since the + * "save" instruction just prior to the load of the wim + * register will result in the local register set changing. + */ + + srl %l3, NWINDOWS-1, %l5 + or %l5, %l4, %l5 ! l5 = (WIM << 1) | + ! (WIM >> (Number Windows-1)) + mov %l5, %wim ! load the new WIM + nop; nop; nop ! 3 slot delay + restore ! Two restores to get into the + restore ! window to restore + ldd [%sp + 0x00], %l0 ! First the local register set + ldd [%sp + 0x08], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 ! Then the input registers + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %i6 + save ! Get back to the trap window. + save + jmp %l1 ! Re-execute restore. + rett %l2 + + /* + * Flush All Windows trap handler. + * + * Flush all windows with valid contents except the current one + * and the one we will be returning to. + * + * In examining the set register windows, one may logically divide + * the windows into sets (some of which may be empty) based on their + * current status: + * + * + current (i.e. in use), + * + used (i.e. a restore would not trap) + * + invalid (i.e. 1 in corresponding bit in WIM) + * + unused + * + * Either the used or unused set of windows may be empty. + * + * NOTE: We assume only one bit is set in the WIM at a time. + * + * Given a CWP of 5 and a WIM of 0x1, the registers are divided + * into sets as follows: + * + * + 0 - invalid + * + 1-4 - unused + * + 5 - current + * + 6-7 - used + * + * In this case, we only would save the used windows which we + * will not be returning to -- 6. + * + * Register Usage while saving the windows: + * g1 = current PSR + * g2 = current wim + * g3 = CWP + * g4 = wim scratch + * g5 = scratch + * + * On entry: + * + * l0 = psr (from trap table) + * l1 = pc + * l2 = npc + */ + .global _flush_windows +_flush_windows: + /* + * Save the global registers we will be using + */ + + mov %g1, %l3 + mov %g2, %l4 + mov %g3, %l5 + mov %g4, %l6 + mov %g5, %l7 + + mov %l0, %g1 ! g1 = psr + mov %wim, %g2 ! g2 = wim + and %l0, SPARC_PSR_CWP_MASK, %g3 ! g3 = CWP + + add %g3, 1, %g5 ! g5 = CWP + 1 + and %g5, SPARC_NUMBER_OF_REGISTER_WINDOWS - 1, %g5 + + mov 1, %g4 + sll %g4, %g5, %g4 ! g4 = WIM mask for CWP+1 invalid + + restore ! go back one register window + +save_frame_loop: + sll %g4, 1, %g5 ! rotate the "wim" left 1 + srl %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS - 1, %g4 + or %g4, %g5, %g4 ! g4 = wim if we do one restore + + /* + * If a restore would not underflow, then continue. + */ + + andcc %g4, %g2, %g0 ! Any windows to flush? + bnz done_flushing ! No, then continue + nop + + restore ! back one window + + /* + * Now save the window just as if we overflowed to it. + */ + + std %l0, [%sp + CPU_STACK_FRAME_L0_OFFSET] + std %l2, [%sp + CPU_STACK_FRAME_L2_OFFSET] + std %l4, [%sp + CPU_STACK_FRAME_L4_OFFSET] + std %l6, [%sp + CPU_STACK_FRAME_L6_OFFSET] + + std %i0, [%sp + CPU_STACK_FRAME_I0_OFFSET] + std %i2, [%sp + CPU_STACK_FRAME_I2_OFFSET] + std %i4, [%sp + CPU_STACK_FRAME_I4_OFFSET] + std %i6, [%sp + CPU_STACK_FRAME_I6_FP_OFFSET] + + ba save_frame_loop + nop + +done_flushing: + + add %g3, 2, %g3 ! calculate desired WIM + and %g3, SPARC_NUMBER_OF_REGISTER_WINDOWS - 1, %g3 + mov 1, %g4 + sll %g4, %g3, %g4 ! g4 = new WIM + mov %g4, %wim + + mov %g1, %psr ! restore PSR + nop + nop + nop + + /* + * Restore the global registers we used + */ + + mov %l3, %g1 + mov %l4, %g2 + mov %l5, %g3 + mov %l6, %g4 + mov %l7, %g5 + + jmpl %l2, %g0 + rett %l2 + 4 + +/* fpdis_enable */ + .global _fpdis_enable +_fpdis_enable: + + jmpl %l2, %g0 + rett %l2 + 4 + nop + nop +/*************************************************/ + +/*************************************************/ + .data + .global _fsrinit, _fpdata + + .align 8 +_fsrinit: + .word 0x0,0 + + .align 8 +_fpdata: + .word 0x0,0 + + .align 8 + .global g_idle_topstack + .type g_idle_topstack, object + +g_idle_topstack: + .long S698PM_STACK_TOP + .size g_idle_topstack, .-g_idle_topstack + +/*************************************************************************** + * .noinit section data + ***************************************************************************/ + + .section .noinit, "aw" + + .align 8 + .globl g_cpu0_idlestack + .type g_cpu0_idlestack, object + +g_cpu0_idlestack: + .space CONFIG_IDLETHREAD_STACKSIZE +.Lcpu0_stacktop: + .size g_cpu0_idlestack, .Lcpu0_stacktop-g_cpu0_idlestack + +#ifdef CONFIG_SMP +#if CONFIG_SMP_NCPUS > 1 + .align 8 + .globl g_cpu1_idlestack + .type g_cpu1_idlestack, object + +g_cpu1_idlestack: + .space CONFIG_IDLETHREAD_STACKSIZE +.Lcpu1_stacktop: + .size g_cpu1_idlestack, .Lcpu1_stacktop-g_cpu1_idlestack + +#if CONFIG_SMP_NCPUS > 2 + .align 8 + .globl g_cpu2_idlestack + .type g_cpu2_idlestack, object + +g_cpu2_idlestack: + .space CONFIG_IDLETHREAD_STACKSIZE +.Lcpu2_stacktop: + .size g_cpu2_idlestack, .Lcpu2_stacktop-g_cpu2_idlestack + +#if CONFIG_SMP_NCPUS > 3 + .align 8 + .globl g_cpu3_idlestack + .type g_cpu3_idlestack, object + +g_cpu3_idlestack: + .space CONFIG_IDLETHREAD_STACKSIZE +.Lcpu3_stacktop: + .size g_cpu3_idlestack, .Lcpu3_stacktop-g_cpu3_idlestack + +#if CONFIG_SMP_NCPUS > 4 +# error This logic needs to extended for CONFIG_SMP_NCPUS > 4 + +#endif /* CONFIG_SMP_NCPUS > 4 */ +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +#endif /* CONFIG_SMP_NCPUS > 1 */ +#endif /* CONFIG_SMP */ + + .end + diff --git a/arch/sparc/src/s698pm/s698pm_irq.h b/arch/sparc/src/s698pm/s698pm_irq.h new file mode 100644 index 0000000000..f22c4b7473 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_irq.h @@ -0,0 +1,116 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_irq.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_SPARC_SRC_S698PM_S698PM_IRQ_H +#define __ARCH_SPARC_SRC_S698PM_S698PM_IRQ_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* CPU interrupt types. */ + +#define S698PM_CPUINT_LEVEL 0 +#define S698PM_CPUINT_EDGE 1 + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: s698pm_cpuint_initialize + * + * Description: + * Initialize CPU interrupts + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int s698pm_cpuint_initialize(void); + +/**************************************************************************** + * Name: s698pm_setup_irq + * + * Description: + * This function sets up the IRQ. It allocates a CPU interrupt of the given + * priority andattaches it to the given irq. + * + * Input Parameters: + * cpu - The CPU to receive the interrupt 0~3 + * irq - The irq number from irq.h to be assigned to a EXT interrupt. + * priority - Interrupt's priority (0~1). + * + * Returned Value: + * The allocated CPU interrupt on success, a negated errno value on + * failure. + * + ****************************************************************************/ + +int s698pm_setup_irq(int cpu, int irq, int priority); + +/**************************************************************************** + * Name: s698pm_teardown_irq + * + * Description: + * This function undoes the operations done by s698pm_setup_irq. + * It detaches a ext interrupt from a CPU irq. + * + * Input Parameters: + * irq - The irq number from irq.h to be assigned to a EXT interrupt. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void s698pm_teardown_irq(int irq); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_SPARC_SRC_S698PM_S698PM_IRQ_H */ diff --git a/arch/sparc/src/s698pm/s698pm_tim.c b/arch/sparc/src/s698pm/s698pm_tim.c new file mode 100644 index 0000000000..2147fbeb8d --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_tim.c @@ -0,0 +1,750 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_tim.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "chip.h" +#include "up_internal.h" +#include "s698pm.h" +#include "s698pm_tim.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This module then only compiles if there are enabled timers that are not + * intended for some other purpose. + */ + +#if defined(CONFIG_S698PM_TIM1) || defined(CONFIG_S698PM_TIM2) || \ + defined(CONFIG_S698PM_TIM3) || defined(CONFIG_S698PM_TIM4) +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* TIM Device Structure */ + +struct s698pm_tim_priv_s +{ + const struct s698pm_tim_ops_s *ops; + enum s698pm_tim_mode_e mode; + uint32_t base; /* TIMn base address */ +}; + +/**************************************************************************** + * Private Function prototypes + ****************************************************************************/ + +/* Register helpers */ + +static inline uint16_t s698pm_getreg16(struct s698pm_tim_dev_s *dev, + uint8_t offset); +static inline void s698pm_putreg16(struct s698pm_tim_dev_s *dev, + uint8_t offset, uint16_t value); +static inline void s698pm_modifyreg32(struct s698pm_tim_dev_s *dev, + uint8_t offset, uint32_t clearbits, + uint32_t setbits); +static inline uint32_t s698pm_getreg32(struct s698pm_tim_dev_s *dev, + uint8_t offset); +static inline void s698pm_putreg32(struct s698pm_tim_dev_s *dev, + uint8_t offset, uint32_t value); + +/* Timer helpers */ + +static void s698pm_tim_reload_counter(struct s698pm_tim_dev_s *dev); +static void s698pm_tim_enable(struct s698pm_tim_dev_s *dev); +static void s698pm_tim_disable(struct s698pm_tim_dev_s *dev); +static void s698pm_tim_reset(struct s698pm_tim_dev_s *dev); + +/* Timer methods */ + +static int s698pm_tim_setmode(struct s698pm_tim_dev_s *dev, + enum s698pm_tim_mode_e mode); + +static int s698pm_tim_setclock(struct s698pm_tim_dev_s *dev, + uint32_t freq); +static uint32_t s698pm_tim_getclock(struct s698pm_tim_dev_s *dev); +static void s698pm_tim_setperiod(struct s698pm_tim_dev_s *dev, + uint32_t period); +static uint32_t s698pm_tim_getperiod(struct s698pm_tim_dev_s *dev); +static uint32_t s698pm_tim_getcounter(struct s698pm_tim_dev_s *dev); + +static int s698pm_tim_setisr(struct s698pm_tim_dev_s *dev, + xcpt_t handler, void *arg, int source); + +static int s698pm_tim_checkint(struct s698pm_tim_dev_s *dev, int source); +static void s698pm_tim_clrint(struct s698pm_tim_dev_s *dev, int source); +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct s698pm_tim_ops_s s698pm_tim_ops = +{ + .setmode = s698pm_tim_setmode, + .setclock = s698pm_tim_setclock, + .getclock = s698pm_tim_getclock, + .setperiod = s698pm_tim_setperiod, + .getperiod = s698pm_tim_getperiod, + .getcounter = s698pm_tim_getcounter, + .setisr = s698pm_tim_setisr, + .clrint = s698pm_tim_clrint, + .checkint = s698pm_tim_checkint, +}; + +#ifdef CONFIG_S698PM_TIM1 +struct s698pm_tim_priv_s s698pm_tim1_priv = +{ + .ops = &s698pm_tim_ops, + .mode = S698PM_TIM_MODE_UNUSED, + .base = S698PM_TIM1_BASE, +}; +#endif +#ifdef CONFIG_S698PM_TIM2 +struct s698pm_tim_priv_s s698pm_tim2_priv = +{ + .ops = &s698pm_tim_ops, + .mode = S698PM_TIM_MODE_UNUSED, + .base = S698PM_TIM2_BASE, +}; +#endif +#ifdef CONFIG_S698PM_TIM3 +struct s698pm_tim_priv_s s698pm_tim3_priv = +{ + .ops = &s698pm_tim_ops, + .mode = S698PM_TIM_MODE_UNUSED, + .base = S698PM_TIM3_BASE, +}; +#endif +#ifdef CONFIG_S698PM_TIM4 +struct s698pm_tim_priv_s s698pm_tim4_priv = +{ + .ops = &s698pm_tim_ops, + .mode = S698PM_TIM_MODE_UNUSED, + .base = S698PM_TIM4_BASE, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s698pm_getreg16 + * + * Description: + * Get a 16-bit register value by offset + * + ****************************************************************************/ + +static inline uint16_t s698pm_getreg16(struct s698pm_tim_dev_s *dev, + uint8_t offset) +{ + return getreg16(((struct s698pm_tim_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: s698pm_putreg16 + * + * Description: + * Put a 16-bit register value by offset + * + ****************************************************************************/ + +static inline void s698pm_putreg16(struct s698pm_tim_dev_s *dev, + uint8_t offset, uint16_t value) +{ + putreg16(value, ((struct s698pm_tim_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: s698pm_modifyreg32 + * + * Description: + * Modify a 16-bit register value by offset + * + ****************************************************************************/ + +static inline void s698pm_modifyreg32(struct s698pm_tim_dev_s *dev, + uint8_t offset, uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(((struct s698pm_tim_priv_s *)dev)->base + offset, clearbits, + setbits); +} + +/**************************************************************************** + * Name: s698pm_getreg32 + * + * Description: + * Get a 32-bit register value by offset. This applies only for the s698pm + * 32-bit registers (CNT, ARR, CRR1-4) in the 32-bit timers TIM2-5. + * + ****************************************************************************/ + +static inline uint32_t s698pm_getreg32(struct s698pm_tim_dev_s *dev, + uint8_t offset) +{ + return getreg32(((struct s698pm_tim_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: s698pm_putreg32 + * + * Description: + * Put a 32-bit register value by offset. This applies only for the s698pm + * 32-bit registers (CNT, ARR, CRR1-4) in the 32-bit timers TIM2-5. + * + ****************************************************************************/ + +static inline void s698pm_putreg32(struct s698pm_tim_dev_s *dev, + uint8_t offset, uint32_t value) +{ + putreg32(value, ((struct s698pm_tim_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: s698pm_tim_reload_counter + ****************************************************************************/ + +static void s698pm_tim_reload_counter(struct s698pm_tim_dev_s *dev) +{ + uint32_t val = s698pm_getreg32(dev, S698PM_TIM_CR_OFFSET); + val |= TIMER_LOADCOUNT; + s698pm_putreg32(dev, S698PM_TIM_CR_OFFSET, val); +} + +/**************************************************************************** + * Name: s698pm_tim_enable + ****************************************************************************/ + +static void s698pm_tim_enable(struct s698pm_tim_dev_s *dev) +{ + uint32_t val = s698pm_getreg32(dev, S698PM_TIM_CR_OFFSET); + val |= TIMER_ENABLE | TIMER_RELOADCOUNT; + s698pm_tim_reload_counter(dev); + s698pm_putreg32(dev, S698PM_TIM_CR_OFFSET, val); +} + +/**************************************************************************** + * Name: s698pm_tim_disable + ****************************************************************************/ + +static void s698pm_tim_disable(struct s698pm_tim_dev_s *dev) +{ + uint32_t val = s698pm_getreg32(dev, S698PM_TIM_CR_OFFSET); + val &= ~TIMER_ENABLE; + s698pm_putreg32(dev, S698PM_TIM_CR_OFFSET, val); +} + +/**************************************************************************** + * Name: s698pm_tim_reset + * + * Description: + * Reset timer into system default state, but do not affect output/input pins + * + ****************************************************************************/ + +static void s698pm_tim_reset(struct s698pm_tim_dev_s *dev) +{ + ((struct s698pm_tim_priv_s *)dev)->mode = S698PM_TIM_MODE_DISABLED; + s698pm_tim_disable(dev); +} + +/**************************************************************************** + * Name: s698pm_tim_setmode + ****************************************************************************/ + +static int s698pm_tim_setmode(struct s698pm_tim_dev_s *dev, + enum s698pm_tim_mode_e mode) +{ + uint32_t val = s698pm_getreg32(dev, S698PM_TIM_CR_OFFSET); + + DEBUGASSERT(dev != NULL); + + /* Decode operational modes */ + + switch (mode & S698PM_TIM_MODE_MASK) + { + case S698PM_TIM_MODE_DISABLED: + val &= (~TIMER_LOADCOUNT) & (~TIMER_RELOADCOUNT) & (~TIMER_ENABLE); + break; + + case S698PM_TIM_MODE_DOWN: + val |= (TIMER_LOADCOUNT | TIMER_RELOADCOUNT | TIMER_ENABLE); + break; + + default: + return -EINVAL; + } + + s698pm_tim_reload_counter(dev); + s698pm_putreg32(dev, S698PM_TIM_CR_OFFSET, val); + + return OK; +} + +/**************************************************************************** + * Name: s698pm_tim_setclock + ****************************************************************************/ + +static int s698pm_tim_setclock(struct s698pm_tim_dev_s *dev, + uint32_t freq) +{ + uint32_t freqin; + int prescaler; + + DEBUGASSERT(dev != NULL); + + /* Disable Timer? */ + + if (freq == 0) + { + s698pm_tim_disable(dev); + return 0; + } + + /* Get the input clock frequency for this timer. These vary with + * different timer clock sources, MCU-specific timer configuration, and + * board-specific clock configuration. The correct input clock frequency + * must be defined in the board.h header file. + */ + + switch (((struct s698pm_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_S698PM_TIM1 + case S698PM_TIM1_BASE: + freqin = BOARD_TIM1_FREQUENCY; + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case S698PM_TIM2_BASE: + freqin = BOARD_TIM2_FREQUENCY; + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case S698PM_TIM3_BASE: + freqin = BOARD_TIM3_FREQUENCY; + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case S698PM_TIM4_BASE: + freqin = BOARD_TIM4_FREQUENCY; + break; +#endif + + default: + return -EINVAL; + } + + /* Select a pre-scaler value for this timer using the input clock + * frequency. + */ + + prescaler = freqin / freq; + + /* We need to decrement value for '1', but only, if that will not to + * cause underflow. + */ + + if (prescaler > 0) + { + prescaler--; + } + + /* Check for overflow as well. */ + + if (prescaler > 0x3ff) + { + prescaler = 0x3ff; + } + + putreg32(prescaler, S698PM_TIMPRE_BASE + S698PM_TIM_PSCLOAD_OFFSET); + putreg32(prescaler, S698PM_TIMPRE_BASE + S698PM_TIM_PSCCONT_OFFSET); + s698pm_tim_enable(dev); + + return prescaler; +} + +/**************************************************************************** + * Name: s698pm_tim_getclock + ****************************************************************************/ + +static uint32_t s698pm_tim_getclock(struct s698pm_tim_dev_s *dev) +{ + uint32_t freqin; + uint32_t clock; + DEBUGASSERT(dev != NULL); + + /* Get the input clock frequency for this timer. These vary with + * different timer clock sources, MCU-specific timer configuration, and + * board-specific clock configuration. The correct input clock frequency + * must be defined in the board.h header file. + */ + + switch (((struct s698pm_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_S698PM_TIM1 + case S698PM_TIM1_BASE: + freqin = BOARD_TIM1_FREQUENCY; + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case S698PM_TIM2_BASE: + freqin = BOARD_TIM2_FREQUENCY; + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case S698PM_TIM3_BASE: + freqin = BOARD_TIM3_FREQUENCY; + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case S698PM_TIM4_BASE: + freqin = BOARD_TIM4_FREQUENCY; + break; +#endif + + default: + return -EINVAL; + } + + /* From chip datasheet, at page 1179. */ + + clock = freqin / ((0x3ff & getreg32(S698PM_TIMPRE_BASE + + S698PM_TIM_PSCLOAD_OFFSET)) + 1); + + return clock; +} + +/**************************************************************************** + * Name: s698pm_tim_setperiod + ****************************************************************************/ + +static void s698pm_tim_setperiod(struct s698pm_tim_dev_s *dev, + uint32_t period) +{ + DEBUGASSERT(dev != NULL); + s698pm_putreg32(dev, S698PM_TIM_ARR_OFFSET, period); + s698pm_putreg32(dev, S698PM_TIM_CNT_OFFSET, period); +} + +/**************************************************************************** + * Name: s698pm_tim_getperiod + ****************************************************************************/ + +static uint32_t s698pm_tim_getperiod (struct s698pm_tim_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + return s698pm_getreg32(dev, S698PM_TIM_ARR_OFFSET); +} + +/**************************************************************************** + * Name: s698pm_tim_getcounter + ****************************************************************************/ + +static uint32_t s698pm_tim_getcounter(struct s698pm_tim_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + uint32_t counter = s698pm_getreg32(dev, S698PM_TIM_CNT_OFFSET); + + return counter; +} + +/**************************************************************************** + * Name: s698pm_tim_setisr + ****************************************************************************/ + +static int s698pm_tim_setisr(struct s698pm_tim_dev_s *dev, + xcpt_t handler, void *arg, int source) +{ + int vectorno; + + DEBUGASSERT(dev != NULL); + DEBUGASSERT(source == 0); + + switch (((struct s698pm_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_S698PM_TIM1 + case S698PM_TIM1_BASE: + vectorno = S698PM_IRQ_TIMER1; + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case S698PM_TIM2_BASE: + vectorno = S698PM_IRQ_TIMER2; + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case S698PM_TIM3_BASE: + vectorno = S698PM_IRQ_TIMER3; + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case S698PM_TIM4_BASE: + vectorno = S698PM_IRQ_TIMER4; + break; +#endif + + default: + vectorno = S698PM_IRQ_TIMER1; + return -EINVAL; + } + + /* Disable interrupt when callback is removed */ + + if (!handler) + { + up_disable_irq(vectorno); + irq_detach(vectorno); + return OK; + } + + /* Otherwise set callback and enable interrupt */ + + irq_attach(vectorno, handler, arg); + up_enable_irq(vectorno); + + return OK; +} + +/**************************************************************************** + * Name: s698pm_tim_clrint + ****************************************************************************/ + +static void s698pm_tim_clrint(struct s698pm_tim_dev_s *dev, int source) +{ + int vectorno; + + DEBUGASSERT(dev != NULL); + DEBUGASSERT(source == 0); + + switch (((struct s698pm_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_S698PM_TIM1 + case S698PM_TIM1_BASE: + vectorno = S698PM_IRQ_TIMER1; + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case S698PM_TIM2_BASE: + vectorno = S698PM_IRQ_TIMER2; + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case S698PM_TIM3_BASE: + vectorno = S698PM_IRQ_TIMER3; + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case S698PM_TIM4_BASE: + vectorno = S698PM_IRQ_TIMER4; + break; +#endif + + default: + break; + } + + up_clrpend_irq(vectorno); +} + +/**************************************************************************** + * Name: s698pm_tim_checkint + ****************************************************************************/ + +static int s698pm_tim_checkint(struct s698pm_tim_dev_s *dev, int source) +{ + int vectorno; + + DEBUGASSERT(dev != NULL); + DEBUGASSERT(source == 0); + + switch (((struct s698pm_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_S698PM_TIM1 + case S698PM_TIM1_BASE: + vectorno = S698PM_IRQ_TIMER1; + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case S698PM_TIM2_BASE: + vectorno = S698PM_IRQ_TIMER2; + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case S698PM_TIM3_BASE: + vectorno = S698PM_IRQ_TIMER3; + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case S698PM_TIM4_BASE: + vectorno = S698PM_IRQ_TIMER4; + break; +#endif + + default: + return -EINVAL; + } + + return up_pending_irq(vectorno); +} + +/**************************************************************************** + * Pubic Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s698pm_tim_init + ****************************************************************************/ + +struct s698pm_tim_dev_s *s698pm_tim_init(int timer) +{ + struct s698pm_tim_dev_s *dev = NULL; + + /* Get structure and enable power */ + + switch (timer) + { +#ifdef CONFIG_S698PM_TIM1 + case 1: + dev = (struct s698pm_tim_dev_s *)&s698pm_tim1_priv; + + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case 2: + dev = (struct s698pm_tim_dev_s *)&s698pm_tim2_priv; + + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case 3: + dev = (struct s698pm_tim_dev_s *)&s698pm_tim3_priv; + + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case 4: + dev = (struct s698pm_tim_dev_s *)&s698pm_tim4_priv; + + break; +#endif + + default: + return NULL; + } + + /* Is device already allocated */ + + if (((struct s698pm_tim_priv_s *)dev)->mode != S698PM_TIM_MODE_UNUSED) + { + return NULL; + } + + s698pm_tim_reset(dev); + + return dev; +} + +/**************************************************************************** + * Name: s698pm_tim_deinit + * + * TODO: Detach interrupts, and close down all TIM Channels + * + ****************************************************************************/ + +int s698pm_tim_deinit(struct s698pm_tim_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + /* Disable power */ + + switch (((struct s698pm_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_S698PM_TIM1 + case S698PM_TIM1_BASE: + + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case S698PM_TIM2_BASE: + + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case S698PM_TIM3_BASE: + + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case S698PM_TIM4_BASE: + + break; +#endif + + default: + return -EINVAL; + } + + /* Mark it as free */ + + ((struct s698pm_tim_priv_s *)dev)->mode = S698PM_TIM_MODE_UNUSED; + + return OK; +} + +#endif /* defined(CONFIG_S698PM_TIM1 ||...|| CONFIG_S698PM_TIM4) */ diff --git a/arch/sparc/src/s698pm/s698pm_tim.h b/arch/sparc/src/s698pm/s698pm_tim.h new file mode 100644 index 0000000000..07e8524b2d --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_tim.h @@ -0,0 +1,154 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_tim.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_SPARC_SRC_S698PM_S698PM_TIM_H +#define __ARCH_SPARC_SRC_S698PM_S698PM_TIM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +#define S698PM_TIM1_BASE 0x80000310 +#define S698PM_TIM2_BASE 0x80000320 +#define S698PM_TIM3_BASE 0x80000330 +#define S698PM_TIM4_BASE 0x80000340 + +#define S698PM_TIMPRE_BASE 0x80000300 + +#define S698PM_TIM_CR_OFFSET 0x0008 /* Control register 1 (16-bit) */ +#define S698PM_TIM_CNT_OFFSET 0x0000 /* Counter (16-bit) */ +#define S698PM_TIM_ARR_OFFSET 0x0004 /* Auto-reload register (16-bit) */ + +#define S698PM_TIM_PSCLOAD_OFFSET 0x0004 /* Prescaler load (16-bit) */ +#define S698PM_TIM_PSCCONT_OFFSET 0x0000 /* Prescaler count (16-bit) */ + +#define TIMER_LOADCOUNT 0x4 +#define TIMER_RELOADCOUNT 0x2 +#define TIMER_ENABLE 0x1 + +/* Helpers ******************************************************************/ +#define S698PM_TIM_SETMODE(d,mode) ((d)->ops->setmode(d,mode)) +#define S698PM_TIM_SETCLOCK(d,freq) ((d)->ops->setclock(d,freq)) +#define S698PM_TIM_GETCLOCK(d) ((d)->ops->getclock(d)) +#define S698PM_TIM_SETPERIOD(d,period) ((d)->ops->setperiod(d,period)) +#define S698PM_TIM_GETPERIOD(d) ((d)->ops->getperiod(d)) +#define S698PM_TIM_GETCOUNTER(d) ((d)->ops->getcounter(d)) +#define S698PM_TIM_SETISR(d,hnd,arg,s) ((d)->ops->setisr(d,hnd,arg,s)) +#define S698PM_TIM_CLRINT(d,s) ((d)->ops->clrint(d,s)) +#define S698PM_TIM_CHECKINT(d,s) ((d)->ops->checkint(d,s)) +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/* TIM Device Structure */ + +struct s698pm_tim_dev_s +{ + struct s698pm_tim_ops_s *ops; +}; + +enum s698pm_tim_mode_e +{ + S698PM_TIM_MODE_UNUSED = -1, + + /* One of the following */ + + S698PM_TIM_MODE_MASK = 0x3, + S698PM_TIM_MODE_DISABLED = 0x1, + S698PM_TIM_MODE_DOWN = 0x2, +}; + +/* TIM Operations */ + +struct s698pm_tim_ops_s +{ + /* Basic Timers */ + + int (*setmode)(struct s698pm_tim_dev_s *dev, + enum s698pm_tim_mode_e mode); + int (*setclock)(struct s698pm_tim_dev_s *dev, uint32_t freq); + uint32_t (*getclock)(struct s698pm_tim_dev_s *dev); + void (*setperiod)(struct s698pm_tim_dev_s *dev, uint32_t period); + uint32_t (*getperiod)(struct s698pm_tim_dev_s *dev); + uint32_t (*getcounter)(struct s698pm_tim_dev_s *dev); + + /* Timer interrupts */ + + int (*setisr)(struct s698pm_tim_dev_s *dev, + xcpt_t handler, void *arg, int source); + void (*clrint)(struct s698pm_tim_dev_s *dev, int source); + int (*checkint)(struct s698pm_tim_dev_s *dev, int source); +}; + +/* Power-up timer and get its structure */ + +struct s698pm_tim_dev_s *s698pm_tim_init(int timer); + +/* Power-down timer, mark it as unused */ + +int s698pm_tim_deinit(struct s698pm_tim_dev_s *dev); + +/**************************************************************************** + * Name: s698pm_timer_initialize + * + * Description: + * Bind the configuration timer to a timer lower half instance and + * register the timer drivers at 'devpath' + * + * Input Parameters: + * devpath - The full path to the timer device. This should be of the form + * /dev/timer0 + * timer - the timer number. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_TIMER +int s698pm_timer_initialize(const char *devpath, int timer); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_SPARC_SRC_S698PM_S698PM_TIM_H */ diff --git a/arch/sparc/src/s698pm/s698pm_tim_lowerhalf.c b/arch/sparc/src/s698pm/s698pm_tim_lowerhalf.c new file mode 100644 index 0000000000..bdce692033 --- /dev/null +++ b/arch/sparc/src/s698pm/s698pm_tim_lowerhalf.c @@ -0,0 +1,488 @@ +/**************************************************************************** + * arch/sparc/src/s698pm/s698pm_tim_lowerhalf.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include "s698pm_tim.h" + +#if defined(CONFIG_TIMER) && \ + (defined(CONFIG_S698PM_TIM1) || defined(CONFIG_S698PM_TIM2)) || \ + defined(CONFIG_S698PM_TIM3) || defined(CONFIG_S698PM_TIM4)) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define S698PM_TIM1_RES 32 +#define S698PM_TIM2_RES 32 +#define S698PM_TIM3_RES 32 +#define S698PM_TIM4_RES 32 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure provides the private representation of the "lower-half" + * driver state structure. This structure must be cast-compatible with the + * timer_lowerhalf_s structure. + */ + +struct s698pm_lowerhalf_s +{ + const struct timer_ops_s *ops; /* Lower half operations */ + struct s698pm_tim_dev_s *tim; /* stm32 timer driver */ + tccb_t callback; /* Current upper half interrupt + * callback */ + void *arg; /* Argument passed to upper half + * callback */ + bool started; /* True: Timer has been started */ + const uint8_t resolution; /* Number of bits in the timer + * (16 or 32 bits) */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Interrupt handling *******************************************************/ + +static int s698pm_timer_handler(int irq, void *context, void *arg); + +/* "Lower half" driver methods **********************************************/ + +static int s698pm_start(struct timer_lowerhalf_s *lower); +static int s698pm_stop(struct timer_lowerhalf_s *lower); +static int s698pm_getstatus(struct timer_lowerhalf_s *lower, + struct timer_status_s *status); +static int s698pm_settimeout(struct timer_lowerhalf_s *lower, + uint32_t timeout); +static void s698pm_setcallback(struct timer_lowerhalf_s *lower, + tccb_t callback, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* "Lower half" driver methods */ + +static const struct timer_ops_s g_timer_ops = +{ + .start = s698pm_start, + .stop = s698pm_stop, + .getstatus = s698pm_getstatus, + .settimeout = s698pm_settimeout, + .setcallback = s698pm_setcallback, + .ioctl = NULL, +}; + +#ifdef CONFIG_S698PM_TIM1 +static struct s698pm_lowerhalf_s g_tim1_lowerhalf = +{ + .ops = &g_timer_ops, + .resolution = S698PM_TIM1_RES, +}; +#endif + +#ifdef CONFIG_S698PM_TIM2 +static struct s698pm_lowerhalf_s g_tim2_lowerhalf = +{ + .ops = &g_timer_ops, + .resolution = S698PM_TIM2_RES, +}; +#endif + +#ifdef CONFIG_S698PM_TIM3 +static struct s698pm_lowerhalf_s g_tim3_lowerhalf = +{ + .ops = &g_timer_ops, + .resolution = S698PM_TIM3_RES, +}; +#endif + +#ifdef CONFIG_S698PM_TIM4 +static struct s698pm_lowerhalf_s g_tim4_lowerhalf = +{ + .ops = &g_timer_ops, + .resolution = S698PM_TIM4_RES, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s698pm_timer_handler + * + * Description: + * timer interrupt handler + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int s698pm_timer_handler(int irq, void *context, void *arg) +{ + struct s698pm_lowerhalf_s *lower = (struct s698pm_lowerhalf_s *)arg; + uint32_t next_interval_us = 0; + + S698PM_TIM_ACKINT(lower->tim, 0); + + if (lower->callback(&next_interval_us, lower->arg)) + { + if (next_interval_us > 0) + { + S698PM_TIM_SETPERIOD(lower->tim, next_interval_us); + } + } + else + { + s698pm_stop((struct timer_lowerhalf_s *)lower); + } + + return OK; +} + +/**************************************************************************** + * Name: s698pm_start + * + * Description: + * Start the timer, resetting the time to the current timeout, + * + * Input Parameters: + * lower- A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int s698pm_start(struct timer_lowerhalf_s *lower) +{ + struct s698pm_lowerhalf_s *priv = (struct s698pm_lowerhalf_s *)lower; + + if (!priv->started) + { + S698PM_TIM_SETMODE(priv->tim, S698PM_TIM_MODE_DOWN); + + if (priv->callback != NULL) + { + S698PM_TIM_SETISR(priv->tim, s698pm_timer_handler, priv, 0); + } + + priv->started = true; + return OK; + } + + /* Return EBUSY to indicate that the timer was already running */ + + return -EBUSY; +} + +/**************************************************************************** + * Name: s698pm_stop + * + * Description: + * Stop the timer + * + * Input Parameters: + * lower- A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int s698pm_stop(struct timer_lowerhalf_s *lower) +{ + struct s698pm_lowerhalf_s *priv = (struct s698pm_lowerhalf_s *)lower; + + if (priv->started) + { + S698PM_TIM_SETMODE(priv->tim, S698PM_TIM_MODE_DISABLED); + S698PM_TIM_SETISR(priv->tim, NULL, NULL, 0); + priv->started = false; + return OK; + } + + /* Return ENODEV to indicate that the timer was not running */ + + return -ENODEV; +} + +/**************************************************************************** + * Name: s698pm_getstatus + * + * Description: + * get timer status + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower- + * half" driver state structure. + * status - The location to return the status information. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int s698pm_getstatus(struct timer_lowerhalf_s *lower, + struct timer_status_s *status) +{ + struct s698pm_lowerhalf_s *priv = (struct s698pm_lowerhalf_s *)lower; + uint64_t maxtimeout; + uint32_t timeout; + uint32_t clock; + uint32_t period; + uint32_t clock_factor; + + DEBUGASSERT(priv); + + /* Return the status bit */ + + status->flags = 0; + if (priv->started) + { + status->flags |= TCFLAGS_ACTIVE; + } + + if (priv->callback) + { + status->flags |= TCFLAGS_HANDLER; + } + + /* Get timeout */ + + maxtimeout = (1 << priv->resolution) - 1; + clock = S698PM_TIM_GETCLOCK(priv->tim); + period = S698PM_TIM_GETPERIOD(priv->tim); + + if (clock == 1000000) + { + timeout = period; + } + else + { + timeout = (maxtimeout * 1000000) / clock; + } + + status->timeout = timeout; + + /* Get the time remaining until the timer expires (in microseconds) */ + + clock_factor = (clock == 1000000) ? 1 : (clock / 1000000); + status->timeleft = (timeout - S698PM_TIM_GETCOUNTER(priv->tim)) * + clock_factor; + return OK; +} + +/**************************************************************************** + * Name: s698pm_settimeout + * + * Description: + * Set a new timeout value (and reset the timer) + * + * Input Parameters: + * lower- A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * timeout - The new timeout value in microseconds. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int s698pm_settimeout(struct timer_lowerhalf_s *lower, + uint32_t timeout) +{ + struct s698pm_lowerhalf_s *priv = (struct s698pm_lowerhalf_s *)lower; + uint64_t maxtimeout; + + if (priv->started) + { + return -EPERM; + } + + maxtimeout = (1 << priv->resolution) - 1; + if (timeout > maxtimeout) + { + uint64_t freq = (maxtimeout * 1000000) / timeout; + S698PM_TIM_SETCLOCK(priv->tim, freq); + S698PM_TIM_SETPERIOD(priv->tim, maxtimeout); + } + else + { + S698PM_TIM_SETCLOCK(priv->tim, 1000000); + S698PM_TIM_SETPERIOD(priv->tim, timeout); + } + + return OK; +} + +/**************************************************************************** + * Name: s698pm_sethandler + * + * Description: + * Call this user provided timeout handler. + * + * Input Parameters: + * lower- A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * callback - The new timer expiration function pointer. If this + * function pointer is NULL, then the reset-on-expiration + * behavior is restored, + * arg - Argument that will be provided in the callback + * + * Returned Value: + * The previous timer expiration function pointer or NULL is there was + * no previous function pointer. + * + ****************************************************************************/ + +static void s698pm_setcallback(struct timer_lowerhalf_s *lower, + tccb_t callback, void *arg) +{ + struct s698pm_lowerhalf_s *priv = (struct s698pm_lowerhalf_s *)lower; + irqstate_t flags = enter_critical_section(); + + /* Save the new callback */ + + priv->callback = callback; + priv->arg = arg; + + if (callback != NULL && priv->started) + { + S698PM_TIM_SETISR(priv->tim, s698pm_timer_handler, priv, 0); + } + else + { + S698PM_TIM_SETISR(priv->tim, NULL, NULL, 0); + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s698pm_timer_initialize + * + * Description: + * Bind the configuration timer to a timer lower half instance and + * register the timer drivers at 'devpath' + * + * Input Parameters: + * devpath - The full path to the timer device. This should be of the + * form /dev/timer0 + * timer - the timer's number. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +int s698pm_timer_initialize(const char *devpath, int timer) +{ + struct s698pm_lowerhalf_s *lower; + + switch (timer) + { +#ifdef CONFIG_S698PM_TIM1 + case 1: + lower = &g_tim1_lowerhalf; + break; +#endif + +#ifdef CONFIG_S698PM_TIM2 + case 2: + lower = &g_tim2_lowerhalf; + break; +#endif + +#ifdef CONFIG_S698PM_TIM3 + case 3: + lower = &g_tim3_lowerhalf; + break; +#endif + +#ifdef CONFIG_S698PM_TIM4 + case 4: + lower = &g_tim4_lowerhalf; + break; +#endif + + default: + return -ENODEV; + } + + /* Initialize the elements of lower half state structure */ + + lower->started = false; + lower->callback = NULL; + lower->tim = s698pm_tim_init(timer); + + if (lower->tim == NULL) + { + return -EINVAL; + } + + /* Register the timer driver as /dev/timerX. The returned value from + * timer_register is a handle that could be used with timer_unregister(). + * REVISIT: The returned handle is discard here. + */ + + void *drvr = timer_register(devpath, (struct timer_lowerhalf_s *)lower); + if (drvr == NULL) + { + /* The actual cause of the failure may have been a failure to allocate + * perhaps a failure to register the timer driver (such as if the + * 'depath' were not unique). We know here but we return EEXIST to + * indicate the failure (implying the non-unique devpath). + */ + + return -EEXIST; + } + + return OK; +} + +#endif /* CONFIG_TIMER */ diff --git a/arch/sparc/src/sparc_v8/up_initialstate.c b/arch/sparc/src/sparc_v8/up_initialstate.c index f608e87a4b..502793ef6e 100644 --- a/arch/sparc/src/sparc_v8/up_initialstate.c +++ b/arch/sparc/src/sparc_v8/up_initialstate.c @@ -71,7 +71,7 @@ void up_initial_state(struct tcb_s *tcb) if (tcb->pid == IDLE_PROCESS_ID) { tcb->stack_alloc_ptr = (void *)(g_idle_topstack - - (CONFIG_SMP_NCPUS * CONFIG_IDLETHREAD_STACKSIZE)); + (CONFIG_SMP_NCPUS * CONFIG_IDLETHREAD_STACKSIZE)); tcb->stack_base_ptr = tcb->stack_alloc_ptr; tcb->adj_stack_size = CONFIG_IDLETHREAD_STACKSIZE; diff --git a/arch/sparc/src/sparc_v8/up_schedulesigaction.c b/arch/sparc/src/sparc_v8/up_schedulesigaction.c index 374bfea381..ced7e6bc78 100644 --- a/arch/sparc/src/sparc_v8/up_schedulesigaction.c +++ b/arch/sparc/src/sparc_v8/up_schedulesigaction.c @@ -251,7 +251,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) * been delivered. */ - tcb->xcp.sigdeliver = (FAR void *)sigdeliver; + tcb->xcp.sigdeliver = (void *)sigdeliver; tcb->xcp.saved_pc = tcb->xcp.regs[REG_PC]; tcb->xcp.saved_npc = tcb->xcp.regs[REG_NPC]; tcb->xcp.saved_status = tcb->xcp.regs[REG_PSR]; @@ -274,7 +274,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) * trampoline after the signal(s) have been delivered. */ - tcb->xcp.sigdeliver = (FAR void *)sigdeliver; + tcb->xcp.sigdeliver = (void *)sigdeliver; tcb->xcp.saved_pc = CURRENT_REGS[REG_PC]; tcb->xcp.saved_npc = CURRENT_REGS[REG_NPC]; tcb->xcp.saved_status = CURRENT_REGS[REG_PSR]; @@ -330,7 +330,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) * trampoline after the signal(s) have been delivered. */ - tcb->xcp.sigdeliver = (FAR void *)sigdeliver; + tcb->xcp.sigdeliver = (void *)sigdeliver; tcb->xcp.saved_pc = CURRENT_REGS[REG_PC]; tcb->xcp.saved_npc = CURRENT_REGS[REG_NPC]; tcb->xcp.saved_status = CURRENT_REGS[REG_PSR];