root / hw / ssi.c @ 9a6ee9fd
History | View | Annotate | Download (4.1 kB)
1 |
/*
|
---|---|
2 |
* QEMU Synchronous Serial Interface support
|
3 |
*
|
4 |
* Copyright (c) 2009 CodeSourcery.
|
5 |
* Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
|
6 |
* Copyright (c) 2012 PetaLogix Pty Ltd.
|
7 |
* Written by Paul Brook
|
8 |
*
|
9 |
* This code is licensed under the GNU GPL v2.
|
10 |
*
|
11 |
* Contributions after 2012-01-13 are licensed under the terms of the
|
12 |
* GNU GPL, version 2 or (at your option) any later version.
|
13 |
*/
|
14 |
|
15 |
#include "ssi.h" |
16 |
|
17 |
struct SSIBus {
|
18 |
BusState qbus; |
19 |
}; |
20 |
|
21 |
#define TYPE_SSI_BUS "SSI" |
22 |
#define SSI_BUS(obj) OBJECT_CHECK(SSIBus, (obj), TYPE_SSI_BUS)
|
23 |
|
24 |
static const TypeInfo ssi_bus_info = { |
25 |
.name = TYPE_SSI_BUS, |
26 |
.parent = TYPE_BUS, |
27 |
.instance_size = sizeof(SSIBus),
|
28 |
}; |
29 |
|
30 |
static void ssi_cs_default(void *opaque, int n, int level) |
31 |
{ |
32 |
SSISlave *s = SSI_SLAVE(opaque); |
33 |
bool cs = !!level;
|
34 |
assert(n == 0);
|
35 |
if (s->cs != cs) {
|
36 |
SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s); |
37 |
if (ssc->set_cs) {
|
38 |
ssc->set_cs(s, cs); |
39 |
} |
40 |
} |
41 |
s->cs = cs; |
42 |
} |
43 |
|
44 |
static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val)
|
45 |
{ |
46 |
SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(dev); |
47 |
|
48 |
if ((dev->cs && ssc->cs_polarity == SSI_CS_HIGH) ||
|
49 |
(!dev->cs && ssc->cs_polarity == SSI_CS_LOW) || |
50 |
ssc->cs_polarity == SSI_CS_NONE) { |
51 |
return ssc->transfer(dev, val);
|
52 |
} |
53 |
return 0; |
54 |
} |
55 |
|
56 |
static int ssi_slave_init(DeviceState *dev) |
57 |
{ |
58 |
SSISlave *s = SSI_SLAVE(dev); |
59 |
SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s); |
60 |
|
61 |
if (ssc->transfer_raw == ssi_transfer_raw_default &&
|
62 |
ssc->cs_polarity != SSI_CS_NONE) { |
63 |
qdev_init_gpio_in(&s->qdev, ssi_cs_default, 1);
|
64 |
} |
65 |
|
66 |
return ssc->init(s);
|
67 |
} |
68 |
|
69 |
static void ssi_slave_class_init(ObjectClass *klass, void *data) |
70 |
{ |
71 |
SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass); |
72 |
DeviceClass *dc = DEVICE_CLASS(klass); |
73 |
|
74 |
dc->init = ssi_slave_init; |
75 |
dc->bus_type = TYPE_SSI_BUS; |
76 |
if (!ssc->transfer_raw) {
|
77 |
ssc->transfer_raw = ssi_transfer_raw_default; |
78 |
} |
79 |
} |
80 |
|
81 |
static const TypeInfo ssi_slave_info = { |
82 |
.name = TYPE_SSI_SLAVE, |
83 |
.parent = TYPE_DEVICE, |
84 |
.class_init = ssi_slave_class_init, |
85 |
.class_size = sizeof(SSISlaveClass),
|
86 |
.abstract = true,
|
87 |
}; |
88 |
|
89 |
DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name) |
90 |
{ |
91 |
return qdev_create(&bus->qbus, name);
|
92 |
} |
93 |
|
94 |
DeviceState *ssi_create_slave(SSIBus *bus, const char *name) |
95 |
{ |
96 |
DeviceState *dev = ssi_create_slave_no_init(bus, name); |
97 |
|
98 |
qdev_init_nofail(dev); |
99 |
return dev;
|
100 |
} |
101 |
|
102 |
SSIBus *ssi_create_bus(DeviceState *parent, const char *name) |
103 |
{ |
104 |
BusState *bus; |
105 |
bus = qbus_create(TYPE_SSI_BUS, parent, name); |
106 |
return FROM_QBUS(SSIBus, bus);
|
107 |
} |
108 |
|
109 |
uint32_t ssi_transfer(SSIBus *bus, uint32_t val) |
110 |
{ |
111 |
BusChild *kid; |
112 |
SSISlaveClass *ssc; |
113 |
uint32_t r = 0;
|
114 |
|
115 |
QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { |
116 |
SSISlave *slave = SSI_SLAVE(kid->child); |
117 |
ssc = SSI_SLAVE_GET_CLASS(slave); |
118 |
r |= ssc->transfer_raw(slave, val); |
119 |
} |
120 |
|
121 |
return r;
|
122 |
} |
123 |
|
124 |
const VMStateDescription vmstate_ssi_slave = {
|
125 |
.name = "SSISlave",
|
126 |
.version_id = 1,
|
127 |
.minimum_version_id = 1,
|
128 |
.minimum_version_id_old = 1,
|
129 |
.fields = (VMStateField[]) { |
130 |
VMSTATE_BOOL(cs, SSISlave), |
131 |
VMSTATE_END_OF_LIST() |
132 |
} |
133 |
}; |
134 |
|
135 |
static void ssi_slave_register_types(void) |
136 |
{ |
137 |
type_register_static(&ssi_bus_info); |
138 |
type_register_static(&ssi_slave_info); |
139 |
} |
140 |
|
141 |
type_init(ssi_slave_register_types) |
142 |
|
143 |
typedef struct SSIAutoConnectArg { |
144 |
qemu_irq **cs_linep; |
145 |
SSIBus *bus; |
146 |
} SSIAutoConnectArg; |
147 |
|
148 |
static int ssi_auto_connect_slave(Object *child, void *opaque) |
149 |
{ |
150 |
SSIAutoConnectArg *arg = opaque; |
151 |
SSISlave *dev = (SSISlave *)object_dynamic_cast(child, TYPE_SSI_SLAVE); |
152 |
qemu_irq cs_line; |
153 |
|
154 |
if (!dev) {
|
155 |
return 0; |
156 |
} |
157 |
|
158 |
cs_line = qdev_get_gpio_in(DEVICE(dev), 0);
|
159 |
qdev_set_parent_bus(DEVICE(dev), &arg->bus->qbus); |
160 |
**arg->cs_linep = cs_line; |
161 |
(*arg->cs_linep)++; |
162 |
return 0; |
163 |
} |
164 |
|
165 |
void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_line,
|
166 |
SSIBus *bus) |
167 |
{ |
168 |
SSIAutoConnectArg arg = { |
169 |
.cs_linep = &cs_line, |
170 |
.bus = bus |
171 |
}; |
172 |
|
173 |
object_child_foreach(OBJECT(parent), ssi_auto_connect_slave, &arg); |
174 |
} |