Skip to content

Commit

Permalink
litex: Working spiflash emulation.
Browse files Browse the repository at this point in the history
  • Loading branch information
mithro committed Apr 2, 2017
1 parent 1eae57c commit fe10ef8
Show file tree
Hide file tree
Showing 3 changed files with 425 additions and 0 deletions.
258 changes: 258 additions & 0 deletions hw/ssi/bitbang_ssi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* Bit-Bang ssi emulation extracted from
* Marvell MV88W8618 / Freecom MusicPal emulation.
*
* Copyright (c) 2008 Jan Kiszka
*
* This code is licensed under the GNU GPL v2.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "bitbang_ssi.h"
#include "hw/sysbus.h"

#define DEBUG_BITBANG_SSI

#ifdef DEBUG_BITBANG_SSI
#define DPRINTF(fmt, ...) \
do { printf("bitbang_ssi: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while(0)
#endif

typedef enum {
ACTIVE = 0,
IDLE
} bitbang_ssi_state;

typedef enum bitbang_ssi_state {
ACTIVE2IDLE = 0,
IDLE2ACTIVE
} bitbang_ssi_state;

struct bitbang_ssi_interface {
SSIBus *bus;

// Configuration
int transfer_size;
bitbang_ssi_cpol_mode cpol_mode;
bitbang_ssi_cpol_mode cpha_mode;

// Pin state
int last_sclk;
int last_mosi;
int last_miso;

// Data transmission state
bitbang_ssi_state state;
uint32_t data_miso;
int data_miso_count;
uint32_t data_mosi;
int data_mosi_count;

};

void bitbang_ssi_get_miso(bitbang_ssi_interface *ssi)
{
if (ssi->data_miso_count > 0) {
ssi->last_miso = ssi->data_miso & 0x1;
ssi->data_miso = ssi->data_miso >> 1;
ssi->data_miso_count -= 1;
}
DEBUG_BITBANG_SSI("get_miso %d %d %d\n", out_bit, ssi->data_miso, ssi->data_miso_count);
}

void bitbang_ssi_set_mosi(bitbang_ssi_interface *ssi)
{
ssi->data_mosi = ssi->data_in << 1 | ssi->mosi_level;
ssi->data_mosi_count += 1;
DEBUG_BITBANG_SSI("set_mosi %d %d %d\n", ssi->mosi_level, ssi->data_mosi, ssi->data_mosi_count);
}


/* Returns MISO line level or -1. */
int bitbang_ssi_set(bitbang_ssi_interface *ssi, bitbang_ssi_line line, int level)
{
if (level != 0 && level != 1) {
abort();
}

switch(line) {
case BITBANG_SSI_SCLK:
if (ssi->last_sclk == level)
return s->last_miso;
break;
case BITBANG_SSI_MOSI:
ssi->mosi_level = level;
return s->last_miso;
case BITBANG_SSI_MISO:
return s->last_miso;
}

DEBUG_BITBANG_SSI("sclk %d->%d (state: %s)\n", ssi->last_sclk, level, ssi->state == ACTIVE ? "active" : "idle");

if (ssi->last_sclk == 0 && level == 1) {
switch(ssi->cpol_mode) {
case BITBANG_SSI_CPOL0:
mode = IDLE2ACTIVE;
break;
case BITBANG_SSI_CPOL1:
mode = ACTIVE2IDLE;
break;
}
} else if (ssi->last_sclk == 1 && level == 0) {
switch(ssi->cpol_mode) {
case BITBANG_SSI_CPOL0:
mode = ACTIVE2IDLE;
break;
case BITBANG_SSI_CPOL1:
mode = IDLE2ACTIVE;
break;
}
} else {
abort();
}
ssi->last_sclk = level;

if (ssi->state == IDLE) {
ssi->state = ACTIVE;
ssi->data_count_mosi = 0;

if (ssi->cpha_mode == BITBANG_SSI_CPHA0) {
return s->last_miso;
}
}

if (ssi->cpha_mode == BITBANG_SSI_CPHA0) {
switch(mode) {
case IDLE2ACTIVE:
bitbang_ssi_set_mosi(ssi);
break;
case ACTIVE2IDLE:
bitbang_ssi_get_miso(ssi);
break;
}
} else if (ssi->cpha_mode == BITBANG_SSI_CPHA1) {
switch(mode) {
case IDLE2ACTIVE:
bitbang_ssi_get_miso(ssi);
break;
case ACTIVE2IDLE:
bitbang_ssi_set_mosi(ssi);
break;
}
}

if (ssi->data_mosi_count == ssi->transfer_size) {
ssi->data_miso = ssi->bus->transfer(ssi->data_mosi);
ssi->data_miso_count = ssi->transfer_size;
DEBUG_BITBANG_SSI("transfer(%d) ->%d\n", ssi->data_mosi, data->miso);
ssi->state = IDLE;
}
return s->last_miso;
}

bitbang_ssi_interface *bitbang_ssi_init(SSIBus *bus, bitbang_ssi_cpol_mode cpol_mode, bitbang_ssi_cpha_mode cpha_mode, int transfer_size)
{
DEBUG_BITBANG_SSI("init(cpol:%d, cpha:%d, size:%d\n", cpol_mode, cpha_mode, transfer_size);
bitbang_ssi_interface *s;

s = g_malloc0(sizeof(bitbang_ssi_interface));

s->bus = bus;

// Configuration
assert(transfer_size < (sizeof(s->data_miso)*8));
s->transfer_size = transfer_size;
s->cpol_mode = cpol_mode;
s->cpha_mode = cpha_mode;

// Pin values
switch (cpol_mode) {
case BITBANG_SSI_CPOL0:
s->last_sclk = 0;
break;
case BITBANG_SSI_CPOL1:
s->last_sclk = 1;
break;
}
s->last_mosi = 0;
s->last_miso = 0;

// Data transmission state
s->state = IDLE;
s->data_miso = 0;
s->data_miso_count = 0;
s->data_mosi = 0;
s->data_mosi_count = 0;

return s;
}

/* GPIO interface. */

#define TYPE_GPIO_SSI "gpio_ssi"
#define GPIO_SSI(obj) OBJECT_CHECK(GPIOSSIState, (obj), TYPE_GPIO_SSI)

typedef struct GPIOSSIState {
SysBusDevice parent_obj;

MemoryRegion dummy_iomem;
bitbang_ssi_interface *bitbang;
int last_level;
qemu_irq out;
} GPIOSSIState;

static void bitbang_ssi_gpio_set(void *opaque, int irq, int level)
{
GPIOSSIState *s = opaque;

level = bitbang_ssi_set(s->bitbang, irq, level);
if (level != s->last_level) {
s->last_level = level;
qemu_set_irq(s->out, level);
}
}

static void gpio_ssi_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
GPIOSSIState *s = GPIO_SSI(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
SSIBus *bus;

memory_region_init(&s->dummy_iomem, obj, "gpio_ssi", 0);
sysbus_init_mmio(sbd, &s->dummy_iomem);

bus = ssi_init_bus(dev, "ssi");
s->bitbang = bitbang_ssi_init(bus);

qdev_init_gpio_in(dev, bitbang_ssi_gpio_set, 2);
qdev_init_gpio_out(dev, &s->out, 1);
}

static void gpio_ssi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);

set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->desc = "Virtual GPIO to SSI bridge";
}

static const TypeInfo gpio_ssi_info = {
.name = TYPE_GPIO_SSI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(GPIOSSIState),
.instance_init = gpio_ssi_init,
.class_init = gpio_ssi_class_init,
};

static void bitbang_ssi_register_types(void)
{
type_register_static(&gpio_ssi_info);
}

type_init(bitbang_ssi_register_types)
27 changes: 27 additions & 0 deletions hw/ssi/bitbang_ssi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef BITBANG_SSI_H
#define BITBANG_SSI_H

#include "hw/ssi/ssi.h"

typedef struct bitbang_ssi_interface bitbang_ssi_interface;

typedef enum {
BITBANG_SSI_SCLK = 0,
BITBANG_SSI_MOSI,
BITBANG_SSI_MISO,
} bitbang_ssi_line;

typedef enum {
BITBANG_SSI_CPOL0 = 0, // idle == low, active == high
BITBANG_SSI_CPOL1 = 1 // idle == high, active == low
} bitbang_ssi_cpol_mode;

typedef enum {
BITBANG_SSI_CPHA0 = 0, // Data on 0th edge
BITBANG_SSI_CPHA1 = 1 // Data on 1st edge
} bitbang_ssi_cpha_mode;

bitbang_ssi_interface *bitbang_ssi_init(SSIBus *bus, bitbang_ssi_cpol_mode cpol_mode, bitbang_ssi_cpha_mode cpha_mode, int transfer_size);
int bitbang_ssi_set(bitbang_ssi_interface *ssi, int line, int level);

#endif
Loading

0 comments on commit fe10ef8

Please sign in to comment.