[DO NOT MERGE] [WIP] Add PCI driver
This is work in progress, I intend to merge the bbswitch_dev code into the main module. Some comments in README and bbswitch.c might be stale. Create a PCI driver such that runtime PM works. Without a bound driver, the kernel assumes that the device is D0 state during suspend (which therefore needs the notifier hack in bbswitch) but more importantly, it will prevent the PCIe port from going to sleep in Linux v4.7. Currently I use this to debug an infinite loop on my Clevo P651RA laptop, it can be loaded as follows: make modname=bbswitch_dev insmod bbswitch_dev.ko echo > /sys/bus/pci/drivers/bbswitch/new_id 10de 13d9 Needs Mika's pci/pm series ("PCI: Put PCIe ports into D3 during suspend"), qeued for v4.7 via https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=pci/pmacpi-pr3
parent
915413ab92
commit
daa6411911
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* TODO merge into main bbswitch module.
|
||||
* TODO on ON call device_release_driver
|
||||
* TODO how to bind to a specific device from kernel space? Can't use
|
||||
* driver_probe_device (https://lkml.org/lkml/2014/2/14/628). Maybe use
|
||||
* driver_override or new_id/bind/remove_id from userspace?
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Toggle the discrete graphics card (PCI driver)");
|
||||
MODULE_AUTHOR("Peter Wu <peter@lekensteyn.nl>");
|
||||
|
||||
static int bbswitch_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
/* TODO how to discover devices? */
|
||||
/* Prevent kernel from detaching the PCI device for some devices that
|
||||
* generate hotplug events. The graphics card is typically not physically
|
||||
* removable. */
|
||||
pci_ignore_hotplug(dev);
|
||||
|
||||
pm_runtime_set_active(&dev->dev); /* clear any errors */
|
||||
pm_runtime_allow(&dev->dev);
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bbswitch_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
pm_runtime_get_noresume(&dev->dev);
|
||||
}
|
||||
|
||||
static int bbswitch_runtime_suspend(struct device *dev) {
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
pr_info("disabling discrete graphics\n");
|
||||
|
||||
/* TODO if _PR3 is not supported, call Optimus DSM here. */
|
||||
/* TODO for v1 Optimus, call DSM here. */
|
||||
|
||||
/* Save state now that the device is still awake, makes PCI layer happy */
|
||||
pci_save_state(pdev);
|
||||
/* TODO if _PR3 is supported, should this be PCI_D3hot? */
|
||||
pci_set_power_state(pdev, PCI_D3cold);
|
||||
mdelay(1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bbswitch_runtime_resume(struct device *dev) {
|
||||
pr_info("enabling discrete graphics\n");
|
||||
|
||||
/* TODO for v1 Optimus, call DSM here. */
|
||||
/* Nothing to do for Optimus, the PCI layer already moved into D0 state. */
|
||||
mdelay(1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops bbswitch_pm_ops = {
|
||||
.runtime_suspend = bbswitch_runtime_suspend,
|
||||
.runtime_resume = bbswitch_runtime_resume,
|
||||
/* No runtime_idle callback, the default zero delay is sufficient. */
|
||||
};
|
||||
|
||||
static struct pci_driver bbswitch_pci_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = NULL, /* will be added dynamically */
|
||||
.probe = bbswitch_pci_probe,
|
||||
.remove = bbswitch_pci_remove,
|
||||
.driver.pm = &bbswitch_pm_ops,
|
||||
};
|
||||
|
||||
static int __init bbswitch_dev_init(void) {
|
||||
int ret;
|
||||
|
||||
ret = pci_register_driver(&bbswitch_pci_driver);
|
||||
#if 0
|
||||
ret = pci_add_dynid(&bbswitch_pci_driver, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
|
||||
PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, 0xff0000);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit bbswitch_dev_exit(void) {
|
||||
pci_unregister_driver(&bbswitch_pci_driver);
|
||||
}
|
||||
|
||||
module_init(bbswitch_dev_init);
|
||||
module_exit(bbswitch_dev_exit);
|
||||
|
||||
/* vim: set sw=4 ts=4 et: */
|
Loading…
Reference in New Issue