xref: /openbmc/qemu/hw/m68k/mcf5208.c (revision b957a1b0)
1  /*
2   * Motorola ColdFire MCF5208 SoC emulation.
3   *
4   * Copyright (c) 2007 CodeSourcery.
5   *
6   * This code is licensed under the GPL
7   */
8  #include "hw/hw.h"
9  #include "hw/m68k/mcf.h"
10  #include "qemu/timer.h"
11  #include "hw/ptimer.h"
12  #include "sysemu/sysemu.h"
13  #include "net/net.h"
14  #include "hw/boards.h"
15  #include "hw/loader.h"
16  #include "elf.h"
17  #include "exec/address-spaces.h"
18  
19  #define SYS_FREQ 66000000
20  
21  #define PCSR_EN         0x0001
22  #define PCSR_RLD        0x0002
23  #define PCSR_PIF        0x0004
24  #define PCSR_PIE        0x0008
25  #define PCSR_OVW        0x0010
26  #define PCSR_DBG        0x0020
27  #define PCSR_DOZE       0x0040
28  #define PCSR_PRE_SHIFT  8
29  #define PCSR_PRE_MASK   0x0f00
30  
31  typedef struct {
32      MemoryRegion iomem;
33      qemu_irq irq;
34      ptimer_state *timer;
35      uint16_t pcsr;
36      uint16_t pmr;
37      uint16_t pcntr;
38  } m5208_timer_state;
39  
40  static void m5208_timer_update(m5208_timer_state *s)
41  {
42      if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF))
43          qemu_irq_raise(s->irq);
44      else
45          qemu_irq_lower(s->irq);
46  }
47  
48  static void m5208_timer_write(void *opaque, hwaddr offset,
49                                uint64_t value, unsigned size)
50  {
51      m5208_timer_state *s = (m5208_timer_state *)opaque;
52      int prescale;
53      int limit;
54      switch (offset) {
55      case 0:
56          /* The PIF bit is set-to-clear.  */
57          if (value & PCSR_PIF) {
58              s->pcsr &= ~PCSR_PIF;
59              value &= ~PCSR_PIF;
60          }
61          /* Avoid frobbing the timer if we're just twiddling IRQ bits. */
62          if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) {
63              s->pcsr = value;
64              m5208_timer_update(s);
65              return;
66          }
67  
68          if (s->pcsr & PCSR_EN)
69              ptimer_stop(s->timer);
70  
71          s->pcsr = value;
72  
73          prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT);
74          ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale);
75          if (s->pcsr & PCSR_RLD)
76              limit = s->pmr;
77          else
78              limit = 0xffff;
79          ptimer_set_limit(s->timer, limit, 0);
80  
81          if (s->pcsr & PCSR_EN)
82              ptimer_run(s->timer, 0);
83          break;
84      case 2:
85          s->pmr = value;
86          s->pcsr &= ~PCSR_PIF;
87          if ((s->pcsr & PCSR_RLD) == 0) {
88              if (s->pcsr & PCSR_OVW)
89                  ptimer_set_count(s->timer, value);
90          } else {
91              ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW);
92          }
93          break;
94      case 4:
95          break;
96      default:
97          hw_error("m5208_timer_write: Bad offset 0x%x\n", (int)offset);
98          break;
99      }
100      m5208_timer_update(s);
101  }
102  
103  static void m5208_timer_trigger(void *opaque)
104  {
105      m5208_timer_state *s = (m5208_timer_state *)opaque;
106      s->pcsr |= PCSR_PIF;
107      m5208_timer_update(s);
108  }
109  
110  static uint64_t m5208_timer_read(void *opaque, hwaddr addr,
111                                   unsigned size)
112  {
113      m5208_timer_state *s = (m5208_timer_state *)opaque;
114      switch (addr) {
115      case 0:
116          return s->pcsr;
117      case 2:
118          return s->pmr;
119      case 4:
120          return ptimer_get_count(s->timer);
121      default:
122          hw_error("m5208_timer_read: Bad offset 0x%x\n", (int)addr);
123          return 0;
124      }
125  }
126  
127  static const MemoryRegionOps m5208_timer_ops = {
128      .read = m5208_timer_read,
129      .write = m5208_timer_write,
130      .endianness = DEVICE_NATIVE_ENDIAN,
131  };
132  
133  static uint64_t m5208_sys_read(void *opaque, hwaddr addr,
134                                 unsigned size)
135  {
136      switch (addr) {
137      case 0x110: /* SDCS0 */
138          {
139              int n;
140              for (n = 0; n < 32; n++) {
141                  if (ram_size < (2u << n))
142                      break;
143              }
144              return (n - 1)  | 0x40000000;
145          }
146      case 0x114: /* SDCS1 */
147          return 0;
148  
149      default:
150          hw_error("m5208_sys_read: Bad offset 0x%x\n", (int)addr);
151          return 0;
152      }
153  }
154  
155  static void m5208_sys_write(void *opaque, hwaddr addr,
156                              uint64_t value, unsigned size)
157  {
158      hw_error("m5208_sys_write: Bad offset 0x%x\n", (int)addr);
159  }
160  
161  static const MemoryRegionOps m5208_sys_ops = {
162      .read = m5208_sys_read,
163      .write = m5208_sys_write,
164      .endianness = DEVICE_NATIVE_ENDIAN,
165  };
166  
167  static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic)
168  {
169      MemoryRegion *iomem = g_new(MemoryRegion, 1);
170      m5208_timer_state *s;
171      QEMUBH *bh;
172      int i;
173  
174      /* SDRAMC.  */
175      memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys", 0x00004000);
176      memory_region_add_subregion(address_space, 0xfc0a8000, iomem);
177      /* Timers.  */
178      for (i = 0; i < 2; i++) {
179          s = (m5208_timer_state *)g_malloc0(sizeof(m5208_timer_state));
180          bh = qemu_bh_new(m5208_timer_trigger, s);
181          s->timer = ptimer_init(bh);
182          memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s,
183                                "m5208-timer", 0x00004000);
184          memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i,
185                                      &s->iomem);
186          s->irq = pic[4 + i];
187      }
188  }
189  
190  static void mcf5208evb_init(QEMUMachineInitArgs *args)
191  {
192      ram_addr_t ram_size = args->ram_size;
193      const char *cpu_model = args->cpu_model;
194      const char *kernel_filename = args->kernel_filename;
195      M68kCPU *cpu;
196      CPUM68KState *env;
197      int kernel_size;
198      uint64_t elf_entry;
199      hwaddr entry;
200      qemu_irq *pic;
201      MemoryRegion *address_space_mem = get_system_memory();
202      MemoryRegion *ram = g_new(MemoryRegion, 1);
203      MemoryRegion *sram = g_new(MemoryRegion, 1);
204  
205      if (!cpu_model) {
206          cpu_model = "m5208";
207      }
208      cpu = cpu_m68k_init(cpu_model);
209      if (!cpu) {
210          fprintf(stderr, "Unable to find m68k CPU definition\n");
211          exit(1);
212      }
213      env = &cpu->env;
214  
215      /* Initialize CPU registers.  */
216      env->vbr = 0;
217      /* TODO: Configure BARs.  */
218  
219      /* DRAM at 0x40000000 */
220      memory_region_init_ram(ram, NULL, "mcf5208.ram", ram_size);
221      vmstate_register_ram_global(ram);
222      memory_region_add_subregion(address_space_mem, 0x40000000, ram);
223  
224      /* Internal SRAM.  */
225      memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384);
226      vmstate_register_ram_global(sram);
227      memory_region_add_subregion(address_space_mem, 0x80000000, sram);
228  
229      /* Internal peripherals.  */
230      pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu);
231  
232      mcf_uart_mm_init(address_space_mem, 0xfc060000, pic[26], serial_hds[0]);
233      mcf_uart_mm_init(address_space_mem, 0xfc064000, pic[27], serial_hds[1]);
234      mcf_uart_mm_init(address_space_mem, 0xfc068000, pic[28], serial_hds[2]);
235  
236      mcf5208_sys_init(address_space_mem, pic);
237  
238      if (nb_nics > 1) {
239          fprintf(stderr, "Too many NICs\n");
240          exit(1);
241      }
242      if (nd_table[0].used)
243          mcf_fec_init(address_space_mem, &nd_table[0],
244                       0xfc030000, pic + 36);
245  
246      /*  0xfc000000 SCM.  */
247      /*  0xfc004000 XBS.  */
248      /*  0xfc008000 FlexBus CS.  */
249      /* 0xfc030000 FEC.  */
250      /*  0xfc040000 SCM + Power management.  */
251      /*  0xfc044000 eDMA.  */
252      /* 0xfc048000 INTC.  */
253      /*  0xfc058000 I2C.  */
254      /*  0xfc05c000 QSPI.  */
255      /* 0xfc060000 UART0.  */
256      /* 0xfc064000 UART0.  */
257      /* 0xfc068000 UART0.  */
258      /*  0xfc070000 DMA timers.  */
259      /* 0xfc080000 PIT0.  */
260      /* 0xfc084000 PIT1.  */
261      /*  0xfc088000 EPORT.  */
262      /*  0xfc08c000 Watchdog.  */
263      /*  0xfc090000 clock module.  */
264      /*  0xfc0a0000 CCM + reset.  */
265      /*  0xfc0a4000 GPIO.  */
266      /* 0xfc0a8000 SDRAM controller.  */
267  
268      /* Load kernel.  */
269      if (!kernel_filename) {
270          fprintf(stderr, "Kernel image must be specified\n");
271          exit(1);
272      }
273  
274      kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
275                             NULL, NULL, 1, ELF_MACHINE, 0);
276      entry = elf_entry;
277      if (kernel_size < 0) {
278          kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
279      }
280      if (kernel_size < 0) {
281          kernel_size = load_image_targphys(kernel_filename, 0x40000000,
282                                            ram_size);
283          entry = 0x40000000;
284      }
285      if (kernel_size < 0) {
286          fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
287          exit(1);
288      }
289  
290      env->pc = entry;
291  }
292  
293  static QEMUMachine mcf5208evb_machine = {
294      .name = "mcf5208evb",
295      .desc = "MCF5206EVB",
296      .init = mcf5208evb_init,
297      .is_default = 1,
298      DEFAULT_MACHINE_OPTIONS,
299  };
300  
301  static void mcf5208evb_machine_init(void)
302  {
303      qemu_register_machine(&mcf5208evb_machine);
304  }
305  
306  machine_init(mcf5208evb_machine_init);
307