]>
Commit | Line | Data |
---|---|---|
aa406e8b JC |
1 | /* |
2 | * CTU CAN FD PCI device emulation | |
3 | * http://canbus.pages.fel.cvut.cz/ | |
4 | * | |
5 | * Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.com) | |
6 | * | |
7 | * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by | |
8 | * Jin Yang and Pavel Pisa | |
9 | * | |
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
11 | * of this software and associated documentation files (the "Software"), to deal | |
12 | * in the Software without restriction, including without limitation the rights | |
13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
14 | * copies of the Software, and to permit persons to whom the Software is | |
15 | * furnished to do so, subject to the following conditions: | |
16 | * | |
17 | * The above copyright notice and this permission notice shall be included in | |
18 | * all copies or substantial portions of the Software. | |
19 | * | |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
23 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
26 | * THE SOFTWARE. | |
27 | */ | |
28 | ||
29 | #include "qemu/osdep.h" | |
30 | #include "qemu/event_notifier.h" | |
31 | #include "qemu/module.h" | |
32 | #include "qemu/thread.h" | |
33 | #include "qemu/sockets.h" | |
34 | #include "qapi/error.h" | |
35 | #include "chardev/char.h" | |
36 | #include "hw/irq.h" | |
edf5ca5d | 37 | #include "hw/pci/pci_device.h" |
aa406e8b JC |
38 | #include "hw/qdev-properties.h" |
39 | #include "migration/vmstate.h" | |
40 | #include "net/can_emu.h" | |
41 | ||
42 | #include "ctucan_core.h" | |
43 | ||
44 | #define TYPE_CTUCAN_PCI_DEV "ctucan_pci" | |
45 | ||
46 | typedef struct CtuCanPCIState CtuCanPCIState; | |
47 | DECLARE_INSTANCE_CHECKER(CtuCanPCIState, CTUCAN_PCI_DEV, | |
48 | TYPE_CTUCAN_PCI_DEV) | |
49 | ||
50 | #define CTUCAN_PCI_CORE_COUNT 2 | |
51 | #define CTUCAN_PCI_CORE_RANGE 0x10000 | |
52 | ||
53 | #define CTUCAN_PCI_BAR_COUNT 2 | |
54 | ||
55 | #define CTUCAN_PCI_BYTES_PER_CORE 0x4000 | |
56 | ||
57 | #ifndef PCI_VENDOR_ID_TEDIA | |
58 | #define PCI_VENDOR_ID_TEDIA 0x1760 | |
59 | #endif | |
60 | ||
61 | #define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0xff00 | |
62 | ||
63 | #define CTUCAN_BAR0_RANGE 0x8000 | |
64 | #define CTUCAN_BAR0_CTUCAN_ID 0x0000 | |
65 | #define CTUCAN_BAR0_CRA_BASE 0x4000 | |
66 | #define CYCLONE_IV_CRA_A2P_IE (0x0050) | |
67 | ||
68 | #define CTUCAN_WITHOUT_CTUCAN_ID 0 | |
69 | #define CTUCAN_WITH_CTUCAN_ID 1 | |
70 | ||
71 | struct CtuCanPCIState { | |
72 | /*< private >*/ | |
73 | PCIDevice dev; | |
74 | /*< public >*/ | |
75 | MemoryRegion ctucan_io[CTUCAN_PCI_BAR_COUNT]; | |
76 | ||
77 | CtuCanCoreState ctucan_state[CTUCAN_PCI_CORE_COUNT]; | |
78 | qemu_irq irq; | |
79 | ||
80 | char *model; /* The model that support, only SJA1000 now. */ | |
81 | CanBusState *canbus[CTUCAN_PCI_CORE_COUNT]; | |
82 | }; | |
83 | ||
84 | static void ctucan_pci_reset(DeviceState *dev) | |
85 | { | |
86 | CtuCanPCIState *d = CTUCAN_PCI_DEV(dev); | |
87 | int i; | |
88 | ||
89 | for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) { | |
90 | ctucan_hardware_reset(&d->ctucan_state[i]); | |
91 | } | |
92 | } | |
93 | ||
94 | static uint64_t ctucan_pci_id_cra_io_read(void *opaque, hwaddr addr, | |
95 | unsigned size) | |
96 | { | |
97 | if (addr >= 4) { | |
98 | return 0; | |
99 | } | |
100 | ||
101 | uint64_t tmp = 0xC0000000 + CTUCAN_PCI_CORE_COUNT; | |
102 | tmp >>= ((addr & 3) << 3); | |
103 | if (size < 8) { | |
104 | tmp &= ((uint64_t)1 << (size << 3)) - 1; | |
105 | } | |
106 | return tmp; | |
107 | } | |
108 | ||
109 | static void ctucan_pci_id_cra_io_write(void *opaque, hwaddr addr, uint64_t data, | |
110 | unsigned size) | |
111 | { | |
112 | ||
113 | } | |
114 | ||
115 | static uint64_t ctucan_pci_cores_io_read(void *opaque, hwaddr addr, | |
116 | unsigned size) | |
117 | { | |
118 | CtuCanPCIState *d = opaque; | |
119 | CtuCanCoreState *s; | |
120 | hwaddr core_num = addr / CTUCAN_PCI_BYTES_PER_CORE; | |
121 | ||
122 | if (core_num >= CTUCAN_PCI_CORE_COUNT) { | |
123 | return 0; | |
124 | } | |
125 | ||
126 | s = &d->ctucan_state[core_num]; | |
127 | ||
128 | return ctucan_mem_read(s, addr % CTUCAN_PCI_BYTES_PER_CORE, size); | |
129 | } | |
130 | ||
131 | static void ctucan_pci_cores_io_write(void *opaque, hwaddr addr, uint64_t data, | |
132 | unsigned size) | |
133 | { | |
134 | CtuCanPCIState *d = opaque; | |
135 | CtuCanCoreState *s; | |
136 | hwaddr core_num = addr / CTUCAN_PCI_BYTES_PER_CORE; | |
137 | ||
138 | if (core_num >= CTUCAN_PCI_CORE_COUNT) { | |
139 | return; | |
140 | } | |
141 | ||
142 | s = &d->ctucan_state[core_num]; | |
143 | ||
144 | return ctucan_mem_write(s, addr % CTUCAN_PCI_BYTES_PER_CORE, data, size); | |
145 | } | |
146 | ||
147 | static const MemoryRegionOps ctucan_pci_id_cra_io_ops = { | |
148 | .read = ctucan_pci_id_cra_io_read, | |
149 | .write = ctucan_pci_id_cra_io_write, | |
150 | .endianness = DEVICE_LITTLE_ENDIAN, | |
151 | .impl.min_access_size = 1, | |
152 | .impl.max_access_size = 4, | |
153 | .valid.min_access_size = 1, | |
154 | .valid.max_access_size = 4, | |
155 | }; | |
156 | ||
157 | static const MemoryRegionOps ctucan_pci_cores_io_ops = { | |
158 | .read = ctucan_pci_cores_io_read, | |
159 | .write = ctucan_pci_cores_io_write, | |
160 | .endianness = DEVICE_LITTLE_ENDIAN, | |
161 | .impl.min_access_size = 1, | |
162 | .impl.max_access_size = 4, | |
163 | .valid.min_access_size = 1, | |
164 | .valid.max_access_size = 4, | |
165 | }; | |
166 | ||
167 | static void ctucan_pci_realize(PCIDevice *pci_dev, Error **errp) | |
168 | { | |
169 | CtuCanPCIState *d = CTUCAN_PCI_DEV(pci_dev); | |
170 | uint8_t *pci_conf; | |
171 | int i; | |
172 | ||
173 | pci_conf = pci_dev->config; | |
174 | pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ | |
175 | ||
176 | d->irq = pci_allocate_irq(&d->dev); | |
177 | ||
178 | for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) { | |
179 | ctucan_init(&d->ctucan_state[i], d->irq); | |
180 | } | |
181 | ||
182 | for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) { | |
183 | if (ctucan_connect_to_bus(&d->ctucan_state[i], d->canbus[i]) < 0) { | |
184 | error_setg(errp, "ctucan_connect_to_bus failed"); | |
185 | return; | |
186 | } | |
187 | } | |
188 | ||
189 | memory_region_init_io(&d->ctucan_io[0], OBJECT(d), | |
190 | &ctucan_pci_id_cra_io_ops, d, | |
191 | "ctucan_pci-core0", CTUCAN_BAR0_RANGE); | |
192 | memory_region_init_io(&d->ctucan_io[1], OBJECT(d), | |
193 | &ctucan_pci_cores_io_ops, d, | |
194 | "ctucan_pci-core1", CTUCAN_PCI_CORE_RANGE); | |
195 | ||
196 | for (i = 0 ; i < CTUCAN_PCI_BAR_COUNT; i++) { | |
197 | pci_register_bar(&d->dev, i, PCI_BASE_ADDRESS_MEM_MASK & 0, | |
198 | &d->ctucan_io[i]); | |
199 | } | |
200 | } | |
201 | ||
202 | static void ctucan_pci_exit(PCIDevice *pci_dev) | |
203 | { | |
204 | CtuCanPCIState *d = CTUCAN_PCI_DEV(pci_dev); | |
205 | int i; | |
206 | ||
207 | for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) { | |
208 | ctucan_disconnect(&d->ctucan_state[i]); | |
209 | } | |
210 | ||
211 | qemu_free_irq(d->irq); | |
212 | } | |
213 | ||
214 | static const VMStateDescription vmstate_ctucan_pci = { | |
215 | .name = "ctucan_pci", | |
216 | .version_id = 1, | |
217 | .minimum_version_id = 1, | |
1de81b42 | 218 | .fields = (const VMStateField[]) { |
aa406e8b JC |
219 | VMSTATE_PCI_DEVICE(dev, CtuCanPCIState), |
220 | VMSTATE_STRUCT(ctucan_state[0], CtuCanPCIState, 0, vmstate_ctucan, | |
221 | CtuCanCoreState), | |
222 | #if CTUCAN_PCI_CORE_COUNT >= 2 | |
223 | VMSTATE_STRUCT(ctucan_state[1], CtuCanPCIState, 0, vmstate_ctucan, | |
224 | CtuCanCoreState), | |
225 | #endif | |
226 | VMSTATE_END_OF_LIST() | |
227 | } | |
228 | }; | |
229 | ||
230 | static void ctucan_pci_instance_init(Object *obj) | |
231 | { | |
232 | CtuCanPCIState *d = CTUCAN_PCI_DEV(obj); | |
233 | ||
234 | object_property_add_link(obj, "canbus0", TYPE_CAN_BUS, | |
235 | (Object **)&d->canbus[0], | |
236 | qdev_prop_allow_set_link_before_realize, 0); | |
237 | #if CTUCAN_PCI_CORE_COUNT >= 2 | |
238 | object_property_add_link(obj, "canbus1", TYPE_CAN_BUS, | |
239 | (Object **)&d->canbus[1], | |
240 | qdev_prop_allow_set_link_before_realize, 0); | |
241 | #endif | |
242 | } | |
243 | ||
244 | static void ctucan_pci_class_init(ObjectClass *klass, void *data) | |
245 | { | |
246 | DeviceClass *dc = DEVICE_CLASS(klass); | |
247 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
248 | ||
249 | k->realize = ctucan_pci_realize; | |
250 | k->exit = ctucan_pci_exit; | |
251 | k->vendor_id = PCI_VENDOR_ID_TEDIA; | |
252 | k->device_id = PCI_DEVICE_ID_TEDIA_CTUCAN_VER21; | |
253 | k->revision = 0x00; | |
254 | k->class_id = 0x000c09; | |
255 | k->subsystem_vendor_id = PCI_VENDOR_ID_TEDIA; | |
256 | k->subsystem_id = PCI_DEVICE_ID_TEDIA_CTUCAN_VER21; | |
257 | dc->desc = "CTU CAN PCI"; | |
258 | dc->vmsd = &vmstate_ctucan_pci; | |
259 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); | |
260 | dc->reset = ctucan_pci_reset; | |
261 | } | |
262 | ||
263 | static const TypeInfo ctucan_pci_info = { | |
264 | .name = TYPE_CTUCAN_PCI_DEV, | |
265 | .parent = TYPE_PCI_DEVICE, | |
266 | .instance_size = sizeof(CtuCanPCIState), | |
267 | .class_init = ctucan_pci_class_init, | |
268 | .instance_init = ctucan_pci_instance_init, | |
269 | .interfaces = (InterfaceInfo[]) { | |
270 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, | |
271 | { }, | |
272 | }, | |
273 | }; | |
274 | ||
275 | static void ctucan_pci_register_types(void) | |
276 | { | |
277 | type_register_static(&ctucan_pci_info); | |
278 | } | |
279 | ||
280 | type_init(ctucan_pci_register_types) |