xref: /qemu/hw/ssi/xilinx_spi.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
1929d1b52SPeter A. G. Crosthwaite /*
2929d1b52SPeter A. G. Crosthwaite  * QEMU model of the Xilinx SPI Controller
3929d1b52SPeter A. G. Crosthwaite  *
4929d1b52SPeter A. G. Crosthwaite  * Copyright (C) 2010 Edgar E. Iglesias.
5929d1b52SPeter A. G. Crosthwaite  * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
6929d1b52SPeter A. G. Crosthwaite  * Copyright (C) 2012 PetaLogix
7929d1b52SPeter A. G. Crosthwaite  *
8929d1b52SPeter A. G. Crosthwaite  * Permission is hereby granted, free of charge, to any person obtaining a copy
9929d1b52SPeter A. G. Crosthwaite  * of this software and associated documentation files (the "Software"), to deal
10929d1b52SPeter A. G. Crosthwaite  * in the Software without restriction, including without limitation the rights
11929d1b52SPeter A. G. Crosthwaite  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12929d1b52SPeter A. G. Crosthwaite  * copies of the Software, and to permit persons to whom the Software is
13929d1b52SPeter A. G. Crosthwaite  * furnished to do so, subject to the following conditions:
14929d1b52SPeter A. G. Crosthwaite  *
15929d1b52SPeter A. G. Crosthwaite  * The above copyright notice and this permission notice shall be included in
16929d1b52SPeter A. G. Crosthwaite  * all copies or substantial portions of the Software.
17929d1b52SPeter A. G. Crosthwaite  *
18929d1b52SPeter A. G. Crosthwaite  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19929d1b52SPeter A. G. Crosthwaite  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20929d1b52SPeter A. G. Crosthwaite  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21929d1b52SPeter A. G. Crosthwaite  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22929d1b52SPeter A. G. Crosthwaite  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23929d1b52SPeter A. G. Crosthwaite  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24929d1b52SPeter A. G. Crosthwaite  * THE SOFTWARE.
25929d1b52SPeter A. G. Crosthwaite  */
26929d1b52SPeter A. G. Crosthwaite 
278ef94f0bSPeter Maydell #include "qemu/osdep.h"
28e87c93dfSPhilippe Mathieu-Daudé #include "qapi/error.h"
2983c9f4caSPaolo Bonzini #include "hw/sysbus.h"
30d6454270SMarkus Armbruster #include "migration/vmstate.h"
310b8fa32fSMarkus Armbruster #include "qemu/module.h"
32fd7f0d66SPaolo Bonzini #include "qemu/fifo8.h"
33929d1b52SPeter A. G. Crosthwaite 
3464552b6bSMarkus Armbruster #include "hw/irq.h"
35a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
36e87c93dfSPhilippe Mathieu-Daudé #include "hw/qdev-properties-system.h"
378fd06719SAlistair Francis #include "hw/ssi/ssi.h"
38db1015e9SEduardo Habkost #include "qom/object.h"
39929d1b52SPeter A. G. Crosthwaite 
40929d1b52SPeter A. G. Crosthwaite #ifdef XILINX_SPI_ERR_DEBUG
41929d1b52SPeter A. G. Crosthwaite #define DB_PRINT(...) do { \
42929d1b52SPeter A. G. Crosthwaite     fprintf(stderr,  ": %s: ", __func__); \
43929d1b52SPeter A. G. Crosthwaite     fprintf(stderr, ## __VA_ARGS__); \
442562755eSEric Blake     } while (0)
45929d1b52SPeter A. G. Crosthwaite #else
46929d1b52SPeter A. G. Crosthwaite     #define DB_PRINT(...)
47929d1b52SPeter A. G. Crosthwaite #endif
48929d1b52SPeter A. G. Crosthwaite 
49929d1b52SPeter A. G. Crosthwaite #define R_DGIER     (0x1c / 4)
50929d1b52SPeter A. G. Crosthwaite #define R_DGIER_IE  (1 << 31)
51929d1b52SPeter A. G. Crosthwaite 
52929d1b52SPeter A. G. Crosthwaite #define R_IPISR     (0x20 / 4)
53929d1b52SPeter A. G. Crosthwaite #define IRQ_DRR_NOT_EMPTY    (1 << (31 - 23))
54929d1b52SPeter A. G. Crosthwaite #define IRQ_DRR_OVERRUN      (1 << (31 - 26))
55929d1b52SPeter A. G. Crosthwaite #define IRQ_DRR_FULL         (1 << (31 - 27))
56929d1b52SPeter A. G. Crosthwaite #define IRQ_TX_FF_HALF_EMPTY (1 << 6)
57929d1b52SPeter A. G. Crosthwaite #define IRQ_DTR_UNDERRUN     (1 << 3)
58929d1b52SPeter A. G. Crosthwaite #define IRQ_DTR_EMPTY        (1 << (31 - 29))
59929d1b52SPeter A. G. Crosthwaite 
60929d1b52SPeter A. G. Crosthwaite #define R_IPIER     (0x28 / 4)
61929d1b52SPeter A. G. Crosthwaite #define R_SRR       (0x40 / 4)
62929d1b52SPeter A. G. Crosthwaite #define R_SPICR     (0x60 / 4)
63929d1b52SPeter A. G. Crosthwaite #define R_SPICR_TXFF_RST     (1 << 5)
64929d1b52SPeter A. G. Crosthwaite #define R_SPICR_RXFF_RST     (1 << 6)
65929d1b52SPeter A. G. Crosthwaite #define R_SPICR_MTI          (1 << 8)
66929d1b52SPeter A. G. Crosthwaite 
67929d1b52SPeter A. G. Crosthwaite #define R_SPISR     (0x64 / 4)
68929d1b52SPeter A. G. Crosthwaite #define SR_TX_FULL    (1 << 3)
69929d1b52SPeter A. G. Crosthwaite #define SR_TX_EMPTY   (1 << 2)
70929d1b52SPeter A. G. Crosthwaite #define SR_RX_FULL    (1 << 1)
71929d1b52SPeter A. G. Crosthwaite #define SR_RX_EMPTY   (1 << 0)
72929d1b52SPeter A. G. Crosthwaite 
73929d1b52SPeter A. G. Crosthwaite #define R_SPIDTR    (0x68 / 4)
74929d1b52SPeter A. G. Crosthwaite #define R_SPIDRR    (0x6C / 4)
75929d1b52SPeter A. G. Crosthwaite #define R_SPISSR    (0x70 / 4)
76929d1b52SPeter A. G. Crosthwaite #define R_TX_FF_OCY (0x74 / 4)
77929d1b52SPeter A. G. Crosthwaite #define R_RX_FF_OCY (0x78 / 4)
78929d1b52SPeter A. G. Crosthwaite #define R_MAX       (0x7C / 4)
79929d1b52SPeter A. G. Crosthwaite 
80929d1b52SPeter A. G. Crosthwaite #define FIFO_CAPACITY 256
81929d1b52SPeter A. G. Crosthwaite 
823efc10e1SAndreas Färber #define TYPE_XILINX_SPI "xlnx.xps-spi"
838063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(XilinxSPI, XILINX_SPI)
843efc10e1SAndreas Färber 
85db1015e9SEduardo Habkost struct XilinxSPI {
863efc10e1SAndreas Färber     SysBusDevice parent_obj;
873efc10e1SAndreas Färber 
88e87c93dfSPhilippe Mathieu-Daudé     EndianMode model_endianness;
89929d1b52SPeter A. G. Crosthwaite     MemoryRegion mmio;
90929d1b52SPeter A. G. Crosthwaite 
91929d1b52SPeter A. G. Crosthwaite     qemu_irq irq;
92929d1b52SPeter A. G. Crosthwaite     int irqline;
93929d1b52SPeter A. G. Crosthwaite 
94929d1b52SPeter A. G. Crosthwaite     uint8_t num_cs;
95929d1b52SPeter A. G. Crosthwaite     qemu_irq *cs_lines;
96929d1b52SPeter A. G. Crosthwaite 
97929d1b52SPeter A. G. Crosthwaite     SSIBus *spi;
98929d1b52SPeter A. G. Crosthwaite 
99929d1b52SPeter A. G. Crosthwaite     Fifo8 rx_fifo;
100929d1b52SPeter A. G. Crosthwaite     Fifo8 tx_fifo;
101929d1b52SPeter A. G. Crosthwaite 
102929d1b52SPeter A. G. Crosthwaite     uint32_t regs[R_MAX];
103db1015e9SEduardo Habkost };
104929d1b52SPeter A. G. Crosthwaite 
txfifo_reset(XilinxSPI * s)105929d1b52SPeter A. G. Crosthwaite static void txfifo_reset(XilinxSPI *s)
106929d1b52SPeter A. G. Crosthwaite {
107929d1b52SPeter A. G. Crosthwaite     fifo8_reset(&s->tx_fifo);
108929d1b52SPeter A. G. Crosthwaite 
109929d1b52SPeter A. G. Crosthwaite     s->regs[R_SPISR] &= ~SR_TX_FULL;
110929d1b52SPeter A. G. Crosthwaite     s->regs[R_SPISR] |= SR_TX_EMPTY;
111929d1b52SPeter A. G. Crosthwaite }
112929d1b52SPeter A. G. Crosthwaite 
rxfifo_reset(XilinxSPI * s)113929d1b52SPeter A. G. Crosthwaite static void rxfifo_reset(XilinxSPI *s)
114929d1b52SPeter A. G. Crosthwaite {
115929d1b52SPeter A. G. Crosthwaite     fifo8_reset(&s->rx_fifo);
116929d1b52SPeter A. G. Crosthwaite 
117929d1b52SPeter A. G. Crosthwaite     s->regs[R_SPISR] |= SR_RX_EMPTY;
118929d1b52SPeter A. G. Crosthwaite     s->regs[R_SPISR] &= ~SR_RX_FULL;
119929d1b52SPeter A. G. Crosthwaite }
120929d1b52SPeter A. G. Crosthwaite 
xlx_spi_update_cs(XilinxSPI * s)121929d1b52SPeter A. G. Crosthwaite static void xlx_spi_update_cs(XilinxSPI *s)
122929d1b52SPeter A. G. Crosthwaite {
123929d1b52SPeter A. G. Crosthwaite     int i;
124929d1b52SPeter A. G. Crosthwaite 
125929d1b52SPeter A. G. Crosthwaite     for (i = 0; i < s->num_cs; ++i) {
126929d1b52SPeter A. G. Crosthwaite         qemu_set_irq(s->cs_lines[i], !(~s->regs[R_SPISSR] & 1 << i));
127929d1b52SPeter A. G. Crosthwaite     }
128929d1b52SPeter A. G. Crosthwaite }
129929d1b52SPeter A. G. Crosthwaite 
xlx_spi_update_irq(XilinxSPI * s)130929d1b52SPeter A. G. Crosthwaite static void xlx_spi_update_irq(XilinxSPI *s)
131929d1b52SPeter A. G. Crosthwaite {
132929d1b52SPeter A. G. Crosthwaite     uint32_t pending;
133929d1b52SPeter A. G. Crosthwaite 
134929d1b52SPeter A. G. Crosthwaite     s->regs[R_IPISR] |=
135929d1b52SPeter A. G. Crosthwaite             (!fifo8_is_empty(&s->rx_fifo) ? IRQ_DRR_NOT_EMPTY : 0) |
136929d1b52SPeter A. G. Crosthwaite             (fifo8_is_full(&s->rx_fifo) ? IRQ_DRR_FULL : 0);
137929d1b52SPeter A. G. Crosthwaite 
138929d1b52SPeter A. G. Crosthwaite     pending = s->regs[R_IPISR] & s->regs[R_IPIER];
139929d1b52SPeter A. G. Crosthwaite 
140929d1b52SPeter A. G. Crosthwaite     pending = pending && (s->regs[R_DGIER] & R_DGIER_IE);
141929d1b52SPeter A. G. Crosthwaite     pending = !!pending;
142929d1b52SPeter A. G. Crosthwaite 
143929d1b52SPeter A. G. Crosthwaite     /* This call lies right in the data paths so don't call the
144929d1b52SPeter A. G. Crosthwaite        irq chain unless things really changed.  */
145929d1b52SPeter A. G. Crosthwaite     if (pending != s->irqline) {
146929d1b52SPeter A. G. Crosthwaite         s->irqline = pending;
1479df0a972SAlexChen         DB_PRINT("irq_change of state %u ISR:%x IER:%X\n",
148929d1b52SPeter A. G. Crosthwaite                     pending, s->regs[R_IPISR], s->regs[R_IPIER]);
149929d1b52SPeter A. G. Crosthwaite         qemu_set_irq(s->irq, pending);
150929d1b52SPeter A. G. Crosthwaite     }
151929d1b52SPeter A. G. Crosthwaite 
152929d1b52SPeter A. G. Crosthwaite }
153929d1b52SPeter A. G. Crosthwaite 
xlx_spi_do_reset(XilinxSPI * s)154929d1b52SPeter A. G. Crosthwaite static void xlx_spi_do_reset(XilinxSPI *s)
155929d1b52SPeter A. G. Crosthwaite {
156929d1b52SPeter A. G. Crosthwaite     memset(s->regs, 0, sizeof s->regs);
157929d1b52SPeter A. G. Crosthwaite 
158929d1b52SPeter A. G. Crosthwaite     rxfifo_reset(s);
159929d1b52SPeter A. G. Crosthwaite     txfifo_reset(s);
160929d1b52SPeter A. G. Crosthwaite 
161929d1b52SPeter A. G. Crosthwaite     s->regs[R_SPISSR] = ~0;
162a0eaa126SChris Rauer     s->regs[R_SPICR] = R_SPICR_MTI;
163929d1b52SPeter A. G. Crosthwaite     xlx_spi_update_irq(s);
164929d1b52SPeter A. G. Crosthwaite     xlx_spi_update_cs(s);
165929d1b52SPeter A. G. Crosthwaite }
166929d1b52SPeter A. G. Crosthwaite 
xlx_spi_reset(DeviceState * d)167929d1b52SPeter A. G. Crosthwaite static void xlx_spi_reset(DeviceState *d)
168929d1b52SPeter A. G. Crosthwaite {
1693efc10e1SAndreas Färber     xlx_spi_do_reset(XILINX_SPI(d));
170929d1b52SPeter A. G. Crosthwaite }
171929d1b52SPeter A. G. Crosthwaite 
spi_master_enabled(XilinxSPI * s)172929d1b52SPeter A. G. Crosthwaite static inline int spi_master_enabled(XilinxSPI *s)
173929d1b52SPeter A. G. Crosthwaite {
174929d1b52SPeter A. G. Crosthwaite     return !(s->regs[R_SPICR] & R_SPICR_MTI);
175929d1b52SPeter A. G. Crosthwaite }
176929d1b52SPeter A. G. Crosthwaite 
spi_flush_txfifo(XilinxSPI * s)177929d1b52SPeter A. G. Crosthwaite static void spi_flush_txfifo(XilinxSPI *s)
178929d1b52SPeter A. G. Crosthwaite {
179929d1b52SPeter A. G. Crosthwaite     uint32_t tx;
180929d1b52SPeter A. G. Crosthwaite     uint32_t rx;
181929d1b52SPeter A. G. Crosthwaite 
182929d1b52SPeter A. G. Crosthwaite     while (!fifo8_is_empty(&s->tx_fifo)) {
183929d1b52SPeter A. G. Crosthwaite         tx = (uint32_t)fifo8_pop(&s->tx_fifo);
184929d1b52SPeter A. G. Crosthwaite         DB_PRINT("data tx:%x\n", tx);
185929d1b52SPeter A. G. Crosthwaite         rx = ssi_transfer(s->spi, tx);
186929d1b52SPeter A. G. Crosthwaite         DB_PRINT("data rx:%x\n", rx);
187929d1b52SPeter A. G. Crosthwaite         if (fifo8_is_full(&s->rx_fifo)) {
188929d1b52SPeter A. G. Crosthwaite             s->regs[R_IPISR] |= IRQ_DRR_OVERRUN;
189929d1b52SPeter A. G. Crosthwaite         } else {
190929d1b52SPeter A. G. Crosthwaite             fifo8_push(&s->rx_fifo, (uint8_t)rx);
191929d1b52SPeter A. G. Crosthwaite             if (fifo8_is_full(&s->rx_fifo)) {
192929d1b52SPeter A. G. Crosthwaite                 s->regs[R_SPISR] |= SR_RX_FULL;
193929d1b52SPeter A. G. Crosthwaite                 s->regs[R_IPISR] |= IRQ_DRR_FULL;
194929d1b52SPeter A. G. Crosthwaite             }
195929d1b52SPeter A. G. Crosthwaite         }
196929d1b52SPeter A. G. Crosthwaite 
197929d1b52SPeter A. G. Crosthwaite         s->regs[R_SPISR] &= ~SR_RX_EMPTY;
198929d1b52SPeter A. G. Crosthwaite         s->regs[R_SPISR] &= ~SR_TX_FULL;
199929d1b52SPeter A. G. Crosthwaite         s->regs[R_SPISR] |= SR_TX_EMPTY;
200929d1b52SPeter A. G. Crosthwaite 
201929d1b52SPeter A. G. Crosthwaite         s->regs[R_IPISR] |= IRQ_DTR_EMPTY;
202929d1b52SPeter A. G. Crosthwaite         s->regs[R_IPISR] |= IRQ_DRR_NOT_EMPTY;
203929d1b52SPeter A. G. Crosthwaite     }
204929d1b52SPeter A. G. Crosthwaite 
205929d1b52SPeter A. G. Crosthwaite }
206929d1b52SPeter A. G. Crosthwaite 
207929d1b52SPeter A. G. Crosthwaite static uint64_t
spi_read(void * opaque,hwaddr addr,unsigned int size)208a8170e5eSAvi Kivity spi_read(void *opaque, hwaddr addr, unsigned int size)
209929d1b52SPeter A. G. Crosthwaite {
210929d1b52SPeter A. G. Crosthwaite     XilinxSPI *s = opaque;
211929d1b52SPeter A. G. Crosthwaite     uint32_t r = 0;
212929d1b52SPeter A. G. Crosthwaite 
213929d1b52SPeter A. G. Crosthwaite     addr >>= 2;
214929d1b52SPeter A. G. Crosthwaite     switch (addr) {
215929d1b52SPeter A. G. Crosthwaite     case R_SPIDRR:
216929d1b52SPeter A. G. Crosthwaite         if (fifo8_is_empty(&s->rx_fifo)) {
217929d1b52SPeter A. G. Crosthwaite             DB_PRINT("Read from empty FIFO!\n");
218929d1b52SPeter A. G. Crosthwaite             return 0xdeadbeef;
219929d1b52SPeter A. G. Crosthwaite         }
220929d1b52SPeter A. G. Crosthwaite 
221929d1b52SPeter A. G. Crosthwaite         s->regs[R_SPISR] &= ~SR_RX_FULL;
222929d1b52SPeter A. G. Crosthwaite         r = fifo8_pop(&s->rx_fifo);
223929d1b52SPeter A. G. Crosthwaite         if (fifo8_is_empty(&s->rx_fifo)) {
224929d1b52SPeter A. G. Crosthwaite             s->regs[R_SPISR] |= SR_RX_EMPTY;
225929d1b52SPeter A. G. Crosthwaite         }
226929d1b52SPeter A. G. Crosthwaite         break;
227929d1b52SPeter A. G. Crosthwaite 
228929d1b52SPeter A. G. Crosthwaite     case R_SPISR:
229929d1b52SPeter A. G. Crosthwaite         r = s->regs[addr];
230929d1b52SPeter A. G. Crosthwaite         break;
231929d1b52SPeter A. G. Crosthwaite 
232929d1b52SPeter A. G. Crosthwaite     default:
233929d1b52SPeter A. G. Crosthwaite         if (addr < ARRAY_SIZE(s->regs)) {
234929d1b52SPeter A. G. Crosthwaite             r = s->regs[addr];
235929d1b52SPeter A. G. Crosthwaite         }
236929d1b52SPeter A. G. Crosthwaite         break;
237929d1b52SPeter A. G. Crosthwaite 
238929d1b52SPeter A. G. Crosthwaite     }
239883f2c59SPhilippe Mathieu-Daudé     DB_PRINT("addr=" HWADDR_FMT_plx " = %x\n", addr * 4, r);
240929d1b52SPeter A. G. Crosthwaite     xlx_spi_update_irq(s);
241929d1b52SPeter A. G. Crosthwaite     return r;
242929d1b52SPeter A. G. Crosthwaite }
243929d1b52SPeter A. G. Crosthwaite 
244929d1b52SPeter A. G. Crosthwaite static void
spi_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)245a8170e5eSAvi Kivity spi_write(void *opaque, hwaddr addr,
246929d1b52SPeter A. G. Crosthwaite             uint64_t val64, unsigned int size)
247929d1b52SPeter A. G. Crosthwaite {
248929d1b52SPeter A. G. Crosthwaite     XilinxSPI *s = opaque;
249929d1b52SPeter A. G. Crosthwaite     uint32_t value = val64;
250929d1b52SPeter A. G. Crosthwaite 
251883f2c59SPhilippe Mathieu-Daudé     DB_PRINT("addr=" HWADDR_FMT_plx " = %x\n", addr, value);
252929d1b52SPeter A. G. Crosthwaite     addr >>= 2;
253929d1b52SPeter A. G. Crosthwaite     switch (addr) {
254929d1b52SPeter A. G. Crosthwaite     case R_SRR:
255929d1b52SPeter A. G. Crosthwaite         if (value != 0xa) {
256929d1b52SPeter A. G. Crosthwaite             DB_PRINT("Invalid write to SRR %x\n", value);
257929d1b52SPeter A. G. Crosthwaite         } else {
258929d1b52SPeter A. G. Crosthwaite             xlx_spi_do_reset(s);
259929d1b52SPeter A. G. Crosthwaite         }
260929d1b52SPeter A. G. Crosthwaite         break;
261929d1b52SPeter A. G. Crosthwaite 
262929d1b52SPeter A. G. Crosthwaite     case R_SPIDTR:
263929d1b52SPeter A. G. Crosthwaite         s->regs[R_SPISR] &= ~SR_TX_EMPTY;
264929d1b52SPeter A. G. Crosthwaite         fifo8_push(&s->tx_fifo, (uint8_t)value);
265929d1b52SPeter A. G. Crosthwaite         if (fifo8_is_full(&s->tx_fifo)) {
266929d1b52SPeter A. G. Crosthwaite             s->regs[R_SPISR] |= SR_TX_FULL;
267929d1b52SPeter A. G. Crosthwaite         }
268929d1b52SPeter A. G. Crosthwaite         if (!spi_master_enabled(s)) {
269929d1b52SPeter A. G. Crosthwaite             goto done;
270929d1b52SPeter A. G. Crosthwaite         } else {
271929d1b52SPeter A. G. Crosthwaite             DB_PRINT("DTR and master enabled\n");
272929d1b52SPeter A. G. Crosthwaite         }
273929d1b52SPeter A. G. Crosthwaite         spi_flush_txfifo(s);
274929d1b52SPeter A. G. Crosthwaite         break;
275929d1b52SPeter A. G. Crosthwaite 
276929d1b52SPeter A. G. Crosthwaite     case R_SPISR:
277929d1b52SPeter A. G. Crosthwaite         DB_PRINT("Invalid write to SPISR %x\n", value);
278929d1b52SPeter A. G. Crosthwaite         break;
279929d1b52SPeter A. G. Crosthwaite 
280929d1b52SPeter A. G. Crosthwaite     case R_IPISR:
281929d1b52SPeter A. G. Crosthwaite         /* Toggle the bits.  */
282929d1b52SPeter A. G. Crosthwaite         s->regs[addr] ^= value;
283929d1b52SPeter A. G. Crosthwaite         break;
284929d1b52SPeter A. G. Crosthwaite 
285929d1b52SPeter A. G. Crosthwaite     /* Slave Select Register.  */
286929d1b52SPeter A. G. Crosthwaite     case R_SPISSR:
287929d1b52SPeter A. G. Crosthwaite         s->regs[addr] = value;
288929d1b52SPeter A. G. Crosthwaite         xlx_spi_update_cs(s);
289929d1b52SPeter A. G. Crosthwaite         break;
290929d1b52SPeter A. G. Crosthwaite 
291929d1b52SPeter A. G. Crosthwaite     case R_SPICR:
292929d1b52SPeter A. G. Crosthwaite         /* FIXME: reset irq and sr state to empty queues.  */
293929d1b52SPeter A. G. Crosthwaite         if (value & R_SPICR_RXFF_RST) {
294929d1b52SPeter A. G. Crosthwaite             rxfifo_reset(s);
295929d1b52SPeter A. G. Crosthwaite         }
296929d1b52SPeter A. G. Crosthwaite 
297929d1b52SPeter A. G. Crosthwaite         if (value & R_SPICR_TXFF_RST) {
298929d1b52SPeter A. G. Crosthwaite             txfifo_reset(s);
299929d1b52SPeter A. G. Crosthwaite         }
300929d1b52SPeter A. G. Crosthwaite         value &= ~(R_SPICR_RXFF_RST | R_SPICR_TXFF_RST);
301929d1b52SPeter A. G. Crosthwaite         s->regs[addr] = value;
302929d1b52SPeter A. G. Crosthwaite 
303929d1b52SPeter A. G. Crosthwaite         if (!(value & R_SPICR_MTI)) {
304929d1b52SPeter A. G. Crosthwaite             spi_flush_txfifo(s);
305929d1b52SPeter A. G. Crosthwaite         }
306929d1b52SPeter A. G. Crosthwaite         break;
307929d1b52SPeter A. G. Crosthwaite 
308929d1b52SPeter A. G. Crosthwaite     default:
309929d1b52SPeter A. G. Crosthwaite         if (addr < ARRAY_SIZE(s->regs)) {
310929d1b52SPeter A. G. Crosthwaite             s->regs[addr] = value;
311929d1b52SPeter A. G. Crosthwaite         }
312929d1b52SPeter A. G. Crosthwaite         break;
313929d1b52SPeter A. G. Crosthwaite     }
314929d1b52SPeter A. G. Crosthwaite 
315929d1b52SPeter A. G. Crosthwaite done:
316929d1b52SPeter A. G. Crosthwaite     xlx_spi_update_irq(s);
317929d1b52SPeter A. G. Crosthwaite }
318929d1b52SPeter A. G. Crosthwaite 
319e87c93dfSPhilippe Mathieu-Daudé static const MemoryRegionOps spi_ops[2] = {
320e87c93dfSPhilippe Mathieu-Daudé     [0 ... 1] = {
321929d1b52SPeter A. G. Crosthwaite         .read = spi_read,
322929d1b52SPeter A. G. Crosthwaite         .write = spi_write,
323929d1b52SPeter A. G. Crosthwaite         .valid = {
324929d1b52SPeter A. G. Crosthwaite             .min_access_size = 4,
325e87c93dfSPhilippe Mathieu-Daudé             .max_access_size = 4,
326e87c93dfSPhilippe Mathieu-Daudé         },
327e87c93dfSPhilippe Mathieu-Daudé     },
328e87c93dfSPhilippe Mathieu-Daudé     [0].endianness = DEVICE_LITTLE_ENDIAN,
329e87c93dfSPhilippe Mathieu-Daudé     [1].endianness = DEVICE_BIG_ENDIAN,
330929d1b52SPeter A. G. Crosthwaite };
331929d1b52SPeter A. G. Crosthwaite 
xilinx_spi_realize(DeviceState * dev,Error ** errp)332a7e1562cSPhilippe Mathieu-Daudé static void xilinx_spi_realize(DeviceState *dev, Error **errp)
333929d1b52SPeter A. G. Crosthwaite {
334a7e1562cSPhilippe Mathieu-Daudé     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
3353efc10e1SAndreas Färber     XilinxSPI *s = XILINX_SPI(dev);
336929d1b52SPeter A. G. Crosthwaite     int i;
337929d1b52SPeter A. G. Crosthwaite 
338e87c93dfSPhilippe Mathieu-Daudé     if (s->model_endianness == ENDIAN_MODE_UNSPECIFIED) {
339e87c93dfSPhilippe Mathieu-Daudé         error_setg(errp, TYPE_XILINX_SPI " property 'endianness'"
340e87c93dfSPhilippe Mathieu-Daudé                          " must be set to 'big' or 'little'");
341e87c93dfSPhilippe Mathieu-Daudé         return;
342e87c93dfSPhilippe Mathieu-Daudé     }
343e87c93dfSPhilippe Mathieu-Daudé 
344929d1b52SPeter A. G. Crosthwaite     DB_PRINT("\n");
345b4ae3cfaSPeter Crosthwaite 
3463efc10e1SAndreas Färber     s->spi = ssi_create_bus(dev, "spi");
347b4ae3cfaSPeter Crosthwaite 
3483efc10e1SAndreas Färber     sysbus_init_irq(sbd, &s->irq);
349c75f3c04SPeter Crosthwaite     s->cs_lines = g_new0(qemu_irq, s->num_cs);
350929d1b52SPeter A. G. Crosthwaite     for (i = 0; i < s->num_cs; ++i) {
3513efc10e1SAndreas Färber         sysbus_init_irq(sbd, &s->cs_lines[i]);
352929d1b52SPeter A. G. Crosthwaite     }
353929d1b52SPeter A. G. Crosthwaite 
354e87c93dfSPhilippe Mathieu-Daudé     memory_region_init_io(&s->mmio, OBJECT(s),
355e87c93dfSPhilippe Mathieu-Daudé                           &spi_ops[s->model_endianness == ENDIAN_MODE_BIG], s,
35629776739SPaolo Bonzini                           "xilinx-spi", R_MAX * 4);
3573efc10e1SAndreas Färber     sysbus_init_mmio(sbd, &s->mmio);
358929d1b52SPeter A. G. Crosthwaite 
359929d1b52SPeter A. G. Crosthwaite     s->irqline = -1;
360929d1b52SPeter A. G. Crosthwaite 
361929d1b52SPeter A. G. Crosthwaite     fifo8_create(&s->tx_fifo, FIFO_CAPACITY);
362929d1b52SPeter A. G. Crosthwaite     fifo8_create(&s->rx_fifo, FIFO_CAPACITY);
363929d1b52SPeter A. G. Crosthwaite }
364929d1b52SPeter A. G. Crosthwaite 
365929d1b52SPeter A. G. Crosthwaite static const VMStateDescription vmstate_xilinx_spi = {
366929d1b52SPeter A. G. Crosthwaite     .name = "xilinx_spi",
367929d1b52SPeter A. G. Crosthwaite     .version_id = 1,
368929d1b52SPeter A. G. Crosthwaite     .minimum_version_id = 1,
3690aa6c7dfSRichard Henderson     .fields = (const VMStateField[]) {
370929d1b52SPeter A. G. Crosthwaite         VMSTATE_FIFO8(tx_fifo, XilinxSPI),
371929d1b52SPeter A. G. Crosthwaite         VMSTATE_FIFO8(rx_fifo, XilinxSPI),
372929d1b52SPeter A. G. Crosthwaite         VMSTATE_UINT32_ARRAY(regs, XilinxSPI, R_MAX),
373929d1b52SPeter A. G. Crosthwaite         VMSTATE_END_OF_LIST()
374929d1b52SPeter A. G. Crosthwaite     }
375929d1b52SPeter A. G. Crosthwaite };
376929d1b52SPeter A. G. Crosthwaite 
377dc418eb2SRichard Henderson static const Property xilinx_spi_properties[] = {
378e87c93dfSPhilippe Mathieu-Daudé     DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XilinxSPI, model_endianness),
379929d1b52SPeter A. G. Crosthwaite     DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1),
380929d1b52SPeter A. G. Crosthwaite };
381929d1b52SPeter A. G. Crosthwaite 
xilinx_spi_class_init(ObjectClass * klass,const void * data)382*12d1a768SPhilippe Mathieu-Daudé static void xilinx_spi_class_init(ObjectClass *klass, const void *data)
383929d1b52SPeter A. G. Crosthwaite {
384929d1b52SPeter A. G. Crosthwaite     DeviceClass *dc = DEVICE_CLASS(klass);
385929d1b52SPeter A. G. Crosthwaite 
386a7e1562cSPhilippe Mathieu-Daudé     dc->realize = xilinx_spi_realize;
387e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, xlx_spi_reset);
3884f67d30bSMarc-André Lureau     device_class_set_props(dc, xilinx_spi_properties);
389929d1b52SPeter A. G. Crosthwaite     dc->vmsd = &vmstate_xilinx_spi;
390929d1b52SPeter A. G. Crosthwaite }
391929d1b52SPeter A. G. Crosthwaite 
3928c43a6f0SAndreas Färber static const TypeInfo xilinx_spi_info = {
3933efc10e1SAndreas Färber     .name           = TYPE_XILINX_SPI,
394929d1b52SPeter A. G. Crosthwaite     .parent         = TYPE_SYS_BUS_DEVICE,
395929d1b52SPeter A. G. Crosthwaite     .instance_size  = sizeof(XilinxSPI),
396929d1b52SPeter A. G. Crosthwaite     .class_init     = xilinx_spi_class_init,
397929d1b52SPeter A. G. Crosthwaite };
398929d1b52SPeter A. G. Crosthwaite 
xilinx_spi_register_types(void)399929d1b52SPeter A. G. Crosthwaite static void xilinx_spi_register_types(void)
400929d1b52SPeter A. G. Crosthwaite {
401929d1b52SPeter A. G. Crosthwaite     type_register_static(&xilinx_spi_info);
402929d1b52SPeter A. G. Crosthwaite }
403929d1b52SPeter A. G. Crosthwaite 
404929d1b52SPeter A. G. Crosthwaite type_init(xilinx_spi_register_types)
405