xref: /openbmc/qemu/hw/openrisc/cputimer.c (revision 56411125)
1 /*
2  * QEMU OpenRISC timer support
3  *
4  * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
5  *                         Zhizhou Zhang <etouzh@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "cpu.h"
22 #include "hw/hw.h"
23 #include "qemu/timer.h"
24 
25 #define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */
26 
27 /* The time when TTCR changes */
28 static uint64_t last_clk;
29 static int is_counting;
30 
31 void cpu_openrisc_count_update(OpenRISCCPU *cpu)
32 {
33     uint64_t now;
34 
35     if (!is_counting) {
36         return;
37     }
38     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
39     cpu->env.ttcr += (uint32_t)((now - last_clk) / TIMER_PERIOD);
40     last_clk = now;
41 }
42 
43 void cpu_openrisc_timer_update(OpenRISCCPU *cpu)
44 {
45     uint32_t wait;
46     uint64_t now, next;
47 
48     if (!is_counting) {
49         return;
50     }
51 
52     cpu_openrisc_count_update(cpu);
53     now = last_clk;
54 
55     if ((cpu->env.ttmr & TTMR_TP) <= (cpu->env.ttcr & TTMR_TP)) {
56         wait = TTMR_TP - (cpu->env.ttcr & TTMR_TP) + 1;
57         wait += cpu->env.ttmr & TTMR_TP;
58     } else {
59         wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP);
60     }
61     next = now + (uint64_t)wait * TIMER_PERIOD;
62     timer_mod(cpu->env.timer, next);
63 }
64 
65 void cpu_openrisc_count_start(OpenRISCCPU *cpu)
66 {
67     is_counting = 1;
68     cpu_openrisc_count_update(cpu);
69 }
70 
71 void cpu_openrisc_count_stop(OpenRISCCPU *cpu)
72 {
73     timer_del(cpu->env.timer);
74     cpu_openrisc_count_update(cpu);
75     is_counting = 0;
76 }
77 
78 static void openrisc_timer_cb(void *opaque)
79 {
80     OpenRISCCPU *cpu = opaque;
81 
82     if ((cpu->env.ttmr & TTMR_IE) &&
83          timer_expired(cpu->env.timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))) {
84         CPUState *cs = CPU(cpu);
85 
86         cpu->env.ttmr |= TTMR_IP;
87         cs->interrupt_request |= CPU_INTERRUPT_TIMER;
88     }
89 
90     switch (cpu->env.ttmr & TTMR_M) {
91     case TIMER_NONE:
92         break;
93     case TIMER_INTR:
94         cpu->env.ttcr = 0;
95         break;
96     case TIMER_SHOT:
97         cpu_openrisc_count_stop(cpu);
98         break;
99     case TIMER_CONT:
100         break;
101     }
102 
103     cpu_openrisc_timer_update(cpu);
104 }
105 
106 void cpu_openrisc_clock_init(OpenRISCCPU *cpu)
107 {
108     cpu->env.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &openrisc_timer_cb, cpu);
109     cpu->env.ttmr = 0x00000000;
110     cpu->env.ttcr = 0x00000000;
111 }
112