2020-04-02 23:49:05 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
2012-10-30 15:03:43 +01:00
|
|
|
|
2018-12-06 10:47:42 +01:00
|
|
|
#include <assert.h>
|
|
|
|
#include <commonlib/helpers.h>
|
2012-10-30 15:03:43 +01:00
|
|
|
#include <console/console.h>
|
|
|
|
#include <device/device.h>
|
|
|
|
#include <device/pci.h>
|
2019-09-21 17:35:37 +02:00
|
|
|
#include <device/pci_def.h>
|
2012-10-30 15:03:43 +01:00
|
|
|
#include <device/pciexp.h>
|
|
|
|
#include <device/pci_ids.h>
|
2018-04-18 10:11:59 +02:00
|
|
|
#include <device/pci_ops.h>
|
2020-05-31 00:55:35 +02:00
|
|
|
#include "iobp.h"
|
2012-10-30 15:03:43 +01:00
|
|
|
#include "pch.h"
|
2016-02-06 18:07:59 +01:00
|
|
|
#include <southbridge/intel/common/gpio.h>
|
2018-12-06 10:47:42 +01:00
|
|
|
#include <stdint.h>
|
2019-08-18 15:33:39 +02:00
|
|
|
#include "chip.h"
|
2012-10-30 15:03:43 +01:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
#define MAX_NUM_ROOT_PORTS 8
|
|
|
|
|
|
|
|
struct root_port_config {
|
|
|
|
/* RPFN is a write-once register so keep a copy until it is written */
|
|
|
|
u32 orig_rpfn;
|
|
|
|
u32 new_rpfn;
|
|
|
|
u32 pin_ownership;
|
|
|
|
u32 strpfusecfg1;
|
|
|
|
u32 strpfusecfg2;
|
|
|
|
u32 strpfusecfg3;
|
2013-12-03 21:13:26 +01:00
|
|
|
u32 b0d28f0_32c;
|
|
|
|
u32 b0d28f4_32c;
|
|
|
|
u32 b0d28f5_32c;
|
2013-06-20 08:20:30 +02:00
|
|
|
int coalesce;
|
|
|
|
int gbe_port;
|
|
|
|
int num_ports;
|
2018-06-08 17:20:38 +02:00
|
|
|
struct device *ports[MAX_NUM_ROOT_PORTS];
|
2013-06-20 08:20:30 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct root_port_config rpc;
|
|
|
|
|
|
|
|
static inline int max_root_ports(void)
|
2013-06-19 20:28:04 +02:00
|
|
|
{
|
sb/intel/lynxpoint: Handle H81 only having 6 PCIe root ports
The H81 chipset is the only non-LP Lynx Point chipset with 6 PCIe root
ports, all others have 8 [1]. The existing PCIe code assumed that all
non-LP chipsets had 8 root ports, which meant that port 6 would not be
considered the last root port on H81, so `root_port_commit_config()`
would not run. Ultimately, while PCIe still worked on H81, all the root
ports would remain enabled, even if disabled in the devicetree.
Also, remove `PCI_DEVICE_ID_INTEL_LYNXPOINT_MOB_DESK_{MIN,MAX}`, as they
are unused, and the MAX constant is incorrect.
Interestingly, this fixes an issue where GRUB is unable to halt the
system.
Tested on an ASRock H81M-HDS. The root ports disabled in the devicetree
do indeed end up disabled.
[1] Intel® 8 Series/C220 Series Chipset Family Platform Controller Hub
(PCH) Datasheet, revision 003, document number 328904.
Change-Id: If3ce217e8a4f4ea4e111e4525b03dbbfc63f92b0
Signed-off-by: Tristan Corrick <tristan@corrick.kiwi>
Reviewed-on: https://review.coreboot.org/c/30077
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-by: Patrick Rudolph <siro@das-labor.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2018-12-06 10:46:58 +01:00
|
|
|
if (pch_is_lp() || pch_silicon_id() == PCI_DEVICE_ID_INTEL_LPT_H81)
|
|
|
|
return 6;
|
|
|
|
|
|
|
|
return 8;
|
2013-06-20 08:20:30 +02:00
|
|
|
}
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2018-06-08 17:20:38 +02:00
|
|
|
static inline int root_port_is_first(struct device *dev)
|
2013-06-20 08:20:30 +02:00
|
|
|
{
|
|
|
|
return PCI_FUNC(dev->path.pci.devfn) == 0;
|
|
|
|
}
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2018-06-08 17:20:38 +02:00
|
|
|
static inline int root_port_is_last(struct device *dev)
|
2013-06-20 08:20:30 +02:00
|
|
|
{
|
|
|
|
return PCI_FUNC(dev->path.pci.devfn) == (rpc.num_ports - 1);
|
2013-06-19 20:28:04 +02:00
|
|
|
}
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
/* Root ports are numbered 1..N in the documentation. */
|
2018-06-08 17:20:38 +02:00
|
|
|
static inline int root_port_number(struct device *dev)
|
2013-06-20 08:20:30 +02:00
|
|
|
{
|
|
|
|
return PCI_FUNC(dev->path.pci.devfn) + 1;
|
|
|
|
}
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2018-12-06 10:47:42 +01:00
|
|
|
static bool is_rp_enabled(int rp)
|
|
|
|
{
|
|
|
|
ASSERT(rp > 0 && rp <= ARRAY_SIZE(rpc.ports));
|
|
|
|
|
|
|
|
if (rpc.ports[rp - 1] == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return rpc.ports[rp - 1]->enabled;
|
|
|
|
}
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
static void root_port_config_update_gbe_port(void)
|
2013-06-19 20:28:04 +02:00
|
|
|
{
|
2013-06-20 08:20:30 +02:00
|
|
|
/* Is the Gbe Port enabled? */
|
|
|
|
if (!((rpc.strpfusecfg1 >> 19) & 1))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (pch_is_lp()) {
|
|
|
|
switch ((rpc.strpfusecfg1 >> 16) & 0x7) {
|
|
|
|
case 0:
|
|
|
|
rpc.gbe_port = 3;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
rpc.gbe_port = 4;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
/* Lanes 0-4 of Root Port 5. */
|
|
|
|
rpc.gbe_port = 5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk(BIOS_DEBUG, "Invalid GbE Port Selection.\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Non-LP has 1:1 mapping with root ports. */
|
|
|
|
rpc.gbe_port = ((rpc.strpfusecfg1 >> 16) & 0x7) + 1;
|
|
|
|
}
|
|
|
|
}
|
2013-06-19 20:28:04 +02:00
|
|
|
|
sb/intel/lynxpoint: Consider root ports being disabled by strap
PCIe RPC (Root Port Configuration) straps will force-disable some root
port functions if some root ports have a width greater than x1. In two
cases, this affects the last function. The PCIe init code will never
finish configuring the root ports if that is the case: it assumes that
the last function will eventually run through the code, but it doesn't.
If PCIe initialization does not complete, pressing the power button will
not power off the board, unless it is held for about five seconds. Also,
Windows 10 will show a BSOD about MACHINE CHECK EXCEPTION, and lock up
instead of rebooting. Depending on the microcode version, the BSOD may
not be visible. This happens even when the root port is not populated.
Use the strap fuse configuration value to know which configuration the
PCH is strapped to. If needed, update the number of ports accordingly.
In addition, print the updated value to ease debugging PCIe init code.
Existing code in coreboot disagrees with public documentation about the
root port width straps. Assume existing code is correct and document
these assumptions in a table, as an explanation for the added code.
Tested on Asrock B85M Pro4, PCIe initialization completes successfully.
Change-Id: Id6da3a1f45467f00002a5ed41df8650f4a74eeba
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/44155
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-08-04 00:26:45 +02:00
|
|
|
static void update_num_ports(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* According to existing code in 'root_port_check_disable()', which does
|
|
|
|
* not agree with the confusing information on the datasheets, the last
|
|
|
|
* visible function depends on the strapped root port width as follows:
|
|
|
|
*
|
|
|
|
* +-----+----+----+----+----+
|
|
|
|
* | RPC | #5 | #6 | #7 | #8 |
|
|
|
|
* +-----+----+----+----+----+
|
|
|
|
* | 0 | x1 | x1 | x1 | x1 |
|
|
|
|
* | 1 | x2 | | x1 | x1 |
|
|
|
|
* | 2 | x2 | | x2 | |
|
|
|
|
* | 3 | x4 | | | |
|
|
|
|
* +-----+----+----+----+----+
|
|
|
|
*/
|
|
|
|
switch ((rpc.strpfusecfg2 >> 14) & 0x3) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rpc.num_ports = MIN(rpc.num_ports, 7);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
rpc.num_ports = MIN(rpc.num_ports, 5);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "Adjusted number of PCIe root ports to %d as per strpfusecfg2\n",
|
|
|
|
rpc.num_ports);
|
|
|
|
}
|
|
|
|
|
2018-06-08 17:20:38 +02:00
|
|
|
static void root_port_init_config(struct device *dev)
|
2013-06-20 08:20:30 +02:00
|
|
|
{
|
|
|
|
int rp;
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
if (root_port_is_first(dev)) {
|
|
|
|
rpc.orig_rpfn = RCBA32(RPFN);
|
|
|
|
rpc.new_rpfn = rpc.orig_rpfn;
|
|
|
|
rpc.num_ports = max_root_ports();
|
|
|
|
rpc.gbe_port = -1;
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
rpc.pin_ownership = pci_read_config32(dev, 0x410);
|
|
|
|
root_port_config_update_gbe_port();
|
|
|
|
|
|
|
|
if (dev->chip_info != NULL) {
|
|
|
|
struct southbridge_intel_lynxpoint_config *config;
|
|
|
|
|
|
|
|
config = dev->chip_info;
|
|
|
|
rpc.coalesce = config->pcie_port_coalesce;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rp = root_port_number(dev);
|
|
|
|
if (rp > rpc.num_ports) {
|
|
|
|
printk(BIOS_ERR, "Found Root Port %d, expecting %d\n",
|
|
|
|
rp, rpc.num_ports);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the fuse configuration and pin ownership. */
|
|
|
|
switch (rp) {
|
|
|
|
case 1:
|
|
|
|
rpc.strpfusecfg1 = pci_read_config32(dev, 0xfc);
|
2013-12-03 21:13:26 +01:00
|
|
|
rpc.b0d28f0_32c = pci_read_config32(dev, 0x32c);
|
2013-06-20 08:20:30 +02:00
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
rpc.strpfusecfg2 = pci_read_config32(dev, 0xfc);
|
2013-12-03 21:13:26 +01:00
|
|
|
rpc.b0d28f4_32c = pci_read_config32(dev, 0x32c);
|
sb/intel/lynxpoint: Consider root ports being disabled by strap
PCIe RPC (Root Port Configuration) straps will force-disable some root
port functions if some root ports have a width greater than x1. In two
cases, this affects the last function. The PCIe init code will never
finish configuring the root ports if that is the case: it assumes that
the last function will eventually run through the code, but it doesn't.
If PCIe initialization does not complete, pressing the power button will
not power off the board, unless it is held for about five seconds. Also,
Windows 10 will show a BSOD about MACHINE CHECK EXCEPTION, and lock up
instead of rebooting. Depending on the microcode version, the BSOD may
not be visible. This happens even when the root port is not populated.
Use the strap fuse configuration value to know which configuration the
PCH is strapped to. If needed, update the number of ports accordingly.
In addition, print the updated value to ease debugging PCIe init code.
Existing code in coreboot disagrees with public documentation about the
root port width straps. Assume existing code is correct and document
these assumptions in a table, as an explanation for the added code.
Tested on Asrock B85M Pro4, PCIe initialization completes successfully.
Change-Id: Id6da3a1f45467f00002a5ed41df8650f4a74eeba
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/44155
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-08-04 00:26:45 +02:00
|
|
|
|
|
|
|
if (!pch_is_lp())
|
|
|
|
update_num_ports();
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
break;
|
|
|
|
case 6:
|
2013-12-03 21:13:26 +01:00
|
|
|
rpc.b0d28f5_32c = pci_read_config32(dev, 0x32c);
|
2013-06-20 08:20:30 +02:00
|
|
|
rpc.strpfusecfg3 = pci_read_config32(dev, 0xfc);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cache pci device. */
|
|
|
|
rpc.ports[rp - 1] = dev;
|
2013-06-19 20:28:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update devicetree with new Root Port function number assignment */
|
2013-06-20 08:20:30 +02:00
|
|
|
static void pch_pcie_device_set_func(int index, int pci_func)
|
2013-06-19 20:28:04 +02:00
|
|
|
{
|
2018-06-08 17:20:38 +02:00
|
|
|
struct device *dev;
|
2019-10-24 05:46:03 +02:00
|
|
|
unsigned int new_devfn;
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
dev = rpc.ports[index];
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
/* Set the new PCI function field for this Root Port. */
|
|
|
|
rpc.new_rpfn &= ~RPFN_FNMASK(index);
|
|
|
|
rpc.new_rpfn |= RPFN_FNSET(index, pci_func);
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
/* Determine the new devfn for this port */
|
|
|
|
new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT, pci_func);
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2018-12-06 10:47:42 +01:00
|
|
|
if (dev && dev->path.pci.devfn != new_devfn) {
|
2013-06-20 08:20:30 +02:00
|
|
|
printk(BIOS_DEBUG,
|
|
|
|
"PCH: PCIe map %02x.%1x -> %02x.%1x\n",
|
|
|
|
PCI_SLOT(dev->path.pci.devfn),
|
|
|
|
PCI_FUNC(dev->path.pci.devfn),
|
|
|
|
PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
dev->path.pci.devfn = new_devfn;
|
2013-06-19 20:28:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-21 21:06:11 +02:00
|
|
|
static void pcie_enable_clock_gating(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int is_lp;
|
|
|
|
int enabled_ports;
|
|
|
|
|
|
|
|
is_lp = pch_is_lp();
|
|
|
|
enabled_ports = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < rpc.num_ports; i++) {
|
2018-06-08 17:20:38 +02:00
|
|
|
struct device *dev;
|
2013-06-21 21:06:11 +02:00
|
|
|
int rp;
|
|
|
|
|
|
|
|
dev = rpc.ports[i];
|
2018-12-06 10:47:42 +01:00
|
|
|
if (!dev)
|
|
|
|
continue;
|
|
|
|
|
2013-06-21 21:06:11 +02:00
|
|
|
rp = root_port_number(dev);
|
|
|
|
|
2018-12-06 10:47:42 +01:00
|
|
|
if (!is_rp_enabled(rp)) {
|
2017-07-04 22:14:16 +02:00
|
|
|
|
2013-06-21 21:06:11 +02:00
|
|
|
/* Configure shared resource clock gating. */
|
|
|
|
if (rp == 1 || rp == 5 || (rp == 6 && is_lp))
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe1, 0x3c);
|
2013-06-21 21:06:11 +02:00
|
|
|
|
|
|
|
if (!is_lp) {
|
2018-12-06 10:47:42 +01:00
|
|
|
if (rp == 1 && !is_rp_enabled(2) &&
|
|
|
|
!is_rp_enabled(3) && !is_rp_enabled(4)) {
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe2, 1);
|
|
|
|
pci_or_config8(dev, 0xe1, 1 << 7);
|
2013-06-21 21:06:11 +02:00
|
|
|
}
|
2018-12-06 10:47:42 +01:00
|
|
|
if (rp == 5 && !is_rp_enabled(6) &&
|
|
|
|
!is_rp_enabled(7) && !is_rp_enabled(8)) {
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe2, 1);
|
|
|
|
pci_or_config8(dev, 0xe1, 1 << 7);
|
2013-06-21 21:06:11 +02:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe2, 3 << 4);
|
|
|
|
pci_or_config32(dev, 0x420, 1 << 31);
|
2013-06-21 21:06:11 +02:00
|
|
|
|
|
|
|
/* Per-Port CLKREQ# handling. */
|
|
|
|
if (is_lp && gpio_is_native(18 + rp - 1))
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config32(dev, 0x420, 3 << 29);
|
2013-06-21 21:06:11 +02:00
|
|
|
|
|
|
|
/* Enable static clock gating. */
|
2018-12-06 10:47:42 +01:00
|
|
|
if (rp == 1 && !is_rp_enabled(2) &&
|
|
|
|
!is_rp_enabled(3) && !is_rp_enabled(4)) {
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe2, 1);
|
|
|
|
pci_or_config8(dev, 0xe1, 1 << 7);
|
2013-06-21 21:06:11 +02:00
|
|
|
} else if (rp == 5 || rp == 6) {
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe2, 1);
|
|
|
|
pci_or_config8(dev, 0xe1, 1 << 7);
|
2013-06-21 21:06:11 +02:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
enabled_ports++;
|
|
|
|
|
|
|
|
/* Enable dynamic clock gating. */
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe1, 0x03);
|
2013-06-21 21:06:11 +02:00
|
|
|
|
|
|
|
if (is_lp) {
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe2, 1 << 6);
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config8(dev, 0xe8, ~(3 << 2), (2 << 2));
|
2013-06-21 21:06:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update PECR1 register. */
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe8, 1);
|
2013-06-21 21:06:11 +02:00
|
|
|
|
2020-06-08 00:12:43 +02:00
|
|
|
/* FIXME: Are we supposed to update this register with a constant boolean? */
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config8(dev, 0x324, ~(1 << 5), (1 < 5));
|
2013-06-21 21:06:11 +02:00
|
|
|
|
|
|
|
/* Per-Port CLKREQ# handling. */
|
|
|
|
if (is_lp && gpio_is_native(18 + rp - 1))
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config32(dev, 0x420, 3 << 29);
|
2013-06-21 21:06:11 +02:00
|
|
|
|
|
|
|
/* Configure shared resource clock gating. */
|
|
|
|
if (rp == 1 || rp == 5 || (rp == 6 && is_lp))
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(dev, 0xe1, 0x3c);
|
2013-06-21 21:06:11 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 10:47:42 +01:00
|
|
|
if (!enabled_ports && is_lp && rpc.ports[0])
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config8(rpc.ports[0], 0xe1, 1 << 6);
|
2013-06-21 21:06:11 +02:00
|
|
|
}
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
static void root_port_commit_config(void)
|
2013-06-19 20:28:04 +02:00
|
|
|
{
|
2013-06-20 08:20:30 +02:00
|
|
|
int i;
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
/* If the first root port is disabled the coalesce ports. */
|
2018-12-06 10:47:42 +01:00
|
|
|
if (!is_rp_enabled(1))
|
2013-06-20 08:20:30 +02:00
|
|
|
rpc.coalesce = 1;
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-21 21:06:11 +02:00
|
|
|
/* Perform clock gating configuration. */
|
|
|
|
pcie_enable_clock_gating();
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
for (i = 0; i < rpc.num_ports; i++) {
|
2018-06-08 17:20:38 +02:00
|
|
|
struct device *dev;
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
dev = rpc.ports[i];
|
2013-06-19 20:28:04 +02:00
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
if (dev == NULL) {
|
|
|
|
printk(BIOS_ERR, "Root Port %d device is NULL?\n", i+1);
|
|
|
|
continue;
|
2013-06-19 20:28:04 +02:00
|
|
|
}
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
if (dev->enabled)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
|
|
|
|
|
2013-06-19 20:28:04 +02:00
|
|
|
/* Ensure memory, io, and bus master are all disabled */
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_and_config16(dev, PCI_COMMAND,
|
|
|
|
~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
|
2013-06-19 20:28:04 +02:00
|
|
|
|
|
|
|
/* Disable this device if possible */
|
|
|
|
pch_disable_devfn(dev);
|
2013-06-20 08:20:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rpc.coalesce) {
|
|
|
|
int current_func;
|
|
|
|
|
|
|
|
/* For all Root Ports N enabled ports get assigned the lower
|
|
|
|
* PCI function number. The disabled ones get upper PCI
|
|
|
|
* function numbers. */
|
|
|
|
current_func = 0;
|
|
|
|
for (i = 0; i < rpc.num_ports; i++) {
|
2018-12-06 10:47:42 +01:00
|
|
|
if (!is_rp_enabled(i + 1))
|
2013-06-20 08:20:30 +02:00
|
|
|
continue;
|
|
|
|
pch_pcie_device_set_func(i, current_func);
|
|
|
|
current_func++;
|
2013-06-19 20:28:04 +02:00
|
|
|
}
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
/* Allocate the disabled devices' PCI function number. */
|
|
|
|
for (i = 0; i < rpc.num_ports; i++) {
|
2018-12-06 10:47:42 +01:00
|
|
|
if (is_rp_enabled(i + 1))
|
2013-06-20 08:20:30 +02:00
|
|
|
continue;
|
|
|
|
pch_pcie_device_set_func(i, current_func);
|
|
|
|
current_func++;
|
|
|
|
}
|
2013-06-19 20:28:04 +02:00
|
|
|
}
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
|
|
|
|
rpc.orig_rpfn, rpc.new_rpfn);
|
|
|
|
RCBA32(RPFN) = rpc.new_rpfn;
|
|
|
|
}
|
|
|
|
|
2018-06-08 17:20:38 +02:00
|
|
|
static void root_port_mark_disable(struct device *dev)
|
2013-06-20 08:20:30 +02:00
|
|
|
{
|
|
|
|
/* Mark device as disabled. */
|
|
|
|
dev->enabled = 0;
|
|
|
|
/* Mark device to be hidden. */
|
|
|
|
rpc.new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
|
|
|
|
}
|
|
|
|
|
2018-06-08 17:20:38 +02:00
|
|
|
static void root_port_check_disable(struct device *dev)
|
2013-06-20 08:20:30 +02:00
|
|
|
{
|
|
|
|
int rp;
|
|
|
|
int is_lp;
|
|
|
|
|
|
|
|
/* Device already disabled. */
|
|
|
|
if (!dev->enabled) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rp = root_port_number(dev);
|
|
|
|
|
|
|
|
/* Is the GbE port mapped to this Root Port? */
|
|
|
|
if (rp == rpc.gbe_port) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
is_lp = pch_is_lp();
|
|
|
|
|
|
|
|
/* Check Root Port Configuration. */
|
|
|
|
switch (rp) {
|
|
|
|
case 2:
|
|
|
|
/* Root Port 2 is disabled for all lane configurations
|
|
|
|
* but config 00b (4x1 links). */
|
|
|
|
if ((rpc.strpfusecfg1 >> 14) & 0x3) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* Root Port 3 is disabled in config 11b (1x4 links). */
|
|
|
|
if (((rpc.strpfusecfg1 >> 14) & 0x3) == 0x3) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
/* Root Port 4 is disabled in configs 11b (1x4 links)
|
|
|
|
* and 10b (2x2 links). */
|
|
|
|
if ((rpc.strpfusecfg1 >> 14) & 0x2) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
if (is_lp)
|
|
|
|
break;
|
|
|
|
/* Root Port 6 is disabled for all lane configurations
|
|
|
|
* but config 00b (4x1 links). */
|
|
|
|
if ((rpc.strpfusecfg2 >> 14) & 0x3) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
if (is_lp)
|
|
|
|
break;
|
2018-12-06 10:47:21 +01:00
|
|
|
/* Root Port 7 is disabled in config 11b (1x4 links). */
|
2013-06-20 08:20:30 +02:00
|
|
|
if (((rpc.strpfusecfg2 >> 14) & 0x3) == 0x3) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (is_lp)
|
|
|
|
break;
|
|
|
|
/* Root Port 8 is disabled in configs 11b (1x4 links)
|
|
|
|
* and 10b (2x2 links). */
|
|
|
|
if ((rpc.strpfusecfg2 >> 14) & 0x2) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check Pin Ownership. */
|
|
|
|
if (is_lp) {
|
|
|
|
switch (rp) {
|
|
|
|
case 1:
|
|
|
|
/* Bit 0 is Root Port 1 ownership. */
|
|
|
|
if ((rpc.pin_ownership & 0x1) == 0) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* Bit 2 is Root Port 2 ownership. */
|
|
|
|
if ((rpc.pin_ownership & 0x4) == 0) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* Bits 7:4 are Root Port 6 pin-lane ownership. */
|
|
|
|
if ((rpc.pin_ownership & 0xf0) == 0) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (rp) {
|
|
|
|
case 1:
|
|
|
|
/* Bits 4 and 0 are Root Port 1 ownership. */
|
|
|
|
if ((rpc.pin_ownership & 0x11) == 0) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* Bits 5 and 2 are Root Port 2 ownership. */
|
|
|
|
if ((rpc.pin_ownership & 0x24) == 0) {
|
|
|
|
root_port_mark_disable(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2013-06-19 20:28:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-03 21:13:26 +01:00
|
|
|
static void pcie_add_0x0202000_iobp(u32 reg)
|
2012-10-30 15:03:43 +01:00
|
|
|
{
|
|
|
|
u32 reg32;
|
|
|
|
|
2013-12-03 21:13:26 +01:00
|
|
|
reg32 = pch_iobp_read(reg);
|
|
|
|
reg32 += (0x2 << 16) | (0x2 << 8);
|
|
|
|
pch_iobp_write(reg, reg32);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pch_pcie_early(struct device *dev)
|
|
|
|
{
|
|
|
|
int rp;
|
|
|
|
int do_aspm;
|
|
|
|
int is_lp;
|
2013-08-09 18:06:41 +02:00
|
|
|
struct southbridge_intel_lynxpoint_config *config = dev->chip_info;
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
rp = root_port_number(dev);
|
|
|
|
do_aspm = 0;
|
|
|
|
is_lp = pch_is_lp();
|
|
|
|
|
|
|
|
if (is_lp) {
|
|
|
|
switch (rp) {
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
/* Bits 31:28 of b0d28f0 0x32c register correspnd to
|
|
|
|
* Root Ports 4:1. */
|
|
|
|
do_aspm = !!(rpc.b0d28f0_32c & (1 << (28 + rp - 1)));
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
/* Bit 28 of b0d28f4 0x32c register correspnd to
|
|
|
|
* Root Ports 4:1. */
|
|
|
|
do_aspm = !!(rpc.b0d28f4_32c & (1 << 28));
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* Bit 28 of b0d28f5 0x32c register correspnd to
|
|
|
|
* Root Ports 4:1. */
|
|
|
|
do_aspm = !!(rpc.b0d28f5_32c & (1 << 28));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (rp) {
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
/* Bits 31:28 of b0d28f0 0x32c register correspnd to
|
|
|
|
* Root Ports 4:1. */
|
|
|
|
do_aspm = !!(rpc.b0d28f0_32c & (1 << (28 + rp - 1)));
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
case 6:
|
|
|
|
case 7:
|
|
|
|
case 8:
|
|
|
|
/* Bit 31:28 of b0d28f4 0x32c register correspnd to
|
|
|
|
* Root Ports 8:5. */
|
|
|
|
do_aspm = !!(rpc.b0d28f4_32c & (1 << (28 + rp - 5)));
|
|
|
|
break;
|
|
|
|
}
|
2012-10-30 15:03:43 +01:00
|
|
|
}
|
2013-12-03 21:13:26 +01:00
|
|
|
|
2013-08-09 18:06:41 +02:00
|
|
|
/* Allow ASPM to be forced on in devicetree */
|
|
|
|
if (config && (config->pcie_port_force_aspm & (1 << (rp - 1))))
|
|
|
|
do_aspm = 1;
|
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "PCIe Root Port %d ASPM is %sabled\n",
|
|
|
|
rp, do_aspm ? "en" : "dis");
|
|
|
|
|
2013-12-03 21:13:26 +01:00
|
|
|
if (do_aspm) {
|
|
|
|
/* Set ASPM bits in MPC2 register. */
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0xd4, ~(0x3 << 2), (1 << 4) | (0x2 << 2));
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Set unique clock exit latency in MPC register. */
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0xd8, ~(0x7 << 18), (0x7 << 18));
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Set L1 exit latency in LCAP register. */
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0x4c, ~(0x7 << 15), (0x4 << 15));
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
if (is_lp) {
|
|
|
|
switch (rp) {
|
|
|
|
case 1:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002440);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002640);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9000840);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9000a40);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9000c40);
|
|
|
|
pcie_add_0x0202000_iobp(0xe9000e40);
|
|
|
|
pcie_add_0x0202000_iobp(0xe9001040);
|
|
|
|
pcie_add_0x0202000_iobp(0xe9001240);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* Update IOBP based on lane ownership. */
|
|
|
|
if (rpc.pin_ownership & (1 << 4))
|
|
|
|
pcie_add_0x0202000_iobp(0xea002040);
|
|
|
|
if (rpc.pin_ownership & (1 << 5))
|
|
|
|
pcie_add_0x0202000_iobp(0xea002240);
|
|
|
|
if (rpc.pin_ownership & (1 << 6))
|
|
|
|
pcie_add_0x0202000_iobp(0xea002440);
|
|
|
|
if (rpc.pin_ownership & (1 << 7))
|
|
|
|
pcie_add_0x0202000_iobp(0xea002640);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (rp) {
|
|
|
|
case 1:
|
|
|
|
if ((rpc.pin_ownership & 0x3) == 1)
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002e40);
|
|
|
|
else
|
|
|
|
pcie_add_0x0202000_iobp(0xea002040);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if ((rpc.pin_ownership & 0xc) == 0x4)
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002c40);
|
|
|
|
else
|
|
|
|
pcie_add_0x0202000_iobp(0xea002240);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002a40);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002840);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002640);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002440);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002240);
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
pcie_add_0x0202000_iobp(0xe9002040);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_and_config32(dev, 0x338, ~(1 << 26));
|
2013-12-03 21:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable LTR in Root Port. */
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config32(dev, 0x64, 1 << 11);
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0x68, ~(1 << 10), (1 << 10));
|
2013-12-03 21:13:26 +01:00
|
|
|
|
2017-07-01 02:15:57 +02:00
|
|
|
pci_update_config32(dev, 0x318, ~(0xffffUL << 16), (0x1414UL << 16));
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Set L1 exit latency in LCAP register. */
|
|
|
|
if (!do_aspm && (pci_read_config8(dev, 0xf5) & 0x1))
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0x4c, ~(0x7 << 15), (0x4 << 15));
|
2013-12-03 21:13:26 +01:00
|
|
|
else
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0x4c, ~(0x7 << 15), (0x2 << 15));
|
2013-12-03 21:13:26 +01:00
|
|
|
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_update_config32(dev, 0x314, 0, 0x743a361b);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Set Common Clock Exit Latency in MPC register. */
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0xd8, ~(0x7 << 15), (0x3 << 15));
|
2013-12-03 21:13:26 +01:00
|
|
|
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0x33c, ~0x00ffffff, 0x854c74);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Set Invalid Recieve Range Check Enable in MPC register. */
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config32(dev, 0xd8, 1 << 25);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_and_config8(dev, 0xf5, 0x3f);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
if (rp == 1 || rp == 5 || (is_lp && rp == 6))
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_and_config8(dev, 0xf7, ~0x0c);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Set EOI forwarding disable. */
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config32(dev, 0xd4, 1 << 1);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Set something involving advanced error reporting. */
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0x100, ~((1 << 20) - 1), 0x10001);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
if (is_lp)
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_or_config32(dev, 0x100, 1 << 29);
|
2013-12-03 21:13:26 +01:00
|
|
|
|
|
|
|
/* Read and write back write-once capability registers. */
|
2013-07-26 07:53:59 +02:00
|
|
|
pci_update_config32(dev, 0x34, ~0, 0);
|
|
|
|
pci_update_config32(dev, 0x40, ~0, 0);
|
|
|
|
pci_update_config32(dev, 0x80, ~0, 0);
|
|
|
|
pci_update_config32(dev, 0x90, ~0, 0);
|
2012-10-30 15:03:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pci_init(struct device *dev)
|
|
|
|
{
|
|
|
|
printk(BIOS_DEBUG, "Initializing PCH PCIe bridge.\n");
|
|
|
|
|
2013-06-20 08:20:30 +02:00
|
|
|
/* Enable SERR */
|
2020-04-28 10:13:05 +02:00
|
|
|
pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR);
|
2013-06-20 08:20:30 +02:00
|
|
|
|
2012-10-30 15:03:43 +01:00
|
|
|
/* Enable Bus Master */
|
2020-04-28 10:13:05 +02:00
|
|
|
pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER);
|
2012-10-30 15:03:43 +01:00
|
|
|
|
|
|
|
/* Set Cache Line Size to 0x10 */
|
|
|
|
// This has no effect but the OS might expect it
|
|
|
|
pci_write_config8(dev, 0x0c, 0x10);
|
|
|
|
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_and_config16(dev, PCI_BRIDGE_CONTROL, ~PCI_BRIDGE_CTL_PARITY);
|
2012-10-30 15:03:43 +01:00
|
|
|
|
|
|
|
/* Clear errors in status registers */
|
2020-06-08 00:12:43 +02:00
|
|
|
pci_update_config16(dev, 0x06, ~0, 0);
|
|
|
|
pci_update_config16(dev, 0x1e, ~0, 0);
|
2012-10-30 15:03:43 +01:00
|
|
|
}
|
|
|
|
|
2018-06-08 17:20:38 +02:00
|
|
|
static void pch_pcie_enable(struct device *dev)
|
2012-10-30 15:03:43 +01:00
|
|
|
{
|
2013-06-20 08:20:30 +02:00
|
|
|
/* Add this device to the root port config structure. */
|
|
|
|
root_port_init_config(dev);
|
|
|
|
|
|
|
|
/* Check to see if this Root Port should be disabled. */
|
|
|
|
root_port_check_disable(dev);
|
|
|
|
|
2012-10-30 15:03:43 +01:00
|
|
|
/* Power Management init before enumeration */
|
2013-06-20 08:20:30 +02:00
|
|
|
if (dev->enabled)
|
2013-12-03 21:13:26 +01:00
|
|
|
pch_pcie_early(dev);
|
2013-06-20 08:20:30 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* When processing the last PCIe root port we can now
|
|
|
|
* update the Root Port Function Number and Hide register.
|
|
|
|
*/
|
|
|
|
if (root_port_is_last(dev))
|
|
|
|
root_port_commit_config();
|
2012-10-30 15:03:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct device_operations device_ops = {
|
|
|
|
.read_resources = pci_bus_read_resources,
|
|
|
|
.set_resources = pci_dev_set_resources,
|
|
|
|
.enable_resources = pci_bus_enable_resources,
|
|
|
|
.init = pci_init,
|
|
|
|
.enable = pch_pcie_enable,
|
|
|
|
.scan_bus = pciexp_scan_bridge,
|
2020-05-31 00:03:28 +02:00
|
|
|
.ops_pci = &pci_dev_ops_pci,
|
2012-10-30 15:03:43 +01:00
|
|
|
};
|
|
|
|
|
2012-12-17 20:31:40 +01:00
|
|
|
static const unsigned short pci_device_ids[] = {
|
|
|
|
/* Lynxpoint Mobile */
|
|
|
|
0x8c10, 0x8c12, 0x8c14, 0x8c16, 0x8c18, 0x8c1a, 0x8c1c, 0x8c1e,
|
|
|
|
/* Lynxpoint Low Power */
|
|
|
|
0x9c10, 0x9c12, 0x9c14, 0x9c16, 0x9c18, 0x9c1a,
|
|
|
|
0
|
|
|
|
};
|
2012-10-30 15:03:43 +01:00
|
|
|
|
|
|
|
static const struct pci_driver pch_pcie __pci_driver = {
|
|
|
|
.ops = &device_ops,
|
|
|
|
.vendor = PCI_VENDOR_ID_INTEL,
|
|
|
|
.devices = pci_device_ids,
|
|
|
|
};
|