===================================================================
@@ -21,6 +21,61 @@
#include <console.h>
#include <opal.h>
#include <psi.h>
+#include <bt.h>
+#include <errorlog.h>
+#include <ipmi.h>
+#include <ast.h>
+
+/* BT config */
+#define BT_IO_BASE 0xe4
+#define BT_IO_COUNT 3
+#define BT_LPC_IRQ 10
+
+static void qemu_ipmi_error(struct ipmi_msg *msg)
+{
+ prlog(PR_DEBUG, "QEMU: error sending msg. cc = %02x\n", msg->cc);
+
+ ipmi_free_msg(msg);
+}
+
+static void qemu_ipmi_setenables(void)
+{
+ struct ipmi_msg *msg;
+
+ struct {
+ uint8_t oem2_en : 1;
+ uint8_t oem1_en : 1;
+ uint8_t oem0_en : 1;
+ uint8_t reserved : 1;
+ uint8_t sel_en : 1;
+ uint8_t msgbuf_en : 1;
+ uint8_t msgbuf_full_int_en : 1;
+ uint8_t rxmsg_queue_int_en : 1;
+ } data;
+
+ memset(&data, 0, sizeof(data));
+
+ /* The spec says we need to read-modify-write to not clobber
+ * the state of the other flags. These are set on by the bmc */
+ data.rxmsg_queue_int_en = 1;
+ data.sel_en = 1;
+
+ /* These are the ones we want to set on */
+ data.msgbuf_en = 1;
+
+ msg = ipmi_mkmsg_simple(IPMI_SET_ENABLES, &data, sizeof(data));
+ if (!msg) {
+ prlog(PR_ERR, "QEMU: failed to set enables\n");
+ return;
+ }
+
+ msg->error = qemu_ipmi_error;
+
+ ipmi_queue_msg(msg);
+
+}
+
+extern int pnor_init(void);
static void qemu_init(void)
{
@@ -28,10 +83,58 @@ static void qemu_init(void)
if (!dummy_console_enabled())
uart_setup_opal_console();
+ /* Initialize PNOR/NVRAM */
+ pnor_init();
+
/* Setup LPC RTC and use it as time source. Call after
* chiptod_init()
*/
lpc_rtc_init();
+
+ bt_init();
+ ipmi_sel_init();
+ ipmi_wdt_init();
+ ipmi_fru_init(0);
+ ipmi_opal_init();
+ elog_init();
+ ipmi_sensor_init();
+
+ /* As soon as IPMI is up, inform BMC we are in "S0" */
+ ipmi_set_power_state(IPMI_PWR_SYS_S0_WORKING, IPMI_PWR_NOCHANGE);
+
+ /* Enable IPMI OEM message interrupts */
+ qemu_ipmi_setenables();
+
+ ipmi_set_fw_progress_sensor(IPMI_FW_MOTHERBOARD_INIT);
+
+}
+
+static void qemu_dt_fixup_bt(struct dt_node *lpc)
+{
+ struct dt_node *bt;
+ char namebuf[32];
+
+ /* First check if the BT interface is already there */
+ dt_for_each_child(lpc, bt) {
+ if (dt_node_is_compatible(bt, "ipmi-bt")) {
+ prlog(PR_WARNING, "QEMU: bt device already here\n");
+ return;
+ }
+ }
+
+ snprintf(namebuf, sizeof(namebuf), "ipmi-bt@i%x", BT_IO_BASE);
+ bt = dt_new(lpc, namebuf);
+
+ dt_add_property_cells(bt, "reg",
+ 1, /* IO space */
+ BT_IO_BASE, BT_IO_COUNT);
+ dt_add_property_strings(bt, "compatible", "ipmi-bt");
+
+ /* Mark it as reserved to avoid Linux trying to claim it */
+ dt_add_property_strings(bt, "status", "reserved");
+
+ dt_add_property_cells(bt, "interrupts", BT_LPC_IRQ);
+ dt_add_property_cells(bt, "interrupt-parent", lpc->phandle);
}
static void qemu_dt_fixup_uart(struct dt_node *lpc)
@@ -53,6 +156,14 @@ static void qemu_dt_fixup_uart(struct dt
#define UART_IO_COUNT 8
#define UART_LPC_IRQ 4
+ /* First check if the BT interface is already there */
+ dt_for_each_child(lpc, uart) {
+ if (dt_node_is_compatible(uart, "pnpPNP,501")) {
+ prlog(PR_WARNING, "QEMU: uart device already here\n");
+ return;
+ }
+ }
+
snprintf(namebuf, sizeof(namebuf), "serial@i%x", UART_IO_BASE);
uart = dt_new(lpc, namebuf);
@@ -84,6 +195,14 @@ static void qemu_dt_fixup_rtc(struct dt_
struct dt_node *rtc;
char namebuf[32];
+ /* First check if the BT interface is already there */
+ dt_for_each_child(lpc, rtc) {
+ if (dt_node_is_compatible(rtc, "pnpPNP,b00")) {
+ prlog(PR_WARNING, "QEMU: rtc device already here\n");
+ return;
+ }
+ }
+
/*
* Follows the structure expected by the kernel file
* arch/powerpc/sysdev/rtc_cmos_setup.c
@@ -113,6 +232,7 @@ static void qemu_dt_fixup(void)
qemu_dt_fixup_rtc(primary_lpc);
qemu_dt_fixup_uart(primary_lpc);
+ qemu_dt_fixup_bt(primary_lpc);
}
static void qemu_ext_irq_serirq_cpld(unsigned int chip_id)
@@ -120,6 +240,21 @@ static void qemu_ext_irq_serirq_cpld(uns
lpc_all_interrupts(chip_id);
}
+static int64_t qemu_ipmi_power_down(uint64_t request)
+{
+ if (request != IPMI_CHASSIS_PWR_DOWN) {
+ prlog(PR_WARNING, "PLAT: unexpected shutdown request %llx\n",
+ request);
+ }
+
+ return ipmi_chassis_control(request);
+}
+
+static int64_t qemu_ipmi_reboot(void)
+{
+ return ipmi_chassis_control(IPMI_CHASSIS_HARD_RESET);
+}
+
static bool qemu_probe(void)
{
if (!dt_node_is_compatible(dt_root, "qemu,powernv"))
@@ -130,6 +265,9 @@ static bool qemu_probe(void)
psi_set_external_irq_policy(EXTERNAL_IRQ_POLICY_SKIBOOT);
+ /* Initialize AHB accesses via AST2400 */
+ ast_io_init();
+
/*
* Setup UART and use it as console. For now, we
* don't expose the interrupt as we know it's not
@@ -145,4 +283,9 @@ DECLARE_PLATFORM(qemu) = {
.probe = qemu_probe,
.init = qemu_init,
.external_irq = qemu_ext_irq_serirq_cpld,
+ .cec_power_down = qemu_ipmi_power_down,
+ .cec_reboot = qemu_ipmi_reboot,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .terminate = ipmi_terminate,
};