xref: /openbmc/qemu/hw/m68k/mcf5206.c (revision 1f7685fa)
1  /*
2   * Motorola ColdFire MCF5206 SoC embedded peripheral emulation.
3   *
4   * Copyright (c) 2007 CodeSourcery.
5   *
6   * This code is licensed under the GPL
7   */
8  #include "qemu/osdep.h"
9  #include "qemu-common.h"
10  #include "cpu.h"
11  #include "hw/hw.h"
12  #include "hw/m68k/mcf.h"
13  #include "qemu/timer.h"
14  #include "hw/ptimer.h"
15  #include "sysemu/sysemu.h"
16  #include "exec/address-spaces.h"
17  
18  /* General purpose timer module.  */
19  typedef struct {
20      uint16_t tmr;
21      uint16_t trr;
22      uint16_t tcr;
23      uint16_t ter;
24      ptimer_state *timer;
25      qemu_irq irq;
26      int irq_state;
27  } m5206_timer_state;
28  
29  #define TMR_RST 0x01
30  #define TMR_CLK 0x06
31  #define TMR_FRR 0x08
32  #define TMR_ORI 0x10
33  #define TMR_OM  0x20
34  #define TMR_CE  0xc0
35  
36  #define TER_CAP 0x01
37  #define TER_REF 0x02
38  
39  static void m5206_timer_update(m5206_timer_state *s)
40  {
41      if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF))
42          qemu_irq_raise(s->irq);
43      else
44          qemu_irq_lower(s->irq);
45  }
46  
47  static void m5206_timer_reset(m5206_timer_state *s)
48  {
49      s->tmr = 0;
50      s->trr = 0;
51  }
52  
53  static void m5206_timer_recalibrate(m5206_timer_state *s)
54  {
55      int prescale;
56      int mode;
57  
58      ptimer_stop(s->timer);
59  
60      if ((s->tmr & TMR_RST) == 0)
61          return;
62  
63      prescale = (s->tmr >> 8) + 1;
64      mode = (s->tmr >> 1) & 3;
65      if (mode == 2)
66          prescale *= 16;
67  
68      if (mode == 3 || mode == 0)
69          hw_error("m5206_timer: mode %d not implemented\n", mode);
70      if ((s->tmr & TMR_FRR) == 0)
71          hw_error("m5206_timer: free running mode not implemented\n");
72  
73      /* Assume 66MHz system clock.  */
74      ptimer_set_freq(s->timer, 66000000 / prescale);
75  
76      ptimer_set_limit(s->timer, s->trr, 0);
77  
78      ptimer_run(s->timer, 0);
79  }
80  
81  static void m5206_timer_trigger(void *opaque)
82  {
83      m5206_timer_state *s = (m5206_timer_state *)opaque;
84      s->ter |= TER_REF;
85      m5206_timer_update(s);
86  }
87  
88  static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr)
89  {
90      switch (addr) {
91      case 0:
92          return s->tmr;
93      case 4:
94          return s->trr;
95      case 8:
96          return s->tcr;
97      case 0xc:
98          return s->trr - ptimer_get_count(s->timer);
99      case 0x11:
100          return s->ter;
101      default:
102          return 0;
103      }
104  }
105  
106  static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
107  {
108      switch (addr) {
109      case 0:
110          if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) {
111              m5206_timer_reset(s);
112          }
113          s->tmr = val;
114          m5206_timer_recalibrate(s);
115          break;
116      case 4:
117          s->trr = val;
118          m5206_timer_recalibrate(s);
119          break;
120      case 8:
121          s->tcr = val;
122          break;
123      case 0xc:
124          ptimer_set_count(s->timer, val);
125          break;
126      case 0x11:
127          s->ter &= ~val;
128          break;
129      default:
130          break;
131      }
132      m5206_timer_update(s);
133  }
134  
135  static m5206_timer_state *m5206_timer_init(qemu_irq irq)
136  {
137      m5206_timer_state *s;
138      QEMUBH *bh;
139  
140      s = (m5206_timer_state *)g_malloc0(sizeof(m5206_timer_state));
141      bh = qemu_bh_new(m5206_timer_trigger, s);
142      s->timer = ptimer_init(bh);
143      s->irq = irq;
144      m5206_timer_reset(s);
145      return s;
146  }
147  
148  /* System Integration Module.  */
149  
150  typedef struct {
151      M68kCPU *cpu;
152      MemoryRegion iomem;
153      m5206_timer_state *timer[2];
154      void *uart[2];
155      uint8_t scr;
156      uint8_t icr[14];
157      uint16_t imr; /* 1 == interrupt is masked.  */
158      uint16_t ipr;
159      uint8_t rsr;
160      uint8_t swivr;
161      uint8_t par;
162      /* Include the UART vector registers here.  */
163      uint8_t uivr[2];
164  } m5206_mbar_state;
165  
166  /* Interrupt controller.  */
167  
168  static int m5206_find_pending_irq(m5206_mbar_state *s)
169  {
170      int level;
171      int vector;
172      uint16_t active;
173      int i;
174  
175      level = 0;
176      vector = 0;
177      active = s->ipr & ~s->imr;
178      if (!active)
179          return 0;
180  
181      for (i = 1; i < 14; i++) {
182          if (active & (1 << i)) {
183              if ((s->icr[i] & 0x1f) > level) {
184                  level = s->icr[i] & 0x1f;
185                  vector = i;
186              }
187          }
188      }
189  
190      if (level < 4)
191          vector = 0;
192  
193      return vector;
194  }
195  
196  static void m5206_mbar_update(m5206_mbar_state *s)
197  {
198      int irq;
199      int vector;
200      int level;
201  
202      irq = m5206_find_pending_irq(s);
203      if (irq) {
204          int tmp;
205          tmp = s->icr[irq];
206          level = (tmp >> 2) & 7;
207          if (tmp & 0x80) {
208              /* Autovector.  */
209              vector = 24 + level;
210          } else {
211              switch (irq) {
212              case 8: /* SWT */
213                  vector = s->swivr;
214                  break;
215              case 12: /* UART1 */
216                  vector = s->uivr[0];
217                  break;
218              case 13: /* UART2 */
219                  vector = s->uivr[1];
220                  break;
221              default:
222                  /* Unknown vector.  */
223                  fprintf(stderr, "Unhandled vector for IRQ %d\n", irq);
224                  vector = 0xf;
225                  break;
226              }
227          }
228      } else {
229          level = 0;
230          vector = 0;
231      }
232      m68k_set_irq_level(s->cpu, level, vector);
233  }
234  
235  static void m5206_mbar_set_irq(void *opaque, int irq, int level)
236  {
237      m5206_mbar_state *s = (m5206_mbar_state *)opaque;
238      if (level) {
239          s->ipr |= 1 << irq;
240      } else {
241          s->ipr &= ~(1 << irq);
242      }
243      m5206_mbar_update(s);
244  }
245  
246  /* System Integration Module.  */
247  
248  static void m5206_mbar_reset(m5206_mbar_state *s)
249  {
250      s->scr = 0xc0;
251      s->icr[1] = 0x04;
252      s->icr[2] = 0x08;
253      s->icr[3] = 0x0c;
254      s->icr[4] = 0x10;
255      s->icr[5] = 0x14;
256      s->icr[6] = 0x18;
257      s->icr[7] = 0x1c;
258      s->icr[8] = 0x1c;
259      s->icr[9] = 0x80;
260      s->icr[10] = 0x80;
261      s->icr[11] = 0x80;
262      s->icr[12] = 0x00;
263      s->icr[13] = 0x00;
264      s->imr = 0x3ffe;
265      s->rsr = 0x80;
266      s->swivr = 0x0f;
267      s->par = 0;
268  }
269  
270  static uint64_t m5206_mbar_read(m5206_mbar_state *s,
271                                  uint64_t offset, unsigned size)
272  {
273      if (offset >= 0x100 && offset < 0x120) {
274          return m5206_timer_read(s->timer[0], offset - 0x100);
275      } else if (offset >= 0x120 && offset < 0x140) {
276          return m5206_timer_read(s->timer[1], offset - 0x120);
277      } else if (offset >= 0x140 && offset < 0x160) {
278          return mcf_uart_read(s->uart[0], offset - 0x140, size);
279      } else if (offset >= 0x180 && offset < 0x1a0) {
280          return mcf_uart_read(s->uart[1], offset - 0x180, size);
281      }
282      switch (offset) {
283      case 0x03: return s->scr;
284      case 0x14 ... 0x20: return s->icr[offset - 0x13];
285      case 0x36: return s->imr;
286      case 0x3a: return s->ipr;
287      case 0x40: return s->rsr;
288      case 0x41: return 0;
289      case 0x42: return s->swivr;
290      case 0x50:
291          /* DRAM mask register.  */
292          /* FIXME: currently hardcoded to 128Mb.  */
293          {
294              uint32_t mask = ~0;
295              while (mask > ram_size)
296                  mask >>= 1;
297              return mask & 0x0ffe0000;
298          }
299      case 0x5c: return 1; /* DRAM bank 1 empty.  */
300      case 0xcb: return s->par;
301      case 0x170: return s->uivr[0];
302      case 0x1b0: return s->uivr[1];
303      }
304      hw_error("Bad MBAR read offset 0x%x", (int)offset);
305      return 0;
306  }
307  
308  static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset,
309                               uint64_t value, unsigned size)
310  {
311      if (offset >= 0x100 && offset < 0x120) {
312          m5206_timer_write(s->timer[0], offset - 0x100, value);
313          return;
314      } else if (offset >= 0x120 && offset < 0x140) {
315          m5206_timer_write(s->timer[1], offset - 0x120, value);
316          return;
317      } else if (offset >= 0x140 && offset < 0x160) {
318          mcf_uart_write(s->uart[0], offset - 0x140, value, size);
319          return;
320      } else if (offset >= 0x180 && offset < 0x1a0) {
321          mcf_uart_write(s->uart[1], offset - 0x180, value, size);
322          return;
323      }
324      switch (offset) {
325      case 0x03:
326          s->scr = value;
327          break;
328      case 0x14 ... 0x20:
329          s->icr[offset - 0x13] = value;
330          m5206_mbar_update(s);
331          break;
332      case 0x36:
333          s->imr = value;
334          m5206_mbar_update(s);
335          break;
336      case 0x40:
337          s->rsr &= ~value;
338          break;
339      case 0x41:
340          /* TODO: implement watchdog.  */
341          break;
342      case 0x42:
343          s->swivr = value;
344          break;
345      case 0xcb:
346          s->par = value;
347          break;
348      case 0x170:
349          s->uivr[0] = value;
350          break;
351      case 0x178: case 0x17c: case 0x1c8: case 0x1bc:
352          /* Not implemented: UART Output port bits.  */
353          break;
354      case 0x1b0:
355          s->uivr[1] = value;
356          break;
357      default:
358          hw_error("Bad MBAR write offset 0x%x", (int)offset);
359          break;
360      }
361  }
362  
363  /* Internal peripherals use a variety of register widths.
364     This lookup table allows a single routine to handle all of them.  */
365  static const uint8_t m5206_mbar_width[] =
366  {
367    /* 000-040 */ 1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  2, 2, 2, 2,
368    /* 040-080 */ 1, 2, 2, 2,  4, 1, 2, 4,  1, 2, 4, 2,  2, 4, 2, 2,
369    /* 080-0c0 */ 4, 2, 2, 4,  2, 2, 4, 2,  2, 4, 2, 2,  4, 2, 2, 4,
370    /* 0c0-100 */ 2, 2, 1, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
371    /* 100-140 */ 2, 2, 2, 2,  1, 0, 0, 0,  2, 2, 2, 2,  1, 0, 0, 0,
372    /* 140-180 */ 1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
373    /* 180-1c0 */ 1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
374    /* 1c0-200 */ 1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
375  };
376  
377  static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset);
378  static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset);
379  
380  static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset)
381  {
382      m5206_mbar_state *s = (m5206_mbar_state *)opaque;
383      offset &= 0x3ff;
384      if (offset >= 0x200) {
385          hw_error("Bad MBAR read offset 0x%x", (int)offset);
386      }
387      if (m5206_mbar_width[offset >> 2] > 1) {
388          uint16_t val;
389          val = m5206_mbar_readw(opaque, offset & ~1);
390          if ((offset & 1) == 0) {
391              val >>= 8;
392          }
393          return val & 0xff;
394      }
395      return m5206_mbar_read(s, offset, 1);
396  }
397  
398  static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset)
399  {
400      m5206_mbar_state *s = (m5206_mbar_state *)opaque;
401      int width;
402      offset &= 0x3ff;
403      if (offset >= 0x200) {
404          hw_error("Bad MBAR read offset 0x%x", (int)offset);
405      }
406      width = m5206_mbar_width[offset >> 2];
407      if (width > 2) {
408          uint32_t val;
409          val = m5206_mbar_readl(opaque, offset & ~3);
410          if ((offset & 3) == 0)
411              val >>= 16;
412          return val & 0xffff;
413      } else if (width < 2) {
414          uint16_t val;
415          val = m5206_mbar_readb(opaque, offset) << 8;
416          val |= m5206_mbar_readb(opaque, offset + 1);
417          return val;
418      }
419      return m5206_mbar_read(s, offset, 2);
420  }
421  
422  static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset)
423  {
424      m5206_mbar_state *s = (m5206_mbar_state *)opaque;
425      int width;
426      offset &= 0x3ff;
427      if (offset >= 0x200) {
428          hw_error("Bad MBAR read offset 0x%x", (int)offset);
429      }
430      width = m5206_mbar_width[offset >> 2];
431      if (width < 4) {
432          uint32_t val;
433          val = m5206_mbar_readw(opaque, offset) << 16;
434          val |= m5206_mbar_readw(opaque, offset + 2);
435          return val;
436      }
437      return m5206_mbar_read(s, offset, 4);
438  }
439  
440  static void m5206_mbar_writew(void *opaque, hwaddr offset,
441                                uint32_t value);
442  static void m5206_mbar_writel(void *opaque, hwaddr offset,
443                                uint32_t value);
444  
445  static void m5206_mbar_writeb(void *opaque, hwaddr offset,
446                                uint32_t value)
447  {
448      m5206_mbar_state *s = (m5206_mbar_state *)opaque;
449      int width;
450      offset &= 0x3ff;
451      if (offset >= 0x200) {
452          hw_error("Bad MBAR write offset 0x%x", (int)offset);
453      }
454      width = m5206_mbar_width[offset >> 2];
455      if (width > 1) {
456          uint32_t tmp;
457          tmp = m5206_mbar_readw(opaque, offset & ~1);
458          if (offset & 1) {
459              tmp = (tmp & 0xff00) | value;
460          } else {
461              tmp = (tmp & 0x00ff) | (value << 8);
462          }
463          m5206_mbar_writew(opaque, offset & ~1, tmp);
464          return;
465      }
466      m5206_mbar_write(s, offset, value, 1);
467  }
468  
469  static void m5206_mbar_writew(void *opaque, hwaddr offset,
470                                uint32_t value)
471  {
472      m5206_mbar_state *s = (m5206_mbar_state *)opaque;
473      int width;
474      offset &= 0x3ff;
475      if (offset >= 0x200) {
476          hw_error("Bad MBAR write offset 0x%x", (int)offset);
477      }
478      width = m5206_mbar_width[offset >> 2];
479      if (width > 2) {
480          uint32_t tmp;
481          tmp = m5206_mbar_readl(opaque, offset & ~3);
482          if (offset & 3) {
483              tmp = (tmp & 0xffff0000) | value;
484          } else {
485              tmp = (tmp & 0x0000ffff) | (value << 16);
486          }
487          m5206_mbar_writel(opaque, offset & ~3, tmp);
488          return;
489      } else if (width < 2) {
490          m5206_mbar_writeb(opaque, offset, value >> 8);
491          m5206_mbar_writeb(opaque, offset + 1, value & 0xff);
492          return;
493      }
494      m5206_mbar_write(s, offset, value, 2);
495  }
496  
497  static void m5206_mbar_writel(void *opaque, hwaddr offset,
498                                uint32_t value)
499  {
500      m5206_mbar_state *s = (m5206_mbar_state *)opaque;
501      int width;
502      offset &= 0x3ff;
503      if (offset >= 0x200) {
504          hw_error("Bad MBAR write offset 0x%x", (int)offset);
505      }
506      width = m5206_mbar_width[offset >> 2];
507      if (width < 4) {
508          m5206_mbar_writew(opaque, offset, value >> 16);
509          m5206_mbar_writew(opaque, offset + 2, value & 0xffff);
510          return;
511      }
512      m5206_mbar_write(s, offset, value, 4);
513  }
514  
515  static const MemoryRegionOps m5206_mbar_ops = {
516      .old_mmio = {
517          .read = {
518              m5206_mbar_readb,
519              m5206_mbar_readw,
520              m5206_mbar_readl,
521          },
522          .write = {
523              m5206_mbar_writeb,
524              m5206_mbar_writew,
525              m5206_mbar_writel,
526          },
527      },
528      .endianness = DEVICE_NATIVE_ENDIAN,
529  };
530  
531  qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, M68kCPU *cpu)
532  {
533      m5206_mbar_state *s;
534      qemu_irq *pic;
535  
536      s = (m5206_mbar_state *)g_malloc0(sizeof(m5206_mbar_state));
537  
538      memory_region_init_io(&s->iomem, NULL, &m5206_mbar_ops, s,
539                            "mbar", 0x00001000);
540      memory_region_add_subregion(sysmem, base, &s->iomem);
541  
542      pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14);
543      s->timer[0] = m5206_timer_init(pic[9]);
544      s->timer[1] = m5206_timer_init(pic[10]);
545      s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]);
546      s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]);
547      s->cpu = cpu;
548  
549      m5206_mbar_reset(s);
550      return pic;
551  }
552