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,7 +411,7 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o | ||
411 | endif | 411 | endif |
412 | 412 | ||
413 | obj-arm-y = integratorcp.o versatilepb.o smc91c111.o arm_pic.o arm_timer.o | 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 | obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o | 415 | obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o |
416 | obj-arm-y += versatile_pci.o | 416 | obj-arm-y += versatile_pci.o |
417 | obj-arm-y += realview_gic.o realview.o arm_sysctl.o mpcore.o | 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,7 +433,7 @@ obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o | ||
433 | obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o | 433 | obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o |
434 | obj-arm-y += syborg_virtio.o | 434 | obj-arm-y += syborg_virtio.o |
435 | obj-arm-y += at91_aic.o at91_dbgu.o at91_pio.o at91_pit.o at91_pmc.o at91_rtt.o | 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 | obj-arm-y += gpio_rotary.o gpio_keypad.o hd44780.o | 437 | obj-arm-y += gpio_rotary.o gpio_keypad.o hd44780.o |
438 | 438 | ||
439 | ifeq ($(TARGET_BASE_ARCH), arm) | 439 | ifeq ($(TARGET_BASE_ARCH), arm) |
hw/at91.h
@@ -29,4 +29,19 @@ | @@ -29,4 +29,19 @@ | ||
29 | Controller. */ | 29 | Controller. */ |
30 | extern int at91_master_clock_frequency; | 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 | #endif /* !AT91_H */ | 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,11 +13,13 @@ | ||
13 | #include "devices.h" | 13 | #include "devices.h" |
14 | #include "net.h" | 14 | #include "net.h" |
15 | #include "sysemu.h" | 15 | #include "sysemu.h" |
16 | +#include "spi.h" | ||
16 | #include "flash.h" | 17 | #include "flash.h" |
17 | #include "boards.h" | 18 | #include "boards.h" |
18 | #include "qemu-char.h" | 19 | #include "qemu-char.h" |
19 | #include "qemu-timer.h" | 20 | #include "qemu-timer.h" |
20 | #include "sysbus.h" | 21 | #include "sysbus.h" |
22 | +#include "at91.h" | ||
21 | 23 | ||
22 | #include "at91sam9263_defs.h" | 24 | #include "at91sam9263_defs.h" |
23 | 25 | ||
@@ -62,7 +64,7 @@ struct at91sam9_state { | @@ -62,7 +64,7 @@ struct at91sam9_state { | ||
62 | uint32_t sdramc0_regs[(AT91_SMC0_BASE - AT91_SDRAMC0_BASE) / sizeof(uint32_t)]; | 64 | uint32_t sdramc0_regs[(AT91_SMC0_BASE - AT91_SDRAMC0_BASE) / sizeof(uint32_t)]; |
63 | uint32_t smc0_regs[(AT91_ECC1_BASE - AT91_SMC0_BASE) / sizeof(uint32_t)]; | 65 | uint32_t smc0_regs[(AT91_ECC1_BASE - AT91_SMC0_BASE) / sizeof(uint32_t)]; |
64 | uint32_t usart0_regs[0x1000 / sizeof(uint32_t)]; | 66 | uint32_t usart0_regs[0x1000 / sizeof(uint32_t)]; |
65 | - struct dbgu_state dbgu; | 67 | +// struct dbgu_state dbgu; |
66 | pflash_t *norflash; | 68 | pflash_t *norflash; |
67 | ram_addr_t internal_sram; | 69 | ram_addr_t internal_sram; |
68 | QEMUTimer *dbgu_tr_timer; | 70 | QEMUTimer *dbgu_tr_timer; |
@@ -70,6 +72,8 @@ struct at91sam9_state { | @@ -70,6 +72,8 @@ struct at91sam9_state { | ||
70 | int timer_active; | 72 | int timer_active; |
71 | CPUState *env; | 73 | CPUState *env; |
72 | qemu_irq *qirq; | 74 | qemu_irq *qirq; |
75 | + ram_addr_t bootrom; | ||
76 | + int rom_size; | ||
73 | }; | 77 | }; |
74 | 78 | ||
75 | static uint32_t at91_bus_matrix_read(void *opaque, target_phys_addr_t offset) | 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,7 +129,7 @@ static void at91_ccfg_write(void *opaque, target_phys_addr_t offset, | ||
125 | static uint32_t at91_sdramc0_read(void *opaque, target_phys_addr_t offset) | 129 | static uint32_t at91_sdramc0_read(void *opaque, target_phys_addr_t offset) |
126 | { | 130 | { |
127 | struct at91sam9_state *sam9 = (struct at91sam9_state *)opaque; | 131 | struct at91sam9_state *sam9 = (struct at91sam9_state *)opaque; |
128 | - | 132 | + |
129 | TRACE("sdramc0 read offset %X\n", offset); | 133 | TRACE("sdramc0 read offset %X\n", offset); |
130 | return sam9->sdramc0_regs[offset / sizeof(sam9->sdramc0_regs[0])]; | 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,7 +138,7 @@ static void at91_sdramc0_write(void *opaque, target_phys_addr_t offset, | ||
134 | uint32_t value) | 138 | uint32_t value) |
135 | { | 139 | { |
136 | struct at91sam9_state *sam9 = (struct at91sam9_state *)opaque; | 140 | struct at91sam9_state *sam9 = (struct at91sam9_state *)opaque; |
137 | - | 141 | + |
138 | TRACE("sdramc0 write offset %X, value %X\n", offset, value); | 142 | TRACE("sdramc0 write offset %X, value %X\n", offset, value); |
139 | sam9->sdramc0_regs[offset / sizeof(sam9->sdramc0_regs[0])] = value; | 143 | sam9->sdramc0_regs[offset / sizeof(sam9->sdramc0_regs[0])] = value; |
140 | } | 144 | } |
@@ -223,6 +227,7 @@ static CPUWriteMemoryFunc *at91_periph_writefn[] = { | @@ -223,6 +227,7 @@ static CPUWriteMemoryFunc *at91_periph_writefn[] = { | ||
223 | at91_periph_write | 227 | at91_periph_write |
224 | }; | 228 | }; |
225 | 229 | ||
230 | +CPUState *g_env; | ||
226 | 231 | ||
227 | static void at91sam9_init(ram_addr_t ram_size, | 232 | static void at91sam9_init(ram_addr_t ram_size, |
228 | const char *boot_device, | 233 | const char *boot_device, |
@@ -241,9 +246,23 @@ static void at91sam9_init(ram_addr_t ram_size, | @@ -241,9 +246,23 @@ static void at91sam9_init(ram_addr_t ram_size, | ||
241 | DeviceState *dev; | 246 | DeviceState *dev; |
242 | DeviceState *pit; | 247 | DeviceState *pit; |
243 | DeviceState *pmc; | 248 | DeviceState *pmc; |
249 | + DeviceState *spi; | ||
244 | int i; | 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 | #ifdef TRACE_ON | 266 | #ifdef TRACE_ON |
248 | trace_file = fopen("/tmp/trace.log", "w"); | 267 | trace_file = fopen("/tmp/trace.log", "w"); |
249 | #endif | 268 | #endif |
@@ -267,6 +286,12 @@ static void at91sam9_init(ram_addr_t ram_size, | @@ -267,6 +286,12 @@ static void at91sam9_init(ram_addr_t ram_size, | ||
267 | /* Internal SRAM */ | 286 | /* Internal SRAM */ |
268 | sam9->internal_sram = qemu_ram_alloc(80 * 1024); | 287 | sam9->internal_sram = qemu_ram_alloc(80 * 1024); |
269 | cpu_register_physical_memory(0x00300000, 80 * 1024, sam9->internal_sram | IO_MEM_RAM); | 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 | /*Internal Peripherals */ | 296 | /*Internal Peripherals */ |
272 | iomemtype = cpu_register_io_memory(at91_periph_readfn, at91_periph_writefn, sam9); | 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,7 +316,7 @@ static void at91sam9_init(ram_addr_t ram_size, | ||
291 | qdev_prop_set_uint32(pmc, "mo_freq", 16000000); | 316 | qdev_prop_set_uint32(pmc, "mo_freq", 16000000); |
292 | pit = sysbus_create_simple("at91,pit", AT91_PITC_BASE, pic1[3]); | 317 | pit = sysbus_create_simple("at91,pit", AT91_PITC_BASE, pic1[3]); |
293 | sysbus_create_varargs("at91,tc", AT91_TC012_BASE, pic[19], pic[19], pic[19], NULL); | 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 | at91_init_bus_matrix(sam9); | 320 | at91_init_bus_matrix(sam9); |
296 | memset(&sam9->ccfg_regs, 0, sizeof(sam9->ccfg_regs)); | 321 | memset(&sam9->ccfg_regs, 0, sizeof(sam9->ccfg_regs)); |
297 | sysbus_create_simple("at91,pio", AT91_PIOA_BASE, pic[2]); | 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,33 +343,46 @@ static void at91sam9_init(ram_addr_t ram_size, | ||
318 | */ | 343 | */ |
319 | dinfo = drive_get(IF_PFLASH, 0, 0); | 344 | dinfo = drive_get(IF_PFLASH, 0, 0); |
320 | if (dinfo) { | 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 | } else { | 381 | } else { |
345 | fprintf(stderr, "qemu: can not start without flash.\n"); | 382 | fprintf(stderr, "qemu: can not start without flash.\n"); |
346 | exit(EXIT_FAILURE); | 383 | exit(EXIT_FAILURE); |
347 | } | 384 | } |
385 | + g_env = env; | ||
348 | env->regs[15] = 0x0; | 386 | env->regs[15] = 0x0; |
349 | } | 387 | } |
350 | 388 |
hw/at91sam9263_defs.h
@@ -6,6 +6,7 @@ | @@ -6,6 +6,7 @@ | ||
6 | #define AT91_TC012_BASE 0xFFF7C000 | 6 | #define AT91_TC012_BASE 0xFFF7C000 |
7 | #define AT91_USART0_BASE 0xFFF8C000 | 7 | #define AT91_USART0_BASE 0xFFF8C000 |
8 | #define AT91_EMAC_BASE 0xFFFBC000 | 8 | #define AT91_EMAC_BASE 0xFFFBC000 |
9 | +#define AT91_SPI0_BASE 0xFFFA4000 | ||
9 | #define AT91_SDRAMC0_BASE 0xFFFFE200 | 10 | #define AT91_SDRAMC0_BASE 0xFFFFE200 |
10 | #define AT91_SMC0_BASE 0xFFFFE400 | 11 | #define AT91_SMC0_BASE 0xFFFFE400 |
11 | #define AT91_ECC1_BASE 0xFFFFE600 | 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,6 +26,10 @@ pflash_t *pflash_cfi_atmel_register(target_phys_addr_t base, ram_addr_t off, | ||
26 | uint16_t id0, uint16_t id1, | 26 | uint16_t id0, uint16_t id1, |
27 | uint16_t id2, uint16_t id3); | 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 | /* nand.c */ | 34 | /* nand.c */ |
31 | typedef struct NANDFlashState NANDFlashState; | 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 | +} |