sifive_spi.c (8810B)
1/* 2 * QEMU model of the SiFive SPI Controller 3 * 4 * Copyright (c) 2021 Wind River Systems, Inc. 5 * 6 * Author: 7 * Bin Meng <bin.meng@windriver.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms and conditions of the GNU General Public License, 11 * version 2 or later, as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22#include "qemu/osdep.h" 23#include "hw/irq.h" 24#include "hw/qdev-properties.h" 25#include "hw/sysbus.h" 26#include "hw/ssi/ssi.h" 27#include "qemu/fifo8.h" 28#include "qemu/log.h" 29#include "qemu/module.h" 30#include "hw/ssi/sifive_spi.h" 31 32#define R_SCKDIV (0x00 / 4) 33#define R_SCKMODE (0x04 / 4) 34#define R_CSID (0x10 / 4) 35#define R_CSDEF (0x14 / 4) 36#define R_CSMODE (0x18 / 4) 37#define R_DELAY0 (0x28 / 4) 38#define R_DELAY1 (0x2C / 4) 39#define R_FMT (0x40 / 4) 40#define R_TXDATA (0x48 / 4) 41#define R_RXDATA (0x4C / 4) 42#define R_TXMARK (0x50 / 4) 43#define R_RXMARK (0x54 / 4) 44#define R_FCTRL (0x60 / 4) 45#define R_FFMT (0x64 / 4) 46#define R_IE (0x70 / 4) 47#define R_IP (0x74 / 4) 48 49#define FMT_DIR (1 << 3) 50 51#define TXDATA_FULL (1 << 31) 52#define RXDATA_EMPTY (1 << 31) 53 54#define IE_TXWM (1 << 0) 55#define IE_RXWM (1 << 1) 56 57#define IP_TXWM (1 << 0) 58#define IP_RXWM (1 << 1) 59 60#define FIFO_CAPACITY 8 61 62static void sifive_spi_txfifo_reset(SiFiveSPIState *s) 63{ 64 fifo8_reset(&s->tx_fifo); 65 66 s->regs[R_TXDATA] &= ~TXDATA_FULL; 67 s->regs[R_IP] &= ~IP_TXWM; 68} 69 70static void sifive_spi_rxfifo_reset(SiFiveSPIState *s) 71{ 72 fifo8_reset(&s->rx_fifo); 73 74 s->regs[R_RXDATA] |= RXDATA_EMPTY; 75 s->regs[R_IP] &= ~IP_RXWM; 76} 77 78static void sifive_spi_update_cs(SiFiveSPIState *s) 79{ 80 int i; 81 82 for (i = 0; i < s->num_cs; i++) { 83 if (s->regs[R_CSDEF] & (1 << i)) { 84 qemu_set_irq(s->cs_lines[i], !(s->regs[R_CSMODE])); 85 } 86 } 87} 88 89static void sifive_spi_update_irq(SiFiveSPIState *s) 90{ 91 int level; 92 93 if (fifo8_num_used(&s->tx_fifo) < s->regs[R_TXMARK]) { 94 s->regs[R_IP] |= IP_TXWM; 95 } else { 96 s->regs[R_IP] &= ~IP_TXWM; 97 } 98 99 if (fifo8_num_used(&s->rx_fifo) > s->regs[R_RXMARK]) { 100 s->regs[R_IP] |= IP_RXWM; 101 } else { 102 s->regs[R_IP] &= ~IP_RXWM; 103 } 104 105 level = s->regs[R_IP] & s->regs[R_IE] ? 1 : 0; 106 qemu_set_irq(s->irq, level); 107} 108 109static void sifive_spi_reset(DeviceState *d) 110{ 111 SiFiveSPIState *s = SIFIVE_SPI(d); 112 113 memset(s->regs, 0, sizeof(s->regs)); 114 115 /* The reset value is high for all implemented CS pins */ 116 s->regs[R_CSDEF] = (1 << s->num_cs) - 1; 117 118 /* Populate register with their default value */ 119 s->regs[R_SCKDIV] = 0x03; 120 s->regs[R_DELAY0] = 0x1001; 121 s->regs[R_DELAY1] = 0x01; 122 123 sifive_spi_txfifo_reset(s); 124 sifive_spi_rxfifo_reset(s); 125 126 sifive_spi_update_cs(s); 127 sifive_spi_update_irq(s); 128} 129 130static void sifive_spi_flush_txfifo(SiFiveSPIState *s) 131{ 132 uint8_t tx; 133 uint8_t rx; 134 135 while (!fifo8_is_empty(&s->tx_fifo)) { 136 tx = fifo8_pop(&s->tx_fifo); 137 rx = ssi_transfer(s->spi, tx); 138 139 if (!fifo8_is_full(&s->rx_fifo)) { 140 if (!(s->regs[R_FMT] & FMT_DIR)) { 141 fifo8_push(&s->rx_fifo, rx); 142 } 143 } 144 } 145} 146 147static bool sifive_spi_is_bad_reg(hwaddr addr, bool allow_reserved) 148{ 149 bool bad; 150 151 switch (addr) { 152 /* reserved offsets */ 153 case 0x08: 154 case 0x0C: 155 case 0x1C: 156 case 0x20: 157 case 0x24: 158 case 0x30: 159 case 0x34: 160 case 0x38: 161 case 0x3C: 162 case 0x44: 163 case 0x58: 164 case 0x5C: 165 case 0x68: 166 case 0x6C: 167 bad = allow_reserved ? false : true; 168 break; 169 default: 170 bad = false; 171 } 172 173 if (addr >= (SIFIVE_SPI_REG_NUM << 2)) { 174 bad = true; 175 } 176 177 return bad; 178} 179 180static uint64_t sifive_spi_read(void *opaque, hwaddr addr, unsigned int size) 181{ 182 SiFiveSPIState *s = opaque; 183 uint32_t r; 184 185 if (sifive_spi_is_bad_reg(addr, true)) { 186 qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read at address 0x%" 187 HWADDR_PRIx "\n", __func__, addr); 188 return 0; 189 } 190 191 addr >>= 2; 192 switch (addr) { 193 case R_TXDATA: 194 if (fifo8_is_full(&s->tx_fifo)) { 195 return TXDATA_FULL; 196 } 197 r = 0; 198 break; 199 200 case R_RXDATA: 201 if (fifo8_is_empty(&s->rx_fifo)) { 202 return RXDATA_EMPTY; 203 } 204 r = fifo8_pop(&s->rx_fifo); 205 break; 206 207 default: 208 r = s->regs[addr]; 209 break; 210 } 211 212 sifive_spi_update_irq(s); 213 214 return r; 215} 216 217static void sifive_spi_write(void *opaque, hwaddr addr, 218 uint64_t val64, unsigned int size) 219{ 220 SiFiveSPIState *s = opaque; 221 uint32_t value = val64; 222 223 if (sifive_spi_is_bad_reg(addr, false)) { 224 qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write at addr=0x%" 225 HWADDR_PRIx " value=0x%x\n", __func__, addr, value); 226 return; 227 } 228 229 addr >>= 2; 230 switch (addr) { 231 case R_CSID: 232 if (value >= s->num_cs) { 233 qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csid %d\n", 234 __func__, value); 235 } else { 236 s->regs[R_CSID] = value; 237 sifive_spi_update_cs(s); 238 } 239 break; 240 241 case R_CSDEF: 242 if (value >= (1 << s->num_cs)) { 243 qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csdef %x\n", 244 __func__, value); 245 } else { 246 s->regs[R_CSDEF] = value; 247 } 248 break; 249 250 case R_CSMODE: 251 if (value > 3) { 252 qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csmode %x\n", 253 __func__, value); 254 } else { 255 s->regs[R_CSMODE] = value; 256 sifive_spi_update_cs(s); 257 } 258 break; 259 260 case R_TXDATA: 261 if (!fifo8_is_full(&s->tx_fifo)) { 262 fifo8_push(&s->tx_fifo, (uint8_t)value); 263 sifive_spi_flush_txfifo(s); 264 } 265 break; 266 267 case R_RXDATA: 268 case R_IP: 269 qemu_log_mask(LOG_GUEST_ERROR, 270 "%s: invalid write to read-only reigster 0x%" 271 HWADDR_PRIx " with 0x%x\n", __func__, addr << 2, value); 272 break; 273 274 case R_TXMARK: 275 case R_RXMARK: 276 if (value >= FIFO_CAPACITY) { 277 qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid watermark %d\n", 278 __func__, value); 279 } else { 280 s->regs[addr] = value; 281 } 282 break; 283 284 case R_FCTRL: 285 case R_FFMT: 286 qemu_log_mask(LOG_UNIMP, 287 "%s: direct-map flash interface unimplemented\n", 288 __func__); 289 break; 290 291 default: 292 s->regs[addr] = value; 293 break; 294 } 295 296 sifive_spi_update_irq(s); 297} 298 299static const MemoryRegionOps sifive_spi_ops = { 300 .read = sifive_spi_read, 301 .write = sifive_spi_write, 302 .endianness = DEVICE_LITTLE_ENDIAN, 303 .valid = { 304 .min_access_size = 4, 305 .max_access_size = 4 306 } 307}; 308 309static void sifive_spi_realize(DeviceState *dev, Error **errp) 310{ 311 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 312 SiFiveSPIState *s = SIFIVE_SPI(dev); 313 int i; 314 315 s->spi = ssi_create_bus(dev, "spi"); 316 sysbus_init_irq(sbd, &s->irq); 317 318 s->cs_lines = g_new0(qemu_irq, s->num_cs); 319 for (i = 0; i < s->num_cs; i++) { 320 sysbus_init_irq(sbd, &s->cs_lines[i]); 321 } 322 323 memory_region_init_io(&s->mmio, OBJECT(s), &sifive_spi_ops, s, 324 TYPE_SIFIVE_SPI, 0x1000); 325 sysbus_init_mmio(sbd, &s->mmio); 326 327 fifo8_create(&s->tx_fifo, FIFO_CAPACITY); 328 fifo8_create(&s->rx_fifo, FIFO_CAPACITY); 329} 330 331static Property sifive_spi_properties[] = { 332 DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1), 333 DEFINE_PROP_END_OF_LIST(), 334}; 335 336static void sifive_spi_class_init(ObjectClass *klass, void *data) 337{ 338 DeviceClass *dc = DEVICE_CLASS(klass); 339 340 device_class_set_props(dc, sifive_spi_properties); 341 dc->reset = sifive_spi_reset; 342 dc->realize = sifive_spi_realize; 343} 344 345static const TypeInfo sifive_spi_info = { 346 .name = TYPE_SIFIVE_SPI, 347 .parent = TYPE_SYS_BUS_DEVICE, 348 .instance_size = sizeof(SiFiveSPIState), 349 .class_init = sifive_spi_class_init, 350}; 351 352static void sifive_spi_register_types(void) 353{ 354 type_register_static(&sifive_spi_info); 355} 356 357type_init(sifive_spi_register_types)