^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Numascale NumaConnect-specific PCI code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2012 Numascale AS. All rights reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Send feedback to <support@numascale.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * PCI accessor functions derived from mmconfig_64.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <asm/pci_x86.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) static u8 limit __read_mostly;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) static inline char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) if (cfg && cfg->virt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static int pci_mmcfg_read_numachip(unsigned int seg, unsigned int bus,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) unsigned int devfn, int reg, int len, u32 *value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) char __iomem *addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) err: *value = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) /* Ensure AMD Northbridges don't decode reads to other devices */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) if (unlikely(bus == 0 && devfn >= limit)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) *value = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) rcu_read_lock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) addr = pci_dev_base(seg, bus, devfn);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) if (!addr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) rcu_read_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) switch (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) *value = mmio_config_readb(addr + reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) *value = mmio_config_readw(addr + reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) case 4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) *value = mmio_config_readl(addr + reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) rcu_read_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static int pci_mmcfg_write_numachip(unsigned int seg, unsigned int bus,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) unsigned int devfn, int reg, int len, u32 value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) char __iomem *addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) /* Ensure AMD Northbridges don't decode writes to other devices */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) if (unlikely(bus == 0 && devfn >= limit))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) rcu_read_lock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) addr = pci_dev_base(seg, bus, devfn);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) if (!addr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) rcu_read_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) switch (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) mmio_config_writeb(addr + reg, value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) mmio_config_writew(addr + reg, value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) case 4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) mmio_config_writel(addr + reg, value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) rcu_read_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) static const struct pci_raw_ops pci_mmcfg_numachip = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) .read = pci_mmcfg_read_numachip,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) .write = pci_mmcfg_write_numachip,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) int __init pci_numachip_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) u32 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) /* For remote I/O, restrict bus 0 access to the actual number of AMD
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) Northbridges, which starts at device number 0x18 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) ret = raw_pci_read(0, 0, PCI_DEVFN(0x18, 0), 0x60, sizeof(val), &val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /* HyperTransport fabric size in bits 6:4 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) limit = PCI_DEVFN(0x18 + ((val >> 4) & 7) + 1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) /* Use NumaChip PCI accessors for non-extended and extended access */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) raw_pci_ops = raw_pci_ext_ops = &pci_mmcfg_numachip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }