xref: /openbmc/qemu/hw/i3c/aspeed_i3c.c (revision 4abac3abc9f23a48056c4a77d9cf09b03431c365)
1 /*
2  * ASPEED I3C Controller
3  *
4  * Copyright (C) 2021 ASPEED Technology Inc.
5  * Copyright (C) 2025 Google, LLC.
6  *
7  * This code is licensed under the GPL version 2 or later.  See
8  * the COPYING file in the top-level directory.
9  */
10 
11 #include "qemu/osdep.h"
12 #include "qemu/log.h"
13 #include "qemu/error-report.h"
14 #include "hw/i3c/aspeed_i3c.h"
15 #include "hw/registerfields.h"
16 #include "hw/qdev-properties.h"
17 #include "qapi/error.h"
18 #include "migration/vmstate.h"
19 #include "trace.h"
20 
21 /* I3C Controller Registers */
22 REG32(I3C1_REG0, 0x10)
23 REG32(I3C1_REG1, 0x14)
24     FIELD(I3C1_REG1, I2C_MODE,  0,  1)
25     FIELD(I3C1_REG1, SA_EN,     15, 1)
26 REG32(I3C2_REG0, 0x20)
27 REG32(I3C2_REG1, 0x24)
28     FIELD(I3C2_REG1, I2C_MODE,  0,  1)
29     FIELD(I3C2_REG1, SA_EN,     15, 1)
30 REG32(I3C3_REG0, 0x30)
31 REG32(I3C3_REG1, 0x34)
32     FIELD(I3C3_REG1, I2C_MODE,  0,  1)
33     FIELD(I3C3_REG1, SA_EN,     15, 1)
34 REG32(I3C4_REG0, 0x40)
35 REG32(I3C4_REG1, 0x44)
36     FIELD(I3C4_REG1, I2C_MODE,  0,  1)
37     FIELD(I3C4_REG1, SA_EN,     15, 1)
38 REG32(I3C5_REG0, 0x50)
39 REG32(I3C5_REG1, 0x54)
40     FIELD(I3C5_REG1, I2C_MODE,  0,  1)
41     FIELD(I3C5_REG1, SA_EN,     15, 1)
42 REG32(I3C6_REG0, 0x60)
43 REG32(I3C6_REG1, 0x64)
44     FIELD(I3C6_REG1, I2C_MODE,  0,  1)
45     FIELD(I3C6_REG1, SA_EN,     15, 1)
46 
47 static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size)
48 {
49     AspeedI3CState *s = ASPEED_I3C(opaque);
50     uint64_t val = 0;
51 
52     val = s->regs[addr >> 2];
53 
54     trace_aspeed_i3c_read(addr, val);
55 
56     return val;
57 }
58 
59 static void aspeed_i3c_write(void *opaque,
60                              hwaddr addr,
61                              uint64_t data,
62                              unsigned int size)
63 {
64     AspeedI3CState *s = ASPEED_I3C(opaque);
65 
66     trace_aspeed_i3c_write(addr, data);
67 
68     addr >>= 2;
69 
70     /* I3C controller register */
71     switch (addr) {
72     case R_I3C1_REG1:
73     case R_I3C2_REG1:
74     case R_I3C3_REG1:
75     case R_I3C4_REG1:
76     case R_I3C5_REG1:
77     case R_I3C6_REG1:
78         if (data & R_I3C1_REG1_I2C_MODE_MASK) {
79             qemu_log_mask(LOG_UNIMP,
80                           "%s: Unsupported I2C mode [0x%08" HWADDR_PRIx
81                           "]=%08" PRIx64 "\n",
82                           __func__, addr << 2, data);
83             break;
84         }
85         if (data & R_I3C1_REG1_SA_EN_MASK) {
86             qemu_log_mask(LOG_UNIMP,
87                           "%s: Unsupported slave mode [%08" HWADDR_PRIx
88                           "]=0x%08" PRIx64 "\n",
89                           __func__, addr << 2, data);
90             break;
91         }
92         s->regs[addr] = data;
93         break;
94     default:
95         s->regs[addr] = data;
96         break;
97     }
98 }
99 
100 static const MemoryRegionOps aspeed_i3c_ops = {
101     .read = aspeed_i3c_read,
102     .write = aspeed_i3c_write,
103     .endianness = DEVICE_LITTLE_ENDIAN,
104     .valid = {
105         .min_access_size = 1,
106         .max_access_size = 4,
107     }
108 };
109 
110 static void aspeed_i3c_reset(DeviceState *dev)
111 {
112     AspeedI3CState *s = ASPEED_I3C(dev);
113     memset(s->regs, 0, sizeof(s->regs));
114 }
115 
116 static void aspeed_i3c_instance_init(Object *obj)
117 {
118     AspeedI3CState *s = ASPEED_I3C(obj);
119     int i;
120 
121     for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
122         object_initialize_child(obj, "device[*]", &s->devices[i],
123                 TYPE_DW_I3C);
124     }
125 }
126 
127 static void aspeed_i3c_realize(DeviceState *dev, Error **errp)
128 {
129     int i;
130     AspeedI3CState *s = ASPEED_I3C(dev);
131     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
132 
133     memory_region_init(&s->iomem_container, OBJECT(s),
134             TYPE_ASPEED_I3C ".container", 0x8000);
135 
136     sysbus_init_mmio(sbd, &s->iomem_container);
137 
138     memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i3c_ops, s,
139             TYPE_ASPEED_I3C ".regs", ASPEED_I3C_NR_REGS << 2);
140 
141     memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem);
142 
143     for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
144         Object *i3c_dev = OBJECT(&s->devices[i]);
145 
146         if (!object_property_set_uint(i3c_dev, "device-id", i, errp)) {
147             return;
148         }
149 
150         if (!sysbus_realize(SYS_BUS_DEVICE(i3c_dev), errp)) {
151             return;
152         }
153 
154         /*
155          * Register Address of I3CX Device =
156          *     (Base Address of Global Register) + (Offset of I3CX) + Offset
157          * X = 0, 1, 2, 3, 4, 5
158          * Offset of I3C0 = 0x2000
159          * Offset of I3C1 = 0x3000
160          * Offset of I3C2 = 0x4000
161          * Offset of I3C3 = 0x5000
162          * Offset of I3C4 = 0x6000
163          * Offset of I3C5 = 0x7000
164          */
165         memory_region_add_subregion(&s->iomem_container,
166                 0x2000 + i * 0x1000, &s->devices[i].mr);
167     }
168 
169 }
170 
171 static const VMStateDescription vmstate_aspeed_i3c = {
172     .name = TYPE_ASPEED_I3C,
173     .version_id = 1,
174     .minimum_version_id = 1,
175     .fields = (const VMStateField[]) {
176         VMSTATE_UINT32_ARRAY(regs, AspeedI3CState, ASPEED_I3C_NR_REGS),
177         VMSTATE_STRUCT_ARRAY(devices, AspeedI3CState, ASPEED_I3C_NR_DEVICES, 1,
178                              vmstate_dw_i3c, DWI3C),
179         VMSTATE_END_OF_LIST(),
180     }
181 };
182 
183 static void aspeed_i3c_class_init(ObjectClass *klass, const void *data)
184 {
185     DeviceClass *dc = DEVICE_CLASS(klass);
186 
187     dc->realize = aspeed_i3c_realize;
188     device_class_set_legacy_reset(dc, aspeed_i3c_reset);
189     dc->desc = "Aspeed I3C Controller";
190     dc->vmsd = &vmstate_aspeed_i3c;
191 }
192 
193 static const TypeInfo aspeed_i3c_info = {
194     .name = TYPE_ASPEED_I3C,
195     .parent = TYPE_SYS_BUS_DEVICE,
196     .instance_init = aspeed_i3c_instance_init,
197     .instance_size = sizeof(AspeedI3CState),
198     .class_init = aspeed_i3c_class_init,
199 };
200 
201 static void aspeed_i3c_register_types(void)
202 {
203     type_register_static(&aspeed_i3c_info);
204 }
205 
206 type_init(aspeed_i3c_register_types);
207