forked from mithro/qemu-litex
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
425 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.