102 lines
2.6 KiB
C
102 lines
2.6 KiB
C
/*
|
|
* ESP32-C3 general-purpose SPI2 controller
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/module.h"
|
|
#include "hw/ssi/esp32c3_spi2.h"
|
|
|
|
#define SPI2_CMD 0x000
|
|
#define SPI2_MS_DLEN 0x01c
|
|
#define SPI2_W0 0x098
|
|
|
|
#define SPI2_CMD_USR BIT(24)
|
|
#define SPI2_DLEN_MASK 0x3ffff
|
|
#define SPI2_BUFFER_SIZE 64
|
|
|
|
static void esp32c3_spi2_transfer(ESP32C3Spi2State *s)
|
|
{
|
|
size_t bytes = (s->regs[SPI2_MS_DLEN / 4] & SPI2_DLEN_MASK) / 8 + 1;
|
|
bytes = MIN(bytes, SPI2_BUFFER_SIZE);
|
|
|
|
for (size_t i = 0; i < bytes; i++) {
|
|
uint32_t *word = &s->regs[SPI2_W0 / 4 + i / 4];
|
|
unsigned shift = (i % 4) * 8;
|
|
uint8_t tx = *word >> shift;
|
|
uint8_t rx = ssi_transfer(s->bus, tx);
|
|
*word = (*word & ~(0xffu << shift)) | ((uint32_t)rx << shift);
|
|
}
|
|
}
|
|
|
|
static uint64_t esp32c3_spi2_read(void *opaque, hwaddr addr, unsigned size)
|
|
{
|
|
ESP32C3Spi2State *s = ESP32C3_SPI2(opaque);
|
|
return s->regs[addr / 4];
|
|
}
|
|
|
|
static void esp32c3_spi2_write(void *opaque, hwaddr addr, uint64_t value,
|
|
unsigned size)
|
|
{
|
|
ESP32C3Spi2State *s = ESP32C3_SPI2(opaque);
|
|
|
|
if (addr == SPI2_CMD) {
|
|
if (value & SPI2_CMD_USR) {
|
|
esp32c3_spi2_transfer(s);
|
|
}
|
|
s->regs[SPI2_CMD / 4] = 0;
|
|
return;
|
|
}
|
|
|
|
s->regs[addr / 4] = value;
|
|
}
|
|
|
|
static const MemoryRegionOps esp32c3_spi2_ops = {
|
|
.read = esp32c3_spi2_read,
|
|
.write = esp32c3_spi2_write,
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
.valid = {
|
|
.min_access_size = 4,
|
|
.max_access_size = 4,
|
|
},
|
|
};
|
|
|
|
static void esp32c3_spi2_reset_hold(Object *obj, ResetType type)
|
|
{
|
|
ESP32C3Spi2State *s = ESP32C3_SPI2(obj);
|
|
memset(s->regs, 0, sizeof(s->regs));
|
|
}
|
|
|
|
static void esp32c3_spi2_init(Object *obj)
|
|
{
|
|
ESP32C3Spi2State *s = ESP32C3_SPI2(obj);
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
|
|
|
memory_region_init_io(&s->iomem, obj, &esp32c3_spi2_ops, s,
|
|
TYPE_ESP32C3_SPI2, ESP32C3_SPI2_IO_SIZE);
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
s->bus = ssi_create_bus(DEVICE(s), "spi");
|
|
}
|
|
|
|
static void esp32c3_spi2_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
|
rc->phases.hold = esp32c3_spi2_reset_hold;
|
|
}
|
|
|
|
static const TypeInfo esp32c3_spi2_info = {
|
|
.name = TYPE_ESP32C3_SPI2,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(ESP32C3Spi2State),
|
|
.instance_init = esp32c3_spi2_init,
|
|
.class_init = esp32c3_spi2_class_init,
|
|
};
|
|
|
|
static void esp32c3_spi2_register_types(void)
|
|
{
|
|
type_register_static(&esp32c3_spi2_info);
|
|
}
|
|
|
|
type_init(esp32c3_spi2_register_types)
|