summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/leon3/startup/cpucounter.c
blob: b76d404ac37788c8fae9424234ed74460b974f58 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
 *
 *  embedded brains GmbH
 *  Dornierstr. 4
 *  82178 Puchheim
 *  Germany
 *  <rtems@embedded-brains.de>
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#include <leon.h>

#include <rtems/counter.h>

static volatile struct gptimer_regs *adev_to_gpt(struct ambapp_dev *adev)
{
  return (volatile struct gptimer_regs *) DEV_TO_APB(adev)->start;
}

static CPU_Counter_ticks timestamp_counter_difference(
  CPU_Counter_ticks second,
  CPU_Counter_ticks first
)
{
  return second - first;
}

static CPU_Counter_ticks free_counter_difference(
  CPU_Counter_ticks second,
  CPU_Counter_ticks first
)
{
  return first - second;
}

static CPU_Counter_ticks clock_counter_difference(
  CPU_Counter_ticks second,
  CPU_Counter_ticks first
)
{
  CPU_Counter_ticks period = rtems_configuration_get_microseconds_per_tick();

  return (first + period - second) % period;
}

static void gpt_counter_initialize(
  volatile struct gptimer_regs *gpt,
  size_t timer_index,
  uint32_t frequency,
  SPARC_Counter_difference counter_difference
)
{
  _SPARC_Counter_initialize(
    (volatile const uint32_t *) &gpt->timer[timer_index].value,
    counter_difference
  );

  rtems_counter_initialize_converter(frequency);
}

static unsigned int get_tstamp(volatile struct irqmp_timestamp_regs *irqmp_ts)
{
  return irqmp_ts->control >> 27;
}

void leon3_cpu_counter_initialize(void)
{
  volatile struct irqmp_timestamp_regs *irqmp_ts =
    &LEON3_IrqCtrl_Regs->timestamp[0];
  struct ambapp_dev *adev;
  int idx = 1;

  if (leon3_irqmp_has_timestamp(irqmp_ts)) {
    /* Use the interrupt controller timestamp counter if available */

    /* Enable interrupt timestamping for an arbitrary interrupt line */
    irqmp_ts->control = 0x1;

    _SPARC_Counter_initialize(
      (volatile const uint32_t *) &irqmp_ts->counter,
      timestamp_counter_difference
    );

    /* Get and set the frequency */
    adev = (void *) ambapp_for_each(
      &ambapp_plb,
      OPTIONS_ALL | OPTIONS_APB_SLVS,
      VENDOR_GAISLER,
      GAISLER_IRQMP,
      ambapp_find_by_idx,
      NULL
    );
    rtems_counter_initialize_converter(ambapp_freq_get(&ambapp_plb, adev));
  } else {
    adev = (void *) ambapp_for_each(
      &ambapp_plb,
      OPTIONS_ALL | OPTIONS_APB_SLVS,
      VENDOR_GAISLER,
      GAISLER_GPTIMER,
      ambapp_find_by_idx,
      &idx
    );
    if (adev != NULL) {
      /* Use the second GPTIMER if available */
      volatile struct gptimer_regs *gpt = adev_to_gpt(adev);
      uint32_t max_frequency = ambapp_freq_get(&ambapp_plb, adev);
      unsigned min_prescaler = 8;
      uint32_t frequency = max_frequency / min_prescaler;

      gpt->scaler_reload = min_prescaler - 1;
      gpt->timer[0].reload = 0xffffffff;
      gpt->timer[0].ctrl = GPTIMER_TIMER_CTRL_EN | GPTIMER_TIMER_CTRL_RS
        | GPTIMER_TIMER_CTRL_LD;

      gpt_counter_initialize(gpt, 0, frequency, free_counter_difference);
    } else if (LEON3_Timer_Regs != NULL) {
      /* Fall back to the first GPTIMER if available */
      gpt_counter_initialize(
        LEON3_Timer_Regs,
        LEON3_CLOCK_INDEX,
        LEON3_GPTIMER_0_FREQUENCY_SET_BY_BOOT_LOADER,
        clock_counter_difference
      );
    }
  }
}