1 | /* |
2 | * Marvell MV88W8618 / Freecom MusicPal emulation. |
3 | * |
4 | * Copyright (c) 2008 Jan Kiszka |
5 | * |
6 | * This code is licensed under the GNU GPL v2. |
7 | * |
8 | * Contributions after 2012-01-13 are licensed under the terms of the |
9 | * GNU GPL, version 2 or (at your option) any later version. |
10 | */ |
11 | |
12 | #include "qemu/osdep.h" |
13 | #include "qapi/error.h" |
14 | #include "cpu.h" |
15 | #include "hw/sysbus.h" |
16 | #include "migration/vmstate.h" |
17 | #include "hw/arm/boot.h" |
18 | #include "net/net.h" |
19 | #include "sysemu/sysemu.h" |
20 | #include "hw/boards.h" |
21 | #include "hw/char/serial.h" |
22 | #include "hw/hw.h" |
23 | #include "qemu/timer.h" |
24 | #include "hw/ptimer.h" |
25 | #include "hw/qdev-properties.h" |
26 | #include "hw/block/flash.h" |
27 | #include "ui/console.h" |
28 | #include "hw/i2c/i2c.h" |
29 | #include "hw/irq.h" |
30 | #include "hw/audio/wm8750.h" |
31 | #include "sysemu/block-backend.h" |
32 | #include "sysemu/runstate.h" |
33 | #include "exec/address-spaces.h" |
34 | #include "ui/pixel_ops.h" |
35 | |
36 | #define MP_MISC_BASE 0x80002000 |
37 | #define MP_MISC_SIZE 0x00001000 |
38 | |
39 | #define MP_ETH_BASE 0x80008000 |
40 | #define MP_ETH_SIZE 0x00001000 |
41 | |
42 | #define MP_WLAN_BASE 0x8000C000 |
43 | #define MP_WLAN_SIZE 0x00000800 |
44 | |
45 | #define MP_UART1_BASE 0x8000C840 |
46 | #define MP_UART2_BASE 0x8000C940 |
47 | |
48 | #define MP_GPIO_BASE 0x8000D000 |
49 | #define MP_GPIO_SIZE 0x00001000 |
50 | |
51 | #define MP_FLASHCFG_BASE 0x90006000 |
52 | #define MP_FLASHCFG_SIZE 0x00001000 |
53 | |
54 | #define MP_AUDIO_BASE 0x90007000 |
55 | |
56 | #define MP_PIC_BASE 0x90008000 |
57 | #define MP_PIC_SIZE 0x00001000 |
58 | |
59 | #define MP_PIT_BASE 0x90009000 |
60 | #define MP_PIT_SIZE 0x00001000 |
61 | |
62 | #define MP_LCD_BASE 0x9000c000 |
63 | #define MP_LCD_SIZE 0x00001000 |
64 | |
65 | #define MP_SRAM_BASE 0xC0000000 |
66 | #define MP_SRAM_SIZE 0x00020000 |
67 | |
68 | #define MP_RAM_DEFAULT_SIZE 32*1024*1024 |
69 | #define MP_FLASH_SIZE_MAX 32*1024*1024 |
70 | |
71 | #define MP_TIMER1_IRQ 4 |
72 | #define MP_TIMER2_IRQ 5 |
73 | #define MP_TIMER3_IRQ 6 |
74 | #define MP_TIMER4_IRQ 7 |
75 | #define MP_EHCI_IRQ 8 |
76 | #define MP_ETH_IRQ 9 |
77 | #define MP_UART1_IRQ 11 |
78 | #define MP_UART2_IRQ 11 |
79 | #define MP_GPIO_IRQ 12 |
80 | #define MP_RTC_IRQ 28 |
81 | #define MP_AUDIO_IRQ 30 |
82 | |
83 | /* Wolfson 8750 I2C address */ |
84 | #define MP_WM_ADDR 0x1A |
85 | |
86 | /* Ethernet register offsets */ |
87 | #define MP_ETH_SMIR 0x010 |
88 | #define MP_ETH_PCXR 0x408 |
89 | #define MP_ETH_SDCMR 0x448 |
90 | #define MP_ETH_ICR 0x450 |
91 | #define MP_ETH_IMR 0x458 |
92 | #define MP_ETH_FRDP0 0x480 |
93 | #define MP_ETH_FRDP1 0x484 |
94 | #define MP_ETH_FRDP2 0x488 |
95 | #define MP_ETH_FRDP3 0x48C |
96 | #define MP_ETH_CRDP0 0x4A0 |
97 | #define MP_ETH_CRDP1 0x4A4 |
98 | #define MP_ETH_CRDP2 0x4A8 |
99 | #define MP_ETH_CRDP3 0x4AC |
100 | #define MP_ETH_CTDP0 0x4E0 |
101 | #define MP_ETH_CTDP1 0x4E4 |
102 | |
103 | /* MII PHY access */ |
104 | #define MP_ETH_SMIR_DATA 0x0000FFFF |
105 | #define MP_ETH_SMIR_ADDR 0x03FF0000 |
106 | #define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */ |
107 | #define MP_ETH_SMIR_RDVALID (1 << 27) |
108 | |
109 | /* PHY registers */ |
110 | #define MP_ETH_PHY1_BMSR 0x00210000 |
111 | #define MP_ETH_PHY1_PHYSID1 0x00410000 |
112 | #define MP_ETH_PHY1_PHYSID2 0x00610000 |
113 | |
114 | #define MP_PHY_BMSR_LINK 0x0004 |
115 | #define MP_PHY_BMSR_AUTONEG 0x0008 |
116 | |
117 | #define MP_PHY_88E3015 0x01410E20 |
118 | |
119 | /* TX descriptor status */ |
120 | #define MP_ETH_TX_OWN (1U << 31) |
121 | |
122 | /* RX descriptor status */ |
123 | #define MP_ETH_RX_OWN (1U << 31) |
124 | |
125 | /* Interrupt cause/mask bits */ |
126 | #define MP_ETH_IRQ_RX_BIT 0 |
127 | #define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT) |
128 | #define MP_ETH_IRQ_TXHI_BIT 2 |
129 | #define MP_ETH_IRQ_TXLO_BIT 3 |
130 | |
131 | /* Port config bits */ |
132 | #define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */ |
133 | |
134 | /* SDMA command bits */ |
135 | #define MP_ETH_CMD_TXHI (1 << 23) |
136 | #define MP_ETH_CMD_TXLO (1 << 22) |
137 | |
138 | typedef struct mv88w8618_tx_desc { |
139 | uint32_t cmdstat; |
140 | uint16_t res; |
141 | uint16_t bytes; |
142 | uint32_t buffer; |
143 | uint32_t next; |
144 | } mv88w8618_tx_desc; |
145 | |
146 | typedef struct mv88w8618_rx_desc { |
147 | uint32_t cmdstat; |
148 | uint16_t bytes; |
149 | uint16_t buffer_size; |
150 | uint32_t buffer; |
151 | uint32_t next; |
152 | } mv88w8618_rx_desc; |
153 | |
154 | #define TYPE_MV88W8618_ETH "mv88w8618_eth" |
155 | #define MV88W8618_ETH(obj) \ |
156 | OBJECT_CHECK(mv88w8618_eth_state, (obj), TYPE_MV88W8618_ETH) |
157 | |
158 | typedef struct mv88w8618_eth_state { |
159 | /*< private >*/ |
160 | SysBusDevice parent_obj; |
161 | /*< public >*/ |
162 | |
163 | MemoryRegion iomem; |
164 | qemu_irq irq; |
165 | uint32_t smir; |
166 | uint32_t icr; |
167 | uint32_t imr; |
168 | int mmio_index; |
169 | uint32_t ; |
170 | uint32_t tx_queue[2]; |
171 | uint32_t rx_queue[4]; |
172 | uint32_t frx_queue[4]; |
173 | uint32_t cur_rx[4]; |
174 | NICState *nic; |
175 | NICConf conf; |
176 | } mv88w8618_eth_state; |
177 | |
178 | static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc) |
179 | { |
180 | cpu_to_le32s(&desc->cmdstat); |
181 | cpu_to_le16s(&desc->bytes); |
182 | cpu_to_le16s(&desc->buffer_size); |
183 | cpu_to_le32s(&desc->buffer); |
184 | cpu_to_le32s(&desc->next); |
185 | cpu_physical_memory_write(addr, desc, sizeof(*desc)); |
186 | } |
187 | |
188 | static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) |
189 | { |
190 | cpu_physical_memory_read(addr, desc, sizeof(*desc)); |
191 | le32_to_cpus(&desc->cmdstat); |
192 | le16_to_cpus(&desc->bytes); |
193 | le16_to_cpus(&desc->buffer_size); |
194 | le32_to_cpus(&desc->buffer); |
195 | le32_to_cpus(&desc->next); |
196 | } |
197 | |
198 | static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) |
199 | { |
200 | mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); |
201 | uint32_t desc_addr; |
202 | mv88w8618_rx_desc desc; |
203 | int i; |
204 | |
205 | for (i = 0; i < 4; i++) { |
206 | desc_addr = s->cur_rx[i]; |
207 | if (!desc_addr) { |
208 | continue; |
209 | } |
210 | do { |
211 | eth_rx_desc_get(desc_addr, &desc); |
212 | if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { |
213 | cpu_physical_memory_write(desc.buffer + s->vlan_header, |
214 | buf, size); |
215 | desc.bytes = size + s->vlan_header; |
216 | desc.cmdstat &= ~MP_ETH_RX_OWN; |
217 | s->cur_rx[i] = desc.next; |
218 | |
219 | s->icr |= MP_ETH_IRQ_RX; |
220 | if (s->icr & s->imr) { |
221 | qemu_irq_raise(s->irq); |
222 | } |
223 | eth_rx_desc_put(desc_addr, &desc); |
224 | return size; |
225 | } |
226 | desc_addr = desc.next; |
227 | } while (desc_addr != s->rx_queue[i]); |
228 | } |
229 | return size; |
230 | } |
231 | |
232 | static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc) |
233 | { |
234 | cpu_to_le32s(&desc->cmdstat); |
235 | cpu_to_le16s(&desc->res); |
236 | cpu_to_le16s(&desc->bytes); |
237 | cpu_to_le32s(&desc->buffer); |
238 | cpu_to_le32s(&desc->next); |
239 | cpu_physical_memory_write(addr, desc, sizeof(*desc)); |
240 | } |
241 | |
242 | static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc) |
243 | { |
244 | cpu_physical_memory_read(addr, desc, sizeof(*desc)); |
245 | le32_to_cpus(&desc->cmdstat); |
246 | le16_to_cpus(&desc->res); |
247 | le16_to_cpus(&desc->bytes); |
248 | le32_to_cpus(&desc->buffer); |
249 | le32_to_cpus(&desc->next); |
250 | } |
251 | |
252 | static void eth_send(mv88w8618_eth_state *s, int queue_index) |
253 | { |
254 | uint32_t desc_addr = s->tx_queue[queue_index]; |
255 | mv88w8618_tx_desc desc; |
256 | uint32_t next_desc; |
257 | uint8_t buf[2048]; |
258 | int len; |
259 | |
260 | do { |
261 | eth_tx_desc_get(desc_addr, &desc); |
262 | next_desc = desc.next; |
263 | if (desc.cmdstat & MP_ETH_TX_OWN) { |
264 | len = desc.bytes; |
265 | if (len < 2048) { |
266 | cpu_physical_memory_read(desc.buffer, buf, len); |
267 | qemu_send_packet(qemu_get_queue(s->nic), buf, len); |
268 | } |
269 | desc.cmdstat &= ~MP_ETH_TX_OWN; |
270 | s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); |
271 | eth_tx_desc_put(desc_addr, &desc); |
272 | } |
273 | desc_addr = next_desc; |
274 | } while (desc_addr != s->tx_queue[queue_index]); |
275 | } |
276 | |
277 | static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, |
278 | unsigned size) |
279 | { |
280 | mv88w8618_eth_state *s = opaque; |
281 | |
282 | switch (offset) { |
283 | case MP_ETH_SMIR: |
284 | if (s->smir & MP_ETH_SMIR_OPCODE) { |
285 | switch (s->smir & MP_ETH_SMIR_ADDR) { |
286 | case MP_ETH_PHY1_BMSR: |
287 | return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | |
288 | MP_ETH_SMIR_RDVALID; |
289 | case MP_ETH_PHY1_PHYSID1: |
290 | return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; |
291 | case MP_ETH_PHY1_PHYSID2: |
292 | return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; |
293 | default: |
294 | return MP_ETH_SMIR_RDVALID; |
295 | } |
296 | } |
297 | return 0; |
298 | |
299 | case MP_ETH_ICR: |
300 | return s->icr; |
301 | |
302 | case MP_ETH_IMR: |
303 | return s->imr; |
304 | |
305 | case MP_ETH_FRDP0 ... MP_ETH_FRDP3: |
306 | return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; |
307 | |
308 | case MP_ETH_CRDP0 ... MP_ETH_CRDP3: |
309 | return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; |
310 | |
311 | case MP_ETH_CTDP0 ... MP_ETH_CTDP1: |
312 | return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; |
313 | |
314 | default: |
315 | return 0; |
316 | } |
317 | } |
318 | |
319 | static void mv88w8618_eth_write(void *opaque, hwaddr offset, |
320 | uint64_t value, unsigned size) |
321 | { |
322 | mv88w8618_eth_state *s = opaque; |
323 | |
324 | switch (offset) { |
325 | case MP_ETH_SMIR: |
326 | s->smir = value; |
327 | break; |
328 | |
329 | case MP_ETH_PCXR: |
330 | s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; |
331 | break; |
332 | |
333 | case MP_ETH_SDCMR: |
334 | if (value & MP_ETH_CMD_TXHI) { |
335 | eth_send(s, 1); |
336 | } |
337 | if (value & MP_ETH_CMD_TXLO) { |
338 | eth_send(s, 0); |
339 | } |
340 | if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { |
341 | qemu_irq_raise(s->irq); |
342 | } |
343 | break; |
344 | |
345 | case MP_ETH_ICR: |
346 | s->icr &= value; |
347 | break; |
348 | |
349 | case MP_ETH_IMR: |
350 | s->imr = value; |
351 | if (s->icr & s->imr) { |
352 | qemu_irq_raise(s->irq); |
353 | } |
354 | break; |
355 | |
356 | case MP_ETH_FRDP0 ... MP_ETH_FRDP3: |
357 | s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; |
358 | break; |
359 | |
360 | case MP_ETH_CRDP0 ... MP_ETH_CRDP3: |
361 | s->rx_queue[(offset - MP_ETH_CRDP0)/4] = |
362 | s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; |
363 | break; |
364 | |
365 | case MP_ETH_CTDP0 ... MP_ETH_CTDP1: |
366 | s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; |
367 | break; |
368 | } |
369 | } |
370 | |
371 | static const MemoryRegionOps mv88w8618_eth_ops = { |
372 | .read = mv88w8618_eth_read, |
373 | .write = mv88w8618_eth_write, |
374 | .endianness = DEVICE_NATIVE_ENDIAN, |
375 | }; |
376 | |
377 | static void eth_cleanup(NetClientState *nc) |
378 | { |
379 | mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); |
380 | |
381 | s->nic = NULL; |
382 | } |
383 | |
384 | static NetClientInfo net_mv88w8618_info = { |
385 | .type = NET_CLIENT_DRIVER_NIC, |
386 | .size = sizeof(NICState), |
387 | .receive = eth_receive, |
388 | .cleanup = eth_cleanup, |
389 | }; |
390 | |
391 | static void mv88w8618_eth_init(Object *obj) |
392 | { |
393 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
394 | DeviceState *dev = DEVICE(sbd); |
395 | mv88w8618_eth_state *s = MV88W8618_ETH(dev); |
396 | |
397 | sysbus_init_irq(sbd, &s->irq); |
398 | memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s, |
399 | "mv88w8618-eth" , MP_ETH_SIZE); |
400 | sysbus_init_mmio(sbd, &s->iomem); |
401 | } |
402 | |
403 | static void mv88w8618_eth_realize(DeviceState *dev, Error **errp) |
404 | { |
405 | mv88w8618_eth_state *s = MV88W8618_ETH(dev); |
406 | |
407 | s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, |
408 | object_get_typename(OBJECT(dev)), dev->id, s); |
409 | } |
410 | |
411 | static const VMStateDescription mv88w8618_eth_vmsd = { |
412 | .name = "mv88w8618_eth" , |
413 | .version_id = 1, |
414 | .minimum_version_id = 1, |
415 | .fields = (VMStateField[]) { |
416 | VMSTATE_UINT32(smir, mv88w8618_eth_state), |
417 | VMSTATE_UINT32(icr, mv88w8618_eth_state), |
418 | VMSTATE_UINT32(imr, mv88w8618_eth_state), |
419 | VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), |
420 | VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), |
421 | VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), |
422 | VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), |
423 | VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), |
424 | VMSTATE_END_OF_LIST() |
425 | } |
426 | }; |
427 | |
428 | static Property mv88w8618_eth_properties[] = { |
429 | DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), |
430 | DEFINE_PROP_END_OF_LIST(), |
431 | }; |
432 | |
433 | static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) |
434 | { |
435 | DeviceClass *dc = DEVICE_CLASS(klass); |
436 | |
437 | dc->vmsd = &mv88w8618_eth_vmsd; |
438 | dc->props = mv88w8618_eth_properties; |
439 | dc->realize = mv88w8618_eth_realize; |
440 | } |
441 | |
442 | static const TypeInfo mv88w8618_eth_info = { |
443 | .name = TYPE_MV88W8618_ETH, |
444 | .parent = TYPE_SYS_BUS_DEVICE, |
445 | .instance_size = sizeof(mv88w8618_eth_state), |
446 | .instance_init = mv88w8618_eth_init, |
447 | .class_init = mv88w8618_eth_class_init, |
448 | }; |
449 | |
450 | /* LCD register offsets */ |
451 | #define MP_LCD_IRQCTRL 0x180 |
452 | #define MP_LCD_IRQSTAT 0x184 |
453 | #define MP_LCD_SPICTRL 0x1ac |
454 | #define MP_LCD_INST 0x1bc |
455 | #define MP_LCD_DATA 0x1c0 |
456 | |
457 | /* Mode magics */ |
458 | #define MP_LCD_SPI_DATA 0x00100011 |
459 | #define MP_LCD_SPI_CMD 0x00104011 |
460 | #define MP_LCD_SPI_INVALID 0x00000000 |
461 | |
462 | /* Commmands */ |
463 | #define MP_LCD_INST_SETPAGE0 0xB0 |
464 | /* ... */ |
465 | #define MP_LCD_INST_SETPAGE7 0xB7 |
466 | |
467 | #define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ |
468 | |
469 | #define TYPE_MUSICPAL_LCD "musicpal_lcd" |
470 | #define MUSICPAL_LCD(obj) \ |
471 | OBJECT_CHECK(musicpal_lcd_state, (obj), TYPE_MUSICPAL_LCD) |
472 | |
473 | typedef struct musicpal_lcd_state { |
474 | /*< private >*/ |
475 | SysBusDevice parent_obj; |
476 | /*< public >*/ |
477 | |
478 | MemoryRegion iomem; |
479 | uint32_t brightness; |
480 | uint32_t mode; |
481 | uint32_t irqctrl; |
482 | uint32_t page; |
483 | uint32_t page_off; |
484 | QemuConsole *con; |
485 | uint8_t video_ram[128*64/8]; |
486 | } musicpal_lcd_state; |
487 | |
488 | static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) |
489 | { |
490 | switch (s->brightness) { |
491 | case 7: |
492 | return col; |
493 | case 0: |
494 | return 0; |
495 | default: |
496 | return (col * s->brightness) / 7; |
497 | } |
498 | } |
499 | |
500 | #define SET_LCD_PIXEL(depth, type) \ |
501 | static inline void glue(set_lcd_pixel, depth) \ |
502 | (musicpal_lcd_state *s, int x, int y, type col) \ |
503 | { \ |
504 | int dx, dy; \ |
505 | DisplaySurface *surface = qemu_console_surface(s->con); \ |
506 | type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ |
507 | \ |
508 | for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ |
509 | for (dx = 0; dx < 3; dx++, pixel++) \ |
510 | *pixel = col; \ |
511 | } |
512 | SET_LCD_PIXEL(8, uint8_t) |
513 | SET_LCD_PIXEL(16, uint16_t) |
514 | SET_LCD_PIXEL(32, uint32_t) |
515 | |
516 | static void lcd_refresh(void *opaque) |
517 | { |
518 | musicpal_lcd_state *s = opaque; |
519 | DisplaySurface *surface = qemu_console_surface(s->con); |
520 | int x, y, col; |
521 | |
522 | switch (surface_bits_per_pixel(surface)) { |
523 | case 0: |
524 | return; |
525 | #define LCD_REFRESH(depth, func) \ |
526 | case depth: \ |
527 | col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ |
528 | scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ |
529 | scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ |
530 | for (x = 0; x < 128; x++) { \ |
531 | for (y = 0; y < 64; y++) { \ |
532 | if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ |
533 | glue(set_lcd_pixel, depth)(s, x, y, col); \ |
534 | } else { \ |
535 | glue(set_lcd_pixel, depth)(s, x, y, 0); \ |
536 | } \ |
537 | } \ |
538 | } \ |
539 | break; |
540 | LCD_REFRESH(8, rgb_to_pixel8) |
541 | LCD_REFRESH(16, rgb_to_pixel16) |
542 | LCD_REFRESH(32, (is_surface_bgr(surface) ? |
543 | rgb_to_pixel32bgr : rgb_to_pixel32)) |
544 | default: |
545 | hw_error("unsupported colour depth %i\n" , |
546 | surface_bits_per_pixel(surface)); |
547 | } |
548 | |
549 | dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); |
550 | } |
551 | |
552 | static void lcd_invalidate(void *opaque) |
553 | { |
554 | } |
555 | |
556 | static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level) |
557 | { |
558 | musicpal_lcd_state *s = opaque; |
559 | s->brightness &= ~(1 << irq); |
560 | s->brightness |= level << irq; |
561 | } |
562 | |
563 | static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, |
564 | unsigned size) |
565 | { |
566 | musicpal_lcd_state *s = opaque; |
567 | |
568 | switch (offset) { |
569 | case MP_LCD_IRQCTRL: |
570 | return s->irqctrl; |
571 | |
572 | default: |
573 | return 0; |
574 | } |
575 | } |
576 | |
577 | static void musicpal_lcd_write(void *opaque, hwaddr offset, |
578 | uint64_t value, unsigned size) |
579 | { |
580 | musicpal_lcd_state *s = opaque; |
581 | |
582 | switch (offset) { |
583 | case MP_LCD_IRQCTRL: |
584 | s->irqctrl = value; |
585 | break; |
586 | |
587 | case MP_LCD_SPICTRL: |
588 | if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { |
589 | s->mode = value; |
590 | } else { |
591 | s->mode = MP_LCD_SPI_INVALID; |
592 | } |
593 | break; |
594 | |
595 | case MP_LCD_INST: |
596 | if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { |
597 | s->page = value - MP_LCD_INST_SETPAGE0; |
598 | s->page_off = 0; |
599 | } |
600 | break; |
601 | |
602 | case MP_LCD_DATA: |
603 | if (s->mode == MP_LCD_SPI_CMD) { |
604 | if (value >= MP_LCD_INST_SETPAGE0 && |
605 | value <= MP_LCD_INST_SETPAGE7) { |
606 | s->page = value - MP_LCD_INST_SETPAGE0; |
607 | s->page_off = 0; |
608 | } |
609 | } else if (s->mode == MP_LCD_SPI_DATA) { |
610 | s->video_ram[s->page*128 + s->page_off] = value; |
611 | s->page_off = (s->page_off + 1) & 127; |
612 | } |
613 | break; |
614 | } |
615 | } |
616 | |
617 | static const MemoryRegionOps musicpal_lcd_ops = { |
618 | .read = musicpal_lcd_read, |
619 | .write = musicpal_lcd_write, |
620 | .endianness = DEVICE_NATIVE_ENDIAN, |
621 | }; |
622 | |
623 | static const GraphicHwOps musicpal_gfx_ops = { |
624 | .invalidate = lcd_invalidate, |
625 | .gfx_update = lcd_refresh, |
626 | }; |
627 | |
628 | static void musicpal_lcd_realize(DeviceState *dev, Error **errp) |
629 | { |
630 | musicpal_lcd_state *s = MUSICPAL_LCD(dev); |
631 | s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); |
632 | qemu_console_resize(s->con, 128 * 3, 64 * 3); |
633 | } |
634 | |
635 | static void musicpal_lcd_init(Object *obj) |
636 | { |
637 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
638 | DeviceState *dev = DEVICE(sbd); |
639 | musicpal_lcd_state *s = MUSICPAL_LCD(dev); |
640 | |
641 | s->brightness = 7; |
642 | |
643 | memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s, |
644 | "musicpal-lcd" , MP_LCD_SIZE); |
645 | sysbus_init_mmio(sbd, &s->iomem); |
646 | |
647 | qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); |
648 | } |
649 | |
650 | static const VMStateDescription musicpal_lcd_vmsd = { |
651 | .name = "musicpal_lcd" , |
652 | .version_id = 1, |
653 | .minimum_version_id = 1, |
654 | .fields = (VMStateField[]) { |
655 | VMSTATE_UINT32(brightness, musicpal_lcd_state), |
656 | VMSTATE_UINT32(mode, musicpal_lcd_state), |
657 | VMSTATE_UINT32(irqctrl, musicpal_lcd_state), |
658 | VMSTATE_UINT32(page, musicpal_lcd_state), |
659 | VMSTATE_UINT32(page_off, musicpal_lcd_state), |
660 | VMSTATE_BUFFER(video_ram, musicpal_lcd_state), |
661 | VMSTATE_END_OF_LIST() |
662 | } |
663 | }; |
664 | |
665 | static void musicpal_lcd_class_init(ObjectClass *klass, void *data) |
666 | { |
667 | DeviceClass *dc = DEVICE_CLASS(klass); |
668 | |
669 | dc->vmsd = &musicpal_lcd_vmsd; |
670 | dc->realize = musicpal_lcd_realize; |
671 | } |
672 | |
673 | static const TypeInfo musicpal_lcd_info = { |
674 | .name = TYPE_MUSICPAL_LCD, |
675 | .parent = TYPE_SYS_BUS_DEVICE, |
676 | .instance_size = sizeof(musicpal_lcd_state), |
677 | .instance_init = musicpal_lcd_init, |
678 | .class_init = musicpal_lcd_class_init, |
679 | }; |
680 | |
681 | /* PIC register offsets */ |
682 | #define MP_PIC_STATUS 0x00 |
683 | #define MP_PIC_ENABLE_SET 0x08 |
684 | #define MP_PIC_ENABLE_CLR 0x0C |
685 | |
686 | #define TYPE_MV88W8618_PIC "mv88w8618_pic" |
687 | #define MV88W8618_PIC(obj) \ |
688 | OBJECT_CHECK(mv88w8618_pic_state, (obj), TYPE_MV88W8618_PIC) |
689 | |
690 | typedef struct mv88w8618_pic_state { |
691 | /*< private >*/ |
692 | SysBusDevice parent_obj; |
693 | /*< public >*/ |
694 | |
695 | MemoryRegion iomem; |
696 | uint32_t level; |
697 | uint32_t enabled; |
698 | qemu_irq parent_irq; |
699 | } mv88w8618_pic_state; |
700 | |
701 | static void mv88w8618_pic_update(mv88w8618_pic_state *s) |
702 | { |
703 | qemu_set_irq(s->parent_irq, (s->level & s->enabled)); |
704 | } |
705 | |
706 | static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) |
707 | { |
708 | mv88w8618_pic_state *s = opaque; |
709 | |
710 | if (level) { |
711 | s->level |= 1 << irq; |
712 | } else { |
713 | s->level &= ~(1 << irq); |
714 | } |
715 | mv88w8618_pic_update(s); |
716 | } |
717 | |
718 | static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, |
719 | unsigned size) |
720 | { |
721 | mv88w8618_pic_state *s = opaque; |
722 | |
723 | switch (offset) { |
724 | case MP_PIC_STATUS: |
725 | return s->level & s->enabled; |
726 | |
727 | default: |
728 | return 0; |
729 | } |
730 | } |
731 | |
732 | static void mv88w8618_pic_write(void *opaque, hwaddr offset, |
733 | uint64_t value, unsigned size) |
734 | { |
735 | mv88w8618_pic_state *s = opaque; |
736 | |
737 | switch (offset) { |
738 | case MP_PIC_ENABLE_SET: |
739 | s->enabled |= value; |
740 | break; |
741 | |
742 | case MP_PIC_ENABLE_CLR: |
743 | s->enabled &= ~value; |
744 | s->level &= ~value; |
745 | break; |
746 | } |
747 | mv88w8618_pic_update(s); |
748 | } |
749 | |
750 | static void mv88w8618_pic_reset(DeviceState *d) |
751 | { |
752 | mv88w8618_pic_state *s = MV88W8618_PIC(d); |
753 | |
754 | s->level = 0; |
755 | s->enabled = 0; |
756 | } |
757 | |
758 | static const MemoryRegionOps mv88w8618_pic_ops = { |
759 | .read = mv88w8618_pic_read, |
760 | .write = mv88w8618_pic_write, |
761 | .endianness = DEVICE_NATIVE_ENDIAN, |
762 | }; |
763 | |
764 | static void mv88w8618_pic_init(Object *obj) |
765 | { |
766 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); |
767 | mv88w8618_pic_state *s = MV88W8618_PIC(dev); |
768 | |
769 | qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); |
770 | sysbus_init_irq(dev, &s->parent_irq); |
771 | memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s, |
772 | "musicpal-pic" , MP_PIC_SIZE); |
773 | sysbus_init_mmio(dev, &s->iomem); |
774 | } |
775 | |
776 | static const VMStateDescription mv88w8618_pic_vmsd = { |
777 | .name = "mv88w8618_pic" , |
778 | .version_id = 1, |
779 | .minimum_version_id = 1, |
780 | .fields = (VMStateField[]) { |
781 | VMSTATE_UINT32(level, mv88w8618_pic_state), |
782 | VMSTATE_UINT32(enabled, mv88w8618_pic_state), |
783 | VMSTATE_END_OF_LIST() |
784 | } |
785 | }; |
786 | |
787 | static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) |
788 | { |
789 | DeviceClass *dc = DEVICE_CLASS(klass); |
790 | |
791 | dc->reset = mv88w8618_pic_reset; |
792 | dc->vmsd = &mv88w8618_pic_vmsd; |
793 | } |
794 | |
795 | static const TypeInfo mv88w8618_pic_info = { |
796 | .name = TYPE_MV88W8618_PIC, |
797 | .parent = TYPE_SYS_BUS_DEVICE, |
798 | .instance_size = sizeof(mv88w8618_pic_state), |
799 | .instance_init = mv88w8618_pic_init, |
800 | .class_init = mv88w8618_pic_class_init, |
801 | }; |
802 | |
803 | /* PIT register offsets */ |
804 | #define MP_PIT_TIMER1_LENGTH 0x00 |
805 | /* ... */ |
806 | #define MP_PIT_TIMER4_LENGTH 0x0C |
807 | #define MP_PIT_CONTROL 0x10 |
808 | #define MP_PIT_TIMER1_VALUE 0x14 |
809 | /* ... */ |
810 | #define MP_PIT_TIMER4_VALUE 0x20 |
811 | #define MP_BOARD_RESET 0x34 |
812 | |
813 | /* Magic board reset value (probably some watchdog behind it) */ |
814 | #define MP_BOARD_RESET_MAGIC 0x10000 |
815 | |
816 | typedef struct mv88w8618_timer_state { |
817 | ptimer_state *ptimer; |
818 | uint32_t limit; |
819 | int freq; |
820 | qemu_irq irq; |
821 | } mv88w8618_timer_state; |
822 | |
823 | #define TYPE_MV88W8618_PIT "mv88w8618_pit" |
824 | #define MV88W8618_PIT(obj) \ |
825 | OBJECT_CHECK(mv88w8618_pit_state, (obj), TYPE_MV88W8618_PIT) |
826 | |
827 | typedef struct mv88w8618_pit_state { |
828 | /*< private >*/ |
829 | SysBusDevice parent_obj; |
830 | /*< public >*/ |
831 | |
832 | MemoryRegion iomem; |
833 | mv88w8618_timer_state timer[4]; |
834 | } mv88w8618_pit_state; |
835 | |
836 | static void mv88w8618_timer_tick(void *opaque) |
837 | { |
838 | mv88w8618_timer_state *s = opaque; |
839 | |
840 | qemu_irq_raise(s->irq); |
841 | } |
842 | |
843 | static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, |
844 | uint32_t freq) |
845 | { |
846 | QEMUBH *bh; |
847 | |
848 | sysbus_init_irq(dev, &s->irq); |
849 | s->freq = freq; |
850 | |
851 | bh = qemu_bh_new(mv88w8618_timer_tick, s); |
852 | s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); |
853 | } |
854 | |
855 | static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, |
856 | unsigned size) |
857 | { |
858 | mv88w8618_pit_state *s = opaque; |
859 | mv88w8618_timer_state *t; |
860 | |
861 | switch (offset) { |
862 | case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: |
863 | t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; |
864 | return ptimer_get_count(t->ptimer); |
865 | |
866 | default: |
867 | return 0; |
868 | } |
869 | } |
870 | |
871 | static void mv88w8618_pit_write(void *opaque, hwaddr offset, |
872 | uint64_t value, unsigned size) |
873 | { |
874 | mv88w8618_pit_state *s = opaque; |
875 | mv88w8618_timer_state *t; |
876 | int i; |
877 | |
878 | switch (offset) { |
879 | case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: |
880 | t = &s->timer[offset >> 2]; |
881 | t->limit = value; |
882 | if (t->limit > 0) { |
883 | ptimer_set_limit(t->ptimer, t->limit, 1); |
884 | } else { |
885 | ptimer_stop(t->ptimer); |
886 | } |
887 | break; |
888 | |
889 | case MP_PIT_CONTROL: |
890 | for (i = 0; i < 4; i++) { |
891 | t = &s->timer[i]; |
892 | if (value & 0xf && t->limit > 0) { |
893 | ptimer_set_limit(t->ptimer, t->limit, 0); |
894 | ptimer_set_freq(t->ptimer, t->freq); |
895 | ptimer_run(t->ptimer, 0); |
896 | } else { |
897 | ptimer_stop(t->ptimer); |
898 | } |
899 | value >>= 4; |
900 | } |
901 | break; |
902 | |
903 | case MP_BOARD_RESET: |
904 | if (value == MP_BOARD_RESET_MAGIC) { |
905 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
906 | } |
907 | break; |
908 | } |
909 | } |
910 | |
911 | static void mv88w8618_pit_reset(DeviceState *d) |
912 | { |
913 | mv88w8618_pit_state *s = MV88W8618_PIT(d); |
914 | int i; |
915 | |
916 | for (i = 0; i < 4; i++) { |
917 | ptimer_stop(s->timer[i].ptimer); |
918 | s->timer[i].limit = 0; |
919 | } |
920 | } |
921 | |
922 | static const MemoryRegionOps mv88w8618_pit_ops = { |
923 | .read = mv88w8618_pit_read, |
924 | .write = mv88w8618_pit_write, |
925 | .endianness = DEVICE_NATIVE_ENDIAN, |
926 | }; |
927 | |
928 | static void mv88w8618_pit_init(Object *obj) |
929 | { |
930 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); |
931 | mv88w8618_pit_state *s = MV88W8618_PIT(dev); |
932 | int i; |
933 | |
934 | /* Letting them all run at 1 MHz is likely just a pragmatic |
935 | * simplification. */ |
936 | for (i = 0; i < 4; i++) { |
937 | mv88w8618_timer_init(dev, &s->timer[i], 1000000); |
938 | } |
939 | |
940 | memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s, |
941 | "musicpal-pit" , MP_PIT_SIZE); |
942 | sysbus_init_mmio(dev, &s->iomem); |
943 | } |
944 | |
945 | static const VMStateDescription mv88w8618_timer_vmsd = { |
946 | .name = "timer" , |
947 | .version_id = 1, |
948 | .minimum_version_id = 1, |
949 | .fields = (VMStateField[]) { |
950 | VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), |
951 | VMSTATE_UINT32(limit, mv88w8618_timer_state), |
952 | VMSTATE_END_OF_LIST() |
953 | } |
954 | }; |
955 | |
956 | static const VMStateDescription mv88w8618_pit_vmsd = { |
957 | .name = "mv88w8618_pit" , |
958 | .version_id = 1, |
959 | .minimum_version_id = 1, |
960 | .fields = (VMStateField[]) { |
961 | VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, |
962 | mv88w8618_timer_vmsd, mv88w8618_timer_state), |
963 | VMSTATE_END_OF_LIST() |
964 | } |
965 | }; |
966 | |
967 | static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) |
968 | { |
969 | DeviceClass *dc = DEVICE_CLASS(klass); |
970 | |
971 | dc->reset = mv88w8618_pit_reset; |
972 | dc->vmsd = &mv88w8618_pit_vmsd; |
973 | } |
974 | |
975 | static const TypeInfo mv88w8618_pit_info = { |
976 | .name = TYPE_MV88W8618_PIT, |
977 | .parent = TYPE_SYS_BUS_DEVICE, |
978 | .instance_size = sizeof(mv88w8618_pit_state), |
979 | .instance_init = mv88w8618_pit_init, |
980 | .class_init = mv88w8618_pit_class_init, |
981 | }; |
982 | |
983 | /* Flash config register offsets */ |
984 | #define MP_FLASHCFG_CFGR0 0x04 |
985 | |
986 | #define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg" |
987 | #define MV88W8618_FLASHCFG(obj) \ |
988 | OBJECT_CHECK(mv88w8618_flashcfg_state, (obj), TYPE_MV88W8618_FLASHCFG) |
989 | |
990 | typedef struct mv88w8618_flashcfg_state { |
991 | /*< private >*/ |
992 | SysBusDevice parent_obj; |
993 | /*< public >*/ |
994 | |
995 | MemoryRegion iomem; |
996 | uint32_t cfgr0; |
997 | } mv88w8618_flashcfg_state; |
998 | |
999 | static uint64_t mv88w8618_flashcfg_read(void *opaque, |
1000 | hwaddr offset, |
1001 | unsigned size) |
1002 | { |
1003 | mv88w8618_flashcfg_state *s = opaque; |
1004 | |
1005 | switch (offset) { |
1006 | case MP_FLASHCFG_CFGR0: |
1007 | return s->cfgr0; |
1008 | |
1009 | default: |
1010 | return 0; |
1011 | } |
1012 | } |
1013 | |
1014 | static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, |
1015 | uint64_t value, unsigned size) |
1016 | { |
1017 | mv88w8618_flashcfg_state *s = opaque; |
1018 | |
1019 | switch (offset) { |
1020 | case MP_FLASHCFG_CFGR0: |
1021 | s->cfgr0 = value; |
1022 | break; |
1023 | } |
1024 | } |
1025 | |
1026 | static const MemoryRegionOps mv88w8618_flashcfg_ops = { |
1027 | .read = mv88w8618_flashcfg_read, |
1028 | .write = mv88w8618_flashcfg_write, |
1029 | .endianness = DEVICE_NATIVE_ENDIAN, |
1030 | }; |
1031 | |
1032 | static void mv88w8618_flashcfg_init(Object *obj) |
1033 | { |
1034 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); |
1035 | mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); |
1036 | |
1037 | s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ |
1038 | memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s, |
1039 | "musicpal-flashcfg" , MP_FLASHCFG_SIZE); |
1040 | sysbus_init_mmio(dev, &s->iomem); |
1041 | } |
1042 | |
1043 | static const VMStateDescription mv88w8618_flashcfg_vmsd = { |
1044 | .name = "mv88w8618_flashcfg" , |
1045 | .version_id = 1, |
1046 | .minimum_version_id = 1, |
1047 | .fields = (VMStateField[]) { |
1048 | VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), |
1049 | VMSTATE_END_OF_LIST() |
1050 | } |
1051 | }; |
1052 | |
1053 | static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) |
1054 | { |
1055 | DeviceClass *dc = DEVICE_CLASS(klass); |
1056 | |
1057 | dc->vmsd = &mv88w8618_flashcfg_vmsd; |
1058 | } |
1059 | |
1060 | static const TypeInfo mv88w8618_flashcfg_info = { |
1061 | .name = TYPE_MV88W8618_FLASHCFG, |
1062 | .parent = TYPE_SYS_BUS_DEVICE, |
1063 | .instance_size = sizeof(mv88w8618_flashcfg_state), |
1064 | .instance_init = mv88w8618_flashcfg_init, |
1065 | .class_init = mv88w8618_flashcfg_class_init, |
1066 | }; |
1067 | |
1068 | /* Misc register offsets */ |
1069 | #define MP_MISC_BOARD_REVISION 0x18 |
1070 | |
1071 | #define MP_BOARD_REVISION 0x31 |
1072 | |
1073 | typedef struct { |
1074 | SysBusDevice parent_obj; |
1075 | MemoryRegion iomem; |
1076 | } MusicPalMiscState; |
1077 | |
1078 | #define TYPE_MUSICPAL_MISC "musicpal-misc" |
1079 | #define MUSICPAL_MISC(obj) \ |
1080 | OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC) |
1081 | |
1082 | static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, |
1083 | unsigned size) |
1084 | { |
1085 | switch (offset) { |
1086 | case MP_MISC_BOARD_REVISION: |
1087 | return MP_BOARD_REVISION; |
1088 | |
1089 | default: |
1090 | return 0; |
1091 | } |
1092 | } |
1093 | |
1094 | static void musicpal_misc_write(void *opaque, hwaddr offset, |
1095 | uint64_t value, unsigned size) |
1096 | { |
1097 | } |
1098 | |
1099 | static const MemoryRegionOps musicpal_misc_ops = { |
1100 | .read = musicpal_misc_read, |
1101 | .write = musicpal_misc_write, |
1102 | .endianness = DEVICE_NATIVE_ENDIAN, |
1103 | }; |
1104 | |
1105 | static void musicpal_misc_init(Object *obj) |
1106 | { |
1107 | SysBusDevice *sd = SYS_BUS_DEVICE(obj); |
1108 | MusicPalMiscState *s = MUSICPAL_MISC(obj); |
1109 | |
1110 | memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL, |
1111 | "musicpal-misc" , MP_MISC_SIZE); |
1112 | sysbus_init_mmio(sd, &s->iomem); |
1113 | } |
1114 | |
1115 | static const TypeInfo musicpal_misc_info = { |
1116 | .name = TYPE_MUSICPAL_MISC, |
1117 | .parent = TYPE_SYS_BUS_DEVICE, |
1118 | .instance_init = musicpal_misc_init, |
1119 | .instance_size = sizeof(MusicPalMiscState), |
1120 | }; |
1121 | |
1122 | /* WLAN register offsets */ |
1123 | #define MP_WLAN_MAGIC1 0x11c |
1124 | #define MP_WLAN_MAGIC2 0x124 |
1125 | |
1126 | static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, |
1127 | unsigned size) |
1128 | { |
1129 | switch (offset) { |
1130 | /* Workaround to allow loading the binary-only wlandrv.ko crap |
1131 | * from the original Freecom firmware. */ |
1132 | case MP_WLAN_MAGIC1: |
1133 | return ~3; |
1134 | case MP_WLAN_MAGIC2: |
1135 | return -1; |
1136 | |
1137 | default: |
1138 | return 0; |
1139 | } |
1140 | } |
1141 | |
1142 | static void mv88w8618_wlan_write(void *opaque, hwaddr offset, |
1143 | uint64_t value, unsigned size) |
1144 | { |
1145 | } |
1146 | |
1147 | static const MemoryRegionOps mv88w8618_wlan_ops = { |
1148 | .read = mv88w8618_wlan_read, |
1149 | .write =mv88w8618_wlan_write, |
1150 | .endianness = DEVICE_NATIVE_ENDIAN, |
1151 | }; |
1152 | |
1153 | static void mv88w8618_wlan_realize(DeviceState *dev, Error **errp) |
1154 | { |
1155 | MemoryRegion *iomem = g_new(MemoryRegion, 1); |
1156 | |
1157 | memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, |
1158 | "musicpal-wlan" , MP_WLAN_SIZE); |
1159 | sysbus_init_mmio(SYS_BUS_DEVICE(dev), iomem); |
1160 | } |
1161 | |
1162 | /* GPIO register offsets */ |
1163 | #define MP_GPIO_OE_LO 0x008 |
1164 | #define MP_GPIO_OUT_LO 0x00c |
1165 | #define MP_GPIO_IN_LO 0x010 |
1166 | #define MP_GPIO_IER_LO 0x014 |
1167 | #define MP_GPIO_IMR_LO 0x018 |
1168 | #define MP_GPIO_ISR_LO 0x020 |
1169 | #define MP_GPIO_OE_HI 0x508 |
1170 | #define MP_GPIO_OUT_HI 0x50c |
1171 | #define MP_GPIO_IN_HI 0x510 |
1172 | #define MP_GPIO_IER_HI 0x514 |
1173 | #define MP_GPIO_IMR_HI 0x518 |
1174 | #define MP_GPIO_ISR_HI 0x520 |
1175 | |
1176 | /* GPIO bits & masks */ |
1177 | #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 |
1178 | #define MP_GPIO_I2C_DATA_BIT 29 |
1179 | #define MP_GPIO_I2C_CLOCK_BIT 30 |
1180 | |
1181 | /* LCD brightness bits in GPIO_OE_HI */ |
1182 | #define MP_OE_LCD_BRIGHTNESS 0x0007 |
1183 | |
1184 | #define TYPE_MUSICPAL_GPIO "musicpal_gpio" |
1185 | #define MUSICPAL_GPIO(obj) \ |
1186 | OBJECT_CHECK(musicpal_gpio_state, (obj), TYPE_MUSICPAL_GPIO) |
1187 | |
1188 | typedef struct musicpal_gpio_state { |
1189 | /*< private >*/ |
1190 | SysBusDevice parent_obj; |
1191 | /*< public >*/ |
1192 | |
1193 | MemoryRegion iomem; |
1194 | uint32_t lcd_brightness; |
1195 | uint32_t out_state; |
1196 | uint32_t in_state; |
1197 | uint32_t ier; |
1198 | uint32_t imr; |
1199 | uint32_t isr; |
1200 | qemu_irq irq; |
1201 | qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ |
1202 | } musicpal_gpio_state; |
1203 | |
1204 | static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { |
1205 | int i; |
1206 | uint32_t brightness; |
1207 | |
1208 | /* compute brightness ratio */ |
1209 | switch (s->lcd_brightness) { |
1210 | case 0x00000007: |
1211 | brightness = 0; |
1212 | break; |
1213 | |
1214 | case 0x00020000: |
1215 | brightness = 1; |
1216 | break; |
1217 | |
1218 | case 0x00020001: |
1219 | brightness = 2; |
1220 | break; |
1221 | |
1222 | case 0x00040000: |
1223 | brightness = 3; |
1224 | break; |
1225 | |
1226 | case 0x00010006: |
1227 | brightness = 4; |
1228 | break; |
1229 | |
1230 | case 0x00020005: |
1231 | brightness = 5; |
1232 | break; |
1233 | |
1234 | case 0x00040003: |
1235 | brightness = 6; |
1236 | break; |
1237 | |
1238 | case 0x00030004: |
1239 | default: |
1240 | brightness = 7; |
1241 | } |
1242 | |
1243 | /* set lcd brightness GPIOs */ |
1244 | for (i = 0; i <= 2; i++) { |
1245 | qemu_set_irq(s->out[i], (brightness >> i) & 1); |
1246 | } |
1247 | } |
1248 | |
1249 | static void musicpal_gpio_pin_event(void *opaque, int pin, int level) |
1250 | { |
1251 | musicpal_gpio_state *s = opaque; |
1252 | uint32_t mask = 1 << pin; |
1253 | uint32_t delta = level << pin; |
1254 | uint32_t old = s->in_state & mask; |
1255 | |
1256 | s->in_state &= ~mask; |
1257 | s->in_state |= delta; |
1258 | |
1259 | if ((old ^ delta) && |
1260 | ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { |
1261 | s->isr = mask; |
1262 | qemu_irq_raise(s->irq); |
1263 | } |
1264 | } |
1265 | |
1266 | static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, |
1267 | unsigned size) |
1268 | { |
1269 | musicpal_gpio_state *s = opaque; |
1270 | |
1271 | switch (offset) { |
1272 | case MP_GPIO_OE_HI: /* used for LCD brightness control */ |
1273 | return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; |
1274 | |
1275 | case MP_GPIO_OUT_LO: |
1276 | return s->out_state & 0xFFFF; |
1277 | case MP_GPIO_OUT_HI: |
1278 | return s->out_state >> 16; |
1279 | |
1280 | case MP_GPIO_IN_LO: |
1281 | return s->in_state & 0xFFFF; |
1282 | case MP_GPIO_IN_HI: |
1283 | return s->in_state >> 16; |
1284 | |
1285 | case MP_GPIO_IER_LO: |
1286 | return s->ier & 0xFFFF; |
1287 | case MP_GPIO_IER_HI: |
1288 | return s->ier >> 16; |
1289 | |
1290 | case MP_GPIO_IMR_LO: |
1291 | return s->imr & 0xFFFF; |
1292 | case MP_GPIO_IMR_HI: |
1293 | return s->imr >> 16; |
1294 | |
1295 | case MP_GPIO_ISR_LO: |
1296 | return s->isr & 0xFFFF; |
1297 | case MP_GPIO_ISR_HI: |
1298 | return s->isr >> 16; |
1299 | |
1300 | default: |
1301 | return 0; |
1302 | } |
1303 | } |
1304 | |
1305 | static void musicpal_gpio_write(void *opaque, hwaddr offset, |
1306 | uint64_t value, unsigned size) |
1307 | { |
1308 | musicpal_gpio_state *s = opaque; |
1309 | switch (offset) { |
1310 | case MP_GPIO_OE_HI: /* used for LCD brightness control */ |
1311 | s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | |
1312 | (value & MP_OE_LCD_BRIGHTNESS); |
1313 | musicpal_gpio_brightness_update(s); |
1314 | break; |
1315 | |
1316 | case MP_GPIO_OUT_LO: |
1317 | s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); |
1318 | break; |
1319 | case MP_GPIO_OUT_HI: |
1320 | s->out_state = (s->out_state & 0xFFFF) | (value << 16); |
1321 | s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | |
1322 | (s->out_state & MP_GPIO_LCD_BRIGHTNESS); |
1323 | musicpal_gpio_brightness_update(s); |
1324 | qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); |
1325 | qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); |
1326 | break; |
1327 | |
1328 | case MP_GPIO_IER_LO: |
1329 | s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); |
1330 | break; |
1331 | case MP_GPIO_IER_HI: |
1332 | s->ier = (s->ier & 0xFFFF) | (value << 16); |
1333 | break; |
1334 | |
1335 | case MP_GPIO_IMR_LO: |
1336 | s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); |
1337 | break; |
1338 | case MP_GPIO_IMR_HI: |
1339 | s->imr = (s->imr & 0xFFFF) | (value << 16); |
1340 | break; |
1341 | } |
1342 | } |
1343 | |
1344 | static const MemoryRegionOps musicpal_gpio_ops = { |
1345 | .read = musicpal_gpio_read, |
1346 | .write = musicpal_gpio_write, |
1347 | .endianness = DEVICE_NATIVE_ENDIAN, |
1348 | }; |
1349 | |
1350 | static void musicpal_gpio_reset(DeviceState *d) |
1351 | { |
1352 | musicpal_gpio_state *s = MUSICPAL_GPIO(d); |
1353 | |
1354 | s->lcd_brightness = 0; |
1355 | s->out_state = 0; |
1356 | s->in_state = 0xffffffff; |
1357 | s->ier = 0; |
1358 | s->imr = 0; |
1359 | s->isr = 0; |
1360 | } |
1361 | |
1362 | static void musicpal_gpio_init(Object *obj) |
1363 | { |
1364 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
1365 | DeviceState *dev = DEVICE(sbd); |
1366 | musicpal_gpio_state *s = MUSICPAL_GPIO(dev); |
1367 | |
1368 | sysbus_init_irq(sbd, &s->irq); |
1369 | |
1370 | memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s, |
1371 | "musicpal-gpio" , MP_GPIO_SIZE); |
1372 | sysbus_init_mmio(sbd, &s->iomem); |
1373 | |
1374 | qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); |
1375 | |
1376 | qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); |
1377 | } |
1378 | |
1379 | static const VMStateDescription musicpal_gpio_vmsd = { |
1380 | .name = "musicpal_gpio" , |
1381 | .version_id = 1, |
1382 | .minimum_version_id = 1, |
1383 | .fields = (VMStateField[]) { |
1384 | VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), |
1385 | VMSTATE_UINT32(out_state, musicpal_gpio_state), |
1386 | VMSTATE_UINT32(in_state, musicpal_gpio_state), |
1387 | VMSTATE_UINT32(ier, musicpal_gpio_state), |
1388 | VMSTATE_UINT32(imr, musicpal_gpio_state), |
1389 | VMSTATE_UINT32(isr, musicpal_gpio_state), |
1390 | VMSTATE_END_OF_LIST() |
1391 | } |
1392 | }; |
1393 | |
1394 | static void musicpal_gpio_class_init(ObjectClass *klass, void *data) |
1395 | { |
1396 | DeviceClass *dc = DEVICE_CLASS(klass); |
1397 | |
1398 | dc->reset = musicpal_gpio_reset; |
1399 | dc->vmsd = &musicpal_gpio_vmsd; |
1400 | } |
1401 | |
1402 | static const TypeInfo musicpal_gpio_info = { |
1403 | .name = TYPE_MUSICPAL_GPIO, |
1404 | .parent = TYPE_SYS_BUS_DEVICE, |
1405 | .instance_size = sizeof(musicpal_gpio_state), |
1406 | .instance_init = musicpal_gpio_init, |
1407 | .class_init = musicpal_gpio_class_init, |
1408 | }; |
1409 | |
1410 | /* Keyboard codes & masks */ |
1411 | #define KEY_RELEASED 0x80 |
1412 | #define KEY_CODE 0x7f |
1413 | |
1414 | #define KEYCODE_TAB 0x0f |
1415 | #define KEYCODE_ENTER 0x1c |
1416 | #define KEYCODE_F 0x21 |
1417 | #define KEYCODE_M 0x32 |
1418 | |
1419 | #define KEYCODE_EXTENDED 0xe0 |
1420 | #define KEYCODE_UP 0x48 |
1421 | #define KEYCODE_DOWN 0x50 |
1422 | #define KEYCODE_LEFT 0x4b |
1423 | #define KEYCODE_RIGHT 0x4d |
1424 | |
1425 | #define MP_KEY_WHEEL_VOL (1 << 0) |
1426 | #define MP_KEY_WHEEL_VOL_INV (1 << 1) |
1427 | #define MP_KEY_WHEEL_NAV (1 << 2) |
1428 | #define MP_KEY_WHEEL_NAV_INV (1 << 3) |
1429 | #define MP_KEY_BTN_FAVORITS (1 << 4) |
1430 | #define (1 << 5) |
1431 | #define MP_KEY_BTN_VOLUME (1 << 6) |
1432 | #define MP_KEY_BTN_NAVIGATION (1 << 7) |
1433 | |
1434 | #define TYPE_MUSICPAL_KEY "musicpal_key" |
1435 | #define MUSICPAL_KEY(obj) \ |
1436 | OBJECT_CHECK(musicpal_key_state, (obj), TYPE_MUSICPAL_KEY) |
1437 | |
1438 | typedef struct musicpal_key_state { |
1439 | /*< private >*/ |
1440 | SysBusDevice parent_obj; |
1441 | /*< public >*/ |
1442 | |
1443 | MemoryRegion iomem; |
1444 | uint32_t kbd_extended; |
1445 | uint32_t pressed_keys; |
1446 | qemu_irq out[8]; |
1447 | } musicpal_key_state; |
1448 | |
1449 | static void musicpal_key_event(void *opaque, int keycode) |
1450 | { |
1451 | musicpal_key_state *s = opaque; |
1452 | uint32_t event = 0; |
1453 | int i; |
1454 | |
1455 | if (keycode == KEYCODE_EXTENDED) { |
1456 | s->kbd_extended = 1; |
1457 | return; |
1458 | } |
1459 | |
1460 | if (s->kbd_extended) { |
1461 | switch (keycode & KEY_CODE) { |
1462 | case KEYCODE_UP: |
1463 | event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; |
1464 | break; |
1465 | |
1466 | case KEYCODE_DOWN: |
1467 | event = MP_KEY_WHEEL_NAV; |
1468 | break; |
1469 | |
1470 | case KEYCODE_LEFT: |
1471 | event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; |
1472 | break; |
1473 | |
1474 | case KEYCODE_RIGHT: |
1475 | event = MP_KEY_WHEEL_VOL; |
1476 | break; |
1477 | } |
1478 | } else { |
1479 | switch (keycode & KEY_CODE) { |
1480 | case KEYCODE_F: |
1481 | event = MP_KEY_BTN_FAVORITS; |
1482 | break; |
1483 | |
1484 | case KEYCODE_TAB: |
1485 | event = MP_KEY_BTN_VOLUME; |
1486 | break; |
1487 | |
1488 | case KEYCODE_ENTER: |
1489 | event = MP_KEY_BTN_NAVIGATION; |
1490 | break; |
1491 | |
1492 | case KEYCODE_M: |
1493 | event = MP_KEY_BTN_MENU; |
1494 | break; |
1495 | } |
1496 | /* Do not repeat already pressed buttons */ |
1497 | if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { |
1498 | event = 0; |
1499 | } |
1500 | } |
1501 | |
1502 | if (event) { |
1503 | /* Raise GPIO pin first if repeating a key */ |
1504 | if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { |
1505 | for (i = 0; i <= 7; i++) { |
1506 | if (event & (1 << i)) { |
1507 | qemu_set_irq(s->out[i], 1); |
1508 | } |
1509 | } |
1510 | } |
1511 | for (i = 0; i <= 7; i++) { |
1512 | if (event & (1 << i)) { |
1513 | qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); |
1514 | } |
1515 | } |
1516 | if (keycode & KEY_RELEASED) { |
1517 | s->pressed_keys &= ~event; |
1518 | } else { |
1519 | s->pressed_keys |= event; |
1520 | } |
1521 | } |
1522 | |
1523 | s->kbd_extended = 0; |
1524 | } |
1525 | |
1526 | static void musicpal_key_init(Object *obj) |
1527 | { |
1528 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
1529 | DeviceState *dev = DEVICE(sbd); |
1530 | musicpal_key_state *s = MUSICPAL_KEY(dev); |
1531 | |
1532 | memory_region_init(&s->iomem, obj, "dummy" , 0); |
1533 | sysbus_init_mmio(sbd, &s->iomem); |
1534 | |
1535 | s->kbd_extended = 0; |
1536 | s->pressed_keys = 0; |
1537 | |
1538 | qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); |
1539 | |
1540 | qemu_add_kbd_event_handler(musicpal_key_event, s); |
1541 | } |
1542 | |
1543 | static const VMStateDescription musicpal_key_vmsd = { |
1544 | .name = "musicpal_key" , |
1545 | .version_id = 1, |
1546 | .minimum_version_id = 1, |
1547 | .fields = (VMStateField[]) { |
1548 | VMSTATE_UINT32(kbd_extended, musicpal_key_state), |
1549 | VMSTATE_UINT32(pressed_keys, musicpal_key_state), |
1550 | VMSTATE_END_OF_LIST() |
1551 | } |
1552 | }; |
1553 | |
1554 | static void musicpal_key_class_init(ObjectClass *klass, void *data) |
1555 | { |
1556 | DeviceClass *dc = DEVICE_CLASS(klass); |
1557 | |
1558 | dc->vmsd = &musicpal_key_vmsd; |
1559 | } |
1560 | |
1561 | static const TypeInfo musicpal_key_info = { |
1562 | .name = TYPE_MUSICPAL_KEY, |
1563 | .parent = TYPE_SYS_BUS_DEVICE, |
1564 | .instance_size = sizeof(musicpal_key_state), |
1565 | .instance_init = musicpal_key_init, |
1566 | .class_init = musicpal_key_class_init, |
1567 | }; |
1568 | |
1569 | static struct arm_boot_info musicpal_binfo = { |
1570 | .loader_start = 0x0, |
1571 | .board_id = 0x20e, |
1572 | }; |
1573 | |
1574 | static void musicpal_init(MachineState *machine) |
1575 | { |
1576 | ARMCPU *cpu; |
1577 | qemu_irq pic[32]; |
1578 | DeviceState *dev; |
1579 | DeviceState *i2c_dev; |
1580 | DeviceState *lcd_dev; |
1581 | DeviceState *key_dev; |
1582 | DeviceState *wm8750_dev; |
1583 | SysBusDevice *s; |
1584 | I2CBus *i2c; |
1585 | int i; |
1586 | unsigned long flash_size; |
1587 | DriveInfo *dinfo; |
1588 | MemoryRegion *address_space_mem = get_system_memory(); |
1589 | MemoryRegion *ram = g_new(MemoryRegion, 1); |
1590 | MemoryRegion *sram = g_new(MemoryRegion, 1); |
1591 | |
1592 | cpu = ARM_CPU(cpu_create(machine->cpu_type)); |
1593 | |
1594 | /* For now we use a fixed - the original - RAM size */ |
1595 | memory_region_allocate_system_memory(ram, NULL, "musicpal.ram" , |
1596 | MP_RAM_DEFAULT_SIZE); |
1597 | memory_region_add_subregion(address_space_mem, 0, ram); |
1598 | |
1599 | memory_region_init_ram(sram, NULL, "musicpal.sram" , MP_SRAM_SIZE, |
1600 | &error_fatal); |
1601 | memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); |
1602 | |
1603 | dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE, |
1604 | qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); |
1605 | for (i = 0; i < 32; i++) { |
1606 | pic[i] = qdev_get_gpio_in(dev, i); |
1607 | } |
1608 | sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE, pic[MP_TIMER1_IRQ], |
1609 | pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], |
1610 | pic[MP_TIMER4_IRQ], NULL); |
1611 | |
1612 | if (serial_hd(0)) { |
1613 | serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], |
1614 | 1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN); |
1615 | } |
1616 | if (serial_hd(1)) { |
1617 | serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], |
1618 | 1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN); |
1619 | } |
1620 | |
1621 | /* Register flash */ |
1622 | dinfo = drive_get(IF_PFLASH, 0, 0); |
1623 | if (dinfo) { |
1624 | BlockBackend *blk = blk_by_legacy_dinfo(dinfo); |
1625 | |
1626 | flash_size = blk_getlength(blk); |
1627 | if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && |
1628 | flash_size != 32*1024*1024) { |
1629 | error_report("Invalid flash image size" ); |
1630 | exit(1); |
1631 | } |
1632 | |
1633 | /* |
1634 | * The original U-Boot accesses the flash at 0xFE000000 instead of |
1635 | * 0xFF800000 (if there is 8 MB flash). So remap flash access if the |
1636 | * image is smaller than 32 MB. |
1637 | */ |
1638 | #ifdef TARGET_WORDS_BIGENDIAN |
1639 | pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, |
1640 | "musicpal.flash" , flash_size, |
1641 | blk, 0x10000, |
1642 | MP_FLASH_SIZE_MAX / flash_size, |
1643 | 2, 0x00BF, 0x236D, 0x0000, 0x0000, |
1644 | 0x5555, 0x2AAA, 1); |
1645 | #else |
1646 | pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, |
1647 | "musicpal.flash" , flash_size, |
1648 | blk, 0x10000, |
1649 | MP_FLASH_SIZE_MAX / flash_size, |
1650 | 2, 0x00BF, 0x236D, 0x0000, 0x0000, |
1651 | 0x5555, 0x2AAA, 0); |
1652 | #endif |
1653 | |
1654 | } |
1655 | sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); |
1656 | |
1657 | qemu_check_nic_model(&nd_table[0], "mv88w8618" ); |
1658 | dev = qdev_create(NULL, TYPE_MV88W8618_ETH); |
1659 | qdev_set_nic_properties(dev, &nd_table[0]); |
1660 | qdev_init_nofail(dev); |
1661 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); |
1662 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); |
1663 | |
1664 | sysbus_create_simple("mv88w8618_wlan" , MP_WLAN_BASE, NULL); |
1665 | |
1666 | sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); |
1667 | |
1668 | dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE, |
1669 | pic[MP_GPIO_IRQ]); |
1670 | i2c_dev = sysbus_create_simple("gpio_i2c" , -1, NULL); |
1671 | i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c" ); |
1672 | |
1673 | lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); |
1674 | key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL); |
1675 | |
1676 | /* I2C read data */ |
1677 | qdev_connect_gpio_out(i2c_dev, 0, |
1678 | qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); |
1679 | /* I2C data */ |
1680 | qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); |
1681 | /* I2C clock */ |
1682 | qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); |
1683 | |
1684 | for (i = 0; i < 3; i++) { |
1685 | qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); |
1686 | } |
1687 | for (i = 0; i < 4; i++) { |
1688 | qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); |
1689 | } |
1690 | for (i = 4; i < 8; i++) { |
1691 | qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); |
1692 | } |
1693 | |
1694 | wm8750_dev = i2c_create_slave(i2c, TYPE_WM8750, MP_WM_ADDR); |
1695 | dev = qdev_create(NULL, TYPE_MV88W8618_AUDIO); |
1696 | s = SYS_BUS_DEVICE(dev); |
1697 | object_property_set_link(OBJECT(dev), OBJECT(wm8750_dev), |
1698 | "wm8750" , NULL); |
1699 | qdev_init_nofail(dev); |
1700 | sysbus_mmio_map(s, 0, MP_AUDIO_BASE); |
1701 | sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); |
1702 | |
1703 | musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; |
1704 | arm_load_kernel(cpu, machine, &musicpal_binfo); |
1705 | } |
1706 | |
1707 | static void musicpal_machine_init(MachineClass *mc) |
1708 | { |
1709 | mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)" ; |
1710 | mc->init = musicpal_init; |
1711 | mc->ignore_memory_transaction_failures = true; |
1712 | mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926" ); |
1713 | } |
1714 | |
1715 | DEFINE_MACHINE("musicpal" , musicpal_machine_init) |
1716 | |
1717 | static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) |
1718 | { |
1719 | DeviceClass *dc = DEVICE_CLASS(klass); |
1720 | |
1721 | dc->realize = mv88w8618_wlan_realize; |
1722 | } |
1723 | |
1724 | static const TypeInfo mv88w8618_wlan_info = { |
1725 | .name = "mv88w8618_wlan" , |
1726 | .parent = TYPE_SYS_BUS_DEVICE, |
1727 | .instance_size = sizeof(SysBusDevice), |
1728 | .class_init = mv88w8618_wlan_class_init, |
1729 | }; |
1730 | |
1731 | static void musicpal_register_types(void) |
1732 | { |
1733 | type_register_static(&mv88w8618_pic_info); |
1734 | type_register_static(&mv88w8618_pit_info); |
1735 | type_register_static(&mv88w8618_flashcfg_info); |
1736 | type_register_static(&mv88w8618_eth_info); |
1737 | type_register_static(&mv88w8618_wlan_info); |
1738 | type_register_static(&musicpal_lcd_info); |
1739 | type_register_static(&musicpal_gpio_info); |
1740 | type_register_static(&musicpal_key_info); |
1741 | type_register_static(&musicpal_misc_info); |
1742 | } |
1743 | |
1744 | type_init(musicpal_register_types) |
1745 | |