1 /* Blackfin Core Timer model. 2 3 Copyright (C) 2010-2014 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 #include "config.h" 22 23 #include "sim-main.h" 24 #include "devices.h" 25 #include "dv-bfin_cec.h" 26 #include "dv-bfin_ctimer.h" 27 28 struct bfin_ctimer 29 { 30 bu32 base; 31 struct hw_event *handler; 32 signed64 timeout; 33 34 /* Order after here is important -- matches hardware MMR layout. */ 35 bu32 tcntl, tperiod, tscale, tcount; 36 }; 37 #define mmr_base() offsetof(struct bfin_ctimer, tcntl) 38 #define mmr_offset(mmr) (offsetof(struct bfin_ctimer, mmr) - mmr_base()) 39 40 static const char * const mmr_names[] = 41 { 42 "TCNTL", "TPERIOD", "TSCALE", "TCOUNT", 43 }; 44 #define mmr_name(off) mmr_names[(off) / 4] 45 46 static bool 47 bfin_ctimer_enabled (struct bfin_ctimer *ctimer) 48 { 49 return (ctimer->tcntl & TMPWR) && (ctimer->tcntl & TMREN); 50 } 51 52 static bu32 53 bfin_ctimer_scale (struct bfin_ctimer *ctimer) 54 { 55 /* Only low 8 bits are actually checked. */ 56 return (ctimer->tscale & 0xff) + 1; 57 } 58 59 static void 60 bfin_ctimer_schedule (struct hw *me, struct bfin_ctimer *ctimer); 61 62 static void 63 bfin_ctimer_expire (struct hw *me, void *data) 64 { 65 struct bfin_ctimer *ctimer = data; 66 67 ctimer->tcntl |= TINT; 68 if (ctimer->tcntl & TAUTORLD) 69 { 70 ctimer->tcount = ctimer->tperiod; 71 bfin_ctimer_schedule (me, ctimer); 72 } 73 else 74 { 75 ctimer->tcount = 0; 76 ctimer->handler = NULL; 77 } 78 79 hw_port_event (me, IVG_IVTMR, 1); 80 } 81 82 static void 83 bfin_ctimer_update_count (struct hw *me, struct bfin_ctimer *ctimer) 84 { 85 bu32 scale, ticks; 86 signed64 timeout; 87 88 /* If the timer was enabled w/out autoreload and has expired, then 89 there's nothing to calculate here. */ 90 if (ctimer->handler == NULL) 91 return; 92 93 scale = bfin_ctimer_scale (ctimer); 94 timeout = hw_event_remain_time (me, ctimer->handler); 95 ticks = ctimer->timeout - timeout; 96 ctimer->tcount -= (scale * ticks); 97 ctimer->timeout = timeout; 98 } 99 100 static void 101 bfin_ctimer_deschedule (struct hw *me, struct bfin_ctimer *ctimer) 102 { 103 if (ctimer->handler) 104 { 105 hw_event_queue_deschedule (me, ctimer->handler); 106 ctimer->handler = NULL; 107 } 108 } 109 110 static void 111 bfin_ctimer_schedule (struct hw *me, struct bfin_ctimer *ctimer) 112 { 113 bu32 scale = bfin_ctimer_scale (ctimer); 114 ctimer->timeout = (ctimer->tcount / scale) + !!(ctimer->tcount % scale); 115 ctimer->handler = hw_event_queue_schedule (me, ctimer->timeout, 116 bfin_ctimer_expire, 117 ctimer); 118 } 119 120 static unsigned 121 bfin_ctimer_io_write_buffer (struct hw *me, const void *source, 122 int space, address_word addr, unsigned nr_bytes) 123 { 124 struct bfin_ctimer *ctimer = hw_data (me); 125 bool curr_enabled; 126 bu32 mmr_off; 127 bu32 value; 128 bu32 *valuep; 129 130 value = dv_load_4 (source); 131 mmr_off = addr - ctimer->base; 132 valuep = (void *)((unsigned long)ctimer + mmr_base() + mmr_off); 133 134 HW_TRACE_WRITE (); 135 136 curr_enabled = bfin_ctimer_enabled (ctimer); 137 switch (mmr_off) 138 { 139 case mmr_offset(tcntl): 140 /* HRM describes TINT as sticky, but it isn't W1C. */ 141 *valuep = value; 142 143 if (bfin_ctimer_enabled (ctimer) == curr_enabled) 144 { 145 /* Do nothing. */ 146 } 147 else if (curr_enabled) 148 { 149 bfin_ctimer_update_count (me, ctimer); 150 bfin_ctimer_deschedule (me, ctimer); 151 } 152 else 153 bfin_ctimer_schedule (me, ctimer); 154 155 break; 156 case mmr_offset(tcount): 157 /* HRM says writes are discarded when enabled. */ 158 /* XXX: But hardware seems to be writeable all the time ? */ 159 /* if (!curr_enabled) */ 160 *valuep = value; 161 break; 162 case mmr_offset(tperiod): 163 /* HRM says writes are discarded when enabled. */ 164 /* XXX: But hardware seems to be writeable all the time ? */ 165 /* if (!curr_enabled) */ 166 { 167 /* Writes are mirrored into TCOUNT. */ 168 ctimer->tcount = value; 169 *valuep = value; 170 } 171 break; 172 case mmr_offset(tscale): 173 if (curr_enabled) 174 { 175 bfin_ctimer_update_count (me, ctimer); 176 bfin_ctimer_deschedule (me, ctimer); 177 } 178 *valuep = value; 179 if (curr_enabled) 180 bfin_ctimer_schedule (me, ctimer); 181 break; 182 } 183 184 return nr_bytes; 185 } 186 187 static unsigned 188 bfin_ctimer_io_read_buffer (struct hw *me, void *dest, 189 int space, address_word addr, unsigned nr_bytes) 190 { 191 struct bfin_ctimer *ctimer = hw_data (me); 192 bu32 mmr_off; 193 bu32 *valuep; 194 195 mmr_off = addr - ctimer->base; 196 valuep = (void *)((unsigned long)ctimer + mmr_base() + mmr_off); 197 198 HW_TRACE_READ (); 199 200 switch (mmr_off) 201 { 202 case mmr_offset(tcount): 203 /* Since we're optimizing events here, we need to calculate 204 the new tcount value. */ 205 if (bfin_ctimer_enabled (ctimer)) 206 bfin_ctimer_update_count (me, ctimer); 207 break; 208 } 209 210 dv_store_4 (dest, *valuep); 211 212 return nr_bytes; 213 } 214 215 static const struct hw_port_descriptor bfin_ctimer_ports[] = 216 { 217 { "ivtmr", IVG_IVTMR, 0, output_port, }, 218 { NULL, 0, 0, 0, }, 219 }; 220 221 static void 222 attach_bfin_ctimer_regs (struct hw *me, struct bfin_ctimer *ctimer) 223 { 224 address_word attach_address; 225 int attach_space; 226 unsigned attach_size; 227 reg_property_spec reg; 228 229 if (hw_find_property (me, "reg") == NULL) 230 hw_abort (me, "Missing \"reg\" property"); 231 232 if (!hw_find_reg_array_property (me, "reg", 0, ®)) 233 hw_abort (me, "\"reg\" property must contain three addr/size entries"); 234 235 hw_unit_address_to_attach_address (hw_parent (me), 236 ®.address, 237 &attach_space, &attach_address, me); 238 hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); 239 240 if (attach_size != BFIN_COREMMR_CTIMER_SIZE) 241 hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_CTIMER_SIZE); 242 243 hw_attach_address (hw_parent (me), 244 0, attach_space, attach_address, attach_size, me); 245 246 ctimer->base = attach_address; 247 } 248 249 static void 250 bfin_ctimer_finish (struct hw *me) 251 { 252 struct bfin_ctimer *ctimer; 253 254 ctimer = HW_ZALLOC (me, struct bfin_ctimer); 255 256 set_hw_data (me, ctimer); 257 set_hw_io_read_buffer (me, bfin_ctimer_io_read_buffer); 258 set_hw_io_write_buffer (me, bfin_ctimer_io_write_buffer); 259 set_hw_ports (me, bfin_ctimer_ports); 260 261 attach_bfin_ctimer_regs (me, ctimer); 262 263 /* Initialize the Core Timer. */ 264 } 265 266 const struct hw_descriptor dv_bfin_ctimer_descriptor[] = 267 { 268 {"bfin_ctimer", bfin_ctimer_finish,}, 269 {NULL, NULL}, 270 }; 271