xref: /openbmc/qemu/hw/ssi/sifive_spi.c (revision b93b3cb1)
10694dabeSBin Meng /*
20694dabeSBin Meng  * QEMU model of the SiFive SPI Controller
30694dabeSBin Meng  *
40694dabeSBin Meng  * Copyright (c) 2021 Wind River Systems, Inc.
50694dabeSBin Meng  *
60694dabeSBin Meng  * Author:
70694dabeSBin Meng  *   Bin Meng <bin.meng@windriver.com>
80694dabeSBin Meng  *
90694dabeSBin Meng  * This program is free software; you can redistribute it and/or modify it
100694dabeSBin Meng  * under the terms and conditions of the GNU General Public License,
110694dabeSBin Meng  * version 2 or later, as published by the Free Software Foundation.
120694dabeSBin Meng  *
130694dabeSBin Meng  * This program is distributed in the hope it will be useful, but WITHOUT
140694dabeSBin Meng  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
150694dabeSBin Meng  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
160694dabeSBin Meng  * more details.
170694dabeSBin Meng  *
180694dabeSBin Meng  * You should have received a copy of the GNU General Public License along with
190694dabeSBin Meng  * this program.  If not, see <http://www.gnu.org/licenses/>.
200694dabeSBin Meng  */
210694dabeSBin Meng 
220694dabeSBin Meng #include "qemu/osdep.h"
230694dabeSBin Meng #include "hw/irq.h"
240694dabeSBin Meng #include "hw/qdev-properties.h"
250694dabeSBin Meng #include "hw/sysbus.h"
260694dabeSBin Meng #include "hw/ssi/ssi.h"
270694dabeSBin Meng #include "qemu/fifo8.h"
280694dabeSBin Meng #include "qemu/log.h"
290694dabeSBin Meng #include "qemu/module.h"
300694dabeSBin Meng #include "hw/ssi/sifive_spi.h"
310694dabeSBin Meng 
320694dabeSBin Meng #define R_SCKDIV        (0x00 / 4)
330694dabeSBin Meng #define R_SCKMODE       (0x04 / 4)
340694dabeSBin Meng #define R_CSID          (0x10 / 4)
350694dabeSBin Meng #define R_CSDEF         (0x14 / 4)
360694dabeSBin Meng #define R_CSMODE        (0x18 / 4)
370694dabeSBin Meng #define R_DELAY0        (0x28 / 4)
380694dabeSBin Meng #define R_DELAY1        (0x2C / 4)
390694dabeSBin Meng #define R_FMT           (0x40 / 4)
400694dabeSBin Meng #define R_TXDATA        (0x48 / 4)
410694dabeSBin Meng #define R_RXDATA        (0x4C / 4)
420694dabeSBin Meng #define R_TXMARK        (0x50 / 4)
430694dabeSBin Meng #define R_RXMARK        (0x54 / 4)
440694dabeSBin Meng #define R_FCTRL         (0x60 / 4)
450694dabeSBin Meng #define R_FFMT          (0x64 / 4)
460694dabeSBin Meng #define R_IE            (0x70 / 4)
470694dabeSBin Meng #define R_IP            (0x74 / 4)
480694dabeSBin Meng 
490694dabeSBin Meng #define FMT_DIR         (1 << 3)
500694dabeSBin Meng 
510694dabeSBin Meng #define TXDATA_FULL     (1 << 31)
520694dabeSBin Meng #define RXDATA_EMPTY    (1 << 31)
530694dabeSBin Meng 
540694dabeSBin Meng #define IE_TXWM         (1 << 0)
550694dabeSBin Meng #define IE_RXWM         (1 << 1)
560694dabeSBin Meng 
570694dabeSBin Meng #define IP_TXWM         (1 << 0)
580694dabeSBin Meng #define IP_RXWM         (1 << 1)
590694dabeSBin Meng 
600694dabeSBin Meng #define FIFO_CAPACITY   8
610694dabeSBin Meng 
sifive_spi_txfifo_reset(SiFiveSPIState * s)620694dabeSBin Meng static void sifive_spi_txfifo_reset(SiFiveSPIState *s)
630694dabeSBin Meng {
640694dabeSBin Meng     fifo8_reset(&s->tx_fifo);
650694dabeSBin Meng 
660694dabeSBin Meng     s->regs[R_TXDATA] &= ~TXDATA_FULL;
670694dabeSBin Meng     s->regs[R_IP] &= ~IP_TXWM;
680694dabeSBin Meng }
690694dabeSBin Meng 
sifive_spi_rxfifo_reset(SiFiveSPIState * s)700694dabeSBin Meng static void sifive_spi_rxfifo_reset(SiFiveSPIState *s)
710694dabeSBin Meng {
720694dabeSBin Meng     fifo8_reset(&s->rx_fifo);
730694dabeSBin Meng 
740694dabeSBin Meng     s->regs[R_RXDATA] |= RXDATA_EMPTY;
750694dabeSBin Meng     s->regs[R_IP] &= ~IP_RXWM;
760694dabeSBin Meng }
770694dabeSBin Meng 
sifive_spi_update_cs(SiFiveSPIState * s)780694dabeSBin Meng static void sifive_spi_update_cs(SiFiveSPIState *s)
790694dabeSBin Meng {
800694dabeSBin Meng     int i;
810694dabeSBin Meng 
820694dabeSBin Meng     for (i = 0; i < s->num_cs; i++) {
830694dabeSBin Meng         if (s->regs[R_CSDEF] & (1 << i)) {
840694dabeSBin Meng             qemu_set_irq(s->cs_lines[i], !(s->regs[R_CSMODE]));
850694dabeSBin Meng         }
860694dabeSBin Meng     }
870694dabeSBin Meng }
880694dabeSBin Meng 
sifive_spi_update_irq(SiFiveSPIState * s)890694dabeSBin Meng static void sifive_spi_update_irq(SiFiveSPIState *s)
900694dabeSBin Meng {
910694dabeSBin Meng     int level;
920694dabeSBin Meng 
930694dabeSBin Meng     if (fifo8_num_used(&s->tx_fifo) < s->regs[R_TXMARK]) {
940694dabeSBin Meng         s->regs[R_IP] |= IP_TXWM;
950694dabeSBin Meng     } else {
960694dabeSBin Meng         s->regs[R_IP] &= ~IP_TXWM;
970694dabeSBin Meng     }
980694dabeSBin Meng 
990694dabeSBin Meng     if (fifo8_num_used(&s->rx_fifo) > s->regs[R_RXMARK]) {
1000694dabeSBin Meng         s->regs[R_IP] |= IP_RXWM;
1010694dabeSBin Meng     } else {
1020694dabeSBin Meng         s->regs[R_IP] &= ~IP_RXWM;
1030694dabeSBin Meng     }
1040694dabeSBin Meng 
1050694dabeSBin Meng     level = s->regs[R_IP] & s->regs[R_IE] ? 1 : 0;
1060694dabeSBin Meng     qemu_set_irq(s->irq, level);
1070694dabeSBin Meng }
1080694dabeSBin Meng 
sifive_spi_reset(DeviceState * d)1090694dabeSBin Meng static void sifive_spi_reset(DeviceState *d)
1100694dabeSBin Meng {
1110694dabeSBin Meng     SiFiveSPIState *s = SIFIVE_SPI(d);
1120694dabeSBin Meng 
1130694dabeSBin Meng     memset(s->regs, 0, sizeof(s->regs));
1140694dabeSBin Meng 
1150694dabeSBin Meng     /* The reset value is high for all implemented CS pins */
1160694dabeSBin Meng     s->regs[R_CSDEF] = (1 << s->num_cs) - 1;
1170694dabeSBin Meng 
1180694dabeSBin Meng     /* Populate register with their default value */
1190694dabeSBin Meng     s->regs[R_SCKDIV] = 0x03;
1200694dabeSBin Meng     s->regs[R_DELAY0] = 0x1001;
1210694dabeSBin Meng     s->regs[R_DELAY1] = 0x01;
1220694dabeSBin Meng 
1230694dabeSBin Meng     sifive_spi_txfifo_reset(s);
1240694dabeSBin Meng     sifive_spi_rxfifo_reset(s);
1250694dabeSBin Meng 
1260694dabeSBin Meng     sifive_spi_update_cs(s);
1270694dabeSBin Meng     sifive_spi_update_irq(s);
1280694dabeSBin Meng }
1290694dabeSBin Meng 
sifive_spi_flush_txfifo(SiFiveSPIState * s)1300694dabeSBin Meng static void sifive_spi_flush_txfifo(SiFiveSPIState *s)
1310694dabeSBin Meng {
1320694dabeSBin Meng     uint8_t tx;
1330694dabeSBin Meng     uint8_t rx;
1340694dabeSBin Meng 
1350694dabeSBin Meng     while (!fifo8_is_empty(&s->tx_fifo)) {
1360694dabeSBin Meng         tx = fifo8_pop(&s->tx_fifo);
1370694dabeSBin Meng         rx = ssi_transfer(s->spi, tx);
1380694dabeSBin Meng 
1390694dabeSBin Meng         if (!fifo8_is_full(&s->rx_fifo)) {
1400694dabeSBin Meng             if (!(s->regs[R_FMT] & FMT_DIR)) {
1410694dabeSBin Meng                 fifo8_push(&s->rx_fifo, rx);
1420694dabeSBin Meng             }
1430694dabeSBin Meng         }
1440694dabeSBin Meng     }
1450694dabeSBin Meng }
1460694dabeSBin Meng 
sifive_spi_is_bad_reg(hwaddr addr,bool allow_reserved)1470694dabeSBin Meng static bool sifive_spi_is_bad_reg(hwaddr addr, bool allow_reserved)
1480694dabeSBin Meng {
1490694dabeSBin Meng     bool bad;
1500694dabeSBin Meng 
1510694dabeSBin Meng     switch (addr) {
1520694dabeSBin Meng     /* reserved offsets */
1530694dabeSBin Meng     case 0x08:
1540694dabeSBin Meng     case 0x0C:
1550694dabeSBin Meng     case 0x1C:
1560694dabeSBin Meng     case 0x20:
1570694dabeSBin Meng     case 0x24:
1580694dabeSBin Meng     case 0x30:
1590694dabeSBin Meng     case 0x34:
1600694dabeSBin Meng     case 0x38:
1610694dabeSBin Meng     case 0x3C:
1620694dabeSBin Meng     case 0x44:
1630694dabeSBin Meng     case 0x58:
1640694dabeSBin Meng     case 0x5C:
1650694dabeSBin Meng     case 0x68:
1660694dabeSBin Meng     case 0x6C:
1670694dabeSBin Meng         bad = allow_reserved ? false : true;
1680694dabeSBin Meng         break;
1690694dabeSBin Meng     default:
1700694dabeSBin Meng         bad = false;
1710694dabeSBin Meng     }
1720694dabeSBin Meng 
1730694dabeSBin Meng     if (addr >= (SIFIVE_SPI_REG_NUM << 2)) {
1740694dabeSBin Meng         bad = true;
1750694dabeSBin Meng     }
1760694dabeSBin Meng 
1770694dabeSBin Meng     return bad;
1780694dabeSBin Meng }
1790694dabeSBin Meng 
sifive_spi_read(void * opaque,hwaddr addr,unsigned int size)1800694dabeSBin Meng static uint64_t sifive_spi_read(void *opaque, hwaddr addr, unsigned int size)
1810694dabeSBin Meng {
1820694dabeSBin Meng     SiFiveSPIState *s = opaque;
1830694dabeSBin Meng     uint32_t r;
1840694dabeSBin Meng 
1850694dabeSBin Meng     if (sifive_spi_is_bad_reg(addr, true)) {
1860694dabeSBin Meng         qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read at address 0x%"
1870694dabeSBin Meng                       HWADDR_PRIx "\n", __func__, addr);
1880694dabeSBin Meng         return 0;
1890694dabeSBin Meng     }
1900694dabeSBin Meng 
1910694dabeSBin Meng     addr >>= 2;
1920694dabeSBin Meng     switch (addr) {
1930694dabeSBin Meng     case R_TXDATA:
1940694dabeSBin Meng         if (fifo8_is_full(&s->tx_fifo)) {
1950694dabeSBin Meng             return TXDATA_FULL;
1960694dabeSBin Meng         }
1970694dabeSBin Meng         r = 0;
1980694dabeSBin Meng         break;
1990694dabeSBin Meng 
2000694dabeSBin Meng     case R_RXDATA:
2010694dabeSBin Meng         if (fifo8_is_empty(&s->rx_fifo)) {
2020694dabeSBin Meng             return RXDATA_EMPTY;
2030694dabeSBin Meng         }
2040694dabeSBin Meng         r = fifo8_pop(&s->rx_fifo);
2050694dabeSBin Meng         break;
2060694dabeSBin Meng 
2070694dabeSBin Meng     default:
2080694dabeSBin Meng         r = s->regs[addr];
2090694dabeSBin Meng         break;
2100694dabeSBin Meng     }
2110694dabeSBin Meng 
2120694dabeSBin Meng     sifive_spi_update_irq(s);
2130694dabeSBin Meng 
2140694dabeSBin Meng     return r;
2150694dabeSBin Meng }
2160694dabeSBin Meng 
sifive_spi_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)2170694dabeSBin Meng static void sifive_spi_write(void *opaque, hwaddr addr,
2180694dabeSBin Meng                              uint64_t val64, unsigned int size)
2190694dabeSBin Meng {
2200694dabeSBin Meng     SiFiveSPIState *s = opaque;
2210694dabeSBin Meng     uint32_t value = val64;
2220694dabeSBin Meng 
2230694dabeSBin Meng     if (sifive_spi_is_bad_reg(addr, false)) {
2240694dabeSBin Meng         qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write at addr=0x%"
2250694dabeSBin Meng                       HWADDR_PRIx " value=0x%x\n", __func__, addr, value);
2260694dabeSBin Meng         return;
2270694dabeSBin Meng     }
2280694dabeSBin Meng 
2290694dabeSBin Meng     addr >>= 2;
2300694dabeSBin Meng     switch (addr) {
2310694dabeSBin Meng     case R_CSID:
2320694dabeSBin Meng         if (value >= s->num_cs) {
2330694dabeSBin Meng             qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csid %d\n",
2340694dabeSBin Meng                           __func__, value);
2350694dabeSBin Meng         } else {
2360694dabeSBin Meng             s->regs[R_CSID] = value;
2370694dabeSBin Meng             sifive_spi_update_cs(s);
2380694dabeSBin Meng         }
2390694dabeSBin Meng         break;
2400694dabeSBin Meng 
2410694dabeSBin Meng     case R_CSDEF:
2420694dabeSBin Meng         if (value >= (1 << s->num_cs)) {
2430694dabeSBin Meng             qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csdef %x\n",
2440694dabeSBin Meng                           __func__, value);
2450694dabeSBin Meng         } else {
2460694dabeSBin Meng             s->regs[R_CSDEF] = value;
2470694dabeSBin Meng         }
2480694dabeSBin Meng         break;
2490694dabeSBin Meng 
2500694dabeSBin Meng     case R_CSMODE:
2510694dabeSBin Meng         if (value > 3) {
2520694dabeSBin Meng             qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csmode %x\n",
2530694dabeSBin Meng                           __func__, value);
2540694dabeSBin Meng         } else {
2550694dabeSBin Meng             s->regs[R_CSMODE] = value;
2560694dabeSBin Meng             sifive_spi_update_cs(s);
2570694dabeSBin Meng         }
2580694dabeSBin Meng         break;
2590694dabeSBin Meng 
2600694dabeSBin Meng     case R_TXDATA:
2610694dabeSBin Meng         if (!fifo8_is_full(&s->tx_fifo)) {
2620694dabeSBin Meng             fifo8_push(&s->tx_fifo, (uint8_t)value);
2630694dabeSBin Meng             sifive_spi_flush_txfifo(s);
2640694dabeSBin Meng         }
2650694dabeSBin Meng         break;
2660694dabeSBin Meng 
2670694dabeSBin Meng     case R_RXDATA:
2680694dabeSBin Meng     case R_IP:
2690694dabeSBin Meng         qemu_log_mask(LOG_GUEST_ERROR,
270*b93b3cb1SMichael Tokarev                       "%s: invalid write to read-only register 0x%"
2710694dabeSBin Meng                       HWADDR_PRIx " with 0x%x\n", __func__, addr << 2, value);
2720694dabeSBin Meng         break;
2730694dabeSBin Meng 
2740694dabeSBin Meng     case R_TXMARK:
2750694dabeSBin Meng     case R_RXMARK:
2760694dabeSBin Meng         if (value >= FIFO_CAPACITY) {
2770694dabeSBin Meng             qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid watermark %d\n",
2780694dabeSBin Meng                           __func__, value);
2790694dabeSBin Meng         } else {
2800694dabeSBin Meng             s->regs[addr] = value;
2810694dabeSBin Meng         }
2820694dabeSBin Meng         break;
2830694dabeSBin Meng 
2840694dabeSBin Meng     case R_FCTRL:
2850694dabeSBin Meng     case R_FFMT:
2860694dabeSBin Meng         qemu_log_mask(LOG_UNIMP,
2870694dabeSBin Meng                       "%s: direct-map flash interface unimplemented\n",
2880694dabeSBin Meng                       __func__);
2890694dabeSBin Meng         break;
2900694dabeSBin Meng 
2910694dabeSBin Meng     default:
2920694dabeSBin Meng         s->regs[addr] = value;
2930694dabeSBin Meng         break;
2940694dabeSBin Meng     }
2950694dabeSBin Meng 
2960694dabeSBin Meng     sifive_spi_update_irq(s);
2970694dabeSBin Meng }
2980694dabeSBin Meng 
2990694dabeSBin Meng static const MemoryRegionOps sifive_spi_ops = {
3000694dabeSBin Meng     .read = sifive_spi_read,
3010694dabeSBin Meng     .write = sifive_spi_write,
3020694dabeSBin Meng     .endianness = DEVICE_LITTLE_ENDIAN,
3030694dabeSBin Meng     .valid = {
3040694dabeSBin Meng         .min_access_size = 4,
3050694dabeSBin Meng         .max_access_size = 4
3060694dabeSBin Meng     }
3070694dabeSBin Meng };
3080694dabeSBin Meng 
sifive_spi_realize(DeviceState * dev,Error ** errp)3090694dabeSBin Meng static void sifive_spi_realize(DeviceState *dev, Error **errp)
3100694dabeSBin Meng {
3110694dabeSBin Meng     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
3120694dabeSBin Meng     SiFiveSPIState *s = SIFIVE_SPI(dev);
3130694dabeSBin Meng     int i;
3140694dabeSBin Meng 
3150694dabeSBin Meng     s->spi = ssi_create_bus(dev, "spi");
3160694dabeSBin Meng     sysbus_init_irq(sbd, &s->irq);
3170694dabeSBin Meng 
3180694dabeSBin Meng     s->cs_lines = g_new0(qemu_irq, s->num_cs);
3190694dabeSBin Meng     for (i = 0; i < s->num_cs; i++) {
3200694dabeSBin Meng         sysbus_init_irq(sbd, &s->cs_lines[i]);
3210694dabeSBin Meng     }
3220694dabeSBin Meng 
3230694dabeSBin Meng     memory_region_init_io(&s->mmio, OBJECT(s), &sifive_spi_ops, s,
3240694dabeSBin Meng                           TYPE_SIFIVE_SPI, 0x1000);
3250694dabeSBin Meng     sysbus_init_mmio(sbd, &s->mmio);
3260694dabeSBin Meng 
3270694dabeSBin Meng     fifo8_create(&s->tx_fifo, FIFO_CAPACITY);
3280694dabeSBin Meng     fifo8_create(&s->rx_fifo, FIFO_CAPACITY);
3290694dabeSBin Meng }
3300694dabeSBin Meng 
3310694dabeSBin Meng static Property sifive_spi_properties[] = {
3320694dabeSBin Meng     DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1),
3330694dabeSBin Meng     DEFINE_PROP_END_OF_LIST(),
3340694dabeSBin Meng };
3350694dabeSBin Meng 
sifive_spi_class_init(ObjectClass * klass,void * data)3360694dabeSBin Meng static void sifive_spi_class_init(ObjectClass *klass, void *data)
3370694dabeSBin Meng {
3380694dabeSBin Meng     DeviceClass *dc = DEVICE_CLASS(klass);
3390694dabeSBin Meng 
3400694dabeSBin Meng     device_class_set_props(dc, sifive_spi_properties);
3410694dabeSBin Meng     dc->reset = sifive_spi_reset;
3420694dabeSBin Meng     dc->realize = sifive_spi_realize;
3430694dabeSBin Meng }
3440694dabeSBin Meng 
3450694dabeSBin Meng static const TypeInfo sifive_spi_info = {
3460694dabeSBin Meng     .name           = TYPE_SIFIVE_SPI,
3470694dabeSBin Meng     .parent         = TYPE_SYS_BUS_DEVICE,
3480694dabeSBin Meng     .instance_size  = sizeof(SiFiveSPIState),
3490694dabeSBin Meng     .class_init     = sifive_spi_class_init,
3500694dabeSBin Meng };
3510694dabeSBin Meng 
sifive_spi_register_types(void)3520694dabeSBin Meng static void sifive_spi_register_types(void)
3530694dabeSBin Meng {
3540694dabeSBin Meng     type_register_static(&sifive_spi_info);
3550694dabeSBin Meng }
3560694dabeSBin Meng 
3570694dabeSBin Meng type_init(sifive_spi_register_types)
358