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, ®)) 242 hw_abort (me, "\"reg\" property must contain three addr/size entries"); 243 244 hw_unit_address_to_attach_address (hw_parent (me), 245 ®.address, 246 &attach_space, &attach_address, me); 247 hw_unit_size_to_attach_size (hw_parent (me), ®.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