1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <device/pci_ops.h>
5 #include <device/device.h>
6 #include <device/pci.h>
7 #include <console/console.h>
11 typedef struct southbridge_intel_i82801ix_config config_t
;
13 static void i82801ix_enable_device(struct device
*dev
)
16 pci_or_config16(dev
, PCI_COMMAND
, PCI_COMMAND_SERR
);
19 static void i82801ix_early_settings(const config_t
*const info
)
21 /* Program FERR# as processor break event indicator. */
22 RCBA32(GCS
) |= (1 << 6);
23 /* BIOS must program...
24 * NB: other CIRs are handled in i82801ix_dmi_setup(). */
25 RCBA32(RCBA_CIR8
) = (RCBA32(RCBA_CIR8
) & ~(0x3 << 0)) | (0x2 << 0);
26 RCBA32(RCBA_FD
) |= (1 << 0);
27 RCBA32(RCBA_CIR9
) = (RCBA32(RCBA_CIR9
) & ~(0x3 << 26)) | (0x2 << 26);
28 RCBA32(RCBA_CIR7
) = (RCBA32(RCBA_CIR7
) & ~(0xf << 16)) | (0x5 << 16);
29 RCBA32(RCBA_CIR13
) = (RCBA32(RCBA_CIR13
) & ~(0xf << 16)) | (0x5 << 16);
30 /* RCBA32(RCBA_CIR5) |= (1 << 0); cf. Specification Update */
31 RCBA32(RCBA_CIR10
) |= (3 << 16);
34 static void i82801ix_pcie_init(const config_t
*const info
)
36 struct device
*pciePort
[6];
37 int i
, slot_number
= 1; /* Reserve slot number 0 for nb's PEG. */
39 /* PCIe - BIOS must program... */
40 for (i
= 0; i
< 6; ++i
) {
41 pciePort
[i
] = pcidev_on_root(0x1c, i
);
43 printk(BIOS_EMERG
, "PCIe port 00:1c.%x", i
);
44 die(" is not listed in devicetree.\n");
46 pci_or_config32(pciePort
[i
], 0x300, 1 << 21);
47 pci_write_config8(pciePort
[i
], 0x324, 0x40);
50 if (LPC_IS_MOBILE(pcidev_on_root(0x1f, 0))) {
51 for (i
= 0; i
< 6; ++i
) {
52 if (pciePort
[i
]->enabled
) {
53 pci_or_config32(pciePort
[i
], 0xe8, 1);
58 for (i
= 5; (i
>= 0) && !pciePort
[i
]->enabled
; --i
) {
59 /* Only for the top disabled ports. */
60 pci_or_config32(pciePort
[i
], 0x300, 0x3 << 16);
63 /* Set slot implemented, slot number and slot power limits. */
64 for (i
= 0; i
< 6; ++i
) {
65 struct device
*const dev
= pciePort
[i
];
66 u32 xcap
= pci_read_config32(dev
, D28Fx_XCAP
);
67 if (info
->pcie_slot_implemented
& (1 << i
))
68 xcap
|= PCI_EXP_FLAGS_SLOT
;
70 xcap
&= ~PCI_EXP_FLAGS_SLOT
;
71 pci_write_config32(dev
, D28Fx_XCAP
, xcap
);
73 if (info
->pcie_slot_implemented
& (1 << i
)) {
74 u32 slcap
= pci_read_config32(dev
, D28Fx_SLCAP
);
75 slcap
&= ~(0x1fff << 19);
76 slcap
|= (slot_number
++ << 19);
77 slcap
&= ~(0x0003 << 16);
78 slcap
|= (info
->pcie_power_limits
[i
].scale
<< 16);
79 slcap
&= ~(0x00ff << 7);
80 slcap
|= (info
->pcie_power_limits
[i
].value
<< 7);
81 pci_write_config32(dev
, D28Fx_SLCAP
, slcap
);
85 /* Lock R/WO ASPM support bits. */
86 for (i
= 0; i
< 6; ++i
)
87 pci_update_config32(pciePort
[i
], 0x4c, ~0, 0);
90 static void i82801ix_ehci_init(void)
92 struct device
*const pciEHCI1
= pcidev_on_root(0x1d, 7);
94 die("EHCI controller (00:1d.7) not listed in devicetree.\n");
95 struct device
*const pciEHCI2
= pcidev_on_root(0x1a, 7);
97 die("EHCI controller (00:1a.7) not listed in devicetree.\n");
101 /* TODO: Maybe we have to save and
102 restore these settings across S3. */
103 reg32
= pci_read_config32(pciEHCI1
, 0xfc);
104 pci_write_config32(pciEHCI1
, 0xfc, (reg32
& ~(3 << 2)) |
105 (1 << 29) | (1 << 17) | (2 << 2));
106 reg32
= pci_read_config32(pciEHCI2
, 0xfc);
107 pci_write_config32(pciEHCI2
, 0xfc, (reg32
& ~(3 << 2)) |
108 (1 << 29) | (1 << 17) | (2 << 2));
111 static int i82801ix_function_disabled(const unsigned int devfn
)
113 struct device
*const dev
= pcidev_path_on_root(devfn
);
116 "PCI device 00:%x.%x",
117 PCI_SLOT(devfn
), PCI_FUNC(devfn
));
118 die(" is not listed in devicetree.\n");
120 return !dev
->enabled
;
123 static void i82801ix_hide_functions(void)
128 /* FIXME: This works pretty good if the devicetree is consistent. But
129 some functions have to be disabled in right order and/or have
130 other constraints. */
132 if (i82801ix_function_disabled(PCI_DEVFN(0x19, 0)))
133 RCBA32(RCBA_BUC
) |= BUC_LAND
;
135 reg32
= RCBA32(RCBA_FD
);
140 { PCI_DEVFN(0x1a, 0), FD_U4D
}, /* UHCI #4 */
141 { PCI_DEVFN(0x1a, 1), FD_U5D
}, /* UHCI #5 */
142 { PCI_DEVFN(0x1a, 2), FD_U6D
}, /* UHCI #6 */
143 { PCI_DEVFN(0x1a, 7), FD_EHCI2D
}, /* EHCI #2 */
144 { PCI_DEVFN(0x1b, 0), FD_HDAD
}, /* HD Audio */
145 { PCI_DEVFN(0x1c, 0), FD_PE1D
}, /* PCIe #1 */
146 { PCI_DEVFN(0x1c, 1), FD_PE2D
}, /* PCIe #2 */
147 { PCI_DEVFN(0x1c, 2), FD_PE3D
}, /* PCIe #3 */
148 { PCI_DEVFN(0x1c, 3), FD_PE4D
}, /* PCIe #4 */
149 { PCI_DEVFN(0x1c, 4), FD_PE5D
}, /* PCIe #5 */
150 { PCI_DEVFN(0x1c, 5), FD_PE6D
}, /* PCIe #6 */
151 { PCI_DEVFN(0x1d, 0), FD_U1D
}, /* UHCI #1 */
152 { PCI_DEVFN(0x1d, 1), FD_U2D
}, /* UHCI #2 */
153 { PCI_DEVFN(0x1d, 2), FD_U3D
}, /* UHCI #3 */
154 { PCI_DEVFN(0x1d, 7), FD_EHCI1D
}, /* EHCI #1 */
155 { PCI_DEVFN(0x1f, 0), FD_LBD
}, /* LPC */
156 { PCI_DEVFN(0x1f, 2), FD_SAD1
}, /* SATA #1 */
157 { PCI_DEVFN(0x1f, 3), FD_SD
}, /* SMBus */
158 { PCI_DEVFN(0x1f, 5), FD_SAD2
}, /* SATA #2 */
159 { PCI_DEVFN(0x1f, 6), FD_TTD
}, /* Thermal Throttle */
161 for (i
= 0; i
< ARRAY_SIZE(functions
); ++i
) {
162 if (i82801ix_function_disabled(functions
[i
].devfn
))
163 reg32
|= functions
[i
].mask
;
165 RCBA32(RCBA_FD
) = reg32
;
166 RCBA32(RCBA_FD
) |= (1 << 0); /* BIOS must write this... */
167 RCBA32(RCBA_FDSW
) |= (1 << 7); /* Lock function-disable? */
169 /* Hide PCIe root port PCI functions. RPFN is partially R/WO. */
170 reg32
= RCBA32(RCBA_RPFN
);
171 for (i
= 0; i
< 6; ++i
) {
172 if (i82801ix_function_disabled(PCI_DEVFN(0x1c, i
)))
173 reg32
|= (1 << ((i
* 4) + 3));
175 RCBA32(RCBA_RPFN
) = reg32
;
177 /* Lock R/WO UHCI controller #6 remapping. */
178 RCBA32(RCBA_MAP
) = RCBA32(RCBA_MAP
);
181 static void i82801ix_init(void *chip_info
)
183 const config_t
*const info
= (config_t
*)chip_info
;
185 printk(BIOS_DEBUG
, "Initializing i82801ix southbridge...\n");
187 i82801ix_early_settings(info
);
189 /* PCI Express setup. */
190 i82801ix_pcie_init(info
);
192 /* EHCI configuration. */
193 i82801ix_ehci_init();
195 /* Now hide internal functions. We can't access them after this. */
196 i82801ix_hide_functions();
198 /* Reset watchdog timer. */
199 #if !CONFIG(HAVE_SMI_HANDLER)
200 outw(0x0008, DEFAULT_TCOBASE
+ 0x12); /* Set higher timer value. */
202 outw(0x0000, DEFAULT_TCOBASE
+ 0x00); /* Update timer. */
205 struct chip_operations southbridge_intel_i82801ix_ops
= {
206 .name
= "Intel ICH9/ICH9-M (82801Ix) Series Southbridge",
207 .enable_dev
= i82801ix_enable_device
,
208 .init
= i82801ix_init
,