xref: /netbsd-src/external/gpl3/gdb/dist/sim/bfin/dv-bfin_ctimer.c (revision 1f4e7eb9e5e045e008f1894823a8e4e6c9f46890)
1 /* Blackfin Core Timer model.
2 
3    Copyright (C) 2010-2024 Free Software Foundation, Inc.
4    Contributed by Analog Devices, Inc.
5 
6    This file is part of simulators.
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20 
21 /* This must come before any other includes.  */
22 #include "defs.h"
23 
24 #include "sim-main.h"
25 #include "devices.h"
26 #include "dv-bfin_cec.h"
27 #include "dv-bfin_ctimer.h"
28 
29 struct bfin_ctimer
30 {
31   bu32 base;
32   struct hw_event *handler;
33   int64_t timeout;
34 
35   /* Order after here is important -- matches hardware MMR layout.  */
36   bu32 tcntl, tperiod, tscale, tcount;
37 };
38 #define mmr_base()      offsetof(struct bfin_ctimer, tcntl)
39 #define mmr_offset(mmr) (offsetof(struct bfin_ctimer, mmr) - mmr_base())
40 
41 static const char * const mmr_names[] =
42 {
43   "TCNTL", "TPERIOD", "TSCALE", "TCOUNT",
44 };
45 #define mmr_name(off) mmr_names[(off) / 4]
46 
47 static bool
48 bfin_ctimer_enabled (struct bfin_ctimer *ctimer)
49 {
50   return (ctimer->tcntl & TMPWR) && (ctimer->tcntl & TMREN);
51 }
52 
53 static bu32
54 bfin_ctimer_scale (struct bfin_ctimer *ctimer)
55 {
56   /* Only low 8 bits are actually checked.  */
57   return (ctimer->tscale & 0xff) + 1;
58 }
59 
60 static void
61 bfin_ctimer_schedule (struct hw *me, struct bfin_ctimer *ctimer);
62 
63 static void
64 bfin_ctimer_expire (struct hw *me, void *data)
65 {
66   struct bfin_ctimer *ctimer = data;
67 
68   ctimer->tcntl |= TINT;
69   if (ctimer->tcntl & TAUTORLD)
70     {
71       ctimer->tcount = ctimer->tperiod;
72       bfin_ctimer_schedule (me, ctimer);
73     }
74   else
75     {
76       ctimer->tcount = 0;
77       ctimer->handler = NULL;
78     }
79 
80   hw_port_event (me, IVG_IVTMR, 1);
81 }
82 
83 static void
84 bfin_ctimer_update_count (struct hw *me, struct bfin_ctimer *ctimer)
85 {
86   bu32 scale, ticks;
87   int64_t timeout;
88 
89   /* If the timer was enabled w/out autoreload and has expired, then
90      there's nothing to calculate here.  */
91   if (ctimer->handler == NULL)
92     return;
93 
94   scale = bfin_ctimer_scale (ctimer);
95   timeout = hw_event_remain_time (me, ctimer->handler);
96   ticks = ctimer->timeout - timeout;
97   ctimer->tcount -= (scale * ticks);
98   ctimer->timeout = timeout;
99 }
100 
101 static void
102 bfin_ctimer_deschedule (struct hw *me, struct bfin_ctimer *ctimer)
103 {
104   if (ctimer->handler)
105     {
106       hw_event_queue_deschedule (me, ctimer->handler);
107       ctimer->handler = NULL;
108     }
109 }
110 
111 static void
112 bfin_ctimer_schedule (struct hw *me, struct bfin_ctimer *ctimer)
113 {
114   bu32 scale = bfin_ctimer_scale (ctimer);
115   ctimer->timeout = (ctimer->tcount / scale) + !!(ctimer->tcount % scale);
116   ctimer->handler = hw_event_queue_schedule (me, ctimer->timeout,
117 					     bfin_ctimer_expire,
118 					     ctimer);
119 }
120 
121 static unsigned
122 bfin_ctimer_io_write_buffer (struct hw *me, const void *source,
123 			     int space, address_word addr, unsigned nr_bytes)
124 {
125   struct bfin_ctimer *ctimer = hw_data (me);
126   bool curr_enabled;
127   bu32 mmr_off;
128   bu32 value;
129   bu32 *valuep;
130 
131   /* Invalid access mode is higher priority than missing register.  */
132   if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, true))
133     return 0;
134 
135   value = dv_load_4 (source);
136   mmr_off = addr - ctimer->base;
137   valuep = (void *)((uintptr_t)ctimer + mmr_base() + mmr_off);
138 
139   HW_TRACE_WRITE ();
140 
141   curr_enabled = bfin_ctimer_enabled (ctimer);
142   switch (mmr_off)
143     {
144     case mmr_offset(tcntl):
145       /* HRM describes TINT as sticky, but it isn't W1C.  */
146       *valuep = value;
147 
148       if (bfin_ctimer_enabled (ctimer) == curr_enabled)
149 	{
150 	  /* Do nothing.  */
151 	}
152       else if (curr_enabled)
153 	{
154 	  bfin_ctimer_update_count (me, ctimer);
155 	  bfin_ctimer_deschedule (me, ctimer);
156 	}
157       else
158 	bfin_ctimer_schedule (me, ctimer);
159 
160       break;
161     case mmr_offset(tcount):
162       /* HRM says writes are discarded when enabled.  */
163       /* XXX: But hardware seems to be writeable all the time ?  */
164       /* if (!curr_enabled) */
165 	*valuep = value;
166       break;
167     case mmr_offset(tperiod):
168       /* HRM says writes are discarded when enabled.  */
169       /* XXX: But hardware seems to be writeable all the time ?  */
170       /* if (!curr_enabled) */
171 	{
172 	  /* Writes are mirrored into TCOUNT.  */
173 	  ctimer->tcount = value;
174 	  *valuep = value;
175 	}
176       break;
177     case mmr_offset(tscale):
178       if (curr_enabled)
179 	{
180 	  bfin_ctimer_update_count (me, ctimer);
181 	  bfin_ctimer_deschedule (me, ctimer);
182 	}
183       *valuep = value;
184       if (curr_enabled)
185 	bfin_ctimer_schedule (me, ctimer);
186       break;
187     }
188 
189   return nr_bytes;
190 }
191 
192 static unsigned
193 bfin_ctimer_io_read_buffer (struct hw *me, void *dest,
194 			    int space, address_word addr, unsigned nr_bytes)
195 {
196   struct bfin_ctimer *ctimer = hw_data (me);
197   bu32 mmr_off;
198   bu32 *valuep;
199 
200   /* Invalid access mode is higher priority than missing register.  */
201   if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, false))
202     return 0;
203 
204   mmr_off = addr - ctimer->base;
205   valuep = (void *)((uintptr_t)ctimer + mmr_base() + mmr_off);
206 
207   HW_TRACE_READ ();
208 
209   switch (mmr_off)
210     {
211     case mmr_offset(tcount):
212       /* Since we're optimizing events here, we need to calculate
213          the new tcount value.  */
214       if (bfin_ctimer_enabled (ctimer))
215 	bfin_ctimer_update_count (me, ctimer);
216       break;
217     }
218 
219   dv_store_4 (dest, *valuep);
220 
221   return nr_bytes;
222 }
223 
224 static const struct hw_port_descriptor bfin_ctimer_ports[] =
225 {
226   { "ivtmr", IVG_IVTMR, 0, output_port, },
227   { NULL, 0, 0, 0, },
228 };
229 
230 static void
231 attach_bfin_ctimer_regs (struct hw *me, struct bfin_ctimer *ctimer)
232 {
233   address_word attach_address;
234   int attach_space;
235   unsigned attach_size;
236   reg_property_spec reg;
237 
238   if (hw_find_property (me, "reg") == NULL)
239     hw_abort (me, "Missing \"reg\" property");
240 
241   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
242     hw_abort (me, "\"reg\" property must contain three addr/size entries");
243 
244   hw_unit_address_to_attach_address (hw_parent (me),
245 				     &reg.address,
246 				     &attach_space, &attach_address, me);
247   hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
248 
249   if (attach_size != BFIN_COREMMR_CTIMER_SIZE)
250     hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_CTIMER_SIZE);
251 
252   hw_attach_address (hw_parent (me),
253 		     0, attach_space, attach_address, attach_size, me);
254 
255   ctimer->base = attach_address;
256 }
257 
258 static void
259 bfin_ctimer_finish (struct hw *me)
260 {
261   struct bfin_ctimer *ctimer;
262 
263   ctimer = HW_ZALLOC (me, struct bfin_ctimer);
264 
265   set_hw_data (me, ctimer);
266   set_hw_io_read_buffer (me, bfin_ctimer_io_read_buffer);
267   set_hw_io_write_buffer (me, bfin_ctimer_io_write_buffer);
268   set_hw_ports (me, bfin_ctimer_ports);
269 
270   attach_bfin_ctimer_regs (me, ctimer);
271 
272   /* Initialize the Core Timer.  */
273 }
274 
275 const struct hw_descriptor dv_bfin_ctimer_descriptor[] =
276 {
277   {"bfin_ctimer", bfin_ctimer_finish,},
278   {NULL, NULL},
279 };
280