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