genode/repos/dde_linux/src/drivers/usb_host/spec/x86/platform.cc

224 lines
5.1 KiB
C++

/*
* \brief PCI device handling
* \author Sebastian Sumpf
* \date 2016-04-25
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/allocator.h>
#include <base/entrypoint.h>
#include <base/lock.h>
#include <base/signal.h>
#include <util/misc_math.h>
#include <lx_emul.h>
#include <platform.h>
#include <lx_kit/env.h>
#include <lx_kit/irq.h>
#include <lx_kit/pci_dev_registry.h>
#include <lx_kit/malloc.h>
#include <lx_kit/mapped_io_mem_range.h>
#include <lx_emul/impl/io.h>
#include <lx_emul/impl/pci_resource.h>
/**
* Quirks
*/
extern "C" void __pci_fixup_quirk_usb_early_handoff(void *data);
/**
* List of pci devices from platform driver
*/
class Pci_dev_list
{
private:
struct Element : public Lx_kit::List<Element>::Element
{
Platform::Device_capability cap;
Element(Platform::Device_capability cap) : cap(cap) { }
};
Lx_kit::List<Element> _pci_caps;
public:
Pci_dev_list()
{
/*
* Obtain first device, the operation may exceed the session quota.
* So we use the 'with_upgrade' mechanism.
*/
Platform::Device_capability cap =
Lx::pci()->with_upgrade([&] () {
return Lx::pci()->first_device(); });
/*
* Iterate over the devices of the platform session.
*/
while (cap.valid()) {
_pci_caps.insert(new (Lx::Malloc::mem()) Element(cap));
/* try next one. Upgrade session quota on demand.*/
Lx::pci()->with_upgrade([&] () {
cap = Lx::pci()->next_device(cap); });
}
}
template <typename FUNC>
void for_each_pci_device(FUNC const &func)
{
for (Element *e = _pci_caps.first(); e; e = e->next())
func(e->cap);
}
};
Pci_dev_list *pci_dev_list()
{
static Pci_dev_list _list;
return &_list;
}
extern "C" int pci_register_driver(struct pci_driver *driver)
{
driver->driver.name = driver->name;
pci_device_id const *id_table = driver->id_table;
if (!id_table)
return -ENODEV;
using namespace Genode;
bool found = false;
auto lamda = [&] (Platform::Device_capability cap) {
Platform::Device_client client(cap);
/* request device ID from platform driver */
unsigned const class_code = client.class_code();
/* look if we find the device ID in the driver's 'id_table' */
pci_device_id const *matching_id = nullptr;
for (pci_device_id const *id = id_table; id->device; id++) {
lx_log(DEBUG_PCI,"idclass: %x idclassm: %x devclass %x", id->class_,
id->class_mask, class_code);
/* check for drivers that support any device for a given class */
if (id->device != (unsigned)PCI_ANY_ID || !id->class_mask)
continue;
if ((id->class_ & id->class_mask) == (class_code & id->class_mask)) {
matching_id = id;
break;
}
}
/* skip device that is not handled by driver */
if (!matching_id)
return false;
/* create 'pci_dev' struct for matching device */
Lx::Pci_dev *pci_dev = new (Lx::Malloc::mem()) Lx::Pci_dev(cap);
/* enable ioremap to work */
Lx::pci_dev_registry()->insert(pci_dev);
/* register driver at the 'pci_dev' struct */
pci_dev->dev.driver = &driver->driver;
/*
* This quirk handles device handoff from BIOS, since the BIOS may still
* access the USB controller after bootup. For this the ext cap register of
* the PCI config space is checked
*/
if (Lx_kit::env().config_rom().xml().attribute_value("bios_handoff", true))
__pci_fixup_quirk_usb_early_handoff(pci_dev);
/* call probe function of the Linux driver */
if (driver->probe(pci_dev, matching_id)) {
/* if the probing failed, revert the creation of 'pci_dev' */
pci_dev_put(pci_dev);
return false;
}
found = true;
/* multiple device support continue */
return true;
};
pci_dev_list()->for_each_pci_device(lamda);
return found ? 0 : -ENODEV;
}
/***********************
** linux/interrupt.h **
***********************/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
for (Lx::Pci_dev *pci_dev = Lx::pci_dev_registry()->first(); pci_dev; pci_dev = pci_dev->next())
if (pci_dev->irq == irq) {
Lx::Irq::irq().request_irq(pci_dev->client(), handler, dev);
return 0;
}
return -ENODEV;
}
/*********************************
** Platform backend alloc init **
*********************************/
void backend_alloc_init(Genode::Env &env, Genode::Ram_session &ram,
Genode::Allocator &alloc)
{
Lx::pci_init(env, ram, alloc);
}
extern "C" void module_ehci_hcd_init();
extern "C" void module_ehci_pci_init();
extern "C" void module_ohci_hcd_mod_init();
extern "C" void module_ohci_pci_init();
extern "C" void module_uhci_hcd_init();
extern "C" void module_xhci_hcd_init();
extern "C" void module_xhci_pci_init();
void platform_hcd_init(Services *s)
{
module_xhci_hcd_init();
module_xhci_pci_init();
/* ehci_hcd should always be loaded before uhci_hcd and ohci_hcd, not after */
module_ehci_hcd_init();
module_ehci_pci_init();
module_ohci_hcd_mod_init();
module_ohci_pci_init();
module_uhci_hcd_init();
}