forked from juhovh/xmm7360_usb
-
Notifications
You must be signed in to change notification settings - Fork 1
/
xmm7560_usb.c
143 lines (117 loc) · 3.86 KB
/
xmm7560_usb.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* Copyright (C) 2021 mrtska
* original author: Juho Vähä-Herttua
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/acpi.h>
#include <linux/version.h>
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
MODULE_AUTHOR("mrtska <[email protected]>");
MODULE_DESCRIPTION("A simple driver that sets the XMM7560 card from PCIe to USB mode.");
MODULE_ALIAS("pci:v00008086d00007560sv*sd*bc*sc*i*");
#define XMM7560 0x7560
static struct pci_device_id pci_ids[] = {
{ PCI_VDEVICE(INTEL, XMM7560), },
{ 0, }
};
static acpi_status acpi_device_reset(struct acpi_device *dev) {
acpi_handle handle;
handle = acpi_device_handle(dev);
return acpi_evaluate_object(handle, "_RST", NULL, NULL);
}
static bool pcie_link_set_enabled(struct pci_dev *dev, bool enable) {
struct pci_dev *rdev;
u16 lnk_ctrl;
bool changed;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0))
rdev = pcie_find_root_port(dev);
#else
rdev = pci_find_pcie_root_port(dev);
#endif
if (!rdev) {
pr_err(KERN_ERR "Could not find the PCIe root port for %04x:%04x\n",
dev->vendor, dev->device);
return false;
}
pcie_capability_read_word(rdev, PCI_EXP_LNKCTL, &lnk_ctrl);
if (enable) {
changed = lnk_ctrl & PCI_EXP_LNKCTL_LD;
lnk_ctrl &= ~PCI_EXP_LNKCTL_LD;
} else {
changed = !(lnk_ctrl & PCI_EXP_LNKCTL_LD);
lnk_ctrl |= PCI_EXP_LNKCTL_LD;
}
pcie_capability_write_word(rdev, PCI_EXP_LNKCTL, lnk_ctrl);
pci_dev_put(rdev);
return changed;
}
static bool xmm7560_set_usb_mode(struct pci_dev *dev, bool enable_usb) {
struct acpi_device *adev;
bool needs_reset;
acpi_status status;
pr_info("Found a PCI device %04x:%04x\n", dev->vendor, dev->device);
needs_reset = pcie_link_set_enabled(dev, !enable_usb);
if (needs_reset) {
pr_info("Link status changed, trying to reset the device\n");
adev = ACPI_COMPANION(&dev->dev);
if (!adev) {
pr_err("Could not find the ACPI companion device\n");
return false;
}
status = acpi_device_reset(adev);
if (ACPI_FAILURE(status)) {
pr_err("Failed to reset the ACPI companion device\n");
return false;
}
pr_info("Device reset successfully\n");
}
return true;
}
static int pci_probe(struct pci_dev *dev, const struct pci_device_id *id) {
if (!xmm7560_set_usb_mode(dev, true)) {
return -1;
}
return 0;
}
static void pci_remove(struct pci_dev *dev) {
xmm7560_set_usb_mode(dev, false);
}
static struct pci_driver pci_driver = {
.name = "xmm7560_usb",
.id_table = pci_ids,
.probe = pci_probe,
.remove = pci_remove,
};
static int __init xmm7560_usb_init(void){
int ret;
ret = pci_register_driver(&pci_driver);
if (ret < 0) {
pr_err("Failed to register the PCI driver\n");
return ret;
}
return 0;
}
static void __exit xmm7560_usb_exit(void){
pci_unregister_driver(&pci_driver);
}
module_init(xmm7560_usb_init);
module_exit(xmm7560_usb_exit);