blob: 4eeaa3f41585e4f19f37048e13af4cef1b47e521 [file] [log] [blame]
Angel Pons4b429832020-04-02 23:48:50 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Chris Morgan5e5e7892020-02-07 09:40:42 -06002
3#include <console/console.h>
4#include <device/device.h>
5#include <device/pci.h>
6#include <device/pciexp.h>
7#include <device/pci_ids.h>
Angel Ponsae999502020-11-05 01:58:34 +01008#include <device/pci_ops.h>
Chris Morgan5e5e7892020-02-07 09:40:42 -06009#include <assert.h>
Angel Ponsae999502020-11-05 01:58:34 +010010#include <types.h>
11
12#include "chip.h"
13#include "haswell.h"
Chris Morgan5e5e7892020-02-07 09:40:42 -060014
Chris Morgan5e5e7892020-02-07 09:40:42 -060015#if CONFIG(HAVE_ACPI_TABLES)
16static const char *pcie_acpi_name(const struct device *dev)
17{
18 assert(dev);
19
20 if (dev->path.type != DEVICE_PATH_PCI)
21 return NULL;
22
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020023 assert(dev->upstream);
24 if (dev->upstream->secondary == 0)
Chris Morgan5e5e7892020-02-07 09:40:42 -060025 switch (dev->path.pci.devfn) {
26 case PCI_DEVFN(1, 0):
27 return "PEGP";
28 case PCI_DEVFN(1, 1):
29 return "PEG1";
30 case PCI_DEVFN(1, 2):
31 return "PEG2";
32 };
33
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020034 struct device *const port = dev->upstream->dev;
Chris Morgan5e5e7892020-02-07 09:40:42 -060035 assert(port);
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020036 assert(port->upstream);
Chris Morgan5e5e7892020-02-07 09:40:42 -060037
38 if (dev->path.pci.devfn == PCI_DEVFN(0, 0) &&
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020039 port->upstream->secondary == 0 &&
Chris Morgan5e5e7892020-02-07 09:40:42 -060040 (port->path.pci.devfn == PCI_DEVFN(1, 0) ||
41 port->path.pci.devfn == PCI_DEVFN(1, 1) ||
42 port->path.pci.devfn == PCI_DEVFN(1, 2)))
43 return "DEV0";
44
45 return NULL;
46}
47#endif
48
Angel Pons6adbfa72021-06-20 14:15:44 +020049static const struct peg_config *get_peg_config(struct device *dev, const uint8_t func)
50{
51 static const struct peg_config default_config = { 0 };
52
53 if (!dev || !dev->chip_info)
54 return &default_config;
55
56 const struct northbridge_intel_haswell_config *config = dev->chip_info;
57
58 if (func >= ARRAY_SIZE(config->peg_cfg)) {
59 printk(BIOS_ERR, "%s: Found PEG function %u, which doesn't exist on Haswell\n",
60 __func__, func);
61 return &default_config;
62 }
63 return &config->peg_cfg[func];
64}
65
Angel Ponsae999502020-11-05 01:58:34 +010066static void peg_enable(struct device *dev)
67{
Angel Pons24e14f92021-06-20 14:03:35 +020068 const uint8_t func = PCI_FUNC(dev->path.pci.devfn);
Angel Ponsae999502020-11-05 01:58:34 +010069
Angel Pons6adbfa72021-06-20 14:15:44 +020070 const struct peg_config *peg_cfg = get_peg_config(dev, func);
Angel Pons6e0dd4e2021-06-20 14:07:22 +020071
72 const bool slot_implemented = !peg_cfg->is_onboard;
Angel Ponsae999502020-11-05 01:58:34 +010073
74 if (slot_implemented) {
75 /* Default is 1, but register is R/WO and needs to be written to once */
76 pci_or_config16(dev, PEG_CAP, 1 << 8);
77 } else {
78 pci_and_config16(dev, PEG_CAP, ~(1 << 8));
79 }
80
81 /* Note: this register is write-once */
82 uint32_t slotcap = pci_read_config32(dev, PEG_SLOTCAP);
83
84 /* Physical slot number (zero for ports connected to onboard devices) */
Angel Pons08391d22023-02-07 15:03:07 +010085 slotcap &= ~(0x1fffU << 19);
Angel Ponsae999502020-11-05 01:58:34 +010086 if (slot_implemented) {
Angel Pons6e0dd4e2021-06-20 14:07:22 +020087 uint16_t slot_number = peg_cfg->phys_slot_number & 0x1fff;
Angel Ponsae999502020-11-05 01:58:34 +010088 if (slot_number == 0) {
89 /* Slot number must be non-zero and unique */
90 slot_number = func + 1;
91 }
92 slotcap |= slot_number << 19;
93 }
94
95 /* Default to 1.0 watt scale */
96 slotcap &= ~(3 << 15);
Angel Pons6e0dd4e2021-06-20 14:07:22 +020097 slotcap |= (peg_cfg->power_limit_scale & 3) << 15;
Angel Ponsae999502020-11-05 01:58:34 +010098
Angel Pons6e0dd4e2021-06-20 14:07:22 +020099 uint8_t power_limit_value = peg_cfg->power_limit_value;
Angel Ponsae999502020-11-05 01:58:34 +0100100 if (power_limit_value == 0) {
101 /* Default to 75 watts */
102 power_limit_value = 75;
103 }
104 slotcap &= ~(0xff << 7);
105 slotcap |= power_limit_value << 7;
106
107 pci_write_config32(dev, PEG_SLOTCAP, slotcap);
108
109 /* Clear errors */
110 pci_write_config16(dev, PCI_STATUS, 0xffff);
111 pci_write_config16(dev, PCI_SEC_STATUS, 0xffff);
112 pci_write_config16(dev, PEG_DSTS, 0xffff);
113 pci_write_config32(dev, PEG_UESTS, 0xffffffff);
114 pci_write_config32(dev, PEG_CESTS, 0xffffffff);
115 pci_write_config32(dev, 0x1f0, 0xffffffff);
116
117 pci_or_config32(dev, PEG_VC0RCTL, 0x7f << 1);
118
119 /* Advertise OBFF support using WAKE# signaling only */
120 pci_or_config32(dev, PEG_DCAP2, 1 << 19);
121
122 pci_or_config32(dev, PEG_UESEV, 1 << 14);
123
124 /* Select -3.5 dB de-emphasis */
125 pci_or_config32(dev, PEG_LCTL2, 1 << 6);
126
Angel Pons08391d22023-02-07 15:03:07 +0100127 pci_or_config32(dev, PEG_L0SLAT, 1U << 31);
Angel Ponsae999502020-11-05 01:58:34 +0100128
129 pci_update_config32(dev, 0x250, ~(7 << 20), 2 << 20);
130
131 pci_or_config32(dev, 0x238, 1 << 29);
132
133 pci_or_config32(dev, 0x1f8, 1 << 16);
134
135 pci_update_config32(dev, PEG_AFE_PM_TMR, ~0x1f, 0x13);
136
137 /* Lock DCAP */
138 pci_update_config32(dev, PEG_DCAP, ~0, 0);
139
140 if (func == 0)
141 pci_or_config32(dev, 0xcd0, 1 << 11);
142
143 /* Enable support for L0s and L1 */
144 pci_or_config32(dev, PEG_LCAP, 3 << 10);
145
146 pci_and_config32(dev, 0x200, ~(3 << 26));
147
148 /* Other fields in this register must not be changed while writing this */
149 pci_or_config16(dev, 0x258, 1 << 2);
150}
151
Chris Morgan5e5e7892020-02-07 09:40:42 -0600152static struct device_operations device_ops = {
153 .read_resources = pci_bus_read_resources,
154 .set_resources = pci_dev_set_resources,
155 .enable_resources = pci_bus_enable_resources,
156 .scan_bus = pciexp_scan_bridge,
157 .reset_bus = pci_bus_reset,
Angel Ponsae999502020-11-05 01:58:34 +0100158 .enable = peg_enable,
Chris Morgan5e5e7892020-02-07 09:40:42 -0600159 .init = pci_dev_init,
Angel Pons1fc0edd2020-05-31 00:03:28 +0200160 .ops_pci = &pci_dev_ops_pci,
Chris Morgan5e5e7892020-02-07 09:40:42 -0600161#if CONFIG(HAVE_ACPI_TABLES)
162 .acpi_name = pcie_acpi_name,
163#endif
164};
165
Iru Cai12a13e12020-05-22 22:57:03 +0800166static const unsigned short pci_device_ids[] = {
167 0x0c01, 0x0c05, 0x0c09, 0x0c0d,
168 0x0d01, 0x0d05, 0x0d09, /* Crystal Well */
169 0 };
Chris Morgan5e5e7892020-02-07 09:40:42 -0600170
171static const struct pci_driver pch_pcie __pci_driver = {
172 .ops = &device_ops,
Felix Singer43b7f412022-03-07 04:34:52 +0100173 .vendor = PCI_VID_INTEL,
Chris Morgan5e5e7892020-02-07 09:40:42 -0600174 .devices = pci_device_ids,
175};