162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Numascale NumaConnect-specific PCI code 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Numascale AS. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Send feedback to <support@numascale.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * PCI accessor functions derived from mmconfig_64.c 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <asm/pci_x86.h> 1562306a36Sopenharmony_ci#include <asm/numachip/numachip.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic u8 limit __read_mostly; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic inline char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (cfg && cfg->virt) 2462306a36Sopenharmony_ci return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12)); 2562306a36Sopenharmony_ci return NULL; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int pci_mmcfg_read_numachip(unsigned int seg, unsigned int bus, 2962306a36Sopenharmony_ci unsigned int devfn, int reg, int len, u32 *value) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci char __iomem *addr; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 3462306a36Sopenharmony_ci if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) { 3562306a36Sopenharmony_cierr: *value = -1; 3662306a36Sopenharmony_ci return -EINVAL; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Ensure AMD Northbridges don't decode reads to other devices */ 4062306a36Sopenharmony_ci if (unlikely(bus == 0 && devfn >= limit)) { 4162306a36Sopenharmony_ci *value = -1; 4262306a36Sopenharmony_ci return 0; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci rcu_read_lock(); 4662306a36Sopenharmony_ci addr = pci_dev_base(seg, bus, devfn); 4762306a36Sopenharmony_ci if (!addr) { 4862306a36Sopenharmony_ci rcu_read_unlock(); 4962306a36Sopenharmony_ci goto err; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci switch (len) { 5362306a36Sopenharmony_ci case 1: 5462306a36Sopenharmony_ci *value = mmio_config_readb(addr + reg); 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case 2: 5762306a36Sopenharmony_ci *value = mmio_config_readw(addr + reg); 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci case 4: 6062306a36Sopenharmony_ci *value = mmio_config_readl(addr + reg); 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci rcu_read_unlock(); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int pci_mmcfg_write_numachip(unsigned int seg, unsigned int bus, 6962306a36Sopenharmony_ci unsigned int devfn, int reg, int len, u32 value) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci char __iomem *addr; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 7462306a36Sopenharmony_ci if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) 7562306a36Sopenharmony_ci return -EINVAL; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Ensure AMD Northbridges don't decode writes to other devices */ 7862306a36Sopenharmony_ci if (unlikely(bus == 0 && devfn >= limit)) 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci rcu_read_lock(); 8262306a36Sopenharmony_ci addr = pci_dev_base(seg, bus, devfn); 8362306a36Sopenharmony_ci if (!addr) { 8462306a36Sopenharmony_ci rcu_read_unlock(); 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci switch (len) { 8962306a36Sopenharmony_ci case 1: 9062306a36Sopenharmony_ci mmio_config_writeb(addr + reg, value); 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case 2: 9362306a36Sopenharmony_ci mmio_config_writew(addr + reg, value); 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci case 4: 9662306a36Sopenharmony_ci mmio_config_writel(addr + reg, value); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci rcu_read_unlock(); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic const struct pci_raw_ops pci_mmcfg_numachip = { 10562306a36Sopenharmony_ci .read = pci_mmcfg_read_numachip, 10662306a36Sopenharmony_ci .write = pci_mmcfg_write_numachip, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciint __init pci_numachip_init(void) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int ret = 0; 11262306a36Sopenharmony_ci u32 val; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* For remote I/O, restrict bus 0 access to the actual number of AMD 11562306a36Sopenharmony_ci Northbridges, which starts at device number 0x18 */ 11662306a36Sopenharmony_ci ret = raw_pci_read(0, 0, PCI_DEVFN(0x18, 0), 0x60, sizeof(val), &val); 11762306a36Sopenharmony_ci if (ret) 11862306a36Sopenharmony_ci goto out; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* HyperTransport fabric size in bits 6:4 */ 12162306a36Sopenharmony_ci limit = PCI_DEVFN(0x18 + ((val >> 4) & 7) + 1, 0); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Use NumaChip PCI accessors for non-extended and extended access */ 12462306a36Sopenharmony_ci raw_pci_ops = raw_pci_ext_ops = &pci_mmcfg_numachip; 12562306a36Sopenharmony_ciout: 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci} 128