Instantiate EHCI and UHCI in TYPE_ICH9_SOUTHBRIDGE.
Since machines can disable USB, add the 'ehci-count'
property. Machine can disable USB functions by setting
ehci-count=0.
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
include/hw/southbridge/ich9.h | 5 ---
hw/i386/pc_q35.c | 62 ++---------------------------------
hw/southbridge/ich9.c | 54 ++++++++++++++++++++++++++++++
hw/southbridge/Kconfig | 2 ++
4 files changed, 58 insertions(+), 65 deletions(-)
diff --git a/include/hw/southbridge/ich9.h b/include/hw/southbridge/ich9.h
index d4b299bf3c..7e75496b0b 100644
--- a/include/hw/southbridge/ich9.h
+++ b/include/hw/southbridge/ich9.h
@@ -103,11 +103,6 @@ struct ICH9LPCState {
#define ICH9_PCIE_DEV 28
#define ICH9_PCIE_FUNC_MAX 6
-
-/* D29:F0 USB UHCI Controller #1 */
-#define ICH9_USB_UHCI1_DEV 29
-#define ICH9_USB_UHCI1_FUNC 0
-
/* D31:F0 LPC Processor Interface */
#define ICH9_RST_CNT_IOPORT 0xCF9
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 0ff94b7afd..63fec8b439 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -50,8 +50,6 @@
#include "hw/ide/ahci-pci.h"
#include "hw/intc/ioapic.h"
#include "hw/southbridge/ich9.h"
-#include "hw/usb.h"
-#include "hw/usb/hcd-uhci.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "sysemu/numa.h"
@@ -60,59 +58,6 @@
#include "hw/i386/acpi-build.h"
#include "target/i386/cpu.h"
-struct ehci_companions {
- const char *name;
- int func;
- int port;
-};
-
-static const struct ehci_companions ich9_1d[] = {
- { .name = TYPE_ICH9_USB_UHCI(1), .func = 0, .port = 0 },
- { .name = TYPE_ICH9_USB_UHCI(2), .func = 1, .port = 2 },
- { .name = TYPE_ICH9_USB_UHCI(3), .func = 2, .port = 4 },
-};
-
-static const struct ehci_companions ich9_1a[] = {
- { .name = TYPE_ICH9_USB_UHCI(4), .func = 0, .port = 0 },
- { .name = TYPE_ICH9_USB_UHCI(5), .func = 1, .port = 2 },
- { .name = TYPE_ICH9_USB_UHCI(6), .func = 2, .port = 4 },
-};
-
-static int ehci_create_ich9_with_companions(PCIBus *bus, int slot)
-{
- const struct ehci_companions *comp;
- PCIDevice *ehci, *uhci;
- BusState *usbbus;
- const char *name;
- int i;
-
- switch (slot) {
- case 0x1d:
- name = "ich9-usb-ehci1";
- comp = ich9_1d;
- break;
- case 0x1a:
- name = "ich9-usb-ehci2";
- comp = ich9_1a;
- break;
- default:
- return -1;
- }
-
- ehci = pci_new_multifunction(PCI_DEVFN(slot, 7), name);
- pci_realize_and_unref(ehci, bus, &error_fatal);
- usbbus = QLIST_FIRST(&ehci->qdev.child_bus);
-
- for (i = 0; i < 3; i++) {
- uhci = pci_new_multifunction(PCI_DEVFN(slot, comp[i].func),
- comp[i].name);
- qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name);
- qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port);
- pci_realize_and_unref(uhci, bus, &error_fatal);
- }
- return 0;
-}
-
/* PC hardware initialisation */
static void pc_q35_init(MachineState *machine)
{
@@ -225,6 +170,8 @@ static void pc_q35_init(MachineState *machine)
qdev_prop_set_bit(ich9, "d2p-enabled", false);
qdev_prop_set_bit(ich9, "sata-enabled", pcms->sata_enabled);
qdev_prop_set_bit(ich9, "smbus-enabled", pcms->smbus_enabled);
+ /* Should we create 6 UHCI according to ich9 spec? */
+ qdev_prop_set_uint8(ich9, "ehci-count", machine_usb(machine) ? 1 : 0);
qdev_realize_and_unref(ich9, NULL, &error_fatal);
/* create ISA bus */
@@ -289,11 +236,6 @@ static void pc_q35_init(MachineState *machine)
pcms->idebus[1] = qdev_get_child_bus(ich9, "ide.1");
}
- if (machine_usb(machine)) {
- /* Should we create 6 UHCI according to ich9 spec? */
- ehci_create_ich9_with_companions(pcms->pcibus, 0x1d);
- }
-
if (pcms->smbus_enabled) {
pcms->smbus = I2C_BUS(qdev_get_child_bus(ich9, "i2c"));
/* TODO: Populate SPD eeprom data. */
diff --git a/hw/southbridge/ich9.c b/hw/southbridge/ich9.c
index 413e9187e4..f05012959d 100644
--- a/hw/southbridge/ich9.c
+++ b/hw/southbridge/ich9.c
@@ -16,12 +16,18 @@
#include "hw/ide/ahci-pci.h"
#include "hw/ide/ide-dev.h"
#include "hw/i2c/ich9_smbus.h"
+#include "hw/usb.h"
+#include "hw/usb/hcd-ehci.h"
+#include "hw/usb/hcd-uhci.h"
#define ICH9_D2P_DEVFN PCI_DEVFN(30, 0)
#define ICH9_SATA1_DEVFN PCI_DEVFN(31, 2)
#define ICH9_SMB_DEVFN PCI_DEVFN(31, 3)
+#define ICH9_EHCI_FUNC 7
#define SATA_PORTS 6
+#define EHCI_PER_FN 2
+#define UHCI_PER_FN 3
struct ICH9State {
DeviceState parent_obj;
@@ -29,11 +35,14 @@ struct ICH9State {
I82801b11Bridge d2p;
AHCIPCIState sata0;
ICH9SMBState smb;
+ EHCIPCIState ehci[EHCI_PER_FN];
+ UHCIState uhci[EHCI_PER_FN * UHCI_PER_FN];
PCIBus *pci_bus;
bool d2p_enabled;
bool sata_enabled;
bool smbus_enabled;
+ uint8_t ehci_count;
};
static Property ich9_props[] = {
@@ -42,6 +51,7 @@ static Property ich9_props[] = {
DEFINE_PROP_BOOL("d2p-enabled", ICH9State, d2p_enabled, true),
DEFINE_PROP_BOOL("sata-enabled", ICH9State, sata_enabled, true),
DEFINE_PROP_BOOL("smbus-enabled", ICH9State, smbus_enabled, true),
+ DEFINE_PROP_UINT8("ehci-count", ICH9State, ehci_count, 2),
DEFINE_PROP_END_OF_LIST(),
};
@@ -100,6 +110,46 @@ static bool ich9_realize_smbus(ICH9State *s, Error **errp)
return true;
}
+static bool ich9_realize_usb(ICH9State *s, Error **errp)
+{
+ if (!module_object_class_by_name(TYPE_ICH9_USB_UHCI(1))
+ || !module_object_class_by_name("ich9-usb-ehci1")) {
+ error_setg(errp, "USB functions not available in this build");
+ return false;
+ }
+ for (unsigned e = 0; e < s->ehci_count; e++) {
+ g_autofree gchar *ename = g_strdup_printf("ich9-usb-ehci%u", e + 1);
+ EHCIPCIState *ehci = &s->ehci[e];
+ const unsigned devid = e ? 0x1a : 0x1d;
+ BusState *masterbus;
+
+ object_initialize_child(OBJECT(s), "ehci[*]", ehci, ename);
+ qdev_prop_set_int32(DEVICE(ehci), "addr", PCI_DEVFN(devid,
+ ICH9_EHCI_FUNC));
+ if (!qdev_realize(DEVICE(ehci), BUS(s->pci_bus), errp)) {
+ return false;
+ }
+ masterbus = QLIST_FIRST(&DEVICE(ehci)->child_bus);
+
+ for (unsigned u = 0; u < UHCI_PER_FN; u++) {
+ unsigned c = UHCI_PER_FN * e + u;
+ UHCIState *uhci = &s->uhci[c];
+ g_autofree gchar *cname = g_strdup_printf("ich9-usb-uhci%u", c + 1);
+
+ object_initialize_child(OBJECT(s), "uhci[*]", uhci, cname);
+ qdev_prop_set_bit(DEVICE(uhci), "multifunction", true);
+ qdev_prop_set_int32(DEVICE(uhci), "addr", PCI_DEVFN(devid, u));
+ qdev_prop_set_string(DEVICE(uhci), "masterbus", masterbus->name);
+ qdev_prop_set_uint32(DEVICE(uhci), "firstport", 2 * u);
+ if (!qdev_realize(DEVICE(uhci), BUS(s->pci_bus), errp)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
static void ich9_realize(DeviceState *dev, Error **errp)
{
ICH9State *s = ICH9_SOUTHBRIDGE(dev);
@@ -120,6 +170,10 @@ static void ich9_realize(DeviceState *dev, Error **errp)
if (s->smbus_enabled && !ich9_realize_smbus(s, errp)) {
return;
}
+
+ if (!ich9_realize_usb(s, errp)) {
+ return;
+ }
}
static void ich9_class_init(ObjectClass *klass, void *data)
diff --git a/hw/southbridge/Kconfig b/hw/southbridge/Kconfig
index 03e89a55d1..31eb125bf7 100644
--- a/hw/southbridge/Kconfig
+++ b/hw/southbridge/Kconfig
@@ -6,3 +6,5 @@ config ICH9
imply I82801B11
select AHCI_ICH9
select ACPI_ICH9
+ imply USB_EHCI_PCI
+ imply USB_UHCI
--
2.41.0