1268ee7deSSubbaraya Sundeep /* 2268ee7deSSubbaraya Sundeep * Block model of SPI controller present in 3268ee7deSSubbaraya Sundeep * Microsemi's SmartFusion2 and SmartFusion SoCs. 4268ee7deSSubbaraya Sundeep * 5268ee7deSSubbaraya Sundeep * Copyright (C) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com> 6268ee7deSSubbaraya Sundeep * 7268ee7deSSubbaraya Sundeep * Permission is hereby granted, free of charge, to any person obtaining a copy 8268ee7deSSubbaraya Sundeep * of this software and associated documentation files (the "Software"), to deal 9268ee7deSSubbaraya Sundeep * in the Software without restriction, including without limitation the rights 10268ee7deSSubbaraya Sundeep * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11268ee7deSSubbaraya Sundeep * copies of the Software, and to permit persons to whom the Software is 12268ee7deSSubbaraya Sundeep * furnished to do so, subject to the following conditions: 13268ee7deSSubbaraya Sundeep * 14268ee7deSSubbaraya Sundeep * The above copyright notice and this permission notice shall be included in 15268ee7deSSubbaraya Sundeep * all copies or substantial portions of the Software. 16268ee7deSSubbaraya Sundeep * 17268ee7deSSubbaraya Sundeep * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18268ee7deSSubbaraya Sundeep * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19268ee7deSSubbaraya Sundeep * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20268ee7deSSubbaraya Sundeep * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21268ee7deSSubbaraya Sundeep * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22268ee7deSSubbaraya Sundeep * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23268ee7deSSubbaraya Sundeep * THE SOFTWARE. 24268ee7deSSubbaraya Sundeep */ 25268ee7deSSubbaraya Sundeep 26268ee7deSSubbaraya Sundeep #include "qemu/osdep.h" 27268ee7deSSubbaraya Sundeep #include "hw/ssi/mss-spi.h" 28268ee7deSSubbaraya Sundeep #include "qemu/log.h" 29*0b8fa32fSMarkus Armbruster #include "qemu/module.h" 30268ee7deSSubbaraya Sundeep 31268ee7deSSubbaraya Sundeep #ifndef MSS_SPI_ERR_DEBUG 32268ee7deSSubbaraya Sundeep #define MSS_SPI_ERR_DEBUG 0 33268ee7deSSubbaraya Sundeep #endif 34268ee7deSSubbaraya Sundeep 35268ee7deSSubbaraya Sundeep #define DB_PRINT_L(lvl, fmt, args...) do { \ 36268ee7deSSubbaraya Sundeep if (MSS_SPI_ERR_DEBUG >= lvl) { \ 37268ee7deSSubbaraya Sundeep qemu_log("%s: " fmt "\n", __func__, ## args); \ 38268ee7deSSubbaraya Sundeep } \ 392562755eSEric Blake } while (0) 40268ee7deSSubbaraya Sundeep 41268ee7deSSubbaraya Sundeep #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) 42268ee7deSSubbaraya Sundeep 43268ee7deSSubbaraya Sundeep #define FIFO_CAPACITY 32 44268ee7deSSubbaraya Sundeep 45268ee7deSSubbaraya Sundeep #define R_SPI_CONTROL 0 46268ee7deSSubbaraya Sundeep #define R_SPI_DFSIZE 1 47268ee7deSSubbaraya Sundeep #define R_SPI_STATUS 2 48268ee7deSSubbaraya Sundeep #define R_SPI_INTCLR 3 49268ee7deSSubbaraya Sundeep #define R_SPI_RX 4 50268ee7deSSubbaraya Sundeep #define R_SPI_TX 5 51268ee7deSSubbaraya Sundeep #define R_SPI_CLKGEN 6 52268ee7deSSubbaraya Sundeep #define R_SPI_SS 7 53268ee7deSSubbaraya Sundeep #define R_SPI_MIS 8 54268ee7deSSubbaraya Sundeep #define R_SPI_RIS 9 55268ee7deSSubbaraya Sundeep 56268ee7deSSubbaraya Sundeep #define S_TXDONE (1 << 0) 57268ee7deSSubbaraya Sundeep #define S_RXRDY (1 << 1) 58268ee7deSSubbaraya Sundeep #define S_RXCHOVRF (1 << 2) 59268ee7deSSubbaraya Sundeep #define S_RXFIFOFUL (1 << 4) 60268ee7deSSubbaraya Sundeep #define S_RXFIFOFULNXT (1 << 5) 61268ee7deSSubbaraya Sundeep #define S_RXFIFOEMP (1 << 6) 62268ee7deSSubbaraya Sundeep #define S_RXFIFOEMPNXT (1 << 7) 63268ee7deSSubbaraya Sundeep #define S_TXFIFOFUL (1 << 8) 64268ee7deSSubbaraya Sundeep #define S_TXFIFOFULNXT (1 << 9) 65268ee7deSSubbaraya Sundeep #define S_TXFIFOEMP (1 << 10) 66268ee7deSSubbaraya Sundeep #define S_TXFIFOEMPNXT (1 << 11) 67268ee7deSSubbaraya Sundeep #define S_FRAMESTART (1 << 12) 68268ee7deSSubbaraya Sundeep #define S_SSEL (1 << 13) 69268ee7deSSubbaraya Sundeep #define S_ACTIVE (1 << 14) 70268ee7deSSubbaraya Sundeep 71268ee7deSSubbaraya Sundeep #define C_ENABLE (1 << 0) 72268ee7deSSubbaraya Sundeep #define C_MODE (1 << 1) 73268ee7deSSubbaraya Sundeep #define C_INTRXDATA (1 << 4) 74268ee7deSSubbaraya Sundeep #define C_INTTXDATA (1 << 5) 75268ee7deSSubbaraya Sundeep #define C_INTRXOVRFLO (1 << 6) 76268ee7deSSubbaraya Sundeep #define C_SPS (1 << 26) 77268ee7deSSubbaraya Sundeep #define C_BIGFIFO (1 << 29) 78268ee7deSSubbaraya Sundeep #define C_RESET (1 << 31) 79268ee7deSSubbaraya Sundeep 80cda607d5SSubbaraya Sundeep #define FRAMESZ_MASK 0x3F 81268ee7deSSubbaraya Sundeep #define FMCOUNT_MASK 0x00FFFF00 82268ee7deSSubbaraya Sundeep #define FMCOUNT_SHIFT 8 83cda607d5SSubbaraya Sundeep #define FRAMESZ_MAX 32 84268ee7deSSubbaraya Sundeep 85268ee7deSSubbaraya Sundeep static void txfifo_reset(MSSSpiState *s) 86268ee7deSSubbaraya Sundeep { 87268ee7deSSubbaraya Sundeep fifo32_reset(&s->tx_fifo); 88268ee7deSSubbaraya Sundeep 89268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_TXFIFOFUL; 90268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXFIFOEMP; 91268ee7deSSubbaraya Sundeep } 92268ee7deSSubbaraya Sundeep 93268ee7deSSubbaraya Sundeep static void rxfifo_reset(MSSSpiState *s) 94268ee7deSSubbaraya Sundeep { 95268ee7deSSubbaraya Sundeep fifo32_reset(&s->rx_fifo); 96268ee7deSSubbaraya Sundeep 97268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL; 98268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOEMP; 99268ee7deSSubbaraya Sundeep } 100268ee7deSSubbaraya Sundeep 101268ee7deSSubbaraya Sundeep static void set_fifodepth(MSSSpiState *s) 102268ee7deSSubbaraya Sundeep { 103268ee7deSSubbaraya Sundeep unsigned int size = s->regs[R_SPI_DFSIZE] & FRAMESZ_MASK; 104268ee7deSSubbaraya Sundeep 105268ee7deSSubbaraya Sundeep if (size <= 8) { 106268ee7deSSubbaraya Sundeep s->fifo_depth = 32; 107268ee7deSSubbaraya Sundeep } else if (size <= 16) { 108268ee7deSSubbaraya Sundeep s->fifo_depth = 16; 109268ee7deSSubbaraya Sundeep } else { 110cda607d5SSubbaraya Sundeep s->fifo_depth = 8; 111268ee7deSSubbaraya Sundeep } 112268ee7deSSubbaraya Sundeep } 113268ee7deSSubbaraya Sundeep 114268ee7deSSubbaraya Sundeep static void update_mis(MSSSpiState *s) 115268ee7deSSubbaraya Sundeep { 116268ee7deSSubbaraya Sundeep uint32_t reg = s->regs[R_SPI_CONTROL]; 117268ee7deSSubbaraya Sundeep uint32_t tmp; 118268ee7deSSubbaraya Sundeep 119268ee7deSSubbaraya Sundeep /* 120268ee7deSSubbaraya Sundeep * form the Control register interrupt enable bits 121268ee7deSSubbaraya Sundeep * same as RIS, MIS and Interrupt clear registers for simplicity 122268ee7deSSubbaraya Sundeep */ 123268ee7deSSubbaraya Sundeep tmp = ((reg & C_INTRXOVRFLO) >> 4) | ((reg & C_INTRXDATA) >> 3) | 124268ee7deSSubbaraya Sundeep ((reg & C_INTTXDATA) >> 5); 125268ee7deSSubbaraya Sundeep s->regs[R_SPI_MIS] |= tmp & s->regs[R_SPI_RIS]; 126268ee7deSSubbaraya Sundeep } 127268ee7deSSubbaraya Sundeep 128268ee7deSSubbaraya Sundeep static void spi_update_irq(MSSSpiState *s) 129268ee7deSSubbaraya Sundeep { 130268ee7deSSubbaraya Sundeep int irq; 131268ee7deSSubbaraya Sundeep 132268ee7deSSubbaraya Sundeep update_mis(s); 133268ee7deSSubbaraya Sundeep irq = !!(s->regs[R_SPI_MIS]); 134268ee7deSSubbaraya Sundeep 135268ee7deSSubbaraya Sundeep qemu_set_irq(s->irq, irq); 136268ee7deSSubbaraya Sundeep } 137268ee7deSSubbaraya Sundeep 138268ee7deSSubbaraya Sundeep static void mss_spi_reset(DeviceState *d) 139268ee7deSSubbaraya Sundeep { 140268ee7deSSubbaraya Sundeep MSSSpiState *s = MSS_SPI(d); 141268ee7deSSubbaraya Sundeep 142268ee7deSSubbaraya Sundeep memset(s->regs, 0, sizeof s->regs); 143268ee7deSSubbaraya Sundeep s->regs[R_SPI_CONTROL] = 0x80000102; 144268ee7deSSubbaraya Sundeep s->regs[R_SPI_DFSIZE] = 0x4; 145268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] = S_SSEL | S_TXFIFOEMP | S_RXFIFOEMP; 146268ee7deSSubbaraya Sundeep s->regs[R_SPI_CLKGEN] = 0x7; 147268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] = 0x0; 148268ee7deSSubbaraya Sundeep 149268ee7deSSubbaraya Sundeep s->fifo_depth = 4; 150268ee7deSSubbaraya Sundeep s->frame_count = 1; 151268ee7deSSubbaraya Sundeep s->enabled = false; 152268ee7deSSubbaraya Sundeep 153268ee7deSSubbaraya Sundeep rxfifo_reset(s); 154268ee7deSSubbaraya Sundeep txfifo_reset(s); 155268ee7deSSubbaraya Sundeep } 156268ee7deSSubbaraya Sundeep 157268ee7deSSubbaraya Sundeep static uint64_t 158268ee7deSSubbaraya Sundeep spi_read(void *opaque, hwaddr addr, unsigned int size) 159268ee7deSSubbaraya Sundeep { 160268ee7deSSubbaraya Sundeep MSSSpiState *s = opaque; 161268ee7deSSubbaraya Sundeep uint32_t ret = 0; 162268ee7deSSubbaraya Sundeep 163268ee7deSSubbaraya Sundeep addr >>= 2; 164268ee7deSSubbaraya Sundeep switch (addr) { 165268ee7deSSubbaraya Sundeep case R_SPI_RX: 166268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL; 167268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXCHOVRF; 168268ee7deSSubbaraya Sundeep ret = fifo32_pop(&s->rx_fifo); 169268ee7deSSubbaraya Sundeep if (fifo32_is_empty(&s->rx_fifo)) { 170268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOEMP; 171268ee7deSSubbaraya Sundeep } 172268ee7deSSubbaraya Sundeep break; 173268ee7deSSubbaraya Sundeep 174268ee7deSSubbaraya Sundeep case R_SPI_MIS: 175268ee7deSSubbaraya Sundeep update_mis(s); 176268ee7deSSubbaraya Sundeep ret = s->regs[R_SPI_MIS]; 177268ee7deSSubbaraya Sundeep break; 178268ee7deSSubbaraya Sundeep 179268ee7deSSubbaraya Sundeep default: 180268ee7deSSubbaraya Sundeep if (addr < ARRAY_SIZE(s->regs)) { 181268ee7deSSubbaraya Sundeep ret = s->regs[addr]; 182268ee7deSSubbaraya Sundeep } else { 183268ee7deSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 184268ee7deSSubbaraya Sundeep "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, 185268ee7deSSubbaraya Sundeep addr * 4); 186268ee7deSSubbaraya Sundeep return ret; 187268ee7deSSubbaraya Sundeep } 188268ee7deSSubbaraya Sundeep break; 189268ee7deSSubbaraya Sundeep } 190268ee7deSSubbaraya Sundeep 191268ee7deSSubbaraya Sundeep DB_PRINT("addr=0x%" HWADDR_PRIx " = 0x%" PRIx32, addr * 4, ret); 192268ee7deSSubbaraya Sundeep spi_update_irq(s); 193268ee7deSSubbaraya Sundeep return ret; 194268ee7deSSubbaraya Sundeep } 195268ee7deSSubbaraya Sundeep 196268ee7deSSubbaraya Sundeep static void assert_cs(MSSSpiState *s) 197268ee7deSSubbaraya Sundeep { 198268ee7deSSubbaraya Sundeep qemu_set_irq(s->cs_line, 0); 199268ee7deSSubbaraya Sundeep } 200268ee7deSSubbaraya Sundeep 201268ee7deSSubbaraya Sundeep static void deassert_cs(MSSSpiState *s) 202268ee7deSSubbaraya Sundeep { 203268ee7deSSubbaraya Sundeep qemu_set_irq(s->cs_line, 1); 204268ee7deSSubbaraya Sundeep } 205268ee7deSSubbaraya Sundeep 206268ee7deSSubbaraya Sundeep static void spi_flush_txfifo(MSSSpiState *s) 207268ee7deSSubbaraya Sundeep { 208268ee7deSSubbaraya Sundeep uint32_t tx; 209268ee7deSSubbaraya Sundeep uint32_t rx; 210268ee7deSSubbaraya Sundeep bool sps = !!(s->regs[R_SPI_CONTROL] & C_SPS); 211268ee7deSSubbaraya Sundeep 212268ee7deSSubbaraya Sundeep /* 213268ee7deSSubbaraya Sundeep * Chip Select(CS) is automatically controlled by this controller. 214268ee7deSSubbaraya Sundeep * If SPS bit is set in Control register then CS is asserted 215268ee7deSSubbaraya Sundeep * until all the frames set in frame count of Control register are 216268ee7deSSubbaraya Sundeep * transferred. If SPS is not set then CS pulses between frames. 217268ee7deSSubbaraya Sundeep * Note that Slave Select register specifies which of the CS line 218268ee7deSSubbaraya Sundeep * has to be controlled automatically by controller. Bits SS[7:1] are for 219268ee7deSSubbaraya Sundeep * masters in FPGA fabric since we model only Microcontroller subsystem 220268ee7deSSubbaraya Sundeep * of Smartfusion2 we control only one CS(SS[0]) line. 221268ee7deSSubbaraya Sundeep */ 222268ee7deSSubbaraya Sundeep while (!fifo32_is_empty(&s->tx_fifo) && s->frame_count) { 223268ee7deSSubbaraya Sundeep assert_cs(s); 224268ee7deSSubbaraya Sundeep 225268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~(S_TXDONE | S_RXRDY); 226268ee7deSSubbaraya Sundeep 227268ee7deSSubbaraya Sundeep tx = fifo32_pop(&s->tx_fifo); 228268ee7deSSubbaraya Sundeep DB_PRINT("data tx:0x%" PRIx32, tx); 229268ee7deSSubbaraya Sundeep rx = ssi_transfer(s->spi, tx); 230268ee7deSSubbaraya Sundeep DB_PRINT("data rx:0x%" PRIx32, rx); 231268ee7deSSubbaraya Sundeep 232268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->rx_fifo) == s->fifo_depth) { 233268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXCHOVRF; 234268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] |= S_RXCHOVRF; 235268ee7deSSubbaraya Sundeep } else { 236268ee7deSSubbaraya Sundeep fifo32_push(&s->rx_fifo, rx); 237268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXFIFOEMP; 238268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->rx_fifo) == (s->fifo_depth - 1)) { 239268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOFULNXT; 240268ee7deSSubbaraya Sundeep } else if (fifo32_num_used(&s->rx_fifo) == s->fifo_depth) { 241268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOFUL; 242268ee7deSSubbaraya Sundeep } 243268ee7deSSubbaraya Sundeep } 244268ee7deSSubbaraya Sundeep s->frame_count--; 245268ee7deSSubbaraya Sundeep if (!sps) { 246268ee7deSSubbaraya Sundeep deassert_cs(s); 247268ee7deSSubbaraya Sundeep } 248268ee7deSSubbaraya Sundeep } 249268ee7deSSubbaraya Sundeep 250268ee7deSSubbaraya Sundeep if (!s->frame_count) { 251268ee7deSSubbaraya Sundeep s->frame_count = (s->regs[R_SPI_CONTROL] & FMCOUNT_MASK) >> 252268ee7deSSubbaraya Sundeep FMCOUNT_SHIFT; 253268ee7deSSubbaraya Sundeep deassert_cs(s); 254268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] |= S_TXDONE | S_RXRDY; 255268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXDONE | S_RXRDY; 256268ee7deSSubbaraya Sundeep } 257268ee7deSSubbaraya Sundeep } 258268ee7deSSubbaraya Sundeep 259268ee7deSSubbaraya Sundeep static void spi_write(void *opaque, hwaddr addr, 260268ee7deSSubbaraya Sundeep uint64_t val64, unsigned int size) 261268ee7deSSubbaraya Sundeep { 262268ee7deSSubbaraya Sundeep MSSSpiState *s = opaque; 263268ee7deSSubbaraya Sundeep uint32_t value = val64; 264268ee7deSSubbaraya Sundeep 265268ee7deSSubbaraya Sundeep DB_PRINT("addr=0x%" HWADDR_PRIx " =0x%" PRIx32, addr, value); 266268ee7deSSubbaraya Sundeep addr >>= 2; 267268ee7deSSubbaraya Sundeep 268268ee7deSSubbaraya Sundeep switch (addr) { 269268ee7deSSubbaraya Sundeep case R_SPI_TX: 270268ee7deSSubbaraya Sundeep /* adding to already full FIFO */ 271268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->tx_fifo) == s->fifo_depth) { 272268ee7deSSubbaraya Sundeep break; 273268ee7deSSubbaraya Sundeep } 274268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_TXFIFOEMP; 275268ee7deSSubbaraya Sundeep fifo32_push(&s->tx_fifo, value); 276268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->tx_fifo) == (s->fifo_depth - 1)) { 277268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXFIFOFULNXT; 278268ee7deSSubbaraya Sundeep } else if (fifo32_num_used(&s->tx_fifo) == s->fifo_depth) { 279268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXFIFOFUL; 280268ee7deSSubbaraya Sundeep } 281268ee7deSSubbaraya Sundeep if (s->enabled) { 282268ee7deSSubbaraya Sundeep spi_flush_txfifo(s); 283268ee7deSSubbaraya Sundeep } 284268ee7deSSubbaraya Sundeep break; 285268ee7deSSubbaraya Sundeep 286268ee7deSSubbaraya Sundeep case R_SPI_CONTROL: 287268ee7deSSubbaraya Sundeep s->regs[R_SPI_CONTROL] = value; 288268ee7deSSubbaraya Sundeep if (value & C_BIGFIFO) { 289268ee7deSSubbaraya Sundeep set_fifodepth(s); 290268ee7deSSubbaraya Sundeep } else { 291268ee7deSSubbaraya Sundeep s->fifo_depth = 4; 292268ee7deSSubbaraya Sundeep } 293268ee7deSSubbaraya Sundeep s->enabled = value & C_ENABLE; 294268ee7deSSubbaraya Sundeep s->frame_count = (value & FMCOUNT_MASK) >> FMCOUNT_SHIFT; 295268ee7deSSubbaraya Sundeep if (value & C_RESET) { 296268ee7deSSubbaraya Sundeep mss_spi_reset(DEVICE(s)); 297268ee7deSSubbaraya Sundeep } 298268ee7deSSubbaraya Sundeep break; 299268ee7deSSubbaraya Sundeep 300268ee7deSSubbaraya Sundeep case R_SPI_DFSIZE: 301268ee7deSSubbaraya Sundeep if (s->enabled) { 302268ee7deSSubbaraya Sundeep break; 303268ee7deSSubbaraya Sundeep } 304cda607d5SSubbaraya Sundeep /* 305cda607d5SSubbaraya Sundeep * [31:6] bits are reserved bits and for future use. 306cda607d5SSubbaraya Sundeep * [5:0] are for frame size. Only [5:0] bits are validated 307cda607d5SSubbaraya Sundeep * during write, [31:6] bits are untouched. 308cda607d5SSubbaraya Sundeep */ 309cda607d5SSubbaraya Sundeep if ((value & FRAMESZ_MASK) > FRAMESZ_MAX) { 310cda607d5SSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, "%s: Incorrect size %u provided." 311cda607d5SSubbaraya Sundeep "Maximum frame size is %u\n", 312cda607d5SSubbaraya Sundeep __func__, value & FRAMESZ_MASK, FRAMESZ_MAX); 313cda607d5SSubbaraya Sundeep break; 314cda607d5SSubbaraya Sundeep } 315268ee7deSSubbaraya Sundeep s->regs[R_SPI_DFSIZE] = value; 316268ee7deSSubbaraya Sundeep break; 317268ee7deSSubbaraya Sundeep 318268ee7deSSubbaraya Sundeep case R_SPI_INTCLR: 319268ee7deSSubbaraya Sundeep s->regs[R_SPI_INTCLR] = value; 320268ee7deSSubbaraya Sundeep if (value & S_TXDONE) { 321268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] &= ~S_TXDONE; 322268ee7deSSubbaraya Sundeep } 323268ee7deSSubbaraya Sundeep if (value & S_RXRDY) { 324268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] &= ~S_RXRDY; 325268ee7deSSubbaraya Sundeep } 326268ee7deSSubbaraya Sundeep if (value & S_RXCHOVRF) { 327268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] &= ~S_RXCHOVRF; 328268ee7deSSubbaraya Sundeep } 329268ee7deSSubbaraya Sundeep break; 330268ee7deSSubbaraya Sundeep 331268ee7deSSubbaraya Sundeep case R_SPI_MIS: 332268ee7deSSubbaraya Sundeep case R_SPI_STATUS: 333268ee7deSSubbaraya Sundeep case R_SPI_RIS: 334268ee7deSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 335268ee7deSSubbaraya Sundeep "%s: Write to read only register 0x%" HWADDR_PRIx "\n", 336268ee7deSSubbaraya Sundeep __func__, addr * 4); 337268ee7deSSubbaraya Sundeep break; 338268ee7deSSubbaraya Sundeep 339268ee7deSSubbaraya Sundeep default: 340268ee7deSSubbaraya Sundeep if (addr < ARRAY_SIZE(s->regs)) { 341268ee7deSSubbaraya Sundeep s->regs[addr] = value; 342268ee7deSSubbaraya Sundeep } else { 343268ee7deSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 344268ee7deSSubbaraya Sundeep "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, 345268ee7deSSubbaraya Sundeep addr * 4); 346268ee7deSSubbaraya Sundeep } 347268ee7deSSubbaraya Sundeep break; 348268ee7deSSubbaraya Sundeep } 349268ee7deSSubbaraya Sundeep 350268ee7deSSubbaraya Sundeep spi_update_irq(s); 351268ee7deSSubbaraya Sundeep } 352268ee7deSSubbaraya Sundeep 353268ee7deSSubbaraya Sundeep static const MemoryRegionOps spi_ops = { 354268ee7deSSubbaraya Sundeep .read = spi_read, 355268ee7deSSubbaraya Sundeep .write = spi_write, 356268ee7deSSubbaraya Sundeep .endianness = DEVICE_NATIVE_ENDIAN, 357268ee7deSSubbaraya Sundeep .valid = { 358268ee7deSSubbaraya Sundeep .min_access_size = 1, 359268ee7deSSubbaraya Sundeep .max_access_size = 4 360268ee7deSSubbaraya Sundeep } 361268ee7deSSubbaraya Sundeep }; 362268ee7deSSubbaraya Sundeep 363268ee7deSSubbaraya Sundeep static void mss_spi_realize(DeviceState *dev, Error **errp) 364268ee7deSSubbaraya Sundeep { 365268ee7deSSubbaraya Sundeep MSSSpiState *s = MSS_SPI(dev); 366268ee7deSSubbaraya Sundeep SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 367268ee7deSSubbaraya Sundeep 368268ee7deSSubbaraya Sundeep s->spi = ssi_create_bus(dev, "spi"); 369268ee7deSSubbaraya Sundeep 370268ee7deSSubbaraya Sundeep sysbus_init_irq(sbd, &s->irq); 371268ee7deSSubbaraya Sundeep ssi_auto_connect_slaves(dev, &s->cs_line, s->spi); 372268ee7deSSubbaraya Sundeep sysbus_init_irq(sbd, &s->cs_line); 373268ee7deSSubbaraya Sundeep 374268ee7deSSubbaraya Sundeep memory_region_init_io(&s->mmio, OBJECT(s), &spi_ops, s, 375268ee7deSSubbaraya Sundeep TYPE_MSS_SPI, R_SPI_MAX * 4); 376268ee7deSSubbaraya Sundeep sysbus_init_mmio(sbd, &s->mmio); 377268ee7deSSubbaraya Sundeep 378268ee7deSSubbaraya Sundeep fifo32_create(&s->tx_fifo, FIFO_CAPACITY); 379268ee7deSSubbaraya Sundeep fifo32_create(&s->rx_fifo, FIFO_CAPACITY); 380268ee7deSSubbaraya Sundeep } 381268ee7deSSubbaraya Sundeep 382268ee7deSSubbaraya Sundeep static const VMStateDescription vmstate_mss_spi = { 383268ee7deSSubbaraya Sundeep .name = TYPE_MSS_SPI, 384268ee7deSSubbaraya Sundeep .version_id = 1, 385268ee7deSSubbaraya Sundeep .minimum_version_id = 1, 386268ee7deSSubbaraya Sundeep .fields = (VMStateField[]) { 387268ee7deSSubbaraya Sundeep VMSTATE_FIFO32(tx_fifo, MSSSpiState), 388268ee7deSSubbaraya Sundeep VMSTATE_FIFO32(rx_fifo, MSSSpiState), 389268ee7deSSubbaraya Sundeep VMSTATE_UINT32_ARRAY(regs, MSSSpiState, R_SPI_MAX), 390268ee7deSSubbaraya Sundeep VMSTATE_END_OF_LIST() 391268ee7deSSubbaraya Sundeep } 392268ee7deSSubbaraya Sundeep }; 393268ee7deSSubbaraya Sundeep 394268ee7deSSubbaraya Sundeep static void mss_spi_class_init(ObjectClass *klass, void *data) 395268ee7deSSubbaraya Sundeep { 396268ee7deSSubbaraya Sundeep DeviceClass *dc = DEVICE_CLASS(klass); 397268ee7deSSubbaraya Sundeep 398268ee7deSSubbaraya Sundeep dc->realize = mss_spi_realize; 399268ee7deSSubbaraya Sundeep dc->reset = mss_spi_reset; 400268ee7deSSubbaraya Sundeep dc->vmsd = &vmstate_mss_spi; 401268ee7deSSubbaraya Sundeep } 402268ee7deSSubbaraya Sundeep 403268ee7deSSubbaraya Sundeep static const TypeInfo mss_spi_info = { 404268ee7deSSubbaraya Sundeep .name = TYPE_MSS_SPI, 405268ee7deSSubbaraya Sundeep .parent = TYPE_SYS_BUS_DEVICE, 406268ee7deSSubbaraya Sundeep .instance_size = sizeof(MSSSpiState), 407268ee7deSSubbaraya Sundeep .class_init = mss_spi_class_init, 408268ee7deSSubbaraya Sundeep }; 409268ee7deSSubbaraya Sundeep 410268ee7deSSubbaraya Sundeep static void mss_spi_register_types(void) 411268ee7deSSubbaraya Sundeep { 412268ee7deSSubbaraya Sundeep type_register_static(&mss_spi_info); 413268ee7deSSubbaraya Sundeep } 414268ee7deSSubbaraya Sundeep 415268ee7deSSubbaraya Sundeep type_init(mss_spi_register_types) 416