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/module.h"
31 #include "qapi/error.h"
32 #include "hw/irq.h"
33 #include "hw/pci/pci_device.h"
34 #include "hw/qdev-properties.h"
35 #include "migration/vmstate.h"
36 #include "net/can_emu.h"
37
38 #include "ctucan_core.h"
39
40 #define TYPE_CTUCAN_PCI_DEV "ctucan_pci"
41
42 typedef struct CtuCanPCIState CtuCanPCIState;
43 DECLARE_INSTANCE_CHECKER(CtuCanPCIState, CTUCAN_PCI_DEV,
44 TYPE_CTUCAN_PCI_DEV)
45
46 #define CTUCAN_PCI_CORE_COUNT 2
47 #define CTUCAN_PCI_CORE_RANGE 0x10000
48
49 #define CTUCAN_PCI_BAR_COUNT 2
50
51 #define CTUCAN_PCI_BYTES_PER_CORE 0x4000
52
53 #ifndef PCI_VENDOR_ID_TEDIA
54 #define PCI_VENDOR_ID_TEDIA 0x1760
55 #endif
56
57 #define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0xff00
58
59 #define CTUCAN_BAR0_RANGE 0x8000
60 #define CTUCAN_BAR0_CTUCAN_ID 0x0000
61 #define CTUCAN_BAR0_CRA_BASE 0x4000
62 #define CYCLONE_IV_CRA_A2P_IE (0x0050)
63
64 #define CTUCAN_WITHOUT_CTUCAN_ID 0
65 #define CTUCAN_WITH_CTUCAN_ID 1
66
67 struct CtuCanPCIState {
68 /*< private >*/
69 PCIDevice dev;
70 /*< public >*/
71 MemoryRegion ctucan_io[CTUCAN_PCI_BAR_COUNT];
72
73 CtuCanCoreState ctucan_state[CTUCAN_PCI_CORE_COUNT];
74 qemu_irq irq;
75
76 char *model; /* The model that support, only SJA1000 now. */
77 CanBusState *canbus[CTUCAN_PCI_CORE_COUNT];
78 };
79
ctucan_pci_reset(DeviceState * dev)80 static void ctucan_pci_reset(DeviceState *dev)
81 {
82 CtuCanPCIState *d = CTUCAN_PCI_DEV(dev);
83 int i;
84
85 for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
86 ctucan_hardware_reset(&d->ctucan_state[i]);
87 }
88 }
89
ctucan_pci_id_cra_io_read(void * opaque,hwaddr addr,unsigned size)90 static uint64_t ctucan_pci_id_cra_io_read(void *opaque, hwaddr addr,
91 unsigned size)
92 {
93 if (addr >= 4) {
94 return 0;
95 }
96
97 uint64_t tmp = 0xC0000000 + CTUCAN_PCI_CORE_COUNT;
98 tmp >>= ((addr & 3) << 3);
99 if (size < 8) {
100 tmp &= ((uint64_t)1 << (size << 3)) - 1;
101 }
102 return tmp;
103 }
104
ctucan_pci_id_cra_io_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)105 static void ctucan_pci_id_cra_io_write(void *opaque, hwaddr addr, uint64_t data,
106 unsigned size)
107 {
108
109 }
110
ctucan_pci_cores_io_read(void * opaque,hwaddr addr,unsigned size)111 static uint64_t ctucan_pci_cores_io_read(void *opaque, hwaddr addr,
112 unsigned size)
113 {
114 CtuCanPCIState *d = opaque;
115 CtuCanCoreState *s;
116 hwaddr core_num = addr / CTUCAN_PCI_BYTES_PER_CORE;
117
118 if (core_num >= CTUCAN_PCI_CORE_COUNT) {
119 return 0;
120 }
121
122 s = &d->ctucan_state[core_num];
123
124 return ctucan_mem_read(s, addr % CTUCAN_PCI_BYTES_PER_CORE, size);
125 }
126
ctucan_pci_cores_io_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)127 static void ctucan_pci_cores_io_write(void *opaque, hwaddr addr, uint64_t data,
128 unsigned size)
129 {
130 CtuCanPCIState *d = opaque;
131 CtuCanCoreState *s;
132 hwaddr core_num = addr / CTUCAN_PCI_BYTES_PER_CORE;
133
134 if (core_num >= CTUCAN_PCI_CORE_COUNT) {
135 return;
136 }
137
138 s = &d->ctucan_state[core_num];
139
140 return ctucan_mem_write(s, addr % CTUCAN_PCI_BYTES_PER_CORE, data, size);
141 }
142
143 static const MemoryRegionOps ctucan_pci_id_cra_io_ops = {
144 .read = ctucan_pci_id_cra_io_read,
145 .write = ctucan_pci_id_cra_io_write,
146 .endianness = DEVICE_LITTLE_ENDIAN,
147 .impl.min_access_size = 1,
148 .impl.max_access_size = 4,
149 .valid.min_access_size = 1,
150 .valid.max_access_size = 4,
151 };
152
153 static const MemoryRegionOps ctucan_pci_cores_io_ops = {
154 .read = ctucan_pci_cores_io_read,
155 .write = ctucan_pci_cores_io_write,
156 .endianness = DEVICE_LITTLE_ENDIAN,
157 .impl.min_access_size = 1,
158 .impl.max_access_size = 4,
159 .valid.min_access_size = 1,
160 .valid.max_access_size = 4,
161 };
162
ctucan_pci_realize(PCIDevice * pci_dev,Error ** errp)163 static void ctucan_pci_realize(PCIDevice *pci_dev, Error **errp)
164 {
165 CtuCanPCIState *d = CTUCAN_PCI_DEV(pci_dev);
166 uint8_t *pci_conf;
167 int i;
168
169 pci_conf = pci_dev->config;
170 pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
171
172 d->irq = pci_allocate_irq(&d->dev);
173
174 for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
175 ctucan_init(&d->ctucan_state[i], d->irq);
176 }
177
178 for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
179 if (ctucan_connect_to_bus(&d->ctucan_state[i], d->canbus[i]) < 0) {
180 error_setg(errp, "ctucan_connect_to_bus failed");
181 return;
182 }
183 }
184
185 memory_region_init_io(&d->ctucan_io[0], OBJECT(d),
186 &ctucan_pci_id_cra_io_ops, d,
187 "ctucan_pci-core0", CTUCAN_BAR0_RANGE);
188 memory_region_init_io(&d->ctucan_io[1], OBJECT(d),
189 &ctucan_pci_cores_io_ops, d,
190 "ctucan_pci-core1", CTUCAN_PCI_CORE_RANGE);
191
192 for (i = 0 ; i < CTUCAN_PCI_BAR_COUNT; i++) {
193 pci_register_bar(&d->dev, i, PCI_BASE_ADDRESS_MEM_MASK & 0,
194 &d->ctucan_io[i]);
195 }
196 }
197
ctucan_pci_exit(PCIDevice * pci_dev)198 static void ctucan_pci_exit(PCIDevice *pci_dev)
199 {
200 CtuCanPCIState *d = CTUCAN_PCI_DEV(pci_dev);
201 int i;
202
203 for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
204 ctucan_disconnect(&d->ctucan_state[i]);
205 }
206
207 qemu_free_irq(d->irq);
208 }
209
210 static const VMStateDescription vmstate_ctucan_pci = {
211 .name = "ctucan_pci",
212 .version_id = 1,
213 .minimum_version_id = 1,
214 .fields = (const VMStateField[]) {
215 VMSTATE_PCI_DEVICE(dev, CtuCanPCIState),
216 VMSTATE_STRUCT(ctucan_state[0], CtuCanPCIState, 0, vmstate_ctucan,
217 CtuCanCoreState),
218 #if CTUCAN_PCI_CORE_COUNT >= 2
219 VMSTATE_STRUCT(ctucan_state[1], CtuCanPCIState, 0, vmstate_ctucan,
220 CtuCanCoreState),
221 #endif
222 VMSTATE_END_OF_LIST()
223 }
224 };
225
ctucan_pci_instance_init(Object * obj)226 static void ctucan_pci_instance_init(Object *obj)
227 {
228 CtuCanPCIState *d = CTUCAN_PCI_DEV(obj);
229
230 object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
231 (Object **)&d->canbus[0],
232 qdev_prop_allow_set_link_before_realize, 0);
233 #if CTUCAN_PCI_CORE_COUNT >= 2
234 object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
235 (Object **)&d->canbus[1],
236 qdev_prop_allow_set_link_before_realize, 0);
237 #endif
238 }
239
ctucan_pci_class_init(ObjectClass * klass,const void * data)240 static void ctucan_pci_class_init(ObjectClass *klass, const void *data)
241 {
242 DeviceClass *dc = DEVICE_CLASS(klass);
243 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
244
245 k->realize = ctucan_pci_realize;
246 k->exit = ctucan_pci_exit;
247 k->vendor_id = PCI_VENDOR_ID_TEDIA;
248 k->device_id = PCI_DEVICE_ID_TEDIA_CTUCAN_VER21;
249 k->revision = 0x00;
250 k->class_id = 0x000c09;
251 k->subsystem_vendor_id = PCI_VENDOR_ID_TEDIA;
252 k->subsystem_id = PCI_DEVICE_ID_TEDIA_CTUCAN_VER21;
253 dc->desc = "CTU CAN PCI";
254 dc->vmsd = &vmstate_ctucan_pci;
255 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
256 device_class_set_legacy_reset(dc, ctucan_pci_reset);
257 }
258
259 static const TypeInfo ctucan_pci_info = {
260 .name = TYPE_CTUCAN_PCI_DEV,
261 .parent = TYPE_PCI_DEVICE,
262 .instance_size = sizeof(CtuCanPCIState),
263 .class_init = ctucan_pci_class_init,
264 .instance_init = ctucan_pci_instance_init,
265 .interfaces = (const InterfaceInfo[]) {
266 { INTERFACE_CONVENTIONAL_PCI_DEVICE },
267 { },
268 },
269 };
270
ctucan_pci_register_types(void)271 static void ctucan_pci_register_types(void)
272 {
273 type_register_static(&ctucan_pci_info);
274 }
275
276 type_init(ctucan_pci_register_types)
277