1 |
/* |
2 |
* Cisco router simulation platform. |
3 |
* Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) |
4 |
* |
5 |
* Marvell MV64460 system controller. |
6 |
* |
7 |
* Based on GT9100 documentation and Linux kernel sources. |
8 |
*/ |
9 |
|
10 |
#include <stdio.h> |
11 |
#include <stdlib.h> |
12 |
#include <string.h> |
13 |
|
14 |
#include "utils.h" |
15 |
#include "net.h" |
16 |
#include "cpu.h" |
17 |
#include "vm.h" |
18 |
#include "dynamips.h" |
19 |
#include "memory.h" |
20 |
#include "device.h" |
21 |
#include "net_io.h" |
22 |
#include "ptask.h" |
23 |
#include "dev_vtty.h" |
24 |
#include "dev_mv64460.h" |
25 |
|
26 |
/* Debugging flags */ |
27 |
#define DEBUG_ACCESS 0 |
28 |
#define DEBUG_UNKNOWN 0 |
29 |
#define DEBUG_DMA 0 |
30 |
#define DEBUG_MII 0 |
31 |
|
32 |
/* PCI identification */ |
33 |
#define PCI_VENDOR_MARVELL 0x11ab /* Marvell/Galileo */ |
34 |
#define PCI_PRODUCT_MARVELL_MV64460 0x6485 /* MV-64460 */ |
35 |
|
36 |
/* Interrupt Low Main Cause Register */ |
37 |
#define MV64460_REG_ILMCR 0x0004 |
38 |
|
39 |
#define MV64460_ILMCR_IDMA0_COMP 0x00000010 /* IDMA 0 Transfer completed */ |
40 |
#define MV64460_ILMCR_IDMA1_COMP 0x00000020 /* IDMA 1 Transfer completed */ |
41 |
#define MV64460_ILMCR_IDMA2_COMP 0x00000040 /* IDMA 2 Transfer completed */ |
42 |
#define MV64460_ILMCR_IDMA3_COMP 0x00000080 /* IDMA 3 Transfer completed */ |
43 |
#define MV64460_ILMCR_TIMER0_EXP 0x00000100 /* Timer 0 expired */ |
44 |
#define MV64460_ILMCR_TIMER1_EXP 0x00000200 /* Timer 1 expired */ |
45 |
#define MV64460_ILMCR_TIMER2_EXP 0x00000400 /* Timer 2 expired */ |
46 |
#define MV64460_ILMCR_TIMER3_EXP 0x00000800 /* Timer 3 expired */ |
47 |
|
48 |
/* Interrupt High Main Cause Register */ |
49 |
#define MV64460_REG_IHMCR 0x000c |
50 |
|
51 |
/* Interrupt masks for CPU0 */ |
52 |
#define MV64460_REG_CPU0_INTR_MASK_LO 0x0014 |
53 |
#define MV64460_REG_CPU0_INTR_MASK_HI 0x001c |
54 |
|
55 |
#define MV64460_IHMCR_ETH0_SUM 0x00000001 /* Ethernet 0 */ |
56 |
#define MV64460_IHMCR_ETH1_SUM 0x00000002 /* Ethernet 1 */ |
57 |
#define MV64460_IHMCR_ETH2_SUM 0x00000004 /* Ethernet 2 */ |
58 |
#define MV64460_IHMCR_SDMA_SUM 0x00000010 /* Serial DMA */ |
59 |
|
60 |
#define MV64460_IHMCR_GPP_0_7_SUM 0x01000000 |
61 |
#define MV64460_IHMCR_GPP_8_15_SUM 0x02000000 |
62 |
#define MV64460_IHMCR_GPP_16_23_SUM 0x04000000 |
63 |
#define MV64460_IHMCR_GPP_24_31_SUM 0x08000000 |
64 |
|
65 |
/* GPP Interrupt cause and mask registers */ |
66 |
#define MV64460_REG_GPP_INTR_CAUSE 0xf108 |
67 |
#define MV64460_REG_GPP_INTR_MASK 0xf10c |
68 |
|
69 |
/* SDMA - number of channels */ |
70 |
#define MV64460_SDMA_CHANNELS 2 |
71 |
|
72 |
/* SDMA registers base offsets */ |
73 |
#define MV64460_REG_SDMA0 0x4000 |
74 |
#define MV64460_REG_SDMA1 0x6000 |
75 |
|
76 |
/* SDMA cause register */ |
77 |
#define MV64460_REG_SDMA_CAUSE 0xb800 |
78 |
|
79 |
#define MV64460_SDMA_CAUSE_RXBUF0 0x00000001 /* RX Buffer returned */ |
80 |
#define MV64460_SDMA_CAUSE_RXERR0 0x00000002 /* RX Error */ |
81 |
#define MV64460_SDMA_CAUSE_TXBUF0 0x00000004 /* TX Buffer returned */ |
82 |
#define MV64460_SDMA_CAUSE_TXEND0 0x00000008 /* TX End */ |
83 |
#define MV64460_SDMA_CAUSE_RXBUF1 0x00000010 /* RX Buffer returned */ |
84 |
#define MV64460_SDMA_CAUSE_RXERR1 0x00000020 /* RX Error */ |
85 |
#define MV64460_SDMA_CAUSE_TXBUF1 0x00000040 /* TX Buffer returned */ |
86 |
#define MV64460_SDMA_CAUSE_TXEND1 0x00000080 /* TX End */ |
87 |
|
88 |
/* SDMA register offsets */ |
89 |
#define MV64460_SDMA_SDC 0x0000 /* Configuration Register */ |
90 |
#define MV64460_SDMA_SDCM 0x0008 /* Command Register */ |
91 |
#define MV64460_SDMA_RX_DESC 0x0800 /* RX descriptor */ |
92 |
#define MV64460_SDMA_RX_BUF_PTR 0x0808 /* Current buffer address ? */ |
93 |
#define MV64460_SDMA_SCRDP 0x0810 /* Current RX descriptor */ |
94 |
#define MV64460_SDMA_TX_DESC 0x0c00 /* TX descriptor */ |
95 |
#define MV64460_SDMA_SCTDP 0x0c10 /* Current TX desc. pointer */ |
96 |
#define MV64460_SDMA_SFTDP 0x0c14 /* First TX desc. pointer */ |
97 |
|
98 |
/* SDMA Descriptor Command/Status word */ |
99 |
#define MV64460_SDMA_CMD_O 0x80000000 /* Owner bit */ |
100 |
#define MV64460_SDMA_CMD_AM 0x40000000 /* Auto-mode */ |
101 |
#define MV64460_SDMA_CMD_EI 0x00800000 /* Enable Interrupt */ |
102 |
#define MV64460_SDMA_CMD_F 0x00020000 /* First buffer */ |
103 |
#define MV64460_SDMA_CMD_L 0x00010000 /* Last buffer */ |
104 |
|
105 |
/* SDMA Command Register (SDCM) */ |
106 |
#define MV64460_SDCM_ERD 0x00000080 /* Enable RX DMA */ |
107 |
#define MV64460_SDCM_AR 0x00008000 /* Abort Receive */ |
108 |
#define MV64460_SDCM_STD 0x00010000 /* Stop TX */ |
109 |
#define MV64460_SDCM_TXD 0x00800000 /* TX Demand */ |
110 |
#define MV64460_SDCM_AT 0x80000000 /* Abort Transmit */ |
111 |
|
112 |
/* SDMA RX/TX descriptor */ |
113 |
struct sdma_desc { |
114 |
m_uint32_t buf_size; |
115 |
m_uint32_t cmd_stat; |
116 |
m_uint32_t next_ptr; |
117 |
m_uint32_t buf_ptr; |
118 |
}; |
119 |
|
120 |
/* SDMA channel */ |
121 |
struct sdma_channel { |
122 |
m_uint32_t sdc; |
123 |
m_uint32_t sdcm; |
124 |
m_uint32_t rx_desc; |
125 |
m_uint32_t rx_buf_ptr; |
126 |
m_uint32_t scrdp; |
127 |
m_uint32_t tx_desc; |
128 |
m_uint32_t sctdp; |
129 |
m_uint32_t sftdp; |
130 |
|
131 |
/* Associated VTTY for UART */ |
132 |
vtty_t *vtty; |
133 |
}; |
134 |
|
135 |
/* MV64460 system controller private data */ |
136 |
struct mv64460_data { |
137 |
char *name; |
138 |
vm_obj_t vm_obj; |
139 |
struct vdevice dev; |
140 |
struct pci_device *pci_dev; |
141 |
vm_instance_t *vm; |
142 |
|
143 |
/* Interrupt Main Cause Low and High registers */ |
144 |
m_uint32_t intr_lo,intr_hi; |
145 |
|
146 |
/* CPU0 interrupt masks */ |
147 |
m_uint32_t cpu0_intr_mask_lo,cpu0_intr_mask_hi; |
148 |
|
149 |
/* GPP interrupts */ |
150 |
m_uint32_t gpp_intr,gpp_mask; |
151 |
|
152 |
/* SDMA channels */ |
153 |
m_uint32_t sdma_cause; |
154 |
struct sdma_channel sdma[MV64460_SDMA_CHANNELS]; |
155 |
|
156 |
/* PCI busses */ |
157 |
struct pci_bus *bus[2]; |
158 |
}; |
159 |
|
160 |
/* Update the interrupt status for CPU 0 */ |
161 |
static void mv64460_ic_update_cpu0_status(struct mv64460_data *d) |
162 |
{ |
163 |
cpu_ppc_t *cpu0 = CPU_PPC32(d->vm->boot_cpu); |
164 |
m_uint32_t lo_act,hi_act; |
165 |
|
166 |
d->intr_lo = d->intr_hi = 0; |
167 |
|
168 |
/* Serial DMA */ |
169 |
if (d->sdma_cause) |
170 |
d->intr_hi |= MV64460_IHMCR_SDMA_SUM; |
171 |
|
172 |
/* Test GPP bits */ |
173 |
if (d->gpp_intr & d->gpp_mask & 0x000000FF) |
174 |
d->intr_hi |= MV64460_IHMCR_GPP_0_7_SUM; |
175 |
|
176 |
if (d->gpp_intr & d->gpp_mask & 0x0000FF00) |
177 |
d->intr_hi |= MV64460_IHMCR_GPP_8_15_SUM; |
178 |
|
179 |
if (d->gpp_intr & d->gpp_mask & 0x00FF0000) |
180 |
d->intr_hi |= MV64460_IHMCR_GPP_16_23_SUM; |
181 |
|
182 |
if (d->gpp_intr & d->gpp_mask & 0xFF000000) |
183 |
d->intr_hi |= MV64460_IHMCR_GPP_24_31_SUM; |
184 |
|
185 |
lo_act = d->intr_lo & d->cpu0_intr_mask_lo; |
186 |
hi_act = d->intr_hi & d->cpu0_intr_mask_hi; |
187 |
|
188 |
cpu0->irq_pending = lo_act || hi_act; |
189 |
cpu0->irq_check = cpu0->irq_pending; |
190 |
} |
191 |
|
192 |
/* Send contents of a SDMA buffer to the associated VTTY */ |
193 |
static void mv64460_sdma_send_buf_to_vtty(struct mv64460_data *d, |
194 |
struct sdma_channel *chan, |
195 |
struct sdma_desc *desc) |
196 |
{ |
197 |
m_uint32_t buf_addr,len,clen; |
198 |
char buffer[512]; |
199 |
|
200 |
len = (desc->buf_size >> 16) & 0xFFFF; |
201 |
buf_addr = desc->buf_ptr; |
202 |
|
203 |
//vm_log(d->vm,"SDMA","len=0x%8.8x, buf_addr=0x%8.8x\n",len,buf_addr); |
204 |
|
205 |
while(len > 0) { |
206 |
if (len > sizeof(buffer)) |
207 |
clen = sizeof(buffer); |
208 |
else |
209 |
clen = len; |
210 |
|
211 |
physmem_copy_from_vm(d->vm,buffer,buf_addr,clen); |
212 |
vtty_put_buffer(chan->vtty,buffer,clen); |
213 |
|
214 |
len -= clen; |
215 |
buf_addr += clen; |
216 |
} |
217 |
} |
218 |
|
219 |
/* Fetch a SDMA descriptor */ |
220 |
static void mv64460_sdma_fetch_desc(struct mv64460_data *d,m_uint32_t addr, |
221 |
struct sdma_desc *desc) |
222 |
{ |
223 |
physmem_copy_from_vm(d->vm,desc,addr,sizeof(struct sdma_desc)); |
224 |
|
225 |
/* byte-swapping */ |
226 |
desc->buf_size = vmtoh32(desc->buf_size); |
227 |
desc->cmd_stat = vmtoh32(desc->cmd_stat); |
228 |
desc->next_ptr = vmtoh32(desc->next_ptr); |
229 |
desc->buf_ptr = vmtoh32(desc->buf_ptr); |
230 |
} |
231 |
|
232 |
/* Start TX DMA process */ |
233 |
static void mv64460_sdma_tx_start(struct mv64460_data *d, |
234 |
struct sdma_channel *chan) |
235 |
{ |
236 |
struct sdma_desc desc; |
237 |
m_uint32_t desc_addr; |
238 |
|
239 |
desc_addr = chan->sctdp; |
240 |
|
241 |
//vm_log(d->vm,"SDMA","TX fetch starting: 0x%8.8x\n",desc_addr); |
242 |
|
243 |
while(desc_addr != 0) |
244 |
{ |
245 |
//vm_log(d->vm,"SDMA","fetching descriptor at 0x%8.8x\n",desc_addr); |
246 |
|
247 |
/* Fetch the descriptor */ |
248 |
mv64460_sdma_fetch_desc(d,desc_addr,&desc); |
249 |
chan->sctdp = desc_addr; |
250 |
|
251 |
#if 0 |
252 |
vm_log(d->vm,"SDMA","buf_size=0x%8.8x, cmd_stat=0x%8.8x, " |
253 |
"next_ptr=0x%8.8x, buf_ptr=0x%8.8x\n", |
254 |
desc.buf_size,desc.cmd_stat,desc.next_ptr,desc.buf_ptr); |
255 |
#endif |
256 |
|
257 |
if (!(desc.cmd_stat & MV64460_SDMA_CMD_O)) { |
258 |
d->sdma_cause |= 4; |
259 |
mv64460_ic_update_cpu0_status(d); |
260 |
return; |
261 |
} |
262 |
|
263 |
mv64460_sdma_send_buf_to_vtty(d,chan,&desc); |
264 |
|
265 |
desc.buf_size &= 0xFFFF0000; |
266 |
desc.cmd_stat &= ~MV64460_SDMA_CMD_O; |
267 |
|
268 |
physmem_copy_u32_to_vm(d->vm,desc_addr,desc.buf_size); |
269 |
physmem_copy_u32_to_vm(d->vm,desc_addr+4,desc.cmd_stat); |
270 |
|
271 |
desc_addr = desc.next_ptr; |
272 |
} |
273 |
|
274 |
d->sdma_cause |= 4; |
275 |
mv64460_ic_update_cpu0_status(d); |
276 |
|
277 |
/* Clear the TXD bit */ |
278 |
chan->sdcm &= ~MV64460_SDCM_TXD; |
279 |
} |
280 |
|
281 |
/* Put data into a RX DMA buffer */ |
282 |
static void mv64460_sdma_put_rx_data(struct mv64460_data *d, |
283 |
struct sdma_channel *chan, |
284 |
char *buffer,size_t buf_len) |
285 |
{ |
286 |
struct sdma_desc desc; |
287 |
m_uint32_t desc_addr; |
288 |
|
289 |
desc_addr = chan->scrdp; |
290 |
|
291 |
/* Fetch the current SDMA buffer */ |
292 |
mv64460_sdma_fetch_desc(d,desc_addr,&desc); |
293 |
|
294 |
#if 0 |
295 |
vm_log(d->vm,"SDMA_RX","buf_size=0x%8.8x, cmd_stat=0x%8.8x, " |
296 |
"next_ptr=0x%8.8x, buf_ptr=0x%8.8x\n", |
297 |
desc.buf_size,desc.cmd_stat,desc.next_ptr,desc.buf_ptr); |
298 |
#endif |
299 |
|
300 |
if (!(desc.cmd_stat & MV64460_SDMA_CMD_O)) { |
301 |
d->sdma_cause |= 1; |
302 |
mv64460_ic_update_cpu0_status(d); |
303 |
return; |
304 |
} |
305 |
|
306 |
physmem_copy_to_vm(d->vm,buffer,desc.buf_ptr,1); |
307 |
|
308 |
desc.buf_size |= 0x00000001; |
309 |
desc.cmd_stat &= ~MV64460_SDMA_CMD_O; |
310 |
|
311 |
physmem_copy_u32_to_vm(d->vm,desc_addr,desc.buf_size); |
312 |
physmem_copy_u32_to_vm(d->vm,desc_addr+4,desc.cmd_stat); |
313 |
|
314 |
chan->scrdp = desc.next_ptr; |
315 |
|
316 |
d->sdma_cause |= 1; |
317 |
mv64460_ic_update_cpu0_status(d); |
318 |
} |
319 |
|
320 |
/* Input on VTTY 0 */ |
321 |
static void mv64460_tty_input_s0(vtty_t *vtty) |
322 |
{ |
323 |
struct mv64460_data *d = vtty->priv_data; |
324 |
struct sdma_channel *chan = &d->sdma[0]; |
325 |
char c; |
326 |
|
327 |
c = vtty_get_char(vtty); |
328 |
mv64460_sdma_put_rx_data(d,chan,&c,1); |
329 |
} |
330 |
|
331 |
/* Input on VTTY 0 */ |
332 |
static void mv64460_tty_input_s1(vtty_t *vtty) |
333 |
{ |
334 |
struct mv64460_data *d = vtty->priv_data; |
335 |
struct sdma_channel *chan = &d->sdma[1]; |
336 |
char c; |
337 |
|
338 |
c = vtty_get_char(vtty); |
339 |
mv64460_sdma_put_rx_data(d,chan,&c,1); |
340 |
} |
341 |
|
342 |
/* Bind a VTTY to a SDMA channel */ |
343 |
int mv64460_sdma_bind_vtty(struct mv64460_data *d,u_int chan_id,vtty_t *vtty) |
344 |
{ |
345 |
switch(chan_id) { |
346 |
case 0: |
347 |
vtty->priv_data = d; |
348 |
vtty->read_notifier = mv64460_tty_input_s0; |
349 |
break; |
350 |
case 1: |
351 |
vtty->priv_data = d; |
352 |
vtty->read_notifier = mv64460_tty_input_s1; |
353 |
break; |
354 |
default: |
355 |
return(-1); |
356 |
} |
357 |
|
358 |
d->sdma[chan_id].vtty = vtty; |
359 |
return(0); |
360 |
} |
361 |
|
362 |
/* |
363 |
* SDMA registers access. |
364 |
*/ |
365 |
static int mv64460_sdma_access(struct mv64460_data *d,cpu_gen_t *cpu, |
366 |
m_uint32_t offset,m_uint32_t op_type, |
367 |
m_uint64_t *data) |
368 |
{ |
369 |
struct sdma_channel *chan; |
370 |
int id = -1; |
371 |
|
372 |
/* Access to SDMA channel 0 registers ? */ |
373 |
if ((offset >= MV64460_REG_SDMA0) && |
374 |
(offset < (MV64460_REG_SDMA0 + 0x1000))) |
375 |
{ |
376 |
offset -= MV64460_REG_SDMA0; |
377 |
id = 0; |
378 |
} |
379 |
|
380 |
/* Access to SDMA channel 1 registers ? */ |
381 |
if ((offset >= MV64460_REG_SDMA1) && |
382 |
(offset < (MV64460_REG_SDMA1 + 0x1000))) |
383 |
{ |
384 |
offset -= MV64460_REG_SDMA1; |
385 |
id = 1; |
386 |
} |
387 |
|
388 |
if (id == -1) |
389 |
return(FALSE); |
390 |
|
391 |
if (op_type == MTS_WRITE) |
392 |
*data = swap32(*data); |
393 |
|
394 |
chan = &d->sdma[id]; |
395 |
switch(offset) { |
396 |
case MV64460_SDMA_SDCM: |
397 |
if (op_type == MTS_READ) |
398 |
; //*data = chan->sdcm; |
399 |
else { |
400 |
chan->sdcm = *data; |
401 |
|
402 |
if (chan->sdcm & MV64460_SDCM_TXD) |
403 |
mv64460_sdma_tx_start(d,chan); |
404 |
} |
405 |
break; |
406 |
|
407 |
case MV64460_SDMA_SCRDP: |
408 |
if (op_type == MTS_READ) |
409 |
*data = chan->scrdp; |
410 |
else |
411 |
chan->scrdp = *data; |
412 |
break; |
413 |
|
414 |
case MV64460_SDMA_SCTDP: |
415 |
if (op_type == MTS_READ) |
416 |
*data = chan->sctdp; |
417 |
else |
418 |
chan->sctdp = *data; |
419 |
break; |
420 |
|
421 |
case MV64460_SDMA_SFTDP: |
422 |
if (op_type == MTS_READ) |
423 |
*data = chan->sftdp; |
424 |
else |
425 |
chan->sftdp = *data; |
426 |
break; |
427 |
|
428 |
#if DEBUG_UNKNOWN |
429 |
default: |
430 |
if (op_type == MTS_READ) { |
431 |
cpu_log(cpu,"MV64460/SDMA", |
432 |
"read access to unknown register 0x%x, pc=0x%llx\n", |
433 |
offset,cpu_get_pc(cpu)); |
434 |
} else { |
435 |
cpu_log(cpu,"MV64460/SDMA", |
436 |
"write access to unknown register 0x%x, value=0x%llx, " |
437 |
"pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); |
438 |
} |
439 |
#endif |
440 |
} |
441 |
|
442 |
if (op_type == MTS_READ) |
443 |
*data = swap32(*data); |
444 |
|
445 |
/* Update the interrupt status */ |
446 |
mv64460_ic_update_cpu0_status(d); |
447 |
return(TRUE); |
448 |
} |
449 |
|
450 |
/* |
451 |
* dev_mv64460_access() |
452 |
*/ |
453 |
void *dev_mv64460_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, |
454 |
u_int op_size,u_int op_type,m_uint64_t *data) |
455 |
{ |
456 |
struct mv64460_data *mv_data = dev->priv_data; |
457 |
|
458 |
#if DEBUG_ACCESS |
459 |
if (op_type == MTS_READ) { |
460 |
cpu_log(cpu,"MV64460", |
461 |
"read access to register 0x%x, pc=0x%llx\n", |
462 |
offset,cpu_get_pc(cpu)); |
463 |
} else { |
464 |
cpu_log(cpu,"MV64460", |
465 |
"write access to register 0x%x, value=0x%llx, pc=0x%llx\n", |
466 |
offset,*data,cpu_get_pc(cpu)); |
467 |
} |
468 |
#endif |
469 |
|
470 |
if (op_type == MTS_READ) |
471 |
*data = 0x0; |
472 |
|
473 |
if (mv64460_sdma_access(mv_data,cpu,offset,op_type,data)) |
474 |
return NULL; |
475 |
|
476 |
if (op_type == MTS_WRITE) |
477 |
*data = swap32(*data); |
478 |
|
479 |
switch(offset) { |
480 |
/* Interrupt Main Cause Low */ |
481 |
case MV64460_REG_ILMCR: |
482 |
if (op_type == MTS_READ) |
483 |
*data = mv_data->intr_lo; |
484 |
break; |
485 |
|
486 |
/* Interrupt Main Cause High */ |
487 |
case MV64460_REG_IHMCR: |
488 |
if (op_type == MTS_READ) |
489 |
*data = mv_data->intr_hi; |
490 |
break; |
491 |
|
492 |
/* CPU0 Interrupt Mask Low */ |
493 |
case MV64460_REG_CPU0_INTR_MASK_LO: |
494 |
if (op_type == MTS_READ) |
495 |
*data = mv_data->cpu0_intr_mask_lo; |
496 |
else |
497 |
mv_data->cpu0_intr_mask_lo = *data; |
498 |
break; |
499 |
|
500 |
/* CPU0 Interrupt Mask High */ |
501 |
case MV64460_REG_CPU0_INTR_MASK_HI: |
502 |
if (op_type == MTS_READ) |
503 |
*data = mv_data->cpu0_intr_mask_hi; |
504 |
else |
505 |
mv_data->cpu0_intr_mask_hi = *data; |
506 |
break; |
507 |
|
508 |
/* ===== PCI Bus 0 ===== */ |
509 |
case PCI_BUS_ADDR: /* pci configuration address (0xcf8) */ |
510 |
pci_dev_addr_handler(cpu,mv_data->bus[0],op_type,FALSE,data); |
511 |
break; |
512 |
|
513 |
case PCI_BUS_DATA: /* pci data address (0xcfc) */ |
514 |
pci_dev_data_handler(cpu,mv_data->bus[0],op_type,FALSE,data); |
515 |
break; |
516 |
|
517 |
/* ===== PCI Bus 0 ===== */ |
518 |
case 0xc78: /* pci configuration address (0xc78) */ |
519 |
pci_dev_addr_handler(cpu,mv_data->bus[1],op_type,FALSE,data); |
520 |
break; |
521 |
|
522 |
case 0xc7c: /* pci data address (0xc7c) */ |
523 |
pci_dev_data_handler(cpu,mv_data->bus[1],op_type,FALSE,data); |
524 |
break; |
525 |
|
526 |
/* MII */ |
527 |
case 0x2004: |
528 |
if (op_type == MTS_READ) |
529 |
*data = 0x08000000; |
530 |
break; |
531 |
|
532 |
/* GPP interrupt cause */ |
533 |
case MV64460_REG_GPP_INTR_CAUSE: |
534 |
if (op_type == MTS_READ) |
535 |
*data = mv_data->gpp_intr; |
536 |
break; |
537 |
|
538 |
/* GPP interrupt mask */ |
539 |
case MV64460_REG_GPP_INTR_MASK: |
540 |
if (op_type == MTS_READ) |
541 |
*data = mv_data->gpp_mask; |
542 |
else |
543 |
mv_data->gpp_mask = *data; |
544 |
break; |
545 |
|
546 |
case 0x8030: |
547 |
if (op_type == MTS_READ) |
548 |
*data = 0xFFFFFFFF; |
549 |
break; |
550 |
|
551 |
case 0x9030: |
552 |
if (op_type == MTS_READ) |
553 |
*data = 0xFFFFFFFF; |
554 |
break; |
555 |
|
556 |
/* SDMA cause register */ |
557 |
case MV64460_REG_SDMA_CAUSE: |
558 |
if (op_type == MTS_READ) |
559 |
*data = mv_data->sdma_cause; |
560 |
else |
561 |
mv_data->sdma_cause &= *data; |
562 |
break; |
563 |
|
564 |
#if DEBUG_UNKNOWN |
565 |
default: |
566 |
if (op_type == MTS_READ) { |
567 |
cpu_log(cpu,"MV64460","read from addr 0x%x, pc=0x%llx\n", |
568 |
offset,cpu_get_pc(cpu)); |
569 |
} else { |
570 |
cpu_log(cpu,"MV64460","write to addr 0x%x, value=0x%llx, " |
571 |
"pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); |
572 |
} |
573 |
#endif |
574 |
} |
575 |
|
576 |
if (op_type == MTS_READ) |
577 |
*data = swap32(*data); |
578 |
|
579 |
/* Update the interrupt status */ |
580 |
mv64460_ic_update_cpu0_status(mv_data); |
581 |
return NULL; |
582 |
} |
583 |
|
584 |
/* Set value of GPP register */ |
585 |
void dev_mv64460_set_gpp_reg(struct mv64460_data *d,m_uint32_t val) |
586 |
{ |
587 |
d->gpp_intr = val; |
588 |
mv64460_ic_update_cpu0_status(d); |
589 |
} |
590 |
|
591 |
/* Set a GPP interrupt */ |
592 |
void dev_mv64460_set_gpp_intr(struct mv64460_data *d,u_int irq) |
593 |
{ |
594 |
d->gpp_intr |= 1 << irq; |
595 |
mv64460_ic_update_cpu0_status(d); |
596 |
|
597 |
#if 0 |
598 |
printf("SET_GPP_INTR: lo=0x%8.8x, hi=0x%8.8x\n",d->intr_lo,d->intr_hi); |
599 |
printf("gpp_intr = 0x%8.8x, gpp_mask = 0x%8.8x\n",d->gpp_intr,d->gpp_mask); |
600 |
#endif |
601 |
} |
602 |
|
603 |
/* Clear a GPP interrupt */ |
604 |
void dev_mv64460_clear_gpp_intr(struct mv64460_data *d,u_int irq) |
605 |
{ |
606 |
d->gpp_intr &= ~(1 << irq); |
607 |
mv64460_ic_update_cpu0_status(d); |
608 |
} |
609 |
|
610 |
/* |
611 |
* pci_mv64460_read() |
612 |
* |
613 |
* Read a PCI register. |
614 |
*/ |
615 |
static m_uint32_t pci_mv64460_read(cpu_gen_t *cpu,struct pci_device *dev, |
616 |
int reg) |
617 |
{ |
618 |
switch (reg) { |
619 |
default: |
620 |
return(0); |
621 |
} |
622 |
} |
623 |
|
624 |
/* Shutdown a MV64460 system controller */ |
625 |
void dev_mv64460_shutdown(vm_instance_t *vm,struct mv64460_data *d) |
626 |
{ |
627 |
if (d != NULL) { |
628 |
/* Remove the device */ |
629 |
dev_remove(vm,&d->dev); |
630 |
|
631 |
/* Remove the PCI device */ |
632 |
pci_dev_remove(d->pci_dev); |
633 |
|
634 |
/* Free the structure itself */ |
635 |
free(d); |
636 |
} |
637 |
} |
638 |
|
639 |
/* Create a new MV64460 controller */ |
640 |
int dev_mv64460_init(vm_instance_t *vm,char *name, |
641 |
m_uint64_t paddr,m_uint32_t len) |
642 |
{ |
643 |
struct mv64460_data *d; |
644 |
|
645 |
if (!(d = malloc(sizeof(*d)))) { |
646 |
fprintf(stderr,"mv64460: unable to create device data.\n"); |
647 |
return(-1); |
648 |
} |
649 |
|
650 |
memset(d,0,sizeof(*d)); |
651 |
d->name = name; |
652 |
d->vm = vm; |
653 |
d->bus[0] = vm->pci_bus[0]; |
654 |
d->bus[1] = vm->pci_bus[1]; |
655 |
|
656 |
vm_object_init(&d->vm_obj); |
657 |
d->vm_obj.name = name; |
658 |
d->vm_obj.data = d; |
659 |
d->vm_obj.shutdown = (vm_shutdown_t)dev_mv64460_shutdown; |
660 |
|
661 |
dev_init(&d->dev); |
662 |
d->dev.name = name; |
663 |
d->dev.priv_data = d; |
664 |
d->dev.phys_addr = paddr; |
665 |
d->dev.phys_len = len; |
666 |
d->dev.handler = dev_mv64460_access; |
667 |
|
668 |
/* Add the controller as a PCI device */ |
669 |
if (!pci_dev_lookup(d->bus[0],0,0,0)) { |
670 |
d->pci_dev = pci_dev_add(d->bus[0],name, |
671 |
PCI_VENDOR_MARVELL,PCI_PRODUCT_MARVELL_MV64460, |
672 |
0,0,-1,d,NULL,pci_mv64460_read,NULL); |
673 |
if (!d->pci_dev) { |
674 |
fprintf(stderr,"mv64460: unable to create PCI device.\n"); |
675 |
return(-1); |
676 |
} |
677 |
} |
678 |
|
679 |
/* TEST */ |
680 |
pci_dev_add(d->bus[1],name, |
681 |
PCI_VENDOR_MARVELL,PCI_PRODUCT_MARVELL_MV64460, |
682 |
0,0,-1,d,NULL,pci_mv64460_read,NULL); |
683 |
|
684 |
/* Map this device to the VM */ |
685 |
vm_bind_device(vm,&d->dev); |
686 |
vm_object_add(vm,&d->vm_obj); |
687 |
return(0); |
688 |
} |