Commit 5eb9fc83f2a2badaa8389539f789e9e660c3816a
1 parent
0c250455
spi flash and at91_spi plus at91_pdc initial implementation
Showing
9 changed files
with
625 additions
and
29 deletions
Makefile.target
... | ... | @@ -411,7 +411,7 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o |
411 | 411 | endif |
412 | 412 | |
413 | 413 | obj-arm-y = integratorcp.o versatilepb.o smc91c111.o arm_pic.o arm_timer.o |
414 | -obj-arm-y += pflash_atmel.o at91sam9.o | |
414 | +obj-arm-y += pflash_atmel.o at91sam9.o spi_flash.o | |
415 | 415 | obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o |
416 | 416 | obj-arm-y += versatile_pci.o |
417 | 417 | obj-arm-y += realview_gic.o realview.o arm_sysctl.o mpcore.o |
... | ... | @@ -433,7 +433,7 @@ obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o |
433 | 433 | obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o |
434 | 434 | obj-arm-y += syborg_virtio.o |
435 | 435 | obj-arm-y += at91_aic.o at91_dbgu.o at91_pio.o at91_pit.o at91_pmc.o at91_rtt.o |
436 | -obj-arm-y += at91_rstc.o at91_intor.o at91_tc.o at91_emac.o at91pes.o | |
436 | +obj-arm-y += at91_rstc.o at91_intor.o at91_tc.o at91_emac.o at91_spi.o at91_pdc.o at91pes.o | |
437 | 437 | obj-arm-y += gpio_rotary.o gpio_keypad.o hd44780.o |
438 | 438 | |
439 | 439 | ifeq ($(TARGET_BASE_ARCH), arm) | ... | ... |
hw/at91.h
... | ... | @@ -29,4 +29,19 @@ |
29 | 29 | Controller. */ |
30 | 30 | extern int at91_master_clock_frequency; |
31 | 31 | |
32 | +typedef uint32_t (*spi_txrx_callback_fun_t)(void *opaque, uint32_t cmd, int len); | |
33 | + | |
34 | +typedef struct PDCState PDCState; | |
35 | +typedef int (*pdc_start_transfer_t)(void *opaque, | |
36 | + target_phys_addr_t tx, | |
37 | + unsigned int tx_len, | |
38 | + target_phys_addr_t rx, | |
39 | + unsigned int rx_len, | |
40 | + int last_transfer); | |
41 | + | |
42 | +PDCState *at91_pdc_init(void *opaque, pdc_start_transfer_t start_transfer); | |
43 | +extern void at91_pdc_reset(PDCState *s); | |
44 | +extern void at91_pdc_write(void *opaque, target_phys_addr_t offset, uint32_t val); | |
45 | +extern uint32_t at91_pdc_read(void *opaque, target_phys_addr_t offset); | |
46 | + | |
32 | 47 | #endif /* !AT91_H */ | ... | ... |
hw/at91_pdc.c
0 → 100644
1 | +/* | |
2 | + * AT91 Peripheral DMA Controller (PDC) User Interface | |
3 | + * Written by Evgeniy Dushistov | |
4 | + * This code is licenced under the GPL. | |
5 | + */ | |
6 | +#include <stdint.h> | |
7 | +#include "sysbus.h" | |
8 | +#include "at91.h" | |
9 | + | |
10 | + | |
11 | +#define PDC_RPR 0x100 //Receive Pointer Register Read/Write 0 | |
12 | +#define PDC_RCR 0x104 //Receive Counter Register PDC_RCR Read/Write 0 | |
13 | +#define PDC_TPR 0x108 //Transmit Pointer Register PDC_TPR Read/Write 0 | |
14 | +#define PDC_TCR 0x10C //Transmit Counter Register PDC_TCR Read/Write 0 | |
15 | +#define PDC_RNPR 0x110 //Receive Next Pointer Register PDC_RNPR Read/Write 0 | |
16 | +#define PDC_RNCR 0x114 //Receive Next Counter Register PDC_RNCR Read/Write 0 | |
17 | +#define PDC_TNPR 0x118 //Transmit Next Pointer Register PDC_TNPR Read/Write 0 | |
18 | +#define PDC_TNCR 0x11C //Transmit Next Counter Register PDC_TNCR Read/Write 0 | |
19 | +#define PDC_PTCR 0x120 //Transfer Control Register PDC_PTCR Write 0 | |
20 | +#define PDC_PTSR 0x124 //Transfer Status Register PDC_PTSR Read 0 | |
21 | + | |
22 | +#define PDC_PTCR_RXTEN (1 << 0) | |
23 | +#define PDC_PTCR_RXTDIS (1 << 1) | |
24 | +#define PDC_PTCR_TXTEN (1 << 8) | |
25 | +#define PDC_PTCR_TXTDIS (1 << 9) | |
26 | + | |
27 | +#define AT91_PDC_DEBUG | |
28 | +#ifdef AT91_PDC_DEBUG | |
29 | +#define DPRINTF(fmt, ...) \ | |
30 | + do { \ | |
31 | + printf("AT91PDC: " fmt , ## __VA_ARGS__); \ | |
32 | + } while (0) | |
33 | +#else | |
34 | +#define DPRINTF(fmt, ...) do { } while (0) | |
35 | +#endif | |
36 | + | |
37 | +struct PDCState { | |
38 | + uint32_t ptsr; | |
39 | + uint32_t rpr; | |
40 | + uint32_t tpr; | |
41 | + uint16_t rcr; | |
42 | + uint16_t tcr; | |
43 | + uint32_t rnpr; | |
44 | + uint32_t tnpr; | |
45 | + uint16_t rncr; | |
46 | + uint16_t tncr; | |
47 | + void *opaque; | |
48 | + pdc_start_transfer_t start_transfer; | |
49 | +}; | |
50 | + | |
51 | +PDCState *at91_pdc_init(void *opaque, pdc_start_transfer_t start_transfer) | |
52 | +{ | |
53 | + PDCState *s; | |
54 | + | |
55 | + s = qemu_mallocz(sizeof(PDCState)); | |
56 | + s->opaque = opaque; | |
57 | + s->start_transfer = start_transfer; | |
58 | + return s; | |
59 | +} | |
60 | + | |
61 | +void at91_pdc_write(void *opaque, target_phys_addr_t offset, uint32_t val) | |
62 | +{ | |
63 | + PDCState *s = opaque; | |
64 | + int last_transfer = 1; | |
65 | + | |
66 | + switch (offset) { | |
67 | + case PDC_PTCR: | |
68 | + DPRINTF("%s, %s was (%s, %s)\n", | |
69 | + val & PDC_PTCR_RXTEN ? "enable recieve" : "<>", | |
70 | + val & PDC_PTCR_TXTEN ? "enable transfer" : "<>", | |
71 | + s->ptsr & PDC_PTCR_RXTEN ? "enable recieve" : "<>", | |
72 | + s->ptsr & PDC_PTCR_TXTEN ? "enable transfer" : "<>"); | |
73 | + | |
74 | + if (val & PDC_PTCR_RXTEN) { | |
75 | + s->ptsr |= PDC_PTCR_RXTEN; | |
76 | + } | |
77 | + | |
78 | + if (val & PDC_PTCR_RXTDIS) { | |
79 | + s->ptsr &= ~PDC_PTCR_RXTEN; | |
80 | + } | |
81 | + | |
82 | + if (s->ptsr & PDC_PTCR_RXTEN) { | |
83 | + last_transfer = s->rncr == 0; | |
84 | + } | |
85 | + | |
86 | + if (val & PDC_PTCR_TXTEN) { | |
87 | + s->ptsr |= PDC_PTCR_TXTEN; | |
88 | + } | |
89 | + | |
90 | + if (val & PDC_PTCR_TXTDIS) { | |
91 | + s->ptsr &= ~PDC_PTCR_TXTEN; | |
92 | + } | |
93 | + | |
94 | + if (s->ptsr & PDC_PTCR_TXTEN) { | |
95 | + last_transfer |= s->tncr == 0; | |
96 | + } | |
97 | + | |
98 | + if ((s->ptsr & PDC_PTCR_RXTEN) || (s->ptsr & PDC_PTCR_TXTEN)) { | |
99 | + int ret = s->start_transfer(s->opaque, s->tpr, | |
100 | + (s->ptsr & PDC_PTCR_TXTEN) ? s->tcr : 0, | |
101 | + s->rpr, (s->ptsr & PDC_PTCR_RXTEN) ? s->rcr : 0, | |
102 | + last_transfer); | |
103 | + | |
104 | + if (ret == 0) { | |
105 | + if (s->ptsr & PDC_PTCR_RXTEN) { | |
106 | + s->rcr = 0; | |
107 | + } | |
108 | + if (s->ptsr & PDC_PTCR_TXTEN) { | |
109 | + s->tcr = 0; | |
110 | + } | |
111 | + | |
112 | + } else | |
113 | + break; | |
114 | + | |
115 | + | |
116 | + if (last_transfer == 0) { | |
117 | + if (s->ptsr & PDC_PTCR_RXTEN) { | |
118 | + s->rpr = s->rnpr; | |
119 | + s->rcr = s->rncr; | |
120 | + s->rncr = 0; | |
121 | + } | |
122 | + if (s->ptsr & PDC_PTCR_TXTEN) { | |
123 | + s->tpr = s->tnpr; | |
124 | + s->tcr = s->tncr; | |
125 | + s->tncr = 0; | |
126 | + } | |
127 | + s->start_transfer(s->opaque, s->tpr, | |
128 | + (s->ptsr & PDC_PTCR_TXTEN) ? s->tcr : 0, | |
129 | + s->rpr, (s->ptsr & PDC_PTCR_RXTEN) ? s->rcr : 0, 1); | |
130 | + | |
131 | + if (s->ptsr & PDC_PTCR_RXTEN) { | |
132 | + s->rcr = 0; | |
133 | + } | |
134 | + if (s->ptsr & PDC_PTCR_TXTEN) { | |
135 | + s->tcr = 0; | |
136 | + } | |
137 | + } | |
138 | + } | |
139 | + | |
140 | + break; | |
141 | + case PDC_RPR: | |
142 | + s->rpr = val; | |
143 | + break; | |
144 | + case PDC_TPR: | |
145 | + s->tpr = val; | |
146 | + break; | |
147 | + case PDC_RCR: | |
148 | + s->rcr = val; | |
149 | + break; | |
150 | + case PDC_TCR: | |
151 | + s->tcr = val; | |
152 | + break; | |
153 | + case PDC_RNPR: | |
154 | + s->rnpr = val; | |
155 | + break; | |
156 | + case PDC_RNCR: | |
157 | + s->rncr = val; | |
158 | + break; | |
159 | + case PDC_TNPR: | |
160 | + s->tnpr = val; | |
161 | + break; | |
162 | + case PDC_TNCR: | |
163 | + s->tncr = val; | |
164 | + break; | |
165 | + default: | |
166 | + DPRINTF("ignore write of %X to %X\n", val, offset); | |
167 | + /*ignore*/break; | |
168 | + } | |
169 | +} | |
170 | + | |
171 | +uint32_t at91_pdc_read(void *opaque, target_phys_addr_t offset) | |
172 | +{ | |
173 | +//PDCState *s = opaque; | |
174 | + DPRINTF("ignore read from %X\n", offset); | |
175 | + return 0; | |
176 | +} | |
177 | + | |
178 | +void at91_pdc_reset(PDCState *s) | |
179 | +{ | |
180 | + s->ptsr = 0; | |
181 | + s->rpr = 0; | |
182 | + s->tpr = 0; | |
183 | + s->rcr = 0; | |
184 | + s->tcr = 0; | |
185 | + | |
186 | + s->tnpr = 0; | |
187 | + s->rnpr = 0; | |
188 | + s->tncr = 0; | |
189 | + s->rncr = 0; | |
190 | +} | ... | ... |
hw/at91_spi.c
0 → 100644
1 | +/* | |
2 | + * AT91 Serial Peripheral Interface | |
3 | + * Written by Evgeniy Dushistov | |
4 | + * This code is licenced under the GPL. | |
5 | + */ | |
6 | +#include "sysbus.h" | |
7 | +#include "spi.h" | |
8 | +#include "at91.h" | |
9 | + | |
10 | +#define SPI_SIZE 0x4000 | |
11 | +#define SPI_CR 0x0 | |
12 | +#define SPI_MR 0x4 | |
13 | +#define SPI_RDR 0x8 | |
14 | +#define SPI_TDR 0xC | |
15 | +#define SPI_SR 0x10 | |
16 | +#define SPI_IER 0x14 | |
17 | +#define SPI_IDR 0x18 | |
18 | +#define SPI_IMR 0x1C | |
19 | +#define SPI_CSR0 0x30 | |
20 | +#define SPI_CSR1 0x34 | |
21 | +#define SPI_CSR2 0x38 | |
22 | +#define SPI_CSR3 0x3C | |
23 | + | |
24 | +#define SPI_CR_SPIEN 1 | |
25 | +#define SPI_CR_SPIDIS 2 | |
26 | +#define SPI_SR_ENDRX (1 << 4) | |
27 | +#define SPI_SR_ENDTX (1 << 5) | |
28 | +#define SPI_SR_RXBUFF (1 << 6) | |
29 | +#define SPI_SR_TXBUFE (1 << 7) | |
30 | +#define SPI_SR_SPIENS (1 << 16) | |
31 | + | |
32 | +typedef struct SPIState { | |
33 | + SysBusDevice busdev; | |
34 | + qemu_irq irq; | |
35 | + uint32_t mr; | |
36 | + uint32_t rdr; | |
37 | + uint32_t tdr; | |
38 | + uint32_t sr; | |
39 | + uint32_t imr; | |
40 | + uint32_t csr[4]; | |
41 | + SPIControl *spi_control; | |
42 | + PDCState *pdc_state; | |
43 | +} SPIState; | |
44 | + | |
45 | +#define AT91_SPI_DEBUG | |
46 | +#ifdef AT91_SPI_DEBUG | |
47 | +#define DPRINTF(fmt, ...) \ | |
48 | + do { \ | |
49 | + printf("AT91SPI: " fmt , ## __VA_ARGS__); \ | |
50 | + } while (0) | |
51 | +#else | |
52 | +#define DPRINTF(fmt, ...) do { } while (0) | |
53 | +#endif | |
54 | + | |
55 | +static uint32_t txrx_callback_empty_fun(void *opaque, uint32_t cmd, int len) | |
56 | +{ | |
57 | + return 0; | |
58 | +} | |
59 | + | |
60 | +static void set_chipselect_empty(void *opaque, int on) | |
61 | +{ | |
62 | +} | |
63 | + | |
64 | +static const SPIControl txrx_callback_empty = { | |
65 | + .txrx_callback = txrx_callback_empty_fun, | |
66 | + .set_chipselect = set_chipselect_empty, | |
67 | + .opaque = NULL, | |
68 | +}; | |
69 | + | |
70 | +extern CPUState *g_env; | |
71 | + | |
72 | +static uint32_t at91_spi_mem_read(void *opaque, target_phys_addr_t offset) | |
73 | +{ | |
74 | + SPIState *s = opaque; | |
75 | + | |
76 | + offset &= SPI_SIZE - 1; | |
77 | + DPRINTF("spi read off %X, %X\n", offset, g_env->regs[15]); | |
78 | + | |
79 | + switch (offset) { | |
80 | + case SPI_MR: | |
81 | + return s->mr; | |
82 | + case SPI_SR: | |
83 | + DPRINTF("s->sr %X\n", s->sr); | |
84 | + return s->sr; | |
85 | + case SPI_RDR: | |
86 | + return s->rdr; | |
87 | + case SPI_CSR0 ... SPI_CSR3: | |
88 | + return s->csr[(offset - SPI_CSR0) / sizeof(s->csr[0])]; | |
89 | + case 0x100 ... 0x124: | |
90 | + return at91_pdc_read(s->pdc_state, offset); | |
91 | + case SPI_CR:/*write only*/ | |
92 | + default: | |
93 | + DPRINTF("unsupported offset %X\n", offset); | |
94 | + return 0; | |
95 | + } | |
96 | +} | |
97 | + | |
98 | +static void at91_spi_mem_write(void *opaque, target_phys_addr_t offset, | |
99 | + uint32_t value) | |
100 | +{ | |
101 | + SPIState *s = opaque; | |
102 | + | |
103 | + offset &= SPI_SIZE - 1; | |
104 | + DPRINTF("spi write off %X, val %X, %X\n", offset, value, g_env->regs[15]); | |
105 | + switch (offset ){ | |
106 | + case SPI_CR: | |
107 | + if (value & SPI_CR_SPIEN) | |
108 | + s->sr |= SPI_SR_SPIENS; | |
109 | + if (value & SPI_CR_SPIDIS) | |
110 | + s->sr &= ~SPI_SR_SPIENS; | |
111 | + break; | |
112 | + case SPI_MR: | |
113 | + s->mr = value; | |
114 | + break; | |
115 | + case SPI_CSR0 ... SPI_CSR3: | |
116 | + DPRINTF("bits per transfer %d\n", 8 + ((value & 0xF0) >> 4)); | |
117 | + s->csr[(offset - SPI_CSR0) / sizeof(s->csr[0])] = value; | |
118 | + break; | |
119 | + case 0x100 ... 0x124: | |
120 | + at91_pdc_write(s->pdc_state, offset, value); | |
121 | + break; | |
122 | + default: | |
123 | + DPRINTF("unsupported offset %X, val %X\n", offset, value); | |
124 | + break; | |
125 | + } | |
126 | +} | |
127 | + | |
128 | +static CPUReadMemoryFunc *at91_spi_readfn[] = { | |
129 | + at91_spi_mem_read, | |
130 | + at91_spi_mem_read, | |
131 | + at91_spi_mem_read, | |
132 | +}; | |
133 | + | |
134 | +static CPUWriteMemoryFunc *at91_spi_writefn[] = { | |
135 | + at91_spi_mem_write, | |
136 | + at91_spi_mem_write, | |
137 | + at91_spi_mem_write, | |
138 | +}; | |
139 | + | |
140 | +static int pdc_start_transfer(void *opaque, | |
141 | + target_phys_addr_t tx, | |
142 | + unsigned int tx_len, | |
143 | + target_phys_addr_t rx, | |
144 | + unsigned int rx_len, | |
145 | + int last_transfer) | |
146 | +{ | |
147 | + SPIState *s = opaque; | |
148 | + unsigned int i; | |
149 | + unsigned int tlen = rx_len > tx_len ? rx_len : tx_len; | |
150 | + int flags = ((tx_len > 0) ? 1 : 0) | ((rx_len > 0) ? 2 : 0); | |
151 | + | |
152 | + DPRINTF("pdc: start transfer, last trans %d\n", last_transfer); | |
153 | +#if 1 | |
154 | + if (flags == 2) { | |
155 | + DPRINTF("ignore only read request\n"); | |
156 | + return -1; | |
157 | + } | |
158 | +#endif | |
159 | + /* suppose that transfer 8 bit, | |
160 | + TODO: fix this, extract right value from csr | |
161 | + */ | |
162 | + s->spi_control->set_chipselect(s->spi_control->opaque, 1); | |
163 | + for (i = 0; i < tlen; ++i) { | |
164 | + DPRINTF("pdc: transfering\n"); | |
165 | + uint8_t tmp = 0; | |
166 | + if (tx_len > 0) { | |
167 | + cpu_physical_memory_read(tx, &tmp, 1); | |
168 | + ++tx; | |
169 | + --tx_len; | |
170 | + } | |
171 | + tmp = s->spi_control->txrx_callback(s->spi_control->opaque, tmp, 8); | |
172 | + s->rdr = tmp; | |
173 | + if (rx_len > 0) { | |
174 | + cpu_physical_memory_write(rx, &tmp, 1); | |
175 | + ++rx; | |
176 | + --rx_len; | |
177 | + } | |
178 | + } | |
179 | + if (flags & 1) {//tx | |
180 | + s->sr |= SPI_SR_ENDTX | SPI_SR_TXBUFE; | |
181 | + } | |
182 | + if (flags & 2) {//rx | |
183 | + s->sr |= SPI_SR_RXBUFF | SPI_SR_ENDRX; | |
184 | + } | |
185 | + if (last_transfer) | |
186 | + s->spi_control->set_chipselect(s->spi_control->opaque, 0); | |
187 | + return 0; | |
188 | +} | |
189 | + | |
190 | +static void at91_spi_reset(void *opaque) | |
191 | +{ | |
192 | + SPIState *s = opaque; | |
193 | + | |
194 | + s->mr = 0; | |
195 | + s->rdr = 0; | |
196 | + s->sr = 0xf0; | |
197 | + s->imr = 0; | |
198 | + memset(s->csr, 0, sizeof(s->csr)); | |
199 | + at91_pdc_reset(s->pdc_state); | |
200 | +} | |
201 | + | |
202 | +static void at91_spi_init(SysBusDevice *dev) | |
203 | +{ | |
204 | + SPIState *s = FROM_SYSBUS(typeof(*s), dev); | |
205 | + int spi_regs; | |
206 | + | |
207 | + s->pdc_state = at91_pdc_init(s, pdc_start_transfer); | |
208 | + sysbus_init_irq(dev, &s->irq); | |
209 | + spi_regs = cpu_register_io_memory(at91_spi_readfn, at91_spi_writefn, s); | |
210 | + sysbus_init_mmio(dev, SPI_SIZE, spi_regs); | |
211 | + qemu_register_reset(at91_spi_reset, s); | |
212 | +} | |
213 | + | |
214 | +static SysBusDeviceInfo spi_info = { | |
215 | + .init = at91_spi_init, | |
216 | + .qdev.name = "at91,spi", | |
217 | + .qdev.size = sizeof(SPIState), | |
218 | + .qdev.props = (Property[]) { | |
219 | + { | |
220 | + .name = "spi_control", | |
221 | + .info = &qdev_prop_ptr, | |
222 | + .offset = offsetof(SPIState, spi_control), | |
223 | + .defval = (void *)&txrx_callback_empty, | |
224 | + }, | |
225 | + {/* end of list */} | |
226 | + } | |
227 | +}; | |
228 | + | |
229 | + | |
230 | +static void at91_spi_register(void) | |
231 | +{ | |
232 | + sysbus_register_withprop(&spi_info); | |
233 | +} | |
234 | + | |
235 | +device_init(at91_spi_register) | ... | ... |
hw/at91sam9.c
... | ... | @@ -13,11 +13,13 @@ |
13 | 13 | #include "devices.h" |
14 | 14 | #include "net.h" |
15 | 15 | #include "sysemu.h" |
16 | +#include "spi.h" | |
16 | 17 | #include "flash.h" |
17 | 18 | #include "boards.h" |
18 | 19 | #include "qemu-char.h" |
19 | 20 | #include "qemu-timer.h" |
20 | 21 | #include "sysbus.h" |
22 | +#include "at91.h" | |
21 | 23 | |
22 | 24 | #include "at91sam9263_defs.h" |
23 | 25 | |
... | ... | @@ -62,7 +64,7 @@ struct at91sam9_state { |
62 | 64 | uint32_t sdramc0_regs[(AT91_SMC0_BASE - AT91_SDRAMC0_BASE) / sizeof(uint32_t)]; |
63 | 65 | uint32_t smc0_regs[(AT91_ECC1_BASE - AT91_SMC0_BASE) / sizeof(uint32_t)]; |
64 | 66 | uint32_t usart0_regs[0x1000 / sizeof(uint32_t)]; |
65 | - struct dbgu_state dbgu; | |
67 | +// struct dbgu_state dbgu; | |
66 | 68 | pflash_t *norflash; |
67 | 69 | ram_addr_t internal_sram; |
68 | 70 | QEMUTimer *dbgu_tr_timer; |
... | ... | @@ -70,6 +72,8 @@ struct at91sam9_state { |
70 | 72 | int timer_active; |
71 | 73 | CPUState *env; |
72 | 74 | qemu_irq *qirq; |
75 | + ram_addr_t bootrom; | |
76 | + int rom_size; | |
73 | 77 | }; |
74 | 78 | |
75 | 79 | static uint32_t at91_bus_matrix_read(void *opaque, target_phys_addr_t offset) |
... | ... | @@ -125,7 +129,7 @@ static void at91_ccfg_write(void *opaque, target_phys_addr_t offset, |
125 | 129 | static uint32_t at91_sdramc0_read(void *opaque, target_phys_addr_t offset) |
126 | 130 | { |
127 | 131 | struct at91sam9_state *sam9 = (struct at91sam9_state *)opaque; |
128 | - | |
132 | + | |
129 | 133 | TRACE("sdramc0 read offset %X\n", offset); |
130 | 134 | return sam9->sdramc0_regs[offset / sizeof(sam9->sdramc0_regs[0])]; |
131 | 135 | } |
... | ... | @@ -134,7 +138,7 @@ static void at91_sdramc0_write(void *opaque, target_phys_addr_t offset, |
134 | 138 | uint32_t value) |
135 | 139 | { |
136 | 140 | struct at91sam9_state *sam9 = (struct at91sam9_state *)opaque; |
137 | - | |
141 | + | |
138 | 142 | TRACE("sdramc0 write offset %X, value %X\n", offset, value); |
139 | 143 | sam9->sdramc0_regs[offset / sizeof(sam9->sdramc0_regs[0])] = value; |
140 | 144 | } |
... | ... | @@ -223,6 +227,7 @@ static CPUWriteMemoryFunc *at91_periph_writefn[] = { |
223 | 227 | at91_periph_write |
224 | 228 | }; |
225 | 229 | |
230 | +CPUState *g_env; | |
226 | 231 | |
227 | 232 | static void at91sam9_init(ram_addr_t ram_size, |
228 | 233 | const char *boot_device, |
... | ... | @@ -241,9 +246,23 @@ static void at91sam9_init(ram_addr_t ram_size, |
241 | 246 | DeviceState *dev; |
242 | 247 | DeviceState *pit; |
243 | 248 | DeviceState *pmc; |
249 | + DeviceState *spi; | |
244 | 250 | int i; |
251 | + int bms; | |
252 | + SPIControl *cs0_spi_handler; | |
253 | + | |
254 | + cs0_spi_handler = qemu_mallocz(sizeof(SPIControl)); | |
255 | + DEBUG("begin, ram_size %llu, boot dev %s\n", (unsigned long long)ram_size, | |
256 | + boot_device ? boot_device : "<empty>"); | |
257 | + | |
258 | + if (option_rom[0] && boot_device[0] == 'n') { | |
259 | + printf("Emulate ROM code\n"); | |
260 | + bms = 1; | |
261 | + } else { | |
262 | + printf("Emulate start from EBI0_NCS0\n"); | |
263 | + bms = 0; | |
264 | + } | |
245 | 265 | |
246 | - DEBUG("begin, ram_size %llu\n", (unsigned long long)ram_size); | |
247 | 266 | #ifdef TRACE_ON |
248 | 267 | trace_file = fopen("/tmp/trace.log", "w"); |
249 | 268 | #endif |
... | ... | @@ -267,6 +286,12 @@ static void at91sam9_init(ram_addr_t ram_size, |
267 | 286 | /* Internal SRAM */ |
268 | 287 | sam9->internal_sram = qemu_ram_alloc(80 * 1024); |
269 | 288 | cpu_register_physical_memory(0x00300000, 80 * 1024, sam9->internal_sram | IO_MEM_RAM); |
289 | + sam9->bootrom = qemu_ram_alloc(0x100000); | |
290 | + cpu_register_physical_memory(0x00400000, 0x100000, sam9->bootrom | IO_MEM_RAM); | |
291 | + if (option_rom[0]) { | |
292 | + sam9->rom_size = load_image_targphys(option_rom[0], 0x00400000, 0x100000); | |
293 | + printf("load bootrom, size %d\n", sam9->rom_size); | |
294 | + } | |
270 | 295 | |
271 | 296 | /*Internal Peripherals */ |
272 | 297 | iomemtype = cpu_register_io_memory(at91_periph_readfn, at91_periph_writefn, sam9); |
... | ... | @@ -291,7 +316,7 @@ static void at91sam9_init(ram_addr_t ram_size, |
291 | 316 | qdev_prop_set_uint32(pmc, "mo_freq", 16000000); |
292 | 317 | pit = sysbus_create_simple("at91,pit", AT91_PITC_BASE, pic1[3]); |
293 | 318 | sysbus_create_varargs("at91,tc", AT91_TC012_BASE, pic[19], pic[19], pic[19], NULL); |
294 | - | |
319 | + spi = sysbus_create_simple("at91,spi", AT91_SPI0_BASE, pic[14]); | |
295 | 320 | at91_init_bus_matrix(sam9); |
296 | 321 | memset(&sam9->ccfg_regs, 0, sizeof(sam9->ccfg_regs)); |
297 | 322 | sysbus_create_simple("at91,pio", AT91_PIOA_BASE, pic[2]); |
... | ... | @@ -318,33 +343,46 @@ static void at91sam9_init(ram_addr_t ram_size, |
318 | 343 | */ |
319 | 344 | dinfo = drive_get(IF_PFLASH, 0, 0); |
320 | 345 | if (dinfo) { |
321 | - ram_addr_t nor_flash_mem = qemu_ram_alloc(NOR_FLASH_SIZE); | |
322 | - if (!nor_flash_mem) { | |
323 | - fprintf(stderr, "allocation failed\n"); | |
324 | - exit(EXIT_FAILURE); | |
346 | + if (bms) { | |
347 | + if (spi_flash_register(dinfo->bdrv, 2 * 1024 * 1024, cs0_spi_handler) < 0) { | |
348 | + fprintf(stderr, "init of spi flash failed\n"); | |
349 | + exit(EXIT_FAILURE); | |
350 | + } | |
351 | + qdev_prop_set_ptr(spi, "spi_control", cs0_spi_handler); | |
352 | + //rom | |
353 | + cpu_register_physical_memory(0x0, 100 * 1024, | |
354 | + sam9->bootrom | IO_MEM_ROMD); | |
355 | + } else { | |
356 | + //nor flash | |
357 | + ram_addr_t nor_flash_mem = qemu_ram_alloc(NOR_FLASH_SIZE); | |
358 | + if (!nor_flash_mem) { | |
359 | + fprintf(stderr, "allocation failed\n"); | |
360 | + exit(EXIT_FAILURE); | |
361 | + } | |
362 | + | |
363 | + sam9->norflash = pflash_cfi_atmel_register(AT91SAM9263EK_NORFLASH_OFF, | |
364 | + nor_flash_mem, | |
365 | + dinfo->bdrv, | |
366 | + 4 * 1024 * 2, 8, | |
367 | + 32 * 1024 * 2, | |
368 | + (135 - 8), | |
369 | + 2, 0x001F, 0x01D6, 0, 0); | |
370 | + | |
371 | + if (!sam9->norflash) { | |
372 | + fprintf(stderr, "qemu: error registering flash memory.\n"); | |
373 | + exit(EXIT_FAILURE); | |
374 | + } | |
375 | + | |
376 | + DEBUG("register flash at 0x0\n"); | |
377 | + //register only part of flash, to prevent conflict with internal sram | |
378 | + cpu_register_physical_memory(0x0, 100 * 1024, | |
379 | + nor_flash_mem | IO_MEM_ROMD); | |
325 | 380 | } |
326 | - | |
327 | - sam9->norflash = pflash_cfi_atmel_register(AT91SAM9263EK_NORFLASH_OFF, | |
328 | - nor_flash_mem, | |
329 | - dinfo->bdrv, | |
330 | - 4 * 1024 * 2, 8, | |
331 | - 32 * 1024 * 2, | |
332 | - (135 - 8), | |
333 | - 2, 0x001F, 0x01D6, 0, 0); | |
334 | - | |
335 | - if (!sam9->norflash) { | |
336 | - fprintf(stderr, "qemu: error registering flash memory.\n"); | |
337 | - exit(EXIT_FAILURE); | |
338 | - } | |
339 | - | |
340 | - DEBUG("register flash at 0x0\n"); | |
341 | - //register only part of flash, to prevent conflict with internal sram | |
342 | - cpu_register_physical_memory(0x0, 100 * 1024 /*NOR_FLASH_SIZE*/, | |
343 | - nor_flash_mem | IO_MEM_ROMD); | |
344 | 381 | } else { |
345 | 382 | fprintf(stderr, "qemu: can not start without flash.\n"); |
346 | 383 | exit(EXIT_FAILURE); |
347 | 384 | } |
385 | + g_env = env; | |
348 | 386 | env->regs[15] = 0x0; |
349 | 387 | } |
350 | 388 | ... | ... |
hw/at91sam9263_defs.h
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 | #define AT91_TC012_BASE 0xFFF7C000 |
7 | 7 | #define AT91_USART0_BASE 0xFFF8C000 |
8 | 8 | #define AT91_EMAC_BASE 0xFFFBC000 |
9 | +#define AT91_SPI0_BASE 0xFFFA4000 | |
9 | 10 | #define AT91_SDRAMC0_BASE 0xFFFFE200 |
10 | 11 | #define AT91_SMC0_BASE 0xFFFFE400 |
11 | 12 | #define AT91_ECC1_BASE 0xFFFFE600 | ... | ... |
hw/flash.h
... | ... | @@ -26,6 +26,10 @@ pflash_t *pflash_cfi_atmel_register(target_phys_addr_t base, ram_addr_t off, |
26 | 26 | uint16_t id0, uint16_t id1, |
27 | 27 | uint16_t id2, uint16_t id3); |
28 | 28 | |
29 | +struct SPIControl; | |
30 | +/* spi_flash.c */ | |
31 | +extern int spi_flash_register(BlockDriverState *bs, unsigned int len, | |
32 | + struct SPIControl *spi_control); | |
29 | 33 | |
30 | 34 | /* nand.c */ |
31 | 35 | typedef struct NANDFlashState NANDFlashState; | ... | ... |
hw/spi.h
0 → 100644
1 | +#ifndef _HW_SPI_H_ | |
2 | +#define _HW_SPI_H_ | |
3 | + | |
4 | +/* minimal interface to separate device connected via SPI and SPI controller */ | |
5 | +typedef struct SPIControl { | |
6 | + void *opaque; | |
7 | + uint32_t (*txrx_callback)(void *opaque, uint32_t val, int len); | |
8 | + void (*set_chipselect)(void *opaque, int on); | |
9 | +} SPIControl; | |
10 | + | |
11 | +#endif//!_HW_SPI_H_ | ... | ... |
hw/spi_flash.c
0 → 100644
1 | +#include "hw.h" | |
2 | +#include "block.h" | |
3 | +#include "spi.h" | |
4 | +#include "flash.h" | |
5 | + | |
6 | +typedef struct SPIFlash { | |
7 | + uint32_t cmd; | |
8 | + unsigned cmd_len; | |
9 | + uint32_t addr; | |
10 | + void *storage; | |
11 | + unsigned int len; | |
12 | + BlockDriverState *bs; | |
13 | +} SPIFlash; | |
14 | + | |
15 | +#define AT91_SPI_FLASH_DEBUG | |
16 | +#ifdef AT91_SPI_FLASH_DEBUG | |
17 | +#define DPRINTF(fmt, ...) \ | |
18 | + do { \ | |
19 | + printf("AT91SPI_FLASH: " fmt , ## __VA_ARGS__); \ | |
20 | + } while (0) | |
21 | +#else | |
22 | +#define DPRINTF(fmt, ...) do { } while (0) | |
23 | +#endif | |
24 | + | |
25 | +static void spi_flash_reset_state(SPIFlash *s) | |
26 | +{ | |
27 | + s->cmd = 0; | |
28 | + s->cmd_len = 0; | |
29 | + s->addr = 0; | |
30 | +} | |
31 | + | |
32 | +static void spi_flash_set_chipselect(void *opaque, int on) | |
33 | +{ | |
34 | + SPIFlash *s = opaque; | |
35 | + | |
36 | + DPRINTF("set NS %d\n", on); | |
37 | + if (on == 0) | |
38 | + spi_flash_reset_state(s); | |
39 | +} | |
40 | + | |
41 | +static uint32_t spi_flash_txrx(void *opaque, uint32_t val, int len) | |
42 | +{ | |
43 | + SPIFlash *s = opaque; | |
44 | + | |
45 | + DPRINTF("txrx: val %X\n", val); | |
46 | + switch (s->cmd) { | |
47 | + case 0: | |
48 | + s->cmd = val; | |
49 | + ++s->cmd_len; | |
50 | + return 0; | |
51 | + case 0xE8: | |
52 | + ++s->cmd_len; | |
53 | + if (s->cmd_len >= 2 && s->cmd_len <= 4) { | |
54 | + s->addr |= (val & 0xFF) << ((4 - s->cmd_len) * 8); | |
55 | + } else if (s->cmd_len >= 5 && s->cmd_len < (5 + 4)) { | |
56 | + /*ignore bytes*/ | |
57 | + } else { | |
58 | + uint8_t *bytes = s->storage; | |
59 | + /*TODO: handle different page sizes*/ | |
60 | + uint32_t addr = (s->addr >> 10) * 528 + (s->addr & 0x3ff); | |
61 | + DPRINTF("we read %X\n", bytes[addr + s->cmd_len - 9]); | |
62 | + | |
63 | + return bytes[addr + s->cmd_len - 9]; | |
64 | + } | |
65 | + | |
66 | + return 0; | |
67 | + case 0xD7: | |
68 | + spi_flash_reset_state(s); | |
69 | + DPRINTF("return id\n"); | |
70 | + return (1 << 2) | (1 << 3) | (1 << 5) | (1 << 7); | |
71 | + default: | |
72 | + DPRINTF("Unknown cmd\n"); | |
73 | + return 0; | |
74 | + } | |
75 | +} | |
76 | + | |
77 | +int spi_flash_register(BlockDriverState *bs, unsigned int len, | |
78 | + SPIControl *spi_control) | |
79 | +{ | |
80 | + SPIFlash *spi_flash; | |
81 | + int ret = 0; | |
82 | + | |
83 | + spi_flash = qemu_mallocz(sizeof(SPIFlash)); | |
84 | + spi_control->opaque = spi_flash; | |
85 | + spi_control->txrx_callback = spi_flash_txrx; | |
86 | + spi_control->set_chipselect = spi_flash_set_chipselect; | |
87 | + | |
88 | + spi_flash_reset_state(spi_flash); | |
89 | + spi_flash->storage = qemu_malloc(len); | |
90 | + spi_flash->len = len; | |
91 | + spi_flash->bs = bs; | |
92 | + if (spi_flash->bs) { | |
93 | + ret = bdrv_read(spi_flash->bs, 0, spi_flash->storage, len >> 9); | |
94 | + if (ret < 0) { | |
95 | + qemu_free(spi_flash->storage); | |
96 | + qemu_free(spi_flash); | |
97 | + goto out; | |
98 | + } | |
99 | + } | |
100 | +out: | |
101 | + return ret; | |
102 | +} | ... | ... |