xref: /netbsd-src/external/gpl3/gdb.old/dist/sim/mips/dv-tx3904tmr.c (revision c9055873d0546e63388f027d3d7f85381cde0545)
1 /*  This file is part of the program GDB, the GNU debugger.
2 
3     Copyright (C) 1998-2023 Free Software Foundation, Inc.
4     Contributed by Cygnus Solutions.
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 
19     */
20 
21 /* This must come before any other includes.  */
22 #include "defs.h"
23 
24 #include "sim-main.h"
25 #include "hw-main.h"
26 
27 
28 /* DEVICE
29 
30 
31    tx3904tmr - tx3904 timer
32 
33 
34    DESCRIPTION
35 
36 
37    Implements one tx3904 timer/counter described in the tx3904
38    user guide.  Three instances are required for TMR0, TMR1, and
39    TMR3 within the tx3904, at different base addresses.
40 
41    Both internal and system clocks are synthesized as divided versions
42    of the simulator clock.
43 
44    There is no support for:
45     - edge sensitivity of external clock
46     - different mode restrictions for TMR0..2
47     - level interrupts (interrupts are treated as events that occur at edges)
48 
49 
50 
51    PROPERTIES
52 
53 
54    reg <base> <length>
55 
56    Base of TMR control register bank.  <length> must equal 0x100.
57    Register offsets:       0: TCR: timer control  register
58                            4: TISR: timer interrupt status register
59                            8: CPRA: compare register A
60                           12: CPRB: compare register B
61                           16: ITMR: interval timer mode register
62 			  32: CCDR: divider register
63 			  48: PMGR: pulse generator mode register
64 			  64: WTMR: watchdog timer mode register
65 			 240: TRR: timer read register
66 
67 
68    clock <ticks>
69 
70    Rate of timer clock signal.  This number is the number of simulator
71    ticks per clock signal tick.  Default 1.
72 
73 
74    ext <ticks>
75 
76    Rate of "external input clock signal", the other clock input of the
77    timer.  It uses the same scale as above.  Default 100.
78 
79 
80 
81    PORTS
82 
83 
84    int (output)
85 
86    Interrupt port.  An event is generated when a timer interrupt
87    occurs.
88 
89 
90    ff (output)
91 
92    Flip-flop output, corresponds to the TMFFOUT port.  An event is
93    generated when flip-flop changes value.  The integer associated
94    with the event is 1/0 according to flip-flop value.
95 
96 
97    reset (input)
98 
99    Reset port.
100 
101    */
102 
103 
104 
105 /* static functions */
106 
107 static void deliver_tx3904tmr_tick (struct hw *me, void *data);
108 
109 
110 /* register numbers; each is one word long */
111 enum
112 {
113   TCR_REG = 0,
114   TISR_REG = 1,
115   CPRA_REG = 2,
116   CPRB_REG = 3,
117   ITMR_REG = 4,
118   CCDR_REG = 8,
119   PMGR_REG = 12,
120   WTMR_REG = 16,
121   TRR_REG = 60
122 };
123 
124 
125 
126 /* port ID's */
127 
128 enum
129  {
130   RESET_PORT,
131   INT_PORT,
132   FF_PORT
133 };
134 
135 
136 static const struct hw_port_descriptor tx3904tmr_ports[] =
137 {
138   { "int", INT_PORT, 0, output_port, },
139   { "ff", FF_PORT, 0, output_port, },
140   { "reset", RESET_PORT, 0, input_port, },
141   { NULL, },
142 };
143 
144 
145 
146 /* The timer/counter register internal state.  Note that we store
147    state using the control register images, in host endian order. */
148 
149 struct tx3904tmr {
150   address_word base_address; /* control register base */
151   unsigned_4 clock_ticks, ext_ticks; /* clock frequencies */
152   signed_8 last_ticks; /* time at last deliver_*_tick call */
153   signed_8 roundoff_ticks; /* sim ticks unprocessed during last tick call */
154   int ff; /* pulse generator flip-flop value: 1/0 */
155   struct hw_event* event; /* last scheduled event */
156 
157   unsigned_4 tcr;
158 #define GET_TCR_TCE(c)      (((c)->tcr & 0x80) >> 7)
159 #define GET_TCR_CCDE(c)     (((c)->tcr & 0x40) >> 6)
160 #define GET_TCR_CRE(c)      (((c)->tcr & 0x20) >> 5)
161 #define GET_TCR_CCS(c)      (((c)->tcr & 0x04) >> 2)
162 #define GET_TCR_TMODE(c)    (((c)->tcr & 0x03) >> 0)
163   unsigned_4 tisr;
164 #define SET_TISR_TWIS(c)    ((c)->tisr |= 0x08)
165 #define SET_TISR_TPIBS(c)   ((c)->tisr |= 0x04)
166 #define SET_TISR_TPIAS(c)   ((c)->tisr |= 0x02)
167 #define SET_TISR_TIIS(c)    ((c)->tisr |= 0x01)
168   unsigned_4 cpra;
169   unsigned_4 cprb;
170   unsigned_4 itmr;
171 #define GET_ITMR_TIIE(c)    (((c)->itmr & 0x8000) >> 15)
172 #define SET_ITMR_TIIE(c,v)  BLIT32((c)->itmr, 15, (v) ? 1 : 0)
173 #define GET_ITMR_TZCE(c)    (((c)->itmr & 0x0001) >> 0)
174 #define SET_ITMR_TZCE(c,v)  BLIT32((c)->itmr, 0, (v) ? 1 : 0)
175   unsigned_4 ccdr;
176 #define GET_CCDR_CDR(c)     (((c)->ccdr & 0x07) >> 0)
177   unsigned_4 pmgr;
178 #define GET_PMGR_TPIBE(c)   (((c)->pmgr & 0x8000) >> 15)
179 #define SET_PMGR_TPIBE(c,v) BLIT32((c)->pmgr, 15, (v) ? 1 : 0)
180 #define GET_PMGR_TPIAE(c)   (((c)->pmgr & 0x4000) >> 14)
181 #define SET_PMGR_TPIAE(c,v) BLIT32((c)->pmgr, 14, (v) ? 1 : 0)
182 #define GET_PMGR_FFI(c)     (((c)->pmgr & 0x0001) >> 0)
183 #define SET_PMGR_FFI(c,v)   BLIT32((c)->pmgr, 0, (v) ? 1 : 0)
184   unsigned_4 wtmr;
185 #define GET_WTMR_TWIE(c)    (((c)->wtmr & 0x8000) >> 15)
186 #define SET_WTMR_TWIE(c,v)  BLIT32((c)->wtmr, 15, (v) ? 1 : 0)
187 #define GET_WTMR_WDIS(c)    (((c)->wtmr & 0x0080) >> 7)
188 #define SET_WTMR_WDIS(c,v)  BLIT32((c)->wtmr, 7, (v) ? 1 : 0)
189 #define GET_WTMR_TWC(c)     (((c)->wtmr & 0x0001) >> 0)
190 #define SET_WTMR_TWC(c,v)   BLIT32((c)->wtmr, 0, (v) ? 1 : 0)
191   unsigned_4 trr;
192 };
193 
194 
195 
196 /* Finish off the partially created hw device.  Attach our local
197    callbacks.  Wire up our port names etc */
198 
199 static hw_io_read_buffer_method tx3904tmr_io_read_buffer;
200 static hw_io_write_buffer_method tx3904tmr_io_write_buffer;
201 static hw_port_event_method tx3904tmr_port_event;
202 
203 static void
204 attach_tx3904tmr_regs (struct hw *me,
205 		      struct tx3904tmr *controller)
206 {
207   unsigned_word attach_address;
208   int attach_space;
209   unsigned attach_size;
210   reg_property_spec reg;
211 
212   if (hw_find_property (me, "reg") == NULL)
213     hw_abort (me, "Missing \"reg\" property");
214 
215   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
216     hw_abort (me, "\"reg\" property must contain one addr/size entry");
217 
218   hw_unit_address_to_attach_address (hw_parent (me),
219 				     &reg.address,
220 				     &attach_space,
221 				     &attach_address,
222 				     me);
223   hw_unit_size_to_attach_size (hw_parent (me),
224 			       &reg.size,
225 			       &attach_size, me);
226 
227   hw_attach_address (hw_parent (me), 0,
228 		     attach_space, attach_address, attach_size,
229 		     me);
230 
231   if (hw_find_property(me, "clock") != NULL)
232     controller->clock_ticks = (unsigned_4) hw_find_integer_property(me, "clock");
233 
234   if (hw_find_property(me, "ext") != NULL)
235     controller->ext_ticks = (unsigned_4) hw_find_integer_property(me, "ext");
236 
237   controller->base_address = attach_address;
238 }
239 
240 
241 static void
242 tx3904tmr_finish (struct hw *me)
243 {
244   struct tx3904tmr *controller;
245 
246   controller = HW_ZALLOC (me, struct tx3904tmr);
247   set_hw_data (me, controller);
248   set_hw_io_read_buffer (me, tx3904tmr_io_read_buffer);
249   set_hw_io_write_buffer (me, tx3904tmr_io_write_buffer);
250   set_hw_ports (me, tx3904tmr_ports);
251   set_hw_port_event (me, tx3904tmr_port_event);
252 
253   /* Preset clock dividers */
254   controller->clock_ticks = 1;
255   controller->ext_ticks = 100;
256 
257   /* Attach ourself to our parent bus */
258   attach_tx3904tmr_regs (me, controller);
259 
260   /* Initialize to reset state */
261   controller->tcr =
262     controller->itmr =
263     controller->ccdr =
264     controller->pmgr =
265     controller->wtmr =
266     controller->tisr =
267     controller->trr = 0;
268   controller->cpra = controller->cprb = 0x00FFFFFF;
269   controller->ff = 0;
270   controller->last_ticks = controller->roundoff_ticks = 0;
271   controller->event = NULL;
272 }
273 
274 
275 
276 /* An event arrives on an interrupt port */
277 
278 static void
279 tx3904tmr_port_event (struct hw *me,
280 		     int my_port,
281 		     struct hw *source,
282 		     int source_port,
283 		     int level)
284 {
285   struct tx3904tmr *controller = hw_data (me);
286 
287   switch (my_port)
288     {
289     case RESET_PORT:
290       {
291 	HW_TRACE ((me, "reset"));
292 
293 	/* preset flip-flop to FFI value */
294 	controller->ff = GET_PMGR_FFI(controller);
295 
296 	controller->tcr =
297 	  controller->itmr =
298 	  controller->ccdr =
299 	  controller->pmgr =
300 	  controller->wtmr =
301 	  controller->tisr =
302 	  controller->trr = 0;
303 	controller->cpra = controller->cprb = 0x00FFFFFF;
304 	controller->last_ticks = controller->roundoff_ticks = 0;
305 	if (controller->event != NULL)
306 	  hw_event_queue_deschedule(me, controller->event);
307 	controller->event = NULL;
308 	break;
309       }
310 
311     default:
312       hw_abort (me, "Event on unknown port %d", my_port);
313       break;
314     }
315 }
316 
317 
318 /* generic read/write */
319 
320 static unsigned
321 tx3904tmr_io_read_buffer (struct hw *me,
322 			 void *dest,
323 			 int space,
324 			 unsigned_word base,
325 			 unsigned nr_bytes)
326 {
327   struct tx3904tmr *controller = hw_data (me);
328   unsigned byte;
329 
330   HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
331   for (byte = 0; byte < nr_bytes; byte++)
332     {
333       address_word address = base + byte;
334       int reg_number = (address - controller->base_address) / 4;
335       int reg_offset = 3 - (address - controller->base_address) % 4;
336       unsigned_4 register_value; /* in target byte order */
337 
338       /* fill in entire register_value word */
339       switch (reg_number)
340 	{
341 	case TCR_REG: register_value = controller->tcr; break;
342 	case TISR_REG: register_value = controller->tisr; break;
343 	case CPRA_REG: register_value = controller->cpra; break;
344 	case CPRB_REG: register_value = controller->cprb; break;
345 	case ITMR_REG: register_value = controller->itmr; break;
346 	case CCDR_REG: register_value = controller->ccdr; break;
347 	case PMGR_REG: register_value = controller->pmgr; break;
348 	case WTMR_REG: register_value = controller->wtmr; break;
349 	case TRR_REG: register_value = controller->trr; break;
350 	default: register_value = 0;
351 	}
352 
353       /* write requested byte out */
354       memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
355     }
356 
357   return nr_bytes;
358 }
359 
360 
361 
362 static unsigned
363 tx3904tmr_io_write_buffer (struct hw *me,
364 			  const void *source,
365 			  int space,
366 			  unsigned_word base,
367 			  unsigned nr_bytes)
368 {
369   struct tx3904tmr *controller = hw_data (me);
370   unsigned byte;
371 
372   HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
373   for (byte = 0; byte < nr_bytes; byte++)
374     {
375       address_word address = base + byte;
376       unsigned_1 write_byte = ((const char*) source)[byte];
377       int reg_number = (address - controller->base_address) / 4;
378       int reg_offset = 3 - (address - controller->base_address) % 4;
379 
380       /* fill in entire register_value word */
381       switch (reg_number)
382 	{
383 	case TCR_REG:
384 	  if (reg_offset == 0) /* first byte */
385 	    {
386 	      /* update register, but mask out NOP bits */
387 	      controller->tcr = (unsigned_4) (write_byte & 0xef);
388 
389 	      /* Reset counter value if timer suspended and CRE is set. */
390 	      if (GET_TCR_TCE(controller) == 0 &&
391 		 GET_TCR_CRE(controller) == 1)
392 		controller->trr = 0;
393 	    }
394 	  /* HW_TRACE ((me, "tcr: %08lx", (long) controller->tcr)); */
395 	  break;
396 
397 	case ITMR_REG:
398 	  if (reg_offset == 1) /* second byte */
399 	    {
400 	      SET_ITMR_TIIE(controller, write_byte & 0x80);
401 	    }
402 	  else if (reg_offset == 0) /* first byte */
403 	    {
404 	      SET_ITMR_TZCE(controller, write_byte & 0x01);
405 	    }
406 	  /* HW_TRACE ((me, "itmr: %08lx", (long) controller->itmr)); */
407 	  break;
408 
409 	case CCDR_REG:
410 	  if (reg_offset == 0) /* first byte */
411 	    {
412 	      controller->ccdr = write_byte & 0x07;
413 	    }
414 	  /* HW_TRACE ((me, "ccdr: %08lx", (long) controller->ccdr)); */
415 	  break;
416 
417 	case PMGR_REG:
418 	  if (reg_offset == 1) /* second byte */
419 	    {
420 	      SET_PMGR_TPIBE(controller, write_byte & 0x80);
421 	      SET_PMGR_TPIAE(controller, write_byte & 0x40);
422 	    }
423 	  else if (reg_offset == 0) /* first byte */
424 	    {
425 	      SET_PMGR_FFI(controller, write_byte & 0x01);
426 	    }
427 	  /* HW_TRACE ((me, "pmgr: %08lx", (long) controller->pmgr)); */
428 	  break;
429 
430 	case WTMR_REG:
431 	  if (reg_offset == 1) /* second byte */
432 	    {
433 	      SET_WTMR_TWIE(controller, write_byte & 0x80);
434 	    }
435 	  else if (reg_offset == 0) /* first byte */
436 	    {
437 	      SET_WTMR_WDIS(controller, write_byte & 0x80);
438 	      SET_WTMR_TWC(controller, write_byte & 0x01);
439 	    }
440 	  /* HW_TRACE ((me, "wtmr: %08lx", (long) controller->wtmr)); */
441 	  break;
442 
443 	case TISR_REG:
444 	  if (reg_offset == 0) /* first byte */
445 	    {
446 	      /* All bits must be zero in given byte, according to
447                  spec. */
448 
449 	      /* Send an "interrupt off" event on the interrupt port */
450 	      if (controller->tisr != 0) /* any interrupts active? */
451 		{
452 		  hw_port_event (me, INT_PORT, 0);
453 		}
454 
455 	      /* clear interrupt status register */
456 	      controller->tisr = 0;
457 	    }
458 	  /* HW_TRACE ((me, "tisr: %08lx", (long) controller->tisr)); */
459 	  break;
460 
461 	case CPRA_REG:
462 	  if (reg_offset < 3) /* first, second, or third byte */
463 	    {
464 	      MBLIT32(controller->cpra, (reg_offset*8)+7, (reg_offset*8), write_byte);
465 	    }
466 	  /* HW_TRACE ((me, "cpra: %08lx", (long) controller->cpra)); */
467 	  break;
468 
469 	case CPRB_REG:
470 	  if (reg_offset < 3) /* first, second, or third byte */
471 	    {
472 	      MBLIT32(controller->cprb, (reg_offset*8)+7, (reg_offset*8), write_byte);
473 	    }
474 	  /* HW_TRACE ((me, "cprb: %08lx", (long) controller->cprb)); */
475 	  break;
476 
477 	default:
478 	  HW_TRACE ((me, "write to illegal register %d", reg_number));
479 	}
480     } /* loop over bytes */
481 
482   /* Schedule a timer event in near future, so we can increment or
483      stop the counter, to respond to register updates. */
484   hw_event_queue_schedule(me, 1, deliver_tx3904tmr_tick, NULL);
485 
486   return nr_bytes;
487 }
488 
489 
490 
491 /* Deliver a clock tick to the counter. */
492 static void
493 deliver_tx3904tmr_tick (struct hw *me,
494 			void *data)
495 {
496   struct tx3904tmr *controller = hw_data (me);
497   SIM_DESC sd = hw_system (me);
498   signed_8 this_ticks = sim_events_time(sd);
499 
500   signed_8 warp;
501   signed_8 divisor;
502   signed_8 quotient, remainder;
503 
504   /* compute simulation ticks between last tick and this tick */
505   if (controller->last_ticks != 0)
506     warp = this_ticks - controller->last_ticks + controller->roundoff_ticks;
507   else
508     {
509       controller->last_ticks = this_ticks; /* initialize */
510       warp = controller->roundoff_ticks;
511     }
512 
513   if (controller->event != NULL)
514     hw_event_queue_deschedule(me, controller->event);
515   controller->event = NULL;
516 
517   /* Check whether the timer ticking is enabled at this moment.  This
518      largely a function of the TCE bit, but is also slightly
519      mode-dependent. */
520   switch ((int) GET_TCR_TMODE(controller))
521     {
522     case 0: /* interval */
523       /* do not advance counter if TCE = 0 or if holding at count = CPRA */
524       if (GET_TCR_TCE(controller) == 0 ||
525 	 controller->trr == controller->cpra)
526 	return;
527       break;
528 
529     case 1: /* pulse generator */
530       /* do not advance counter if TCE = 0 */
531       if (GET_TCR_TCE(controller) == 0)
532 	return;
533       break;
534 
535     case 2: /* watchdog */
536       /* do not advance counter if TCE = 0 and WDIS = 1 */
537       if (GET_TCR_TCE(controller) == 0 &&
538 	 GET_WTMR_WDIS(controller) == 1)
539 	return;
540       break;
541 
542     case 3: /* disabled */
543       /* regardless of TCE, do not advance counter */
544       return;
545     }
546 
547   /* In any of the above cases that return, a subsequent register
548      write will be needed to restart the timer.  A tick event is
549      scheduled by any register write, so it is more efficient not to
550      reschedule dummy events here. */
551 
552 
553   /* find appropriate divisor etc. */
554   if (GET_TCR_CCS(controller) == 0) /* internal system clock */
555     {
556       /* apply internal clock divider */
557       if (GET_TCR_CCDE(controller)) /* divisor circuit enabled? */
558 	divisor = controller->clock_ticks * (1 << (1 + GET_CCDR_CDR(controller)));
559       else
560 	divisor = controller->clock_ticks;
561     }
562   else
563     {
564       divisor = controller->ext_ticks;
565     }
566 
567   /* how many times to increase counter? */
568   quotient = warp / divisor;
569   remainder = warp % divisor;
570 
571   /* NOTE: If the event rescheduling code works properly, the quotient
572      should never be larger than 1.  That is, we should receive events
573      here at least as frequently as the simulated counter is supposed
574      to decrement.  So the remainder (-> roundoff_ticks) will slowly
575      accumulate, with the quotient == 0.  Once in a while, quotient
576      will equal 1. */
577 
578   controller->roundoff_ticks = remainder;
579   controller->last_ticks = this_ticks;
580   while(quotient > 0) /* Is it time to increment counter? */
581     {
582       /* next 24-bit counter value */
583       unsigned_4 next_trr = (controller->trr + 1) % (1 << 24);
584       quotient --;
585 
586       switch ((int) GET_TCR_TMODE(controller))
587 	{
588 	case 0: /* interval timer mode */
589 	  {
590 	    /* Current or next counter value matches CPRA value?  The
591 	       first case covers counter holding at maximum before
592 	       reset.  The second case covers normal counting
593 	       behavior. */
594 	    if (controller->trr == controller->cpra ||
595 	       next_trr == controller->cpra)
596 	      {
597 		/* likely hold CPRA value */
598 		if (controller->trr == controller->cpra)
599 		  next_trr = controller->cpra;
600 
601 		SET_TISR_TIIS(controller);
602 
603 		/* Signal an interrupt if it is enabled with TIIE,
604 		   and if we just arrived at CPRA.  Don't repeatedly
605 		   interrupt if holding due to TZCE=0 */
606 		if (GET_ITMR_TIIE(controller) &&
607 		   next_trr != controller->trr)
608 		  {
609 		    hw_port_event(me, INT_PORT, 1);
610 		  }
611 
612 		/* Reset counter? */
613 		if (GET_ITMR_TZCE(controller))
614 		  {
615 		    next_trr = 0;
616 		  }
617 	      }
618 	  }
619 	break;
620 
621 	case 1: /* pulse generator mode */
622 	  {
623 	    /* first trip point */
624 	    if (next_trr == controller->cpra)
625 	      {
626 		/* flip flip-flop & report */
627 		controller->ff ^= 1;
628 		hw_port_event(me, FF_PORT, controller->ff);
629 		SET_TISR_TPIAS(controller);
630 
631 		/* signal interrupt */
632 		if (GET_PMGR_TPIAE(controller))
633 		  {
634 		    hw_port_event(me, INT_PORT, 1);
635 		  }
636 
637 	      }
638 	    /* second trip point */
639 	    else if (next_trr == controller->cprb)
640 	      {
641 		/* flip flip-flop & report */
642 		controller->ff ^= 1;
643 		hw_port_event(me, FF_PORT, controller->ff);
644 		SET_TISR_TPIBS(controller);
645 
646 		/* signal interrupt */
647 		if (GET_PMGR_TPIBE(controller))
648 		  {
649 		    hw_port_event(me, INT_PORT, 1);
650 		  }
651 
652 		/* clear counter */
653 		next_trr = 0;
654 	      }
655 	  }
656 	break;
657 
658 	case 2: /* watchdog timer mode */
659 	  {
660 	    /* watchdog timer expiry */
661 	    if (next_trr == controller->cpra)
662 	      {
663 		SET_TISR_TWIS(controller);
664 
665 		/* signal interrupt */
666 		if (GET_WTMR_TWIE(controller))
667 		  {
668 		    hw_port_event(me, INT_PORT, 1);
669 		  }
670 
671 		/* clear counter */
672 		next_trr = 0;
673 	      }
674 	  }
675 	break;
676 
677 	case 3: /* disabled */
678 	default:
679 	  break;
680 	}
681 
682       /* update counter and report */
683       controller->trr = next_trr;
684       /* HW_TRACE ((me, "counter trr %ld tisr %lx",
685 	 (long) controller->trr, (long) controller->tisr)); */
686     } /* end quotient loop */
687 
688   /* Reschedule a timer event in near future, so we can increment the
689      counter again.  Set the event about 75% of divisor time away, so
690      we will experience roughly 1.3 events per counter increment. */
691   controller->event = hw_event_queue_schedule(me, divisor*3/4, deliver_tx3904tmr_tick, NULL);
692 }
693 
694 
695 
696 
697 const struct hw_descriptor dv_tx3904tmr_descriptor[] = {
698   { "tx3904tmr", tx3904tmr_finish, },
699   { NULL },
700 };
701