Angel Pons | 4b42983 | 2020-04-02 23:48:50 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 2 | |
| 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 Pons | ae99950 | 2020-11-05 01:58:34 +0100 | [diff] [blame] | 8 | #include <device/pci_ops.h> |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 9 | #include <assert.h> |
Angel Pons | ae99950 | 2020-11-05 01:58:34 +0100 | [diff] [blame] | 10 | #include <types.h> |
| 11 | |
| 12 | #include "chip.h" |
| 13 | #include "haswell.h" |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 14 | |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 15 | #if CONFIG(HAVE_ACPI_TABLES) |
| 16 | static 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 | |
| 23 | assert(dev->bus); |
| 24 | if (dev->bus->secondary == 0) |
| 25 | 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 | |
| 34 | struct device *const port = dev->bus->dev; |
| 35 | assert(port); |
| 36 | assert(port->bus); |
| 37 | |
| 38 | if (dev->path.pci.devfn == PCI_DEVFN(0, 0) && |
| 39 | port->bus->secondary == 0 && |
| 40 | (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 Pons | ae99950 | 2020-11-05 01:58:34 +0100 | [diff] [blame] | 49 | static void peg_enable(struct device *dev) |
| 50 | { |
| 51 | const struct northbridge_intel_haswell_config *config = config_of(dev); |
| 52 | |
| 53 | const uint8_t func = PCI_FUNC(PCI_BDF(dev)); |
| 54 | |
| 55 | assert(func < ARRAY_SIZE(config->peg_cfg)); |
| 56 | |
| 57 | const bool slot_implemented = !config->peg_cfg[func].is_onboard; |
| 58 | |
| 59 | if (slot_implemented) { |
| 60 | /* Default is 1, but register is R/WO and needs to be written to once */ |
| 61 | pci_or_config16(dev, PEG_CAP, 1 << 8); |
| 62 | } else { |
| 63 | pci_and_config16(dev, PEG_CAP, ~(1 << 8)); |
| 64 | } |
| 65 | |
| 66 | /* Note: this register is write-once */ |
| 67 | uint32_t slotcap = pci_read_config32(dev, PEG_SLOTCAP); |
| 68 | |
| 69 | /* Physical slot number (zero for ports connected to onboard devices) */ |
| 70 | slotcap &= ~(0x1fff << 19); |
| 71 | if (slot_implemented) { |
| 72 | uint16_t slot_number = config->peg_cfg[func].phys_slot_number & 0x1fff; |
| 73 | if (slot_number == 0) { |
| 74 | /* Slot number must be non-zero and unique */ |
| 75 | slot_number = func + 1; |
| 76 | } |
| 77 | slotcap |= slot_number << 19; |
| 78 | } |
| 79 | |
| 80 | /* Default to 1.0 watt scale */ |
| 81 | slotcap &= ~(3 << 15); |
| 82 | slotcap |= (config->peg_cfg[func].power_limit_scale & 3) << 15; |
| 83 | |
| 84 | uint8_t power_limit_value = config->peg_cfg[func].power_limit_value; |
| 85 | if (power_limit_value == 0) { |
| 86 | /* Default to 75 watts */ |
| 87 | power_limit_value = 75; |
| 88 | } |
| 89 | slotcap &= ~(0xff << 7); |
| 90 | slotcap |= power_limit_value << 7; |
| 91 | |
| 92 | pci_write_config32(dev, PEG_SLOTCAP, slotcap); |
| 93 | |
| 94 | /* Clear errors */ |
| 95 | pci_write_config16(dev, PCI_STATUS, 0xffff); |
| 96 | pci_write_config16(dev, PCI_SEC_STATUS, 0xffff); |
| 97 | pci_write_config16(dev, PEG_DSTS, 0xffff); |
| 98 | pci_write_config32(dev, PEG_UESTS, 0xffffffff); |
| 99 | pci_write_config32(dev, PEG_CESTS, 0xffffffff); |
| 100 | pci_write_config32(dev, 0x1f0, 0xffffffff); |
| 101 | |
| 102 | pci_or_config32(dev, PEG_VC0RCTL, 0x7f << 1); |
| 103 | |
| 104 | /* Advertise OBFF support using WAKE# signaling only */ |
| 105 | pci_or_config32(dev, PEG_DCAP2, 1 << 19); |
| 106 | |
| 107 | pci_or_config32(dev, PEG_UESEV, 1 << 14); |
| 108 | |
| 109 | /* Select -3.5 dB de-emphasis */ |
| 110 | pci_or_config32(dev, PEG_LCTL2, 1 << 6); |
| 111 | |
| 112 | pci_or_config32(dev, PEG_L0SLAT, 1 << 31); |
| 113 | |
| 114 | pci_update_config32(dev, 0x250, ~(7 << 20), 2 << 20); |
| 115 | |
| 116 | pci_or_config32(dev, 0x238, 1 << 29); |
| 117 | |
| 118 | pci_or_config32(dev, 0x1f8, 1 << 16); |
| 119 | |
| 120 | pci_update_config32(dev, PEG_AFE_PM_TMR, ~0x1f, 0x13); |
| 121 | |
| 122 | /* Lock DCAP */ |
| 123 | pci_update_config32(dev, PEG_DCAP, ~0, 0); |
| 124 | |
| 125 | if (func == 0) |
| 126 | pci_or_config32(dev, 0xcd0, 1 << 11); |
| 127 | |
| 128 | /* Enable support for L0s and L1 */ |
| 129 | pci_or_config32(dev, PEG_LCAP, 3 << 10); |
| 130 | |
| 131 | pci_and_config32(dev, 0x200, ~(3 << 26)); |
| 132 | |
| 133 | /* Other fields in this register must not be changed while writing this */ |
| 134 | pci_or_config16(dev, 0x258, 1 << 2); |
| 135 | } |
| 136 | |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 137 | static struct device_operations device_ops = { |
| 138 | .read_resources = pci_bus_read_resources, |
| 139 | .set_resources = pci_dev_set_resources, |
| 140 | .enable_resources = pci_bus_enable_resources, |
| 141 | .scan_bus = pciexp_scan_bridge, |
| 142 | .reset_bus = pci_bus_reset, |
Angel Pons | ae99950 | 2020-11-05 01:58:34 +0100 | [diff] [blame] | 143 | .enable = peg_enable, |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 144 | .init = pci_dev_init, |
Angel Pons | 1fc0edd | 2020-05-31 00:03:28 +0200 | [diff] [blame] | 145 | .ops_pci = &pci_dev_ops_pci, |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 146 | #if CONFIG(HAVE_ACPI_TABLES) |
| 147 | .acpi_name = pcie_acpi_name, |
| 148 | #endif |
| 149 | }; |
| 150 | |
Iru Cai | 12a13e1 | 2020-05-22 22:57:03 +0800 | [diff] [blame] | 151 | static const unsigned short pci_device_ids[] = { |
| 152 | 0x0c01, 0x0c05, 0x0c09, 0x0c0d, |
| 153 | 0x0d01, 0x0d05, 0x0d09, /* Crystal Well */ |
| 154 | 0 }; |
Chris Morgan | 5e5e789 | 2020-02-07 09:40:42 -0600 | [diff] [blame] | 155 | |
| 156 | static const struct pci_driver pch_pcie __pci_driver = { |
| 157 | .ops = &device_ops, |
| 158 | .vendor = PCI_VENDOR_ID_INTEL, |
| 159 | .devices = pci_device_ids, |
| 160 | }; |