2 * ASPEED LPC Controller
4 * Copyright (C) 2017-2018 IBM Corp.
6 * This code is licensed under the GPL version 2 or later. See
7 * the COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
12 #include "qemu/error-report.h"
13 #include "hw/misc/aspeed_lpc.h"
14 #include "qapi/error.h"
15 #include "qapi/visitor.h"
17 #include "hw/qdev-properties.h"
18 #include "migration/vmstate.h"
20 #define TO_REG(offset) ((offset) >> 2)
22 #define HICR0 TO_REG(0x00)
23 #define HICR0_LPC3E BIT(7)
24 #define HICR0_LPC2E BIT(6)
25 #define HICR0_LPC1E BIT(5)
26 #define HICR1 TO_REG(0x04)
27 #define HICR2 TO_REG(0x08)
28 #define HICR2_IBFIE3 BIT(3)
29 #define HICR2_IBFIE2 BIT(2)
30 #define HICR2_IBFIE1 BIT(1)
31 #define HICR3 TO_REG(0x0C)
32 #define HICR4 TO_REG(0x10)
33 #define HICR4_KCSENBL BIT(2)
34 #define IDR1 TO_REG(0x24)
35 #define IDR2 TO_REG(0x28)
36 #define IDR3 TO_REG(0x2C)
37 #define ODR1 TO_REG(0x30)
38 #define ODR2 TO_REG(0x34)
39 #define ODR3 TO_REG(0x38)
40 #define STR1 TO_REG(0x3C)
41 #define STR_OBF BIT(0)
42 #define STR_IBF BIT(1)
43 #define STR_CMD_DATA BIT(3)
44 #define STR2 TO_REG(0x40)
45 #define STR3 TO_REG(0x44)
46 #define HICR5 TO_REG(0x80)
47 #define HICR6 TO_REG(0x84)
48 #define HICR7 TO_REG(0x88)
49 #define HICR8 TO_REG(0x8C)
50 #define HICRB TO_REG(0x100)
51 #define HICRB_IBFIE4 BIT(1)
52 #define HICRB_LPC4E BIT(0)
53 #define IDR4 TO_REG(0x114)
54 #define ODR4 TO_REG(0x118)
55 #define STR4 TO_REG(0x11C)
57 enum aspeed_kcs_channel_id
{
64 static const enum aspeed_lpc_subdevice aspeed_kcs_subdevice_map
[] = {
65 [kcs_channel_1
] = aspeed_lpc_kcs_1
,
66 [kcs_channel_2
] = aspeed_lpc_kcs_2
,
67 [kcs_channel_3
] = aspeed_lpc_kcs_3
,
68 [kcs_channel_4
] = aspeed_lpc_kcs_4
,
71 struct aspeed_kcs_channel
{
72 enum aspeed_kcs_channel_id id
;
79 static const struct aspeed_kcs_channel aspeed_kcs_channel_map
[] = {
109 struct aspeed_kcs_register_data
{
112 const struct aspeed_kcs_channel
*chan
;
115 static const struct aspeed_kcs_register_data aspeed_kcs_registers
[] = {
119 .chan
= &aspeed_kcs_channel_map
[kcs_channel_1
],
124 .chan
= &aspeed_kcs_channel_map
[kcs_channel_1
],
129 .chan
= &aspeed_kcs_channel_map
[kcs_channel_1
],
134 .chan
= &aspeed_kcs_channel_map
[kcs_channel_2
],
139 .chan
= &aspeed_kcs_channel_map
[kcs_channel_2
],
144 .chan
= &aspeed_kcs_channel_map
[kcs_channel_2
],
149 .chan
= &aspeed_kcs_channel_map
[kcs_channel_3
],
154 .chan
= &aspeed_kcs_channel_map
[kcs_channel_3
],
159 .chan
= &aspeed_kcs_channel_map
[kcs_channel_3
],
164 .chan
= &aspeed_kcs_channel_map
[kcs_channel_4
],
169 .chan
= &aspeed_kcs_channel_map
[kcs_channel_4
],
174 .chan
= &aspeed_kcs_channel_map
[kcs_channel_4
],
179 static const struct aspeed_kcs_register_data
*
180 aspeed_kcs_get_register_data_by_name(const char *name
)
182 const struct aspeed_kcs_register_data
*pos
= aspeed_kcs_registers
;
185 if (!strcmp(pos
->name
, name
)) {
194 static const struct aspeed_kcs_channel
*
195 aspeed_kcs_get_channel_by_register(int reg
)
197 const struct aspeed_kcs_register_data
*pos
= aspeed_kcs_registers
;
200 if (pos
->reg
== reg
) {
209 static void aspeed_kcs_get_register_property(Object
*obj
,
215 const struct aspeed_kcs_register_data
*data
;
216 AspeedLPCState
*s
= ASPEED_LPC(obj
);
219 data
= aspeed_kcs_get_register_data_by_name(name
);
224 if (!strncmp("odr", name
, 3)) {
225 s
->regs
[data
->chan
->str
] &= ~STR_OBF
;
228 val
= s
->regs
[data
->reg
];
230 visit_type_uint32(v
, name
, &val
, errp
);
233 static bool aspeed_kcs_channel_enabled(AspeedLPCState
*s
,
234 const struct aspeed_kcs_channel
*channel
)
236 switch (channel
->id
) {
237 case kcs_channel_1
: return s
->regs
[HICR0
] & HICR0_LPC1E
;
238 case kcs_channel_2
: return s
->regs
[HICR0
] & HICR0_LPC2E
;
240 return (s
->regs
[HICR0
] & HICR0_LPC3E
) &&
241 (s
->regs
[HICR4
] & HICR4_KCSENBL
);
242 case kcs_channel_4
: return s
->regs
[HICRB
] & HICRB_LPC4E
;
243 default: return false;
248 aspeed_kcs_channel_ibf_irq_enabled(AspeedLPCState
*s
,
249 const struct aspeed_kcs_channel
*channel
)
251 if (!aspeed_kcs_channel_enabled(s
, channel
)) {
255 switch (channel
->id
) {
256 case kcs_channel_1
: return s
->regs
[HICR2
] & HICR2_IBFIE1
;
257 case kcs_channel_2
: return s
->regs
[HICR2
] & HICR2_IBFIE2
;
258 case kcs_channel_3
: return s
->regs
[HICR2
] & HICR2_IBFIE3
;
259 case kcs_channel_4
: return s
->regs
[HICRB
] & HICRB_IBFIE4
;
260 default: return false;
264 static void aspeed_kcs_set_register_property(Object
*obj
,
270 const struct aspeed_kcs_register_data
*data
;
271 AspeedLPCState
*s
= ASPEED_LPC(obj
);
274 data
= aspeed_kcs_get_register_data_by_name(name
);
279 if (!visit_type_uint32(v
, name
, &val
, errp
)) {
283 if (strncmp("str", name
, 3)) {
284 s
->regs
[data
->reg
] = val
;
287 if (!strncmp("idr", name
, 3)) {
288 s
->regs
[data
->chan
->str
] |= STR_IBF
;
289 if (aspeed_kcs_channel_ibf_irq_enabled(s
, data
->chan
)) {
290 enum aspeed_lpc_subdevice subdev
;
292 subdev
= aspeed_kcs_subdevice_map
[data
->chan
->id
];
293 qemu_irq_raise(s
->subdevice_irqs
[subdev
]);
298 static void aspeed_lpc_set_irq(void *opaque
, int irq
, int level
)
300 AspeedLPCState
*s
= (AspeedLPCState
*)opaque
;
303 s
->subdevice_irqs_pending
|= BIT(irq
);
305 s
->subdevice_irqs_pending
&= ~BIT(irq
);
308 qemu_set_irq(s
->irq
, !!s
->subdevice_irqs_pending
);
311 static uint64_t aspeed_lpc_read(void *opaque
, hwaddr offset
, unsigned size
)
313 AspeedLPCState
*s
= ASPEED_LPC(opaque
);
314 int reg
= TO_REG(offset
);
316 if (reg
>= ARRAY_SIZE(s
->regs
)) {
317 qemu_log_mask(LOG_GUEST_ERROR
,
318 "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx
"\n",
329 const struct aspeed_kcs_channel
*channel
;
331 channel
= aspeed_kcs_get_channel_by_register(reg
);
332 if (s
->regs
[channel
->str
] & STR_IBF
) {
333 enum aspeed_lpc_subdevice subdev
;
335 subdev
= aspeed_kcs_subdevice_map
[channel
->id
];
336 qemu_irq_lower(s
->subdevice_irqs
[subdev
]);
339 s
->regs
[channel
->str
] &= ~STR_IBF
;
349 static void aspeed_lpc_write(void *opaque
, hwaddr offset
, uint64_t data
,
352 AspeedLPCState
*s
= ASPEED_LPC(opaque
);
353 int reg
= TO_REG(offset
);
355 if (reg
>= ARRAY_SIZE(s
->regs
)) {
356 qemu_log_mask(LOG_GUEST_ERROR
,
357 "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx
"\n",
368 s
->regs
[aspeed_kcs_get_channel_by_register(reg
)->str
] |= STR_OBF
;
377 static const MemoryRegionOps aspeed_lpc_ops
= {
378 .read
= aspeed_lpc_read
,
379 .write
= aspeed_lpc_write
,
380 .endianness
= DEVICE_LITTLE_ENDIAN
,
382 .min_access_size
= 1,
383 .max_access_size
= 4,
387 static void aspeed_lpc_reset(DeviceState
*dev
)
389 struct AspeedLPCState
*s
= ASPEED_LPC(dev
);
391 s
->subdevice_irqs_pending
= 0;
393 memset(s
->regs
, 0, sizeof(s
->regs
));
395 s
->regs
[HICR7
] = s
->hicr7
;
398 static void aspeed_lpc_realize(DeviceState
*dev
, Error
**errp
)
400 AspeedLPCState
*s
= ASPEED_LPC(dev
);
401 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
403 sysbus_init_irq(sbd
, &s
->irq
);
404 sysbus_init_irq(sbd
, &s
->subdevice_irqs
[aspeed_lpc_kcs_1
]);
405 sysbus_init_irq(sbd
, &s
->subdevice_irqs
[aspeed_lpc_kcs_2
]);
406 sysbus_init_irq(sbd
, &s
->subdevice_irqs
[aspeed_lpc_kcs_3
]);
407 sysbus_init_irq(sbd
, &s
->subdevice_irqs
[aspeed_lpc_kcs_4
]);
408 sysbus_init_irq(sbd
, &s
->subdevice_irqs
[aspeed_lpc_ibt
]);
410 memory_region_init_io(&s
->iomem
, OBJECT(s
), &aspeed_lpc_ops
, s
,
411 TYPE_ASPEED_LPC
, 0x1000);
413 sysbus_init_mmio(sbd
, &s
->iomem
);
415 qdev_init_gpio_in(dev
, aspeed_lpc_set_irq
, ASPEED_LPC_NR_SUBDEVS
);
418 static void aspeed_lpc_init(Object
*obj
)
420 object_property_add(obj
, "idr1", "uint32", aspeed_kcs_get_register_property
,
421 aspeed_kcs_set_register_property
, NULL
, NULL
);
422 object_property_add(obj
, "odr1", "uint32", aspeed_kcs_get_register_property
,
423 aspeed_kcs_set_register_property
, NULL
, NULL
);
424 object_property_add(obj
, "str1", "uint32", aspeed_kcs_get_register_property
,
425 aspeed_kcs_set_register_property
, NULL
, NULL
);
426 object_property_add(obj
, "idr2", "uint32", aspeed_kcs_get_register_property
,
427 aspeed_kcs_set_register_property
, NULL
, NULL
);
428 object_property_add(obj
, "odr2", "uint32", aspeed_kcs_get_register_property
,
429 aspeed_kcs_set_register_property
, NULL
, NULL
);
430 object_property_add(obj
, "str2", "uint32", aspeed_kcs_get_register_property
,
431 aspeed_kcs_set_register_property
, NULL
, NULL
);
432 object_property_add(obj
, "idr3", "uint32", aspeed_kcs_get_register_property
,
433 aspeed_kcs_set_register_property
, NULL
, NULL
);
434 object_property_add(obj
, "odr3", "uint32", aspeed_kcs_get_register_property
,
435 aspeed_kcs_set_register_property
, NULL
, NULL
);
436 object_property_add(obj
, "str3", "uint32", aspeed_kcs_get_register_property
,
437 aspeed_kcs_set_register_property
, NULL
, NULL
);
438 object_property_add(obj
, "idr4", "uint32", aspeed_kcs_get_register_property
,
439 aspeed_kcs_set_register_property
, NULL
, NULL
);
440 object_property_add(obj
, "odr4", "uint32", aspeed_kcs_get_register_property
,
441 aspeed_kcs_set_register_property
, NULL
, NULL
);
442 object_property_add(obj
, "str4", "uint32", aspeed_kcs_get_register_property
,
443 aspeed_kcs_set_register_property
, NULL
, NULL
);
446 static const VMStateDescription vmstate_aspeed_lpc
= {
447 .name
= TYPE_ASPEED_LPC
,
449 .minimum_version_id
= 2,
450 .fields
= (const VMStateField
[]) {
451 VMSTATE_UINT32_ARRAY(regs
, AspeedLPCState
, ASPEED_LPC_NR_REGS
),
452 VMSTATE_UINT32(subdevice_irqs_pending
, AspeedLPCState
),
453 VMSTATE_END_OF_LIST(),
457 static Property aspeed_lpc_properties
[] = {
458 DEFINE_PROP_UINT32("hicr7", AspeedLPCState
, hicr7
, 0),
459 DEFINE_PROP_END_OF_LIST(),
462 static void aspeed_lpc_class_init(ObjectClass
*klass
, void *data
)
464 DeviceClass
*dc
= DEVICE_CLASS(klass
);
466 dc
->realize
= aspeed_lpc_realize
;
467 dc
->reset
= aspeed_lpc_reset
;
468 dc
->desc
= "Aspeed LPC Controller",
469 dc
->vmsd
= &vmstate_aspeed_lpc
;
470 device_class_set_props(dc
, aspeed_lpc_properties
);
473 static const TypeInfo aspeed_lpc_info
= {
474 .name
= TYPE_ASPEED_LPC
,
475 .parent
= TYPE_SYS_BUS_DEVICE
,
476 .instance_size
= sizeof(AspeedLPCState
),
477 .class_init
= aspeed_lpc_class_init
,
478 .instance_init
= aspeed_lpc_init
,
481 static void aspeed_lpc_register_types(void)
483 type_register_static(&aspeed_lpc_info
);
486 type_init(aspeed_lpc_register_types
);