162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Read address ranges from a Broadcom CNB20LE Host Bridge 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/acpi.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/dmi.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <asm/pci_x86.h> 1462306a36Sopenharmony_ci#include <asm/pci-direct.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "bus_numa.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic void __init cnb20le_res(u8 bus, u8 slot, u8 func) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci struct pci_root_info *info; 2162306a36Sopenharmony_ci struct pci_root_res *root_res; 2262306a36Sopenharmony_ci struct resource res; 2362306a36Sopenharmony_ci u16 word1, word2; 2462306a36Sopenharmony_ci u8 fbus, lbus; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* read the PCI bus numbers */ 2762306a36Sopenharmony_ci fbus = read_pci_config_byte(bus, slot, func, 0x44); 2862306a36Sopenharmony_ci lbus = read_pci_config_byte(bus, slot, func, 0x45); 2962306a36Sopenharmony_ci info = alloc_pci_root_info(fbus, lbus, 0, 0); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* 3262306a36Sopenharmony_ci * Add the legacy IDE ports on bus 0 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * These do not exist anywhere in the bridge registers, AFAICT. I do 3562306a36Sopenharmony_ci * not have the datasheet, so this is the best I can do. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci if (fbus == 0) { 3862306a36Sopenharmony_ci update_res(info, 0x01f0, 0x01f7, IORESOURCE_IO, 0); 3962306a36Sopenharmony_ci update_res(info, 0x03f6, 0x03f6, IORESOURCE_IO, 0); 4062306a36Sopenharmony_ci update_res(info, 0x0170, 0x0177, IORESOURCE_IO, 0); 4162306a36Sopenharmony_ci update_res(info, 0x0376, 0x0376, IORESOURCE_IO, 0); 4262306a36Sopenharmony_ci update_res(info, 0xffa0, 0xffaf, IORESOURCE_IO, 0); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* read the non-prefetchable memory window */ 4662306a36Sopenharmony_ci word1 = read_pci_config_16(bus, slot, func, 0xc0); 4762306a36Sopenharmony_ci word2 = read_pci_config_16(bus, slot, func, 0xc2); 4862306a36Sopenharmony_ci if (word1 != word2) { 4962306a36Sopenharmony_ci res.start = ((resource_size_t) word1 << 16) | 0x0000; 5062306a36Sopenharmony_ci res.end = ((resource_size_t) word2 << 16) | 0xffff; 5162306a36Sopenharmony_ci res.flags = IORESOURCE_MEM; 5262306a36Sopenharmony_ci update_res(info, res.start, res.end, res.flags, 0); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* read the prefetchable memory window */ 5662306a36Sopenharmony_ci word1 = read_pci_config_16(bus, slot, func, 0xc4); 5762306a36Sopenharmony_ci word2 = read_pci_config_16(bus, slot, func, 0xc6); 5862306a36Sopenharmony_ci if (word1 != word2) { 5962306a36Sopenharmony_ci res.start = ((resource_size_t) word1 << 16) | 0x0000; 6062306a36Sopenharmony_ci res.end = ((resource_size_t) word2 << 16) | 0xffff; 6162306a36Sopenharmony_ci res.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; 6262306a36Sopenharmony_ci update_res(info, res.start, res.end, res.flags, 0); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* read the IO port window */ 6662306a36Sopenharmony_ci word1 = read_pci_config_16(bus, slot, func, 0xd0); 6762306a36Sopenharmony_ci word2 = read_pci_config_16(bus, slot, func, 0xd2); 6862306a36Sopenharmony_ci if (word1 != word2) { 6962306a36Sopenharmony_ci res.start = word1; 7062306a36Sopenharmony_ci res.end = word2; 7162306a36Sopenharmony_ci res.flags = IORESOURCE_IO; 7262306a36Sopenharmony_ci update_res(info, res.start, res.end, res.flags, 0); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* print information about this host bridge */ 7662306a36Sopenharmony_ci res.start = fbus; 7762306a36Sopenharmony_ci res.end = lbus; 7862306a36Sopenharmony_ci res.flags = IORESOURCE_BUS; 7962306a36Sopenharmony_ci printk(KERN_INFO "CNB20LE PCI Host Bridge (domain 0000 %pR)\n", &res); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci list_for_each_entry(root_res, &info->resources, list) 8262306a36Sopenharmony_ci printk(KERN_INFO "host bridge window %pR\n", &root_res->res); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int __init broadcom_postcore_init(void) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci u8 bus = 0, slot = 0; 8862306a36Sopenharmony_ci u32 id; 8962306a36Sopenharmony_ci u16 vendor, device; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#ifdef CONFIG_ACPI 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * We should get host bridge information from ACPI unless the BIOS 9462306a36Sopenharmony_ci * doesn't support it. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci if (!acpi_disabled && acpi_os_get_root_pointer()) 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci#endif 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID); 10162306a36Sopenharmony_ci vendor = id & 0xffff; 10262306a36Sopenharmony_ci device = (id >> 16) & 0xffff; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (vendor == PCI_VENDOR_ID_SERVERWORKS && 10562306a36Sopenharmony_ci device == PCI_DEVICE_ID_SERVERWORKS_LE) { 10662306a36Sopenharmony_ci cnb20le_res(bus, slot, 0); 10762306a36Sopenharmony_ci cnb20le_res(bus, slot, 1); 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cipostcore_initcall(broadcom_postcore_init); 113