xref: /openbmc/qemu/hw/ssi/stm32f2xx_spi.c (revision e3d08143)
1 /*
2  * STM32F405 SPI
3  *
4  * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "qemu/osdep.h"
26 #include "qemu/log.h"
27 #include "qemu/module.h"
28 #include "hw/ssi/stm32f2xx_spi.h"
29 #include "migration/vmstate.h"
30 
31 #ifndef STM_SPI_ERR_DEBUG
32 #define STM_SPI_ERR_DEBUG 0
33 #endif
34 
35 #define DB_PRINT_L(lvl, fmt, args...) do { \
36     if (STM_SPI_ERR_DEBUG >= lvl) { \
37         qemu_log("%s: " fmt, __func__, ## args); \
38     } \
39 } while (0)
40 
41 #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
42 
stm32f2xx_spi_reset(DeviceState * dev)43 static void stm32f2xx_spi_reset(DeviceState *dev)
44 {
45     STM32F2XXSPIState *s = STM32F2XX_SPI(dev);
46 
47     s->spi_cr1 = 0x00000000;
48     s->spi_cr2 = 0x00000000;
49     s->spi_sr = 0x0000000A;
50     s->spi_dr = 0x0000000C;
51     s->spi_crcpr = 0x00000007;
52     s->spi_rxcrcr = 0x00000000;
53     s->spi_txcrcr = 0x00000000;
54     s->spi_i2scfgr = 0x00000000;
55     s->spi_i2spr = 0x00000002;
56 }
57 
stm32f2xx_spi_transfer(STM32F2XXSPIState * s)58 static void stm32f2xx_spi_transfer(STM32F2XXSPIState *s)
59 {
60     DB_PRINT("Data to send: 0x%x\n", s->spi_dr);
61 
62     s->spi_dr = ssi_transfer(s->ssi, s->spi_dr);
63     s->spi_sr |= STM_SPI_SR_RXNE;
64 
65     DB_PRINT("Data received: 0x%x\n", s->spi_dr);
66 }
67 
stm32f2xx_spi_read(void * opaque,hwaddr addr,unsigned int size)68 static uint64_t stm32f2xx_spi_read(void *opaque, hwaddr addr,
69                                      unsigned int size)
70 {
71     STM32F2XXSPIState *s = opaque;
72 
73     DB_PRINT("Address: 0x%" HWADDR_PRIx "\n", addr);
74 
75     switch (addr) {
76     case STM_SPI_CR1:
77         return s->spi_cr1;
78     case STM_SPI_CR2:
79         qemu_log_mask(LOG_UNIMP, "%s: Interrupts and DMA are not implemented\n",
80                       __func__);
81         return s->spi_cr2;
82     case STM_SPI_SR:
83         return s->spi_sr;
84     case STM_SPI_DR:
85         stm32f2xx_spi_transfer(s);
86         s->spi_sr &= ~STM_SPI_SR_RXNE;
87         return s->spi_dr;
88     case STM_SPI_CRCPR:
89         qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented, the registers " \
90                       "are included for compatibility\n", __func__);
91         return s->spi_crcpr;
92     case STM_SPI_RXCRCR:
93         qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented, the registers " \
94                       "are included for compatibility\n", __func__);
95         return s->spi_rxcrcr;
96     case STM_SPI_TXCRCR:
97         qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented, the registers " \
98                       "are included for compatibility\n", __func__);
99         return s->spi_txcrcr;
100     case STM_SPI_I2SCFGR:
101         qemu_log_mask(LOG_UNIMP, "%s: I2S is not implemented, the registers " \
102                       "are included for compatibility\n", __func__);
103         return s->spi_i2scfgr;
104     case STM_SPI_I2SPR:
105         qemu_log_mask(LOG_UNIMP, "%s: I2S is not implemented, the registers " \
106                       "are included for compatibility\n", __func__);
107         return s->spi_i2spr;
108     default:
109         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
110                       __func__, addr);
111     }
112 
113     return 0;
114 }
115 
stm32f2xx_spi_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)116 static void stm32f2xx_spi_write(void *opaque, hwaddr addr,
117                                 uint64_t val64, unsigned int size)
118 {
119     STM32F2XXSPIState *s = opaque;
120     uint32_t value = val64;
121 
122     DB_PRINT("Address: 0x%" HWADDR_PRIx ", Value: 0x%x\n", addr, value);
123 
124     switch (addr) {
125     case STM_SPI_CR1:
126         s->spi_cr1 = value;
127         return;
128     case STM_SPI_CR2:
129         qemu_log_mask(LOG_UNIMP, "%s: " \
130                       "Interrupts and DMA are not implemented\n", __func__);
131         s->spi_cr2 = value;
132         return;
133     case STM_SPI_SR:
134         /* Read only register, except for clearing the CRCERR bit, which
135          * is not supported
136          */
137         return;
138     case STM_SPI_DR:
139         s->spi_dr = value;
140         stm32f2xx_spi_transfer(s);
141         return;
142     case STM_SPI_CRCPR:
143         qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented\n", __func__);
144         return;
145     case STM_SPI_RXCRCR:
146         qemu_log_mask(LOG_GUEST_ERROR, "%s: Read only register: " \
147                       "0x%" HWADDR_PRIx "\n", __func__, addr);
148         return;
149     case STM_SPI_TXCRCR:
150         qemu_log_mask(LOG_GUEST_ERROR, "%s: Read only register: " \
151                       "0x%" HWADDR_PRIx "\n", __func__, addr);
152         return;
153     case STM_SPI_I2SCFGR:
154         qemu_log_mask(LOG_UNIMP, "%s: " \
155                       "I2S is not implemented\n", __func__);
156         return;
157     case STM_SPI_I2SPR:
158         qemu_log_mask(LOG_UNIMP, "%s: " \
159                       "I2S is not implemented\n", __func__);
160         return;
161     default:
162         qemu_log_mask(LOG_GUEST_ERROR,
163                       "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
164     }
165 }
166 
167 static const MemoryRegionOps stm32f2xx_spi_ops = {
168     .read = stm32f2xx_spi_read,
169     .write = stm32f2xx_spi_write,
170     .endianness = DEVICE_NATIVE_ENDIAN,
171 };
172 
173 static const VMStateDescription vmstate_stm32f2xx_spi = {
174     .name = TYPE_STM32F2XX_SPI,
175     .version_id = 1,
176     .minimum_version_id = 1,
177     .fields = (const VMStateField[]) {
178         VMSTATE_UINT32(spi_cr1, STM32F2XXSPIState),
179         VMSTATE_UINT32(spi_cr2, STM32F2XXSPIState),
180         VMSTATE_UINT32(spi_sr, STM32F2XXSPIState),
181         VMSTATE_UINT32(spi_dr, STM32F2XXSPIState),
182         VMSTATE_UINT32(spi_crcpr, STM32F2XXSPIState),
183         VMSTATE_UINT32(spi_rxcrcr, STM32F2XXSPIState),
184         VMSTATE_UINT32(spi_txcrcr, STM32F2XXSPIState),
185         VMSTATE_UINT32(spi_i2scfgr, STM32F2XXSPIState),
186         VMSTATE_UINT32(spi_i2spr, STM32F2XXSPIState),
187         VMSTATE_END_OF_LIST()
188     }
189 };
190 
stm32f2xx_spi_init(Object * obj)191 static void stm32f2xx_spi_init(Object *obj)
192 {
193     STM32F2XXSPIState *s = STM32F2XX_SPI(obj);
194     DeviceState *dev = DEVICE(obj);
195 
196     memory_region_init_io(&s->mmio, obj, &stm32f2xx_spi_ops, s,
197                           TYPE_STM32F2XX_SPI, 0x400);
198     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
199 
200     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
201 
202     s->ssi = ssi_create_bus(dev, "ssi");
203 }
204 
stm32f2xx_spi_class_init(ObjectClass * klass,void * data)205 static void stm32f2xx_spi_class_init(ObjectClass *klass, void *data)
206 {
207     DeviceClass *dc = DEVICE_CLASS(klass);
208 
209     device_class_set_legacy_reset(dc, stm32f2xx_spi_reset);
210     dc->vmsd = &vmstate_stm32f2xx_spi;
211 }
212 
213 static const TypeInfo stm32f2xx_spi_info = {
214     .name          = TYPE_STM32F2XX_SPI,
215     .parent        = TYPE_SYS_BUS_DEVICE,
216     .instance_size = sizeof(STM32F2XXSPIState),
217     .instance_init = stm32f2xx_spi_init,
218     .class_init    = stm32f2xx_spi_class_init,
219 };
220 
stm32f2xx_spi_register_types(void)221 static void stm32f2xx_spi_register_types(void)
222 {
223     type_register_static(&stm32f2xx_spi_info);
224 }
225 
226 type_init(stm32f2xx_spi_register_types)
227