| 1 | /* |
| 2 | * IPMI ACPI firmware handling |
| 3 | * |
| 4 | * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 7 | * See the COPYING file in the top-level directory. |
| 8 | */ |
| 9 | |
| 10 | #include "qemu/osdep.h" |
| 11 | #include "hw/ipmi/ipmi.h" |
| 12 | #include "hw/acpi/aml-build.h" |
| 13 | #include "hw/acpi/acpi.h" |
| 14 | #include "hw/acpi/ipmi.h" |
| 15 | |
| 16 | static Aml *aml_ipmi_crs(IPMIFwInfo *info) |
| 17 | { |
| 18 | Aml *crs = aml_resource_template(); |
| 19 | |
| 20 | /* |
| 21 | * The base address is fixed and cannot change. That may be different |
| 22 | * if someone does PCI, but we aren't there yet. |
| 23 | */ |
| 24 | switch (info->memspace) { |
| 25 | case IPMI_MEMSPACE_IO: |
| 26 | aml_append(crs, aml_io(AML_DECODE16, info->base_address, |
| 27 | info->base_address + info->register_length - 1, |
| 28 | info->register_spacing, info->register_length)); |
| 29 | break; |
| 30 | case IPMI_MEMSPACE_MEM32: |
| 31 | aml_append(crs, |
| 32 | aml_dword_memory(AML_POS_DECODE, |
| 33 | AML_MIN_FIXED, AML_MAX_FIXED, |
| 34 | AML_NON_CACHEABLE, AML_READ_WRITE, |
| 35 | 0xffffffff, |
| 36 | info->base_address, |
| 37 | info->base_address + info->register_length - 1, |
| 38 | info->register_spacing, info->register_length)); |
| 39 | break; |
| 40 | case IPMI_MEMSPACE_MEM64: |
| 41 | aml_append(crs, |
| 42 | aml_qword_memory(AML_POS_DECODE, |
| 43 | AML_MIN_FIXED, AML_MAX_FIXED, |
| 44 | AML_NON_CACHEABLE, AML_READ_WRITE, |
| 45 | 0xffffffffffffffffULL, |
| 46 | info->base_address, |
| 47 | info->base_address + info->register_length - 1, |
| 48 | info->register_spacing, info->register_length)); |
| 49 | break; |
| 50 | case IPMI_MEMSPACE_SMBUS: |
| 51 | aml_append(crs, aml_i2c_serial_bus_device(info->base_address, |
| 52 | "^")); |
| 53 | break; |
| 54 | default: |
| 55 | abort(); |
| 56 | } |
| 57 | |
| 58 | /* Should PCI interrupts also be appended? */ |
| 59 | if (info->irq_source == IPMI_ISA_IRQ && info->interrupt_number) { |
| 60 | aml_append(crs, aml_irq_no_flags(info->interrupt_number)); |
| 61 | } |
| 62 | |
| 63 | return crs; |
| 64 | } |
| 65 | |
| 66 | void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope) |
| 67 | { |
| 68 | Aml *dev; |
| 69 | IPMIFwInfo info = {}; |
| 70 | IPMIInterface *ii = IPMI_INTERFACE(adev); |
| 71 | IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); |
| 72 | uint16_t version; |
| 73 | |
| 74 | iic->get_fwinfo(ii, &info); |
| 75 | assert(info.ipmi_spec_minor_revision <= 15); |
| 76 | version = ((info.ipmi_spec_major_revision << 8) |
| 77 | | (info.ipmi_spec_minor_revision << 4)); |
| 78 | |
| 79 | dev = aml_device("MI%d", info.uuid); |
| 80 | aml_append(dev, aml_name_decl("_HID", aml_eisaid("IPI0001"))); |
| 81 | aml_append(dev, aml_name_decl("_STR", aml_string("ipmi_%s", |
| 82 | info.interface_name))); |
| 83 | aml_append(dev, aml_name_decl("_UID", aml_int(info.uuid))); |
| 84 | aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(&info))); |
| 85 | aml_append(dev, aml_name_decl("_IFT", aml_int(info.interface_type))); |
| 86 | aml_append(dev, aml_name_decl("_SRV", aml_int(version))); |
| 87 | |
| 88 | aml_append(scope, dev); |
| 89 | } |