MCF5208 emulation.
[qemu] / hw / mcf5208.c
1 /* 
2  * Motorola ColdFire MCF5208 SoC emulation.
3  *
4  * Copyright (c) 2007 CodeSourcery.
5  *
6  * This code is licenced under the GPL
7  */
8 #include "vl.h"
9
10 #define SYS_FREQ 66000000
11
12 #define PCSR_EN         0x0001
13 #define PCSR_RLD        0x0002
14 #define PCSR_PIF        0x0004
15 #define PCSR_PIE        0x0008
16 #define PCSR_OVW        0x0010
17 #define PCSR_DBG        0x0020
18 #define PCSR_DOZE       0x0040
19 #define PCSR_PRE_SHIFT  8
20 #define PCSR_PRE_MASK   0x0f00
21
22 typedef struct {
23     qemu_irq irq;
24     ptimer_state *timer;
25     uint16_t pcsr;
26     uint16_t pmr;
27     uint16_t pcntr;
28 } m5208_timer_state;
29
30 static void m5208_timer_update(m5208_timer_state *s)
31 {
32     if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF))
33         qemu_irq_raise(s->irq);
34     else
35         qemu_irq_lower(s->irq);
36 }
37
38 static void m5208_timer_write(m5208_timer_state *s, int offset,
39                               uint32_t value)
40 {
41     int prescale;
42     int limit;
43     switch (offset) {
44     case 0:
45         /* The PIF bit is set-to-clear.  */
46         if (value & PCSR_PIF) {
47             s->pcsr &= ~PCSR_PIF;
48             value &= ~PCSR_PIF;
49         }
50         /* Avoid frobbing the timer if we're just twiddling IRQ bits. */
51         if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) {
52             s->pcsr = value;
53             m5208_timer_update(s);
54             return;
55         }
56
57         if (s->pcsr & PCSR_EN)
58             ptimer_stop(s->timer);
59
60         s->pcsr = value;
61
62         prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT);
63         ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale);
64         if (s->pcsr & PCSR_RLD)
65             limit = 0xffff;
66         else
67             limit = s->pmr;
68         ptimer_set_limit(s->timer, limit, 0);
69
70         if (s->pcsr & PCSR_EN)
71             ptimer_run(s->timer, 0);
72         break;
73     case 2:
74         s->pmr = value;
75         s->pcsr &= ~PCSR_PIF;
76         if (s->pcsr & PCSR_RLD)
77             value = 0xffff;
78         ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW);
79         break;
80     case 4:
81         break;
82     default:
83         /* Should never happen.  */
84         abort();
85     }
86     m5208_timer_update(s);
87 }
88
89 static void m5208_timer_trigger(void *opaque)
90 {
91     m5208_timer_state *s = (m5208_timer_state *)opaque;
92     s->pcsr |= PCSR_PIF;
93     m5208_timer_update(s);
94 }
95
96 typedef struct {
97     m5208_timer_state timer[2];
98 } m5208_sys_state;
99
100 static uint32_t m5208_sys_read(void *opaque, target_phys_addr_t addr)
101 {
102     m5208_sys_state *s = (m5208_sys_state *)opaque;
103     switch (addr) {
104     /* PIT0 */
105     case 0xfc080000:
106         return s->timer[0].pcsr;
107     case 0xfc080002:
108         return s->timer[0].pmr;
109     case 0xfc080004:
110         return ptimer_get_count(s->timer[0].timer);
111     /* PIT1 */
112     case 0xfc084000:
113         return s->timer[1].pcsr;
114     case 0xfc084002:
115         return s->timer[1].pmr;
116     case 0xfc084004:
117         return ptimer_get_count(s->timer[1].timer);
118
119     /* SDRAM Controller.  */
120     case 0xfc0a8110: /* SDCS0 */
121         {
122             int n;
123             for (n = 0; n < 32; n++) {
124                 if (ram_size < (2u << n))
125                     break;
126             }
127             return (n - 1)  | 0x40000000;
128         }
129     case 0xfc0a8114: /* SDCS1 */
130         return 0;
131
132     default:
133         cpu_abort(cpu_single_env, "m5208_sys_read: Bad offset 0x%x\n",
134                   (int)addr);
135         return 0;
136     }
137 }
138
139 static void m5208_sys_write(void *opaque, target_phys_addr_t addr,
140                             uint32_t value)
141 {
142     m5208_sys_state *s = (m5208_sys_state *)opaque;
143     switch (addr) {
144     /* PIT0 */
145     case 0xfc080000:
146     case 0xfc080002:
147     case 0xfc080004:
148         m5208_timer_write(&s->timer[0], addr & 0xf, value);
149         return;
150     /* PIT1 */
151     case 0xfc084000:
152     case 0xfc084002:
153     case 0xfc084004:
154         m5208_timer_write(&s->timer[1], addr & 0xf, value);
155         return;
156     default:
157         cpu_abort(cpu_single_env, "m5208_sys_write: Bad offset 0x%x\n",
158                   (int)addr);
159         break;
160     }
161 }
162
163 static CPUReadMemoryFunc *m5208_sys_readfn[] = {
164    m5208_sys_read,
165    m5208_sys_read,
166    m5208_sys_read
167 };
168
169 static CPUWriteMemoryFunc *m5208_sys_writefn[] = {
170    m5208_sys_write,
171    m5208_sys_write,
172    m5208_sys_write
173 };
174
175 static void mcf5208_sys_init(qemu_irq *pic)
176 {
177     int iomemtype;
178     m5208_sys_state *s;
179     QEMUBH *bh;
180     int i;
181
182     s = (m5208_sys_state *)qemu_mallocz(sizeof(m5208_sys_state));
183     iomemtype = cpu_register_io_memory(0, m5208_sys_readfn,
184                                        m5208_sys_writefn, s);
185     /* SDRAMC.  */
186     cpu_register_physical_memory(0xfc0a8000, 0x00004000, iomemtype);
187     /* Timers.  */
188     for (i = 0; i < 2; i++) {
189         bh = qemu_bh_new(m5208_timer_trigger, &s->timer[i]);
190         s->timer[i].timer = ptimer_init(bh);
191         cpu_register_physical_memory(0xfc080000 + 0x4000 * i, 0x00004000,
192                                      iomemtype);
193         s->timer[i].irq = pic[4 + i];
194     }
195 }
196
197 static void mcf5208evb_init(int ram_size, int vga_ram_size, int boot_device,
198                      DisplayState *ds, const char **fd_filename, int snapshot,
199                      const char *kernel_filename, const char *kernel_cmdline,
200                      const char *initrd_filename, const char *cpu_model)
201 {
202     CPUState *env;
203     int kernel_size;
204     uint64_t elf_entry;
205     target_ulong entry;
206     qemu_irq *pic;
207
208     env = cpu_init();
209     if (!cpu_model)
210         cpu_model = "m5208";
211     if (cpu_m68k_set_model(env, cpu_model)) {
212         cpu_abort(env, "Unable to find m68k CPU definition\n");
213     }
214
215     /* Initialize CPU registers.  */
216     env->vbr = 0;
217     /* TODO: Configure BARs.  */
218
219     /* DRAM at 0x20000000 */
220     cpu_register_physical_memory(0x40000000, ram_size,
221         qemu_ram_alloc(ram_size) | IO_MEM_RAM);
222
223     /* Internal SRAM.  */
224     cpu_register_physical_memory(0x80000000, 16384,
225         qemu_ram_alloc(16384) | IO_MEM_RAM);
226
227     /* Internal peripherals.  */
228     pic = mcf_intc_init(0xfc048000, env);
229
230     mcf_uart_mm_init(0xfc060000, pic[26], serial_hds[0]);
231     mcf_uart_mm_init(0xfc064000, pic[27], serial_hds[1]);
232     mcf_uart_mm_init(0xfc068000, pic[28], serial_hds[2]);
233
234     mcf5208_sys_init(pic);
235
236     /*  0xfc000000 SCM.  */
237     /*  0xfc004000 XBS.  */
238     /*  0xfc008000 FlexBus CS.  */
239     /*  0xfc030000 FEC.  */
240     /*  0xfc040000 SCM + Power management.  */
241     /*  0xfc044000 eDMA.  */
242     /* 0xfc048000 INTC.  */
243     /*  0xfc058000 I2C.  */
244     /*  0xfc05c000 QSPI.  */
245     /* 0xfc060000 UART0.  */
246     /* 0xfc064000 UART0.  */
247     /* 0xfc068000 UART0.  */
248     /*  0xfc070000 DMA timers.  */
249     /* 0xfc080000 PIT0.  */
250     /* 0xfc084000 PIT1.  */
251     /*  0xfc088000 EPORT.  */
252     /*  0xfc08c000 Watchdog.  */
253     /*  0xfc090000 clock module.  */
254     /*  0xfc0a0000 CCM + reset.  */
255     /*  0xfc0a4000 GPIO.  */
256     /* 0xfc0a8000 SDRAM controller.  */
257
258     /* Load kernel.  */
259     if (!kernel_filename) {
260         fprintf(stderr, "Kernel image must be specified\n");
261         exit(1);
262     }
263
264     kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL);
265     entry = elf_entry;
266     if (kernel_size < 0) {
267         kernel_size = load_uboot(kernel_filename, &entry, NULL);
268     }
269     if (kernel_size < 0) {
270         kernel_size = load_image(kernel_filename, phys_ram_base);
271         entry = 0x20000000;
272     }
273     if (kernel_size < 0) {
274         fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
275         exit(1);
276     }
277
278     env->pc = entry;
279 }
280
281 QEMUMachine mcf5208evb_machine = {
282     "mcf5208evb",
283     "MCF5206EVB",
284     mcf5208evb_init,
285 };