diff --git a/arch/x86_64/include/hpet.h b/arch/x86_64/include/hpet.h new file mode 100644 index 0000000000..40627c82ef --- /dev/null +++ b/arch/x86_64/include/hpet.h @@ -0,0 +1,96 @@ +/**************************************************************************** + * arch/x86_64/include/hpet.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_X86_64_INCLUDE_HPET_H +#define __ARCH_X86_64_INCLUDE_HPET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register definitions */ + +#define HPET_GCAPID_OFFSET (0x00) +#define HPET_GCONF_OFFSET (0x10) +#define HPET_GISR_OFFSET (0x20) +#define HPET_MCNTR_OFFSET (0xf0) +#define HPET_TCONF_OFFSET(n) (0x100 + (0x20 * (n))) +#define HPET_TCOMP_OFFSET(n) (0x108 + (0x20 * (n))) +#define HPET_TFSB_OFFSET(n) (0x110 + (0x20 * (n))) + +/* General Capabilities and ID Register */ + +#define HPET_GCAPID_REVID_SHIFT (0ul) /* Bits 0-7: Revistion */ +# define HPET_GCAPID_REVID_MASK (0xfful << HPET_GCAPID_REVID_SHIFT) +#define HPET_GCAPID_NUMTIM_SHIFT (8ul) /* Bits 8-12: Number of Timers */ +#define HPET_GCAPID_NUMTIM_MASK (0x1ful << HPET_GCAPID_NUMTIM_SHIFT) +#define HPET_GCAPID_COUNTSIZE (1 << 13) /* Bit 13: Counter size, 0: 32 bit, 1: 64 bit */ + /* Bit 14: Reserved */ +#define HPET_GCAPID_LEGROUTE (1 << 15) /* Bit 15: LegacyReplacement Route Capable */ +#define HPET_GCAPID_VENDORID_SHIFT (16ul) /* Bits 16-31: Vendor ID */ +# define HPET_GCAPID_VENDORID_MASK (0x7ffful << HPET_GCAPID_VENDORID_SHIFT) +#define HPET_GCAPID_CLKPER_SHIFT (32ul) /* Bits 32-63: Main Counter Tick Period in ps */ +# define HPET_GCAPID_CLKPER_MASK (0x7ffffffful << HPET_GCAPID_CLKPER_SHIFT) + +/* General Configuration Register */ + +#define HPET_GCONF_LEGERT (1 << 0) /* Bit 0: LegacyReplacement Route */ +#define HPET_GCONF_ENABLE (1 << 1) /* Bit 1: Overall Enable */ + +/* General Interrupt Status Register */ + +#define HPET_GISR_TINT(n) (1 << (n)) /* Timer n Interrupt Active */ + +/* Timer N Configuration and Capabilities Register */ + + /* Bit 0: Reserved */ +#define HPET_TCONF_INTTYPE (1 << 1) /* Bit 1: Timer n Interrupt Type (0: edge, 1: level) */ +#define HPET_TCONF_INTEN (1 << 2) /* Bit 2: Timer n Interrupt Enable */ +#define HPET_TCONF_TYPE (1 << 3) /* Bit 3: Timer n Type (0: non-periodic, 1: periodic) */ +#define HPET_TCONF_PERCAP (1 << 4) /* Bit 4: Timer n Periodic Interrupt Capable */ +#define HPET_TCONF_SIZECAP (1 << 5) /* Bit 5: Timer n Size */ +#define HPET_TCONF_VALSET (1 << 6) /* Bit 6: Timer n Value Set */ + /* Bit 7: Reserved */ +#define HPET_TCONF_32MODE (1 << 8) /* Bit 8: Timer n 32-bit mode */ +#define HPET_TCONF_INTROUTE_SHIFT (9) /* Bits 9-13: Timer n Interrupt Route */ +# define HPET_TCONF_INTROUTE_MASK (0x7f << HPET_TCONF_INTROUTE_SHIFT) +# define HPET_TCONF_INTROUTE(n) (((n) << HPET_TCONF_INTROUTE_SHIFT) & HPET_TCONF_INTROUTE_MASK) +#define HPET_TCONF_FSBEN (1 << 14) /* Bit 14: Timer n FSB Interrupt Enable */ +#define HPET_TCONF_FSBCAP (1 << 15) /* Bit 15: Timer n FSB Interrupt Delivery */ + /* Bits 16-31: Reserved */ +#define HPET_TCONF_ROUTECAP_SHIFT (32) /* Bits 32-63: Timer n Interrupt Routing Capability */ +# define HPET_TCONF_ROUTECAP_MASK (0x7ffffffful << HPET_TCONF_ROUTECAP_SHIFT) + +/* Timer N FSB Interrupt Route Register */ + +#define HPET_TFSB_INT_VAL_SHIFT (0) +#define HPET_TFSB_INT_VAL_MASK (0x00000000ffffffff) +#define HPET_TFSB_INT_ADDR_SHIFT (31) +#define HPET_TFSB_INT_ADDR_MASK (0xffffffff00000000) + +/* HPET register space */ + +#define HPET_REGION_SIZE (1024) + +#endif /* __ARCH_X86_64_INCLUDE_HPET_H */ diff --git a/arch/x86_64/include/intel64/irq.h b/arch/x86_64/include/intel64/irq.h index 02254b1c09..5afad8d322 100644 --- a/arch/x86_64/include/intel64/irq.h +++ b/arch/x86_64/include/intel64/irq.h @@ -97,6 +97,11 @@ #define IRQ_ERROR 51 /* APIC Error */ #define IRQ_SPURIOUS 0xff /* Spurious Interrupts */ +/* Use legacy routing for HPET */ + +#define HPET0_IRQ IRQ2 +#define HPET1_IRQ IRQ8 + #define NR_IRQS 48 /* Common register save structure created by up_saveusercontext() and by diff --git a/arch/x86_64/src/intel64/CMakeLists.txt b/arch/x86_64/src/intel64/CMakeLists.txt index e1738080d3..ae66feace0 100644 --- a/arch/x86_64/src/intel64/CMakeLists.txt +++ b/arch/x86_64/src/intel64/CMakeLists.txt @@ -59,6 +59,14 @@ else() list(APPEND SRCS intel64_timerisr.c) endif() +if(CONFIG_INTEL64_HPET) + list(APPEND SRCS intel64_hpet.c) +endif() + +if(CONFIG_INTEL64_ONESHOT) + list(APPEND SRCS intel64_oneshot.c intel64_oneshot_lower.c) +endif() + if(CONFIG_ARCH_FPU) list(APPEND SRCS intel64_fpucmp.c) endif() diff --git a/arch/x86_64/src/intel64/Kconfig b/arch/x86_64/src/intel64/Kconfig index 6500104700..1da67265c6 100644 --- a/arch/x86_64/src/intel64/Kconfig +++ b/arch/x86_64/src/intel64/Kconfig @@ -46,6 +46,63 @@ config ARCH_INTEL64_APIC_FREQ_KHZ endif +config INTEL64_HPET + bool "HPET timer support" + default n + ---help--- + Select to enable the HPET timer support + +if INTEL64_HPET + +config INTEL64_HPET_BASE + hex "HPET timer base address" + default 0xfed00000 + ---help--- + Configure base address for HPET. In the future, we can get this address from + the ACPI table if ACPI support is enabled. + +config INTEL64_HPET_CHANNELS + int "HPET timer supported channels" + default 2 + range 1 3 + ---help--- + Configure the number of supported HPET channles (called HPET timers + in HPET spec). We use HPET legacy replacement for interrupts which + means that Timer 0 and Timer 1 interrupts are hardcoded, but Timer 2 + needs additional attention to configure interrupt routing. + Timer 2 will most likely not work with QEMU. + +config INTEL64_HPET_MIN_DELAY + int "HPET minimum supported delay in micro-seconds" + default 1000 + ---help--- + HPET use free runnin up-counter and a comparators which generate events + only on a equal event. This can results in event miss if we set too small + delay. In that case we must set a minimum value for delay that always work. + For the QEMU target this value should be as high as 1000, but for real + hardware it may go much lower (for i7-1255U CPU it works with values <10). + +endif # INTEL64_HPET + +config INTEL64_ONESHOT + bool "Oneshot timer support" + select INTEL64_HPET + default n + ---help--- + Select to enable the oneshot timer support + +if INTEL64_ONESHOT + +config INTEL64_ONESHOT_MAXTIMERS + int "Maximum number of oneshot timers" + default 1 + range 1 3 + ---help--- + Determines the maximum number of oneshot timers that can be + supported. + +endif + config ARCH_INTEL64_HAVE_XSAVE bool "XSAVE support" default y diff --git a/arch/x86_64/src/intel64/Make.defs b/arch/x86_64/src/intel64/Make.defs index 58335336d9..09c93c851f 100644 --- a/arch/x86_64/src/intel64/Make.defs +++ b/arch/x86_64/src/intel64/Make.defs @@ -60,6 +60,14 @@ ifeq ($(CONFIG_SCHED_TICKLESS),y) CHIP_CSRCS += intel64_tickless.c endif +ifeq ($(CONFIG_INTEL64_HPET),y) +CHIP_CSRCS += intel64_hpet.c +endif + +ifeq ($(CONFIG_INTEL64_ONESHOT),y) +CHIP_CSRCS += intel64_oneshot.c intel64_oneshot_lower.c +endif + ifeq ($(CONFIG_ARCH_FPU),y) CHIP_CSRCS += intel64_fpucmp.c endif \ No newline at end of file diff --git a/arch/x86_64/src/intel64/intel64_hpet.c b/arch/x86_64/src/intel64/intel64_hpet.c new file mode 100644 index 0000000000..c906fa3b86 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_hpet.c @@ -0,0 +1,496 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_hpet.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 "intel64_hpet.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* HPET timer channel */ + +struct intel64_hpet_chan_s +{ + uint8_t irq; +}; + +/* HPET timer driver */ + +struct intel64_hpet_s +{ + struct intel64_tim_ops_s *ops; + uint64_t base; + uint32_t clk_per_fs; + uint8_t timers; + bool initialized; + struct intel64_hpet_chan_s chans[CONFIG_INTEL64_HPET_CHANNELS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helpers */ + +static void intel64_hpet_putreg(struct intel64_hpet_s *hpet, uint32_t offset, + uint64_t value); +static uint64_t intel64_hpet_getreg(struct intel64_hpet_s *hpet, + uint32_t offset); + +/* Ops */ + +static void intel64_hpet_enable(struct intel64_tim_dev_s *dev, bool en); +static void intel64_hpet_cmpset(struct intel64_tim_dev_s *dev, uint8_t timer, + uint64_t cmp); +static uint64_t intel64_hpet_cmpget(struct intel64_tim_dev_s *dev, + uint8_t timer); +static uint64_t intel64_hpet_cntget(struct intel64_tim_dev_s *dev); +static uint64_t intel64_hpet_intget(struct intel64_tim_dev_s *dev, + uint8_t timer); +static void intel64_hpet_intack(struct intel64_tim_dev_s *dev, + uint8_t timer); +static void intel64_hpet_cntset(struct intel64_tim_dev_s *dev, + uint64_t cntr); +static uint32_t intel64_hpet_perget(struct intel64_tim_dev_s *dev); +static int intel64_hpet_setisr(struct intel64_tim_dev_s *dev, uint8_t timer, + xcpt_t handler, void *arg, bool periodic); +static void intel64_hpet_enint(struct intel64_tim_dev_s *dev, uint8_t tim); +static void intel64_hpet_disint(struct intel64_tim_dev_s *dev, uint8_t tim); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* HPET ops */ + +static struct intel64_tim_ops_s g_intel64_hpet_ops = +{ + .enable = intel64_hpet_enable, + .getperiod = intel64_hpet_perget, + .getcounter = intel64_hpet_cntget, + .setcounter = intel64_hpet_cntset, + .setcompare = intel64_hpet_cmpset, + .getcompare = intel64_hpet_cmpget, + .getint = intel64_hpet_intget, + .ackint = intel64_hpet_intack, + .setisr = intel64_hpet_setisr, + .enableint = intel64_hpet_enint, + .disableint = intel64_hpet_disint +}; + +/* HPET driver instance */ + +static struct intel64_hpet_s g_intel64_hpet = +{ + .ops = &g_intel64_hpet_ops, + .initialized = false, + .chans = + { + /* Channel 0 (Timer 0) */ + + { + .irq = HPET0_IRQ, + }, + +#if CONFIG_INTEL64_HPET_CHANNELS > 1 + /* Channel 1 (Timer 1) */ + + { + .irq = HPET1_IRQ, + }, +#endif + +#if CONFIG_INTEL64_HPET_CHANNELS > 2 + /* Channel 2 (Timer 2) */ + + { + .irq = HPET2_IRQ + }, +#endif + } +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_hpet_putreg + * + * Description: + * Put a 64-bit register value by offset + * + ****************************************************************************/ + +static void intel64_hpet_putreg(struct intel64_hpet_s *hpet, uint32_t offset, + uint64_t value) +{ + return mmio_write64((void *)(hpet->base + offset), value); +} + +/**************************************************************************** + * Name: intel64_hpet_getreg + * + * Description: + * Get a 64-bit register value by offset + * + ****************************************************************************/ + +static uint64_t intel64_hpet_getreg(struct intel64_hpet_s *hpet, + uint32_t offset) +{ + return mmio_read64((void *)(hpet->base + offset)); +} + +/**************************************************************************** + * Name: intel64_hpet_enable + * + * Description: + * Allow main counter to run and allow timer interrupts + * + ****************************************************************************/ + +static void intel64_hpet_enable(struct intel64_tim_dev_s *dev, bool en) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + uint64_t regval = 0; + + regval = intel64_hpet_getreg(hpet, HPET_GCONF_OFFSET); + + if (en) + { + regval |= HPET_GCONF_ENABLE; + } + else + { + regval &= ~HPET_GCONF_ENABLE; + } + + intel64_hpet_putreg(hpet, HPET_GCONF_OFFSET, regval); +} + +/**************************************************************************** + * Name: intel64_hpet_cmpset + * + * Description: + * Set a compare register to a given value. + * + ****************************************************************************/ + +static void intel64_hpet_cmpset(struct intel64_tim_dev_s *dev, uint8_t timer, + uint64_t cmp) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + DEBUGASSERT(timer < hpet->timers); + intel64_hpet_putreg(hpet, HPET_TCOMP_OFFSET(timer), cmp); +} + +/**************************************************************************** + * Name: intel64_hpet_cmpget + * + * Description: + * Get a compare register to a given value. + * + ****************************************************************************/ + +static uint64_t intel64_hpet_cmpget(struct intel64_tim_dev_s *dev, + uint8_t timer) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + DEBUGASSERT(timer < hpet->timers); + return intel64_hpet_getreg(hpet, HPET_TCOMP_OFFSET(timer)); +} + +/**************************************************************************** + * Name: intel64_hpet_intget + * + * Description: + * Get a interrupt status register. + * + ****************************************************************************/ + +static uint64_t intel64_hpet_intget(struct intel64_tim_dev_s *dev, + uint8_t timer) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + return (intel64_hpet_getreg(hpet, HPET_GISR_OFFSET) & + HPET_GISR_TINT(timer)); +} + +/**************************************************************************** + * Name: intel64_hpet_intack + * + * Description: + * ACK interrupt. + * + ****************************************************************************/ + +static void intel64_hpet_intack(struct intel64_tim_dev_s *dev, + uint8_t timer) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + intel64_hpet_putreg(hpet, HPET_GISR_OFFSET, HPET_GISR_TINT(timer)); +} + +/**************************************************************************** + * Name: intel64_hpet_cntget + * + * Description: + * Get the main counter. + * + ****************************************************************************/ + +static uint64_t intel64_hpet_cntget(struct intel64_tim_dev_s *dev) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + return intel64_hpet_getreg(hpet, HPET_MCNTR_OFFSET); +} + +/**************************************************************************** + * Name: intel64_hpet_cntset + * + * Description: + * Set the main counter. + * + ****************************************************************************/ + +static void intel64_hpet_cntset(struct intel64_tim_dev_s *dev, + uint64_t cntr) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + return intel64_hpet_putreg(hpet, HPET_MCNTR_OFFSET, cntr); +} + +/**************************************************************************** + * Name: intel64_hpet_perget + * + * Description: + * Get the main counter period in femtosecounds (1e-15 sec). + * + ****************************************************************************/ + +static uint32_t intel64_hpet_perget(struct intel64_tim_dev_s *dev) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + return hpet->clk_per_fs; +} + +/**************************************************************************** + * Name: intel64_hpet_setisr + * + * Description: + * Configure interrupt handler for a given timer + * + ****************************************************************************/ + +static int intel64_hpet_setisr(struct intel64_tim_dev_s *dev, uint8_t timer, + xcpt_t handler, void *arg, bool periodic) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + uint64_t regval = 0; + uint8_t irq = hpet->chans[timer].irq; + + DEBUGASSERT(timer < hpet->timers); + + regval = intel64_hpet_getreg(hpet, HPET_TCONF_OFFSET(timer)); + + if (periodic) + { + if ((regval & HPET_TCONF_PERCAP) == 0) + { + tmrerr("Periodic not supported"); + return -EPERM; + } + + regval |= HPET_TCONF_PERCAP; + } + else + { + regval &= ~HPET_TCONF_PERCAP; + } + + /* Route interrupts */ + + regval |= HPET_TCONF_INTROUTE(irq - IRQ0); + + /* Set level triggered mode. + * + * Edge triggered mode seems to work well on QEMU, but for real hardware, + * unwanted interrupt is generated when we enable timer interrupts. + */ + + regval |= HPET_TCONF_INTTYPE; + + /* Set 64-bit mode */ + + regval &= ~HPET_TCONF_32MODE; + + /* Write Timer configuration */ + + intel64_hpet_putreg(hpet, HPET_TCONF_OFFSET(timer), regval); + + if (handler == NULL) + { + /* Disable interrupt */ + + irq_attach(irq, handler, arg); + up_disable_irq(irq); + } + else + { + /* Set callback and enable interrupt */ + + irq_attach(irq, handler, arg); + up_enable_irq(irq); + } + + return OK; +} + +/**************************************************************************** + * Name: intel64_hpet_enint + * + * Description: + * Enable interrupt + * + ****************************************************************************/ + +static void intel64_hpet_enint(struct intel64_tim_dev_s *dev, uint8_t tim) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + uint64_t regval = 0; + + DEBUGASSERT(tim < hpet->timers); + + regval = intel64_hpet_getreg(hpet, HPET_TCONF_OFFSET(tim)); + regval |= HPET_TCONF_INTEN; + intel64_hpet_putreg(hpet, HPET_TCONF_OFFSET(tim), regval); +} + +/**************************************************************************** + * Name: intel64_hpet_disint + * + * Description: + * Disable interrupt + * + ****************************************************************************/ + +static void intel64_hpet_disint(struct intel64_tim_dev_s *dev, uint8_t tim) +{ + struct intel64_hpet_s *hpet = (struct intel64_hpet_s *)dev; + uint64_t regval = 0; + + DEBUGASSERT(tim < hpet->timers); + + regval = intel64_hpet_getreg(hpet, HPET_TCONF_OFFSET(tim)); + regval &= ~HPET_TCONF_INTEN; + intel64_hpet_putreg(hpet, HPET_TCONF_OFFSET(tim), regval); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_hpet_init + * + * Description: + * Initialize HPET timer with a given base address + * + ****************************************************************************/ + +struct intel64_tim_dev_s *intel64_hpet_init(uint64_t base) +{ + struct intel64_hpet_s *hpet = &g_intel64_hpet; + uint64_t regval = 0; + + if (hpet->initialized == false) + { + /* Store HPET base */ + + hpet->base = base; + + /* Map HPET region */ + + up_map_region((void *)hpet->base, HPET_REGION_SIZE, + (X86_PAGE_PRESENT | X86_PAGE_WR | X86_PAGE_NOCACHE)); + + /* Get capabilities */ + + regval = intel64_hpet_getreg(hpet, HPET_GCAPID_OFFSET); + hpet->clk_per_fs = ((regval & HPET_GCAPID_CLKPER_MASK) >> + HPET_GCAPID_CLKPER_SHIFT); + hpet->timers = ((regval & HPET_GCAPID_NUMTIM_MASK) >> + HPET_GCAPID_NUMTIM_SHIFT); + + hpet->timers = (hpet->timers > CONFIG_INTEL64_HPET_CHANNELS ? + CONFIG_INTEL64_HPET_CHANNELS : hpet->timers); + + /* Connect ops */ + + hpet->ops = &g_intel64_hpet_ops; + + if (regval & HPET_GCAPID_LEGROUTE) + { + /* Configure legacy mode. + * + * There is no way to disable PIT interrupts (?) other than enable + * legacy mode for HPET. Otherwise unwanted PIT interupts will + * interfere with HPET interrupts, making them useless. + */ + + intel64_hpet_putreg(hpet, HPET_GCONF_OFFSET, HPET_GCONF_LEGERT); + } + else + { + /* Not supported */ + + ASSERT(0); + } + + /* Enable HPET */ + + intel64_hpet_enable((struct intel64_tim_dev_s *)hpet, true); + + tmrinfo("clk_per_fs = %" PRId32 " timers = %d\n", + hpet->clk_per_fs, hpet->timers); + + /* Initialization done */ + + hpet->initialized = true; + } + + return (struct intel64_tim_dev_s *)hpet; +} diff --git a/arch/x86_64/src/intel64/intel64_hpet.h b/arch/x86_64/src/intel64/intel64_hpet.h new file mode 100644 index 0000000000..9eddb865e4 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_hpet.h @@ -0,0 +1,118 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_hpet.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_X86_64_SRC_INTEL64_INTEL64_HPET_H +#define __ARCH_X86_64_SRC_INTEL64_INTEL64_HPET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Helpers ******************************************************************/ + +#define INTEL64_TIM_ENABLE(d, e) ((d)->ops->enable(d, e)) +#define INTEL64_TIM_GETPERIOD(d) ((d)->ops->getperiod(d)) +#define INTEL64_TIM_GETCOUNTER(d) ((d)->ops->getcounter(d)) +#define INTEL64_TIM_SETCOUNTER(d, c) ((d)->ops->setcounter(d, c)) +#define INTEL64_TIM_SETCOMPARE(d, t, c) ((d)->ops->setcompare(d, t, c)) +#define INTEL64_TIM_GETCOMPARE(d, t) ((d)->ops->getcompare(d, t)) +#define INTEL64_TIM_GETINT(d, t) ((d)->ops->getint(d, t)) +#define INTEL64_TIM_ACKINT(d, t) ((d)->ops->ackint(d, t)) +#define INTEL64_TIM_SETISR(d, t, h, a, p) ((d)->ops->setisr(d, t, h, a, p)) +#define INTEL64_TIM_ENABLEINT(d, t) ((d)->ops->enableint(d, t)) +#define INTEL64_TIM_DISABLEINT(d, t) ((d)->ops->disableint(d, t)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* TIM Device Structure */ + +struct intel64_tim_ops_s; +struct intel64_tim_dev_s +{ + struct intel64_tim_ops_s *ops; +}; + +/* TIM Operations */ + +struct intel64_tim_ops_s +{ + void (*enable)(struct intel64_tim_dev_s *dev, bool en); + uint32_t (*getperiod)(struct intel64_tim_dev_s *dev); + uint64_t (*getcounter)(struct intel64_tim_dev_s *dev); + void (*setcounter)(struct intel64_tim_dev_s *dev, uint64_t count); + void (*setcompare)(struct intel64_tim_dev_s *dev, uint8_t timer, + uint64_t compare); + uint64_t (*getcompare)(struct intel64_tim_dev_s *dev, uint8_t timer); + uint64_t (*getint)(struct intel64_tim_dev_s *dev, uint8_t timer); + void (*ackint)(struct intel64_tim_dev_s *dev, uint8_t timer); + int (*setisr)(struct intel64_tim_dev_s *dev, uint8_t timer, + xcpt_t handler, void * arg, bool periodic); + void (*enableint)(struct intel64_tim_dev_s *dev, uint8_t tim); + void (*disableint)(struct intel64_tim_dev_s *dev, uint8_t tim); +}; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_hpet_init + * + * Description: + * Initialize HPET timer with a given base address + * + ****************************************************************************/ + +struct intel64_tim_dev_s *intel64_hpet_init(uint64_t base); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_X86_64_SRC_INTEL64_INTEL64_HPET_H */ diff --git a/arch/x86_64/src/intel64/intel64_oneshot.c b/arch/x86_64/src/intel64/intel64_oneshot.c new file mode 100644 index 0000000000..2822c4100c --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_oneshot.c @@ -0,0 +1,425 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_oneshot.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 "intel64_hpet.h" +#include "intel64_oneshot.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int intel64_oneshot_handler(int irg_num, void * context, void *arg); +static int intel64_allocate_handler(struct intel64_oneshot_s *oneshot); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct intel64_oneshot_s *g_oneshot[CONFIG_INTEL64_ONESHOT_MAXTIMERS]; +static spinlock_t g_oneshot_spin; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_oneshot_handler + * + * Description: + * Common timer interrupt callback. When any oneshot timer interrupt + * expires, this function will be called. It will forward the call to + * the next level up. + * + * Input Parameters: + * oneshot - The state associated with the expired timer + * + * Returned Value: + * Always returns OK + * + ****************************************************************************/ + +static int intel64_oneshot_handler(int irg_num, void * context, void *arg) +{ + struct intel64_oneshot_s *oneshot = (struct intel64_oneshot_s *)arg; + void *oneshot_arg; + oneshot_handler_t oneshot_handler; + + tmrinfo("Expired...\n"); + DEBUGASSERT(oneshot != NULL && oneshot->handler); + + if (INTEL64_TIM_GETINT(oneshot->tch, oneshot->chan)) + { + /* Disable any further interrupts. */ + + INTEL64_TIM_DISABLEINT(oneshot->tch, oneshot->chan); + INTEL64_TIM_SETISR(oneshot->tch, oneshot->chan, NULL, NULL, false); + INTEL64_TIM_ACKINT(oneshot->tch, oneshot->chan); + + /* The timer is no longer running */ + + oneshot->running = false; + + /* Forward the event, clearing out any vestiges */ + + oneshot_handler = (oneshot_handler_t)oneshot->handler; + oneshot->handler = NULL; + oneshot_arg = (void *)oneshot->arg; + oneshot->arg = NULL; + + oneshot_handler(oneshot_arg); + } + + return OK; +} + +/**************************************************************************** + * Name: intel64_allocate_handler + * + * Description: + * Allocate a timer callback handler for the oneshot instance. + * + * Input Parameters: + * oneshot - The state instance the new oneshot timer + * + * Returned Value: + * Returns zero (OK) on success. This can only fail if the number of + * timers exceeds CONFIG_INTEL64_ONESHOT_MAXTIMERS. + * + ****************************************************************************/ + +static int intel64_allocate_handler(struct intel64_oneshot_s *oneshot) +{ + int ret = -EBUSY; + int i; + + /* Search for an unused handler */ + + for (i = 0; i < CONFIG_INTEL64_ONESHOT_MAXTIMERS; i++) + { + /* Is this handler available? */ + + if (g_oneshot[i] == NULL) + { + /* Yes... assign it to this oneshot */ + + g_oneshot[i] = oneshot; + oneshot->cbndx = i; + ret = OK; + break; + } + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int intel64_oneshot_initialize(struct intel64_oneshot_s *oneshot, int chan, + uint16_t resolution) +{ + /* HPET resolution can't be changed, but it is always in terms of ns */ + + UNUSED(resolution); + + /* Get timer */ + + oneshot->tch = intel64_hpet_init(CONFIG_INTEL64_HPET_BASE); + if (oneshot->tch == NULL) + { + tmrerr("ERROR: Failed to allocate HPET timer %d\n", chan); + return -EBUSY; + } + + /* Get the timer period */ + + oneshot->period = INTEL64_TIM_GETPERIOD(oneshot->tch); + oneshot->frequency = 1e15 / oneshot->period; + + /* Initialize the remaining fields in the state structure. */ + + oneshot->chan = chan; + oneshot->running = false; + oneshot->handler = NULL; + oneshot->arg = NULL; + + /* Assign a callback handler to the oneshot */ + + return intel64_allocate_handler(oneshot); +} + +/**************************************************************************** + * Name: intel64_oneshot_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds) + * + ****************************************************************************/ + +int intel64_oneshot_max_delay(struct intel64_oneshot_s *oneshot, + uint64_t *usec) +{ + DEBUGASSERT(oneshot != NULL && usec != NULL); + + *usec = ((uint64_t)(UINT32_MAX / oneshot->frequency) * + (uint64_t)USEC_PER_SEC); + return OK; +} + +/**************************************************************************** + * Name: intel64_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * intel64_oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int intel64_oneshot_start(struct intel64_oneshot_s *oneshot, + oneshot_handler_t handler, void *arg, + const struct timespec *ts) +{ + uint64_t usec = 0; + uint64_t compare = 0; + irqstate_t flags; + + tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n", + handler, arg, (unsigned long)ts->tv_sec, + (unsigned long)ts->tv_nsec); + + DEBUGASSERT(oneshot && handler && ts); + DEBUGASSERT(oneshot->tch); + + /* Was the oneshot already running? */ + + flags = spin_lock_irqsave(&g_oneshot_spin); + if (oneshot->running) + { + /* Yes.. then cancel it */ + + tmrinfo("Already running... cancelling\n"); + intel64_oneshot_cancel(oneshot, NULL); + } + + /* Save the new handler and its argument */ + + oneshot->handler = handler; + oneshot->arg = arg; + + /* Express the delay in microseconds */ + + usec = (uint64_t)ts->tv_sec * USEC_PER_SEC + + (uint64_t)(ts->tv_nsec / NSEC_PER_USEC); + + /* HPET use free runnin up-counter and a comparators which generate events + * only on a equal event. This can results in event miss if we set too + * small delay. In that case we just set a minimum value for delay that + * seem to work. + */ + + if (usec < CONFIG_INTEL64_HPET_MIN_DELAY) + { + usec = CONFIG_INTEL64_HPET_MIN_DELAY; + } + + /* Get the timer counter frequency and determine the number of counts need + * to achieve the requested delay. + * + * frequency = ticks / second + * ticks = seconds * frequency + * = (usecs * frequency) / USEC_PER_SEC; + */ + + compare = (usec * (uint64_t)oneshot->frequency) / USEC_PER_SEC; + + /* Set up to receive the callback when the interrupt occurs */ + + INTEL64_TIM_SETISR(oneshot->tch, oneshot->chan, intel64_oneshot_handler, + oneshot, false); + + /* Set comparator ahed of the current counter */ + + compare += INTEL64_TIM_GETCOUNTER(oneshot->tch); + INTEL64_TIM_SETCOMPARE(oneshot->tch, oneshot->chan, compare); + + /* Enable interrupts. We should get the callback when the interrupt + * occurs. + */ + + INTEL64_TIM_ENABLEINT(oneshot->tch, oneshot->chan); + + oneshot->running = true; + spin_unlock_irqrestore(&g_oneshot_spin, flags); + return OK; +} + +/**************************************************************************** + * Name: intel64_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * intel64_oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. A time of zero is returned if the timer is + * not running. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + ****************************************************************************/ + +int intel64_oneshot_cancel(struct intel64_oneshot_s *oneshot, + struct timespec *ts) +{ + irqstate_t flags; + uint64_t usec; + uint64_t sec; + uint64_t nsec; + uint64_t counter; + uint64_t compare; + + /* Was the timer running? */ + + flags = spin_lock_irqsave(&g_oneshot_spin); + if (!oneshot->running) + { + /* No.. Just return zero timer remaining and successful cancellation. + * This function may execute at a high rate with no timer running + * (as when pre-emption is enabled and disabled). + */ + + ts->tv_sec = 0; + ts->tv_nsec = 0; + spin_unlock_irqrestore(&g_oneshot_spin, flags); + return OK; + } + + /* Yes.. Get the timer counter and compare registers and stop the + * counter. + */ + + tmrinfo("Cancelling...\n"); + + counter = INTEL64_TIM_GETCOUNTER(oneshot->tch); + compare = INTEL64_TIM_GETCOMPARE(oneshot->tch, oneshot->chan); + + /* Now we can disable the interrupt and stop the timer. */ + + INTEL64_TIM_DISABLEINT(oneshot->tch, oneshot->chan); + INTEL64_TIM_SETISR(oneshot->tch, oneshot->chan, NULL, NULL, false); + + oneshot->running = false; + oneshot->handler = NULL; + oneshot->arg = NULL; + spin_unlock_irqrestore(&g_oneshot_spin, flags); + + /* Did the caller provide us with a location to return the time + * remaining? + */ + + if (ts) + { + /* Yes.. then calculate and return the time remaining on the + * oneshot timer. + */ + + /* The total time remaining is the difference. Convert that + * to units of microseconds. + * + * frequency = ticks / second + * seconds = ticks * frequency + * usecs = (ticks * USEC_PER_SEC) / frequency; + */ + + usec = ((compare - counter) * USEC_PER_SEC) / + oneshot->frequency; + + /* Return the time remaining in the correct form */ + + sec = usec / USEC_PER_SEC; + nsec = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; + + ts->tv_sec = (time_t)sec; + ts->tv_nsec = (unsigned long)nsec; + + tmrinfo("remaining (%lu, %lu)\n", + (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + } + + return OK; +} diff --git a/arch/x86_64/src/intel64/intel64_oneshot.h b/arch/x86_64/src/intel64/intel64_oneshot.h new file mode 100644 index 0000000000..fd9a024f66 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_oneshot.h @@ -0,0 +1,177 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_oneshot.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_X86_64_SRC_INTEL64_INTEL64_ONESHOT_H +#define __ARCH_X86_64_SRC_INTEL64_INTEL64_ONESHOT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This describes the callback function that will be invoked when the oneshot + * timer expires. The oneshot fires, the client will receive: + * + * arg - The opaque argument provided when the interrupt was registered + */ + +typedef void (*oneshot_handler_t)(void *arg); + +/* The oneshot client must allocate an instance of this structure and called + * intel64_oneshot_initialize() before using the oneshot facilities. The + * client should not access the contents of this structure directly since + * the contents are subject to change. + */ + +struct intel64_oneshot_s +{ + uint8_t chan; + uint8_t cbndx; + volatile bool running; + struct intel64_tim_dev_s *tch; + volatile oneshot_handler_t handler; + volatile void *arg; + uint32_t frequency; + uint32_t period; +}; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int intel64_oneshot_initialize(struct intel64_oneshot_s *oneshot, int chan, + uint16_t resolution); + +/**************************************************************************** + * Name: intel64_oneshot_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds) + * + ****************************************************************************/ + +int intel64_oneshot_max_delay(struct intel64_oneshot_s *oneshot, + uint64_t *usec); + +/**************************************************************************** + * Name: intel64_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * intel64_oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int intel64_oneshot_start(struct intel64_oneshot_s *oneshot, + oneshot_handler_t handler, void *arg, + const struct timespec *ts); + +/**************************************************************************** + * Name: intel64_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * intel64_oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. A time of zero is returned if the timer is + * not running. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + ****************************************************************************/ + +int intel64_oneshot_cancel(struct intel64_oneshot_s *oneshot, + struct timespec *ts); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_X86_64_SRC_INTEL64_INTEL64_ONESHOT_H */ diff --git a/arch/x86_64/src/intel64/intel64_oneshot_lower.c b/arch/x86_64/src/intel64/intel64_oneshot_lower.c new file mode 100644 index 0000000000..3e6f324b51 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_oneshot_lower.c @@ -0,0 +1,324 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_oneshot_lower.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 "intel64_oneshot.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the state of the oneshot timer lower-half driver + */ + +struct intel64_oneshot_lowerhalf_s +{ + struct oneshot_lowerhalf_s lh; + struct intel64_oneshot_s oneshot; + oneshot_callback_t callback; + void *arg; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void intel64_oneshot_handler(void *arg); + +static int intel64_max_delay(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); +static int intel64_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + const struct timespec *ts); +static int intel64_cancel(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Lower half operations */ + +static const struct oneshot_operations_s g_oneshot_ops = +{ + .max_delay = intel64_max_delay, + .start = intel64_start, + .cancel = intel64_cancel, +}; + +static spinlock_t g_oneshotlow_spin; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_oneshot_handler + * + * Description: + * Timer expiration handler + * + * Input Parameters: + * arg - Should be the same argument provided when intel64_oneshot_start() + * was called. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void intel64_oneshot_handler(void *arg) +{ + struct intel64_oneshot_lowerhalf_s *priv = arg; + oneshot_callback_t callback; + void *cbarg; + + DEBUGASSERT(priv != NULL); + + /* Perhaps the callback was nullified in a race condition with + * intel64_cancel? + */ + + if (priv->callback) + { + /* Sample and nullify BEFORE executing callback (in case the callback + * restarts the oneshot). + */ + + callback = priv->callback; + cbarg = priv->arg; + priv->callback = NULL; + priv->arg = NULL; + + /* Then perform the callback */ + + callback(&priv->lh, cbarg); + } +} + +/**************************************************************************** + * Name: intel64_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds) + * + * Input Parameters: + * lower An instance of the lower-half oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ts The location in which to return the maximum delay. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +static int intel64_max_delay(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + struct intel64_oneshot_lowerhalf_s *priv = + (struct intel64_oneshot_lowerhalf_s *)lower; + uint64_t usecs; + uint64_t sec; + int ret; + + DEBUGASSERT(priv != NULL && ts != NULL); + ret = intel64_oneshot_max_delay(&priv->oneshot, &usecs); + if (ret >= 0) + { + sec = usecs / 1000000; + usecs -= 1000000 * sec; + + ts->tv_sec = (time_t)sec; + ts->tv_nsec = (long)(usecs * 1000); + } + + return ret; +} + +/**************************************************************************** + * Name: intel64_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * lower An instance of the lower-half oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +static int intel64_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + const struct timespec *ts) +{ + struct intel64_oneshot_lowerhalf_s *priv = + (struct intel64_oneshot_lowerhalf_s *)lower; + irqstate_t flags; + int ret; + + DEBUGASSERT(priv != NULL && callback != NULL && ts != NULL); + + /* Save the callback information and start the timer */ + + flags = spin_lock_irqsave(&g_oneshotlow_spin); + priv->callback = callback; + priv->arg = arg; + ret = intel64_oneshot_start(&priv->oneshot, + intel64_oneshot_handler, + priv, ts); + spin_unlock_irqrestore(&g_oneshotlow_spin, flags); + + if (ret < 0) + { + tmrerr("ERROR: intel64_oneshot_start failed\n"); + } + + return ret; +} + +/**************************************************************************** + * Name: intel64_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * lower Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. A time of zero is returned if the timer is + * not running. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + ****************************************************************************/ + +static int intel64_cancel(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + struct intel64_oneshot_lowerhalf_s *priv = + (struct intel64_oneshot_lowerhalf_s *)lower; + irqstate_t flags; + int ret; + + DEBUGASSERT(priv != NULL); + + /* Cancel the timer */ + + flags = spin_lock_irqsave(&g_oneshotlow_spin); + ret = intel64_oneshot_cancel(&priv->oneshot, ts); + priv->callback = NULL; + priv->arg = NULL; + spin_unlock_irqrestore(&g_oneshotlow_spin, flags); + + if (ret < 0) + { + tmrerr("ERROR: intel64_oneshot_cancel failed\n"); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: oneshot_initialize + * + * Description: + * Initialize the oneshot timer and return a oneshot lower half driver + * instance. + * + * Input Parameters: + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * On success, a non-NULL instance of the oneshot lower-half driver is + * returned. NULL is return on any failure. + * + ****************************************************************************/ + +struct oneshot_lowerhalf_s *oneshot_initialize(int chan, uint16_t resolution) +{ + struct intel64_oneshot_lowerhalf_s *priv; + int ret; + + /* Allocate an instance of the lower half driver */ + + priv = (struct intel64_oneshot_lowerhalf_s *) + kmm_zalloc(sizeof(struct intel64_oneshot_lowerhalf_s)); + if (priv == NULL) + { + tmrerr("ERROR: Failed to initialized state structure\n"); + return NULL; + } + + /* Initialize the lower-half driver structure */ + + priv->lh.ops = &g_oneshot_ops; + + /* Initialize the contained INTEL64 oneshot timer */ + + ret = intel64_oneshot_initialize(&priv->oneshot, chan, resolution); + if (ret < 0) + { + tmrerr("ERROR: intel64_oneshot_initialize failed: %d\n", ret); + kmm_free(priv); + return NULL; + } + + return &priv->lh; +} diff --git a/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c b/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c index 7cd5d55137..8a3d0df35c 100644 --- a/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c +++ b/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c @@ -31,7 +31,10 @@ #include #include -#include + +#ifdef CONFIG_ONESHOT +# include +#endif #include "qemu_intel64.h" @@ -45,6 +48,10 @@ int qemu_bringup(void) { +#ifdef CONFIG_ONESHOT + struct oneshot_lowerhalf_s *os = NULL; +#endif + int ret = OK; #ifdef CONFIG_FS_PROCFS @@ -57,5 +64,13 @@ int qemu_bringup(void) } #endif +#ifdef CONFIG_ONESHOT + os = oneshot_initialize(0, 10); + if (os) + { + oneshot_register("/dev/oneshot", os); + } +#endif + return ret; }