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" 2764552b6bSMarkus Armbruster #include "hw/irq.h" 28268ee7deSSubbaraya Sundeep #include "hw/ssi/mss-spi.h" 29d6454270SMarkus Armbruster #include "migration/vmstate.h" 30268ee7deSSubbaraya Sundeep #include "qemu/log.h" 310b8fa32fSMarkus Armbruster #include "qemu/module.h" 32268ee7deSSubbaraya Sundeep 33268ee7deSSubbaraya Sundeep #ifndef MSS_SPI_ERR_DEBUG 34268ee7deSSubbaraya Sundeep #define MSS_SPI_ERR_DEBUG 0 35268ee7deSSubbaraya Sundeep #endif 36268ee7deSSubbaraya Sundeep 37268ee7deSSubbaraya Sundeep #define DB_PRINT_L(lvl, fmt, args...) do { \ 38268ee7deSSubbaraya Sundeep if (MSS_SPI_ERR_DEBUG >= lvl) { \ 39268ee7deSSubbaraya Sundeep qemu_log("%s: " fmt "\n", __func__, ## args); \ 40268ee7deSSubbaraya Sundeep } \ 412562755eSEric Blake } while (0) 42268ee7deSSubbaraya Sundeep 43268ee7deSSubbaraya Sundeep #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) 44268ee7deSSubbaraya Sundeep 45268ee7deSSubbaraya Sundeep #define FIFO_CAPACITY 32 46268ee7deSSubbaraya Sundeep 47268ee7deSSubbaraya Sundeep #define R_SPI_CONTROL 0 48268ee7deSSubbaraya Sundeep #define R_SPI_DFSIZE 1 49268ee7deSSubbaraya Sundeep #define R_SPI_STATUS 2 50268ee7deSSubbaraya Sundeep #define R_SPI_INTCLR 3 51268ee7deSSubbaraya Sundeep #define R_SPI_RX 4 52268ee7deSSubbaraya Sundeep #define R_SPI_TX 5 53268ee7deSSubbaraya Sundeep #define R_SPI_CLKGEN 6 54268ee7deSSubbaraya Sundeep #define R_SPI_SS 7 55268ee7deSSubbaraya Sundeep #define R_SPI_MIS 8 56268ee7deSSubbaraya Sundeep #define R_SPI_RIS 9 57268ee7deSSubbaraya Sundeep 58268ee7deSSubbaraya Sundeep #define S_TXDONE (1 << 0) 59268ee7deSSubbaraya Sundeep #define S_RXRDY (1 << 1) 60268ee7deSSubbaraya Sundeep #define S_RXCHOVRF (1 << 2) 61268ee7deSSubbaraya Sundeep #define S_RXFIFOFUL (1 << 4) 62268ee7deSSubbaraya Sundeep #define S_RXFIFOFULNXT (1 << 5) 63268ee7deSSubbaraya Sundeep #define S_RXFIFOEMP (1 << 6) 64268ee7deSSubbaraya Sundeep #define S_RXFIFOEMPNXT (1 << 7) 65268ee7deSSubbaraya Sundeep #define S_TXFIFOFUL (1 << 8) 66268ee7deSSubbaraya Sundeep #define S_TXFIFOFULNXT (1 << 9) 67268ee7deSSubbaraya Sundeep #define S_TXFIFOEMP (1 << 10) 68268ee7deSSubbaraya Sundeep #define S_TXFIFOEMPNXT (1 << 11) 69268ee7deSSubbaraya Sundeep #define S_FRAMESTART (1 << 12) 70268ee7deSSubbaraya Sundeep #define S_SSEL (1 << 13) 71268ee7deSSubbaraya Sundeep #define S_ACTIVE (1 << 14) 72268ee7deSSubbaraya Sundeep 73268ee7deSSubbaraya Sundeep #define C_ENABLE (1 << 0) 74268ee7deSSubbaraya Sundeep #define C_MODE (1 << 1) 75268ee7deSSubbaraya Sundeep #define C_INTRXDATA (1 << 4) 76268ee7deSSubbaraya Sundeep #define C_INTTXDATA (1 << 5) 77268ee7deSSubbaraya Sundeep #define C_INTRXOVRFLO (1 << 6) 78268ee7deSSubbaraya Sundeep #define C_SPS (1 << 26) 79268ee7deSSubbaraya Sundeep #define C_BIGFIFO (1 << 29) 80268ee7deSSubbaraya Sundeep #define C_RESET (1 << 31) 81268ee7deSSubbaraya Sundeep 82cda607d5SSubbaraya Sundeep #define FRAMESZ_MASK 0x3F 83268ee7deSSubbaraya Sundeep #define FMCOUNT_MASK 0x00FFFF00 84268ee7deSSubbaraya Sundeep #define FMCOUNT_SHIFT 8 85cda607d5SSubbaraya Sundeep #define FRAMESZ_MAX 32 86268ee7deSSubbaraya Sundeep 87268ee7deSSubbaraya Sundeep static void txfifo_reset(MSSSpiState *s) 88268ee7deSSubbaraya Sundeep { 89268ee7deSSubbaraya Sundeep fifo32_reset(&s->tx_fifo); 90268ee7deSSubbaraya Sundeep 91268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_TXFIFOFUL; 92268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXFIFOEMP; 93268ee7deSSubbaraya Sundeep } 94268ee7deSSubbaraya Sundeep 95268ee7deSSubbaraya Sundeep static void rxfifo_reset(MSSSpiState *s) 96268ee7deSSubbaraya Sundeep { 97268ee7deSSubbaraya Sundeep fifo32_reset(&s->rx_fifo); 98268ee7deSSubbaraya Sundeep 99268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL; 100268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOEMP; 101268ee7deSSubbaraya Sundeep } 102268ee7deSSubbaraya Sundeep 103268ee7deSSubbaraya Sundeep static void set_fifodepth(MSSSpiState *s) 104268ee7deSSubbaraya Sundeep { 105268ee7deSSubbaraya Sundeep unsigned int size = s->regs[R_SPI_DFSIZE] & FRAMESZ_MASK; 106268ee7deSSubbaraya Sundeep 107268ee7deSSubbaraya Sundeep if (size <= 8) { 108268ee7deSSubbaraya Sundeep s->fifo_depth = 32; 109268ee7deSSubbaraya Sundeep } else if (size <= 16) { 110268ee7deSSubbaraya Sundeep s->fifo_depth = 16; 111268ee7deSSubbaraya Sundeep } else { 112cda607d5SSubbaraya Sundeep s->fifo_depth = 8; 113268ee7deSSubbaraya Sundeep } 114268ee7deSSubbaraya Sundeep } 115268ee7deSSubbaraya Sundeep 116268ee7deSSubbaraya Sundeep static void update_mis(MSSSpiState *s) 117268ee7deSSubbaraya Sundeep { 118268ee7deSSubbaraya Sundeep uint32_t reg = s->regs[R_SPI_CONTROL]; 119268ee7deSSubbaraya Sundeep uint32_t tmp; 120268ee7deSSubbaraya Sundeep 121268ee7deSSubbaraya Sundeep /* 122268ee7deSSubbaraya Sundeep * form the Control register interrupt enable bits 123268ee7deSSubbaraya Sundeep * same as RIS, MIS and Interrupt clear registers for simplicity 124268ee7deSSubbaraya Sundeep */ 125268ee7deSSubbaraya Sundeep tmp = ((reg & C_INTRXOVRFLO) >> 4) | ((reg & C_INTRXDATA) >> 3) | 126268ee7deSSubbaraya Sundeep ((reg & C_INTTXDATA) >> 5); 127268ee7deSSubbaraya Sundeep s->regs[R_SPI_MIS] |= tmp & s->regs[R_SPI_RIS]; 128268ee7deSSubbaraya Sundeep } 129268ee7deSSubbaraya Sundeep 130268ee7deSSubbaraya Sundeep static void spi_update_irq(MSSSpiState *s) 131268ee7deSSubbaraya Sundeep { 132268ee7deSSubbaraya Sundeep int irq; 133268ee7deSSubbaraya Sundeep 134268ee7deSSubbaraya Sundeep update_mis(s); 135268ee7deSSubbaraya Sundeep irq = !!(s->regs[R_SPI_MIS]); 136268ee7deSSubbaraya Sundeep 137268ee7deSSubbaraya Sundeep qemu_set_irq(s->irq, irq); 138268ee7deSSubbaraya Sundeep } 139268ee7deSSubbaraya Sundeep 140268ee7deSSubbaraya Sundeep static void mss_spi_reset(DeviceState *d) 141268ee7deSSubbaraya Sundeep { 142268ee7deSSubbaraya Sundeep MSSSpiState *s = MSS_SPI(d); 143268ee7deSSubbaraya Sundeep 144268ee7deSSubbaraya Sundeep memset(s->regs, 0, sizeof s->regs); 145268ee7deSSubbaraya Sundeep s->regs[R_SPI_CONTROL] = 0x80000102; 146268ee7deSSubbaraya Sundeep s->regs[R_SPI_DFSIZE] = 0x4; 147268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] = S_SSEL | S_TXFIFOEMP | S_RXFIFOEMP; 148268ee7deSSubbaraya Sundeep s->regs[R_SPI_CLKGEN] = 0x7; 149268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] = 0x0; 150268ee7deSSubbaraya Sundeep 151268ee7deSSubbaraya Sundeep s->fifo_depth = 4; 152268ee7deSSubbaraya Sundeep s->frame_count = 1; 153268ee7deSSubbaraya Sundeep s->enabled = false; 154268ee7deSSubbaraya Sundeep 155268ee7deSSubbaraya Sundeep rxfifo_reset(s); 156268ee7deSSubbaraya Sundeep txfifo_reset(s); 157268ee7deSSubbaraya Sundeep } 158268ee7deSSubbaraya Sundeep 159268ee7deSSubbaraya Sundeep static uint64_t 160268ee7deSSubbaraya Sundeep spi_read(void *opaque, hwaddr addr, unsigned int size) 161268ee7deSSubbaraya Sundeep { 162268ee7deSSubbaraya Sundeep MSSSpiState *s = opaque; 163268ee7deSSubbaraya Sundeep uint32_t ret = 0; 164268ee7deSSubbaraya Sundeep 165268ee7deSSubbaraya Sundeep addr >>= 2; 166268ee7deSSubbaraya Sundeep switch (addr) { 167268ee7deSSubbaraya Sundeep case R_SPI_RX: 168268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL; 169268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXCHOVRF; 170c0bccee9SPhilippe Mathieu-Daudé if (fifo32_is_empty(&s->rx_fifo)) { 171c0bccee9SPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR, 172c0bccee9SPhilippe Mathieu-Daudé "%s: Reading empty RX_FIFO\n", 173c0bccee9SPhilippe Mathieu-Daudé __func__); 174c0bccee9SPhilippe Mathieu-Daudé } else { 175268ee7deSSubbaraya Sundeep ret = fifo32_pop(&s->rx_fifo); 176c0bccee9SPhilippe Mathieu-Daudé } 177268ee7deSSubbaraya Sundeep if (fifo32_is_empty(&s->rx_fifo)) { 178268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOEMP; 179268ee7deSSubbaraya Sundeep } 180268ee7deSSubbaraya Sundeep break; 181268ee7deSSubbaraya Sundeep 182268ee7deSSubbaraya Sundeep case R_SPI_MIS: 183268ee7deSSubbaraya Sundeep update_mis(s); 184268ee7deSSubbaraya Sundeep ret = s->regs[R_SPI_MIS]; 185268ee7deSSubbaraya Sundeep break; 186268ee7deSSubbaraya Sundeep 187268ee7deSSubbaraya Sundeep default: 188268ee7deSSubbaraya Sundeep if (addr < ARRAY_SIZE(s->regs)) { 189268ee7deSSubbaraya Sundeep ret = s->regs[addr]; 190268ee7deSSubbaraya Sundeep } else { 191268ee7deSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 192268ee7deSSubbaraya Sundeep "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, 193268ee7deSSubbaraya Sundeep addr * 4); 194268ee7deSSubbaraya Sundeep return ret; 195268ee7deSSubbaraya Sundeep } 196268ee7deSSubbaraya Sundeep break; 197268ee7deSSubbaraya Sundeep } 198268ee7deSSubbaraya Sundeep 199268ee7deSSubbaraya Sundeep DB_PRINT("addr=0x%" HWADDR_PRIx " = 0x%" PRIx32, addr * 4, ret); 200268ee7deSSubbaraya Sundeep spi_update_irq(s); 201268ee7deSSubbaraya Sundeep return ret; 202268ee7deSSubbaraya Sundeep } 203268ee7deSSubbaraya Sundeep 204268ee7deSSubbaraya Sundeep static void assert_cs(MSSSpiState *s) 205268ee7deSSubbaraya Sundeep { 206268ee7deSSubbaraya Sundeep qemu_set_irq(s->cs_line, 0); 207268ee7deSSubbaraya Sundeep } 208268ee7deSSubbaraya Sundeep 209268ee7deSSubbaraya Sundeep static void deassert_cs(MSSSpiState *s) 210268ee7deSSubbaraya Sundeep { 211268ee7deSSubbaraya Sundeep qemu_set_irq(s->cs_line, 1); 212268ee7deSSubbaraya Sundeep } 213268ee7deSSubbaraya Sundeep 214268ee7deSSubbaraya Sundeep static void spi_flush_txfifo(MSSSpiState *s) 215268ee7deSSubbaraya Sundeep { 216268ee7deSSubbaraya Sundeep uint32_t tx; 217268ee7deSSubbaraya Sundeep uint32_t rx; 218268ee7deSSubbaraya Sundeep bool sps = !!(s->regs[R_SPI_CONTROL] & C_SPS); 219268ee7deSSubbaraya Sundeep 220268ee7deSSubbaraya Sundeep /* 221268ee7deSSubbaraya Sundeep * Chip Select(CS) is automatically controlled by this controller. 222268ee7deSSubbaraya Sundeep * If SPS bit is set in Control register then CS is asserted 223268ee7deSSubbaraya Sundeep * until all the frames set in frame count of Control register are 224268ee7deSSubbaraya Sundeep * transferred. If SPS is not set then CS pulses between frames. 225268ee7deSSubbaraya Sundeep * Note that Slave Select register specifies which of the CS line 226268ee7deSSubbaraya Sundeep * has to be controlled automatically by controller. Bits SS[7:1] are for 227268ee7deSSubbaraya Sundeep * masters in FPGA fabric since we model only Microcontroller subsystem 228268ee7deSSubbaraya Sundeep * of Smartfusion2 we control only one CS(SS[0]) line. 229268ee7deSSubbaraya Sundeep */ 230268ee7deSSubbaraya Sundeep while (!fifo32_is_empty(&s->tx_fifo) && s->frame_count) { 231268ee7deSSubbaraya Sundeep assert_cs(s); 232268ee7deSSubbaraya Sundeep 233268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~(S_TXDONE | S_RXRDY); 234268ee7deSSubbaraya Sundeep 235268ee7deSSubbaraya Sundeep tx = fifo32_pop(&s->tx_fifo); 236268ee7deSSubbaraya Sundeep DB_PRINT("data tx:0x%" PRIx32, tx); 237268ee7deSSubbaraya Sundeep rx = ssi_transfer(s->spi, tx); 238268ee7deSSubbaraya Sundeep DB_PRINT("data rx:0x%" PRIx32, rx); 239268ee7deSSubbaraya Sundeep 240268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->rx_fifo) == s->fifo_depth) { 241268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXCHOVRF; 242268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] |= S_RXCHOVRF; 243268ee7deSSubbaraya Sundeep } else { 244268ee7deSSubbaraya Sundeep fifo32_push(&s->rx_fifo, rx); 245268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_RXFIFOEMP; 246268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->rx_fifo) == (s->fifo_depth - 1)) { 247268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOFULNXT; 248268ee7deSSubbaraya Sundeep } else if (fifo32_num_used(&s->rx_fifo) == s->fifo_depth) { 249268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_RXFIFOFUL; 250268ee7deSSubbaraya Sundeep } 251268ee7deSSubbaraya Sundeep } 252268ee7deSSubbaraya Sundeep s->frame_count--; 253268ee7deSSubbaraya Sundeep if (!sps) { 254268ee7deSSubbaraya Sundeep deassert_cs(s); 255268ee7deSSubbaraya Sundeep } 256268ee7deSSubbaraya Sundeep } 257268ee7deSSubbaraya Sundeep 258268ee7deSSubbaraya Sundeep if (!s->frame_count) { 259268ee7deSSubbaraya Sundeep s->frame_count = (s->regs[R_SPI_CONTROL] & FMCOUNT_MASK) >> 260268ee7deSSubbaraya Sundeep FMCOUNT_SHIFT; 261268ee7deSSubbaraya Sundeep deassert_cs(s); 262268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] |= S_TXDONE | S_RXRDY; 263268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXDONE | S_RXRDY; 264268ee7deSSubbaraya Sundeep } 265268ee7deSSubbaraya Sundeep } 266268ee7deSSubbaraya Sundeep 267268ee7deSSubbaraya Sundeep static void spi_write(void *opaque, hwaddr addr, 268268ee7deSSubbaraya Sundeep uint64_t val64, unsigned int size) 269268ee7deSSubbaraya Sundeep { 270268ee7deSSubbaraya Sundeep MSSSpiState *s = opaque; 271268ee7deSSubbaraya Sundeep uint32_t value = val64; 272268ee7deSSubbaraya Sundeep 273268ee7deSSubbaraya Sundeep DB_PRINT("addr=0x%" HWADDR_PRIx " =0x%" PRIx32, addr, value); 274268ee7deSSubbaraya Sundeep addr >>= 2; 275268ee7deSSubbaraya Sundeep 276268ee7deSSubbaraya Sundeep switch (addr) { 277268ee7deSSubbaraya Sundeep case R_SPI_TX: 278268ee7deSSubbaraya Sundeep /* adding to already full FIFO */ 279268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->tx_fifo) == s->fifo_depth) { 280268ee7deSSubbaraya Sundeep break; 281268ee7deSSubbaraya Sundeep } 282268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] &= ~S_TXFIFOEMP; 283268ee7deSSubbaraya Sundeep fifo32_push(&s->tx_fifo, value); 284268ee7deSSubbaraya Sundeep if (fifo32_num_used(&s->tx_fifo) == (s->fifo_depth - 1)) { 285268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXFIFOFULNXT; 286268ee7deSSubbaraya Sundeep } else if (fifo32_num_used(&s->tx_fifo) == s->fifo_depth) { 287268ee7deSSubbaraya Sundeep s->regs[R_SPI_STATUS] |= S_TXFIFOFUL; 288268ee7deSSubbaraya Sundeep } 289268ee7deSSubbaraya Sundeep if (s->enabled) { 290268ee7deSSubbaraya Sundeep spi_flush_txfifo(s); 291268ee7deSSubbaraya Sundeep } 292268ee7deSSubbaraya Sundeep break; 293268ee7deSSubbaraya Sundeep 294268ee7deSSubbaraya Sundeep case R_SPI_CONTROL: 295268ee7deSSubbaraya Sundeep s->regs[R_SPI_CONTROL] = value; 296268ee7deSSubbaraya Sundeep if (value & C_BIGFIFO) { 297268ee7deSSubbaraya Sundeep set_fifodepth(s); 298268ee7deSSubbaraya Sundeep } else { 299268ee7deSSubbaraya Sundeep s->fifo_depth = 4; 300268ee7deSSubbaraya Sundeep } 301268ee7deSSubbaraya Sundeep s->enabled = value & C_ENABLE; 302268ee7deSSubbaraya Sundeep s->frame_count = (value & FMCOUNT_MASK) >> FMCOUNT_SHIFT; 303268ee7deSSubbaraya Sundeep if (value & C_RESET) { 304268ee7deSSubbaraya Sundeep mss_spi_reset(DEVICE(s)); 305268ee7deSSubbaraya Sundeep } 306268ee7deSSubbaraya Sundeep break; 307268ee7deSSubbaraya Sundeep 308268ee7deSSubbaraya Sundeep case R_SPI_DFSIZE: 309268ee7deSSubbaraya Sundeep if (s->enabled) { 310268ee7deSSubbaraya Sundeep break; 311268ee7deSSubbaraya Sundeep } 312cda607d5SSubbaraya Sundeep /* 313cda607d5SSubbaraya Sundeep * [31:6] bits are reserved bits and for future use. 314cda607d5SSubbaraya Sundeep * [5:0] are for frame size. Only [5:0] bits are validated 315cda607d5SSubbaraya Sundeep * during write, [31:6] bits are untouched. 316cda607d5SSubbaraya Sundeep */ 317cda607d5SSubbaraya Sundeep if ((value & FRAMESZ_MASK) > FRAMESZ_MAX) { 318cda607d5SSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, "%s: Incorrect size %u provided." 319cda607d5SSubbaraya Sundeep "Maximum frame size is %u\n", 320cda607d5SSubbaraya Sundeep __func__, value & FRAMESZ_MASK, FRAMESZ_MAX); 321cda607d5SSubbaraya Sundeep break; 322cda607d5SSubbaraya Sundeep } 323268ee7deSSubbaraya Sundeep s->regs[R_SPI_DFSIZE] = value; 324268ee7deSSubbaraya Sundeep break; 325268ee7deSSubbaraya Sundeep 326268ee7deSSubbaraya Sundeep case R_SPI_INTCLR: 327268ee7deSSubbaraya Sundeep s->regs[R_SPI_INTCLR] = value; 328268ee7deSSubbaraya Sundeep if (value & S_TXDONE) { 329268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] &= ~S_TXDONE; 330268ee7deSSubbaraya Sundeep } 331268ee7deSSubbaraya Sundeep if (value & S_RXRDY) { 332268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] &= ~S_RXRDY; 333268ee7deSSubbaraya Sundeep } 334268ee7deSSubbaraya Sundeep if (value & S_RXCHOVRF) { 335268ee7deSSubbaraya Sundeep s->regs[R_SPI_RIS] &= ~S_RXCHOVRF; 336268ee7deSSubbaraya Sundeep } 337268ee7deSSubbaraya Sundeep break; 338268ee7deSSubbaraya Sundeep 339268ee7deSSubbaraya Sundeep case R_SPI_MIS: 340268ee7deSSubbaraya Sundeep case R_SPI_STATUS: 341268ee7deSSubbaraya Sundeep case R_SPI_RIS: 342268ee7deSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 343268ee7deSSubbaraya Sundeep "%s: Write to read only register 0x%" HWADDR_PRIx "\n", 344268ee7deSSubbaraya Sundeep __func__, addr * 4); 345268ee7deSSubbaraya Sundeep break; 346268ee7deSSubbaraya Sundeep 347268ee7deSSubbaraya Sundeep default: 348268ee7deSSubbaraya Sundeep if (addr < ARRAY_SIZE(s->regs)) { 349268ee7deSSubbaraya Sundeep s->regs[addr] = value; 350268ee7deSSubbaraya Sundeep } else { 351268ee7deSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 352268ee7deSSubbaraya Sundeep "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, 353268ee7deSSubbaraya Sundeep addr * 4); 354268ee7deSSubbaraya Sundeep } 355268ee7deSSubbaraya Sundeep break; 356268ee7deSSubbaraya Sundeep } 357268ee7deSSubbaraya Sundeep 358268ee7deSSubbaraya Sundeep spi_update_irq(s); 359268ee7deSSubbaraya Sundeep } 360268ee7deSSubbaraya Sundeep 361268ee7deSSubbaraya Sundeep static const MemoryRegionOps spi_ops = { 362268ee7deSSubbaraya Sundeep .read = spi_read, 363268ee7deSSubbaraya Sundeep .write = spi_write, 364268ee7deSSubbaraya Sundeep .endianness = DEVICE_NATIVE_ENDIAN, 365268ee7deSSubbaraya Sundeep .valid = { 366268ee7deSSubbaraya Sundeep .min_access_size = 1, 367268ee7deSSubbaraya Sundeep .max_access_size = 4 368268ee7deSSubbaraya Sundeep } 369268ee7deSSubbaraya Sundeep }; 370268ee7deSSubbaraya Sundeep 371268ee7deSSubbaraya Sundeep static void mss_spi_realize(DeviceState *dev, Error **errp) 372268ee7deSSubbaraya Sundeep { 373268ee7deSSubbaraya Sundeep MSSSpiState *s = MSS_SPI(dev); 374268ee7deSSubbaraya Sundeep SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 375268ee7deSSubbaraya Sundeep 376268ee7deSSubbaraya Sundeep s->spi = ssi_create_bus(dev, "spi"); 377268ee7deSSubbaraya Sundeep 378268ee7deSSubbaraya Sundeep sysbus_init_irq(sbd, &s->irq); 379268ee7deSSubbaraya Sundeep sysbus_init_irq(sbd, &s->cs_line); 380268ee7deSSubbaraya Sundeep 381268ee7deSSubbaraya Sundeep memory_region_init_io(&s->mmio, OBJECT(s), &spi_ops, s, 382268ee7deSSubbaraya Sundeep TYPE_MSS_SPI, R_SPI_MAX * 4); 383268ee7deSSubbaraya Sundeep sysbus_init_mmio(sbd, &s->mmio); 384268ee7deSSubbaraya Sundeep 385268ee7deSSubbaraya Sundeep fifo32_create(&s->tx_fifo, FIFO_CAPACITY); 386268ee7deSSubbaraya Sundeep fifo32_create(&s->rx_fifo, FIFO_CAPACITY); 387268ee7deSSubbaraya Sundeep } 388268ee7deSSubbaraya Sundeep 389268ee7deSSubbaraya Sundeep static const VMStateDescription vmstate_mss_spi = { 390268ee7deSSubbaraya Sundeep .name = TYPE_MSS_SPI, 391268ee7deSSubbaraya Sundeep .version_id = 1, 392268ee7deSSubbaraya Sundeep .minimum_version_id = 1, 3930aa6c7dfSRichard Henderson .fields = (const VMStateField[]) { 394268ee7deSSubbaraya Sundeep VMSTATE_FIFO32(tx_fifo, MSSSpiState), 395268ee7deSSubbaraya Sundeep VMSTATE_FIFO32(rx_fifo, MSSSpiState), 396268ee7deSSubbaraya Sundeep VMSTATE_UINT32_ARRAY(regs, MSSSpiState, R_SPI_MAX), 397268ee7deSSubbaraya Sundeep VMSTATE_END_OF_LIST() 398268ee7deSSubbaraya Sundeep } 399268ee7deSSubbaraya Sundeep }; 400268ee7deSSubbaraya Sundeep 401*12d1a768SPhilippe Mathieu-Daudé static void mss_spi_class_init(ObjectClass *klass, const void *data) 402268ee7deSSubbaraya Sundeep { 403268ee7deSSubbaraya Sundeep DeviceClass *dc = DEVICE_CLASS(klass); 404268ee7deSSubbaraya Sundeep 405268ee7deSSubbaraya Sundeep dc->realize = mss_spi_realize; 406e3d08143SPeter Maydell device_class_set_legacy_reset(dc, mss_spi_reset); 407268ee7deSSubbaraya Sundeep dc->vmsd = &vmstate_mss_spi; 408268ee7deSSubbaraya Sundeep } 409268ee7deSSubbaraya Sundeep 410268ee7deSSubbaraya Sundeep static const TypeInfo mss_spi_info = { 411268ee7deSSubbaraya Sundeep .name = TYPE_MSS_SPI, 412268ee7deSSubbaraya Sundeep .parent = TYPE_SYS_BUS_DEVICE, 413268ee7deSSubbaraya Sundeep .instance_size = sizeof(MSSSpiState), 414268ee7deSSubbaraya Sundeep .class_init = mss_spi_class_init, 415268ee7deSSubbaraya Sundeep }; 416268ee7deSSubbaraya Sundeep 417268ee7deSSubbaraya Sundeep static void mss_spi_register_types(void) 418268ee7deSSubbaraya Sundeep { 419268ee7deSSubbaraya Sundeep type_register_static(&mss_spi_info); 420268ee7deSSubbaraya Sundeep } 421268ee7deSSubbaraya Sundeep 422268ee7deSSubbaraya Sundeep type_init(mss_spi_register_types) 423