aspeed_lpc.c (13818B)
1/* 2 * ASPEED LPC Controller 3 * 4 * Copyright (C) 2017-2018 IBM Corp. 5 * 6 * This code is licensed under the GPL version 2 or later. See 7 * the COPYING file in the top-level directory. 8 */ 9 10#include "qemu/osdep.h" 11#include "qemu/log.h" 12#include "qemu/error-report.h" 13#include "hw/misc/aspeed_lpc.h" 14#include "qapi/error.h" 15#include "qapi/visitor.h" 16#include "hw/irq.h" 17#include "hw/qdev-properties.h" 18#include "migration/vmstate.h" 19 20#define TO_REG(offset) ((offset) >> 2) 21 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) 56 57enum aspeed_kcs_channel_id { 58 kcs_channel_1 = 0, 59 kcs_channel_2, 60 kcs_channel_3, 61 kcs_channel_4, 62}; 63 64static 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, 69}; 70 71struct aspeed_kcs_channel { 72 enum aspeed_kcs_channel_id id; 73 74 int idr; 75 int odr; 76 int str; 77}; 78 79static const struct aspeed_kcs_channel aspeed_kcs_channel_map[] = { 80 [kcs_channel_1] = { 81 .id = kcs_channel_1, 82 .idr = IDR1, 83 .odr = ODR1, 84 .str = STR1 85 }, 86 87 [kcs_channel_2] = { 88 .id = kcs_channel_2, 89 .idr = IDR2, 90 .odr = ODR2, 91 .str = STR2 92 }, 93 94 [kcs_channel_3] = { 95 .id = kcs_channel_3, 96 .idr = IDR3, 97 .odr = ODR3, 98 .str = STR3 99 }, 100 101 [kcs_channel_4] = { 102 .id = kcs_channel_4, 103 .idr = IDR4, 104 .odr = ODR4, 105 .str = STR4 106 }, 107}; 108 109struct aspeed_kcs_register_data { 110 const char *name; 111 int reg; 112 const struct aspeed_kcs_channel *chan; 113}; 114 115static const struct aspeed_kcs_register_data aspeed_kcs_registers[] = { 116 { 117 .name = "idr1", 118 .reg = IDR1, 119 .chan = &aspeed_kcs_channel_map[kcs_channel_1], 120 }, 121 { 122 .name = "odr1", 123 .reg = ODR1, 124 .chan = &aspeed_kcs_channel_map[kcs_channel_1], 125 }, 126 { 127 .name = "str1", 128 .reg = STR1, 129 .chan = &aspeed_kcs_channel_map[kcs_channel_1], 130 }, 131 { 132 .name = "idr2", 133 .reg = IDR2, 134 .chan = &aspeed_kcs_channel_map[kcs_channel_2], 135 }, 136 { 137 .name = "odr2", 138 .reg = ODR2, 139 .chan = &aspeed_kcs_channel_map[kcs_channel_2], 140 }, 141 { 142 .name = "str2", 143 .reg = STR2, 144 .chan = &aspeed_kcs_channel_map[kcs_channel_2], 145 }, 146 { 147 .name = "idr3", 148 .reg = IDR3, 149 .chan = &aspeed_kcs_channel_map[kcs_channel_3], 150 }, 151 { 152 .name = "odr3", 153 .reg = ODR3, 154 .chan = &aspeed_kcs_channel_map[kcs_channel_3], 155 }, 156 { 157 .name = "str3", 158 .reg = STR3, 159 .chan = &aspeed_kcs_channel_map[kcs_channel_3], 160 }, 161 { 162 .name = "idr4", 163 .reg = IDR4, 164 .chan = &aspeed_kcs_channel_map[kcs_channel_4], 165 }, 166 { 167 .name = "odr4", 168 .reg = ODR4, 169 .chan = &aspeed_kcs_channel_map[kcs_channel_4], 170 }, 171 { 172 .name = "str4", 173 .reg = STR4, 174 .chan = &aspeed_kcs_channel_map[kcs_channel_4], 175 }, 176 { }, 177}; 178 179static const struct aspeed_kcs_register_data * 180aspeed_kcs_get_register_data_by_name(const char *name) 181{ 182 const struct aspeed_kcs_register_data *pos = aspeed_kcs_registers; 183 184 while (pos->name) { 185 if (!strcmp(pos->name, name)) { 186 return pos; 187 } 188 pos++; 189 } 190 191 return NULL; 192} 193 194static const struct aspeed_kcs_channel * 195aspeed_kcs_get_channel_by_register(int reg) 196{ 197 const struct aspeed_kcs_register_data *pos = aspeed_kcs_registers; 198 199 while (pos->name) { 200 if (pos->reg == reg) { 201 return pos->chan; 202 } 203 pos++; 204 } 205 206 return NULL; 207} 208 209static void aspeed_kcs_get_register_property(Object *obj, 210 Visitor *v, 211 const char *name, 212 void *opaque, 213 Error **errp) 214{ 215 const struct aspeed_kcs_register_data *data; 216 AspeedLPCState *s = ASPEED_LPC(obj); 217 uint32_t val; 218 219 data = aspeed_kcs_get_register_data_by_name(name); 220 if (!data) { 221 return; 222 } 223 224 if (!strncmp("odr", name, 3)) { 225 s->regs[data->chan->str] &= ~STR_OBF; 226 } 227 228 val = s->regs[data->reg]; 229 230 visit_type_uint32(v, name, &val, errp); 231} 232 233static bool aspeed_kcs_channel_enabled(AspeedLPCState *s, 234 const struct aspeed_kcs_channel *channel) 235{ 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; 239 case kcs_channel_3: 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; 244 } 245} 246 247static bool 248aspeed_kcs_channel_ibf_irq_enabled(AspeedLPCState *s, 249 const struct aspeed_kcs_channel *channel) 250{ 251 if (!aspeed_kcs_channel_enabled(s, channel)) { 252 return false; 253 } 254 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; 261 } 262} 263 264static void aspeed_kcs_set_register_property(Object *obj, 265 Visitor *v, 266 const char *name, 267 void *opaque, 268 Error **errp) 269{ 270 const struct aspeed_kcs_register_data *data; 271 AspeedLPCState *s = ASPEED_LPC(obj); 272 uint32_t val; 273 274 data = aspeed_kcs_get_register_data_by_name(name); 275 if (!data) { 276 return; 277 } 278 279 if (!visit_type_uint32(v, name, &val, errp)) { 280 return; 281 } 282 283 if (strncmp("str", name, 3)) { 284 s->regs[data->reg] = val; 285 } 286 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; 291 292 subdev = aspeed_kcs_subdevice_map[data->chan->id]; 293 qemu_irq_raise(s->subdevice_irqs[subdev]); 294 } 295 } 296} 297 298static void aspeed_lpc_set_irq(void *opaque, int irq, int level) 299{ 300 AspeedLPCState *s = (AspeedLPCState *)opaque; 301 302 if (level) { 303 s->subdevice_irqs_pending |= BIT(irq); 304 } else { 305 s->subdevice_irqs_pending &= ~BIT(irq); 306 } 307 308 qemu_set_irq(s->irq, !!s->subdevice_irqs_pending); 309} 310 311static uint64_t aspeed_lpc_read(void *opaque, hwaddr offset, unsigned size) 312{ 313 AspeedLPCState *s = ASPEED_LPC(opaque); 314 int reg = TO_REG(offset); 315 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", 319 __func__, offset); 320 return 0; 321 } 322 323 switch (reg) { 324 case IDR1: 325 case IDR2: 326 case IDR3: 327 case IDR4: 328 { 329 const struct aspeed_kcs_channel *channel; 330 331 channel = aspeed_kcs_get_channel_by_register(reg); 332 if (s->regs[channel->str] & STR_IBF) { 333 enum aspeed_lpc_subdevice subdev; 334 335 subdev = aspeed_kcs_subdevice_map[channel->id]; 336 qemu_irq_lower(s->subdevice_irqs[subdev]); 337 } 338 339 s->regs[channel->str] &= ~STR_IBF; 340 break; 341 } 342 default: 343 break; 344 } 345 346 return s->regs[reg]; 347} 348 349static void aspeed_lpc_write(void *opaque, hwaddr offset, uint64_t data, 350 unsigned int size) 351{ 352 AspeedLPCState *s = ASPEED_LPC(opaque); 353 int reg = TO_REG(offset); 354 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", 358 __func__, offset); 359 return; 360 } 361 362 363 switch (reg) { 364 case ODR1: 365 case ODR2: 366 case ODR3: 367 case ODR4: 368 s->regs[aspeed_kcs_get_channel_by_register(reg)->str] |= STR_OBF; 369 break; 370 default: 371 break; 372 } 373 374 s->regs[reg] = data; 375} 376 377static const MemoryRegionOps aspeed_lpc_ops = { 378 .read = aspeed_lpc_read, 379 .write = aspeed_lpc_write, 380 .endianness = DEVICE_LITTLE_ENDIAN, 381 .valid = { 382 .min_access_size = 1, 383 .max_access_size = 4, 384 }, 385}; 386 387static void aspeed_lpc_reset(DeviceState *dev) 388{ 389 struct AspeedLPCState *s = ASPEED_LPC(dev); 390 391 s->subdevice_irqs_pending = 0; 392 393 memset(s->regs, 0, sizeof(s->regs)); 394 395 s->regs[HICR7] = s->hicr7; 396} 397 398static void aspeed_lpc_realize(DeviceState *dev, Error **errp) 399{ 400 AspeedLPCState *s = ASPEED_LPC(dev); 401 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 402 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]); 409 410 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_lpc_ops, s, 411 TYPE_ASPEED_LPC, 0x1000); 412 413 sysbus_init_mmio(sbd, &s->iomem); 414 415 qdev_init_gpio_in(dev, aspeed_lpc_set_irq, ASPEED_LPC_NR_SUBDEVS); 416} 417 418static void aspeed_lpc_init(Object *obj) 419{ 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); 444} 445 446static const VMStateDescription vmstate_aspeed_lpc = { 447 .name = TYPE_ASPEED_LPC, 448 .version_id = 2, 449 .minimum_version_id = 2, 450 .fields = (VMStateField[]) { 451 VMSTATE_UINT32_ARRAY(regs, AspeedLPCState, ASPEED_LPC_NR_REGS), 452 VMSTATE_UINT32(subdevice_irqs_pending, AspeedLPCState), 453 VMSTATE_END_OF_LIST(), 454 } 455}; 456 457static Property aspeed_lpc_properties[] = { 458 DEFINE_PROP_UINT32("hicr7", AspeedLPCState, hicr7, 0), 459 DEFINE_PROP_END_OF_LIST(), 460}; 461 462static void aspeed_lpc_class_init(ObjectClass *klass, void *data) 463{ 464 DeviceClass *dc = DEVICE_CLASS(klass); 465 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); 471} 472 473static 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, 479}; 480 481static void aspeed_lpc_register_types(void) 482{ 483 type_register_static(&aspeed_lpc_info); 484} 485 486type_init(aspeed_lpc_register_types);