Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
2e3f280
Documentation
LICENSES
arch
block
certs
crypto
drivers
accel
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cache
cdrom
cdx
char
clk
clocksource
comedi
connector
counter
cpufreq
cpuidle
crypto
cxl
dax
dca
devfreq
dio
dma-buf
dma
dpll
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hte
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
parisc
parport
pci
controller
cadence
dwc
mobiveil
Kconfig
Makefile
pci-aardvark.c
pci-ftpci100.c
pci-host-common.c
pci-host-generic.c
pci-hyperv-intf.c
pci-hyperv.c
pci-ixp4xx.c
pci-loongson.c
pci-mvebu.c
pci-rcar-gen2.c
pci-tegra.c
pci-thunder-ecam.c
pci-thunder-pem.c
pci-v3-semi.c
pci-versatile.c
pci-xgene-msi.c
pci-xgene.c
pcie-altera-msi.c
pcie-altera.c
pcie-apple.c
pcie-brcmstb.c
pcie-hisi-error.c
pcie-iproc-bcma.c
pcie-iproc-msi.c
pcie-iproc-platform.c
pcie-iproc.c
pcie-iproc.h
pcie-mediatek-gen3.c
pcie-mediatek.c
pcie-microchip-host.c
pcie-mt7621.c
pcie-rcar-ep.c
pcie-rcar-host.c
pcie-rcar.c
pcie-rcar.h
pcie-rockchip-ep.c
pcie-rockchip-host.c
pcie-rockchip.c
pcie-rockchip.h
pcie-xilinx-common.h
pcie-xilinx-cpm.c
pcie-xilinx-dma-pl.c
pcie-xilinx-nwl.c
pcie-xilinx.c
vmd.c
endpoint
hotplug
msi
pcie
switch
Kconfig
Makefile
access.c
ats.c
bus.c
doe.c
ecam.c
host-bridge.c
iov.c
irq.c
mmap.c
of.c
of_property.c
p2pdma.c
pci-acpi.c
pci-bridge-emul.c
pci-bridge-emul.h
pci-driver.c
pci-label.c
pci-mid.c
pci-pf-stub.c
pci-stub.c
pci-sysfs.c
pci.c
pci.h
probe.c
proc.c
quirks.c
remove.c
rom.c
search.c
setup-bus.c
setup-irq.c
setup-res.c
slot.c
syscall.c
vc.c
vgaarb.c
vpd.c
xen-pcifront.c
pcmcia
peci
perf
phy
pinctrl
platform
pmdomain
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
ufs
uio
usb
vdpa
vfio
vhost
video
virt
virtio
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
io_uring
ipc
kernel
lib
mm
net
rust
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
.rustfmt.toml
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
pci
/
controller
/
pci-loongson.c
Copy path
Blame
Blame
Latest commit
History
History
397 lines (339 loc) · 10.6 KB
Breadcrumbs
linux
/
drivers
/
pci
/
controller
/
pci-loongson.c
Top
File metadata and controls
Code
Blame
397 lines (339 loc) · 10.6 KB
Raw
// SPDX-License-Identifier: GPL-2.0 /* * Loongson PCI Host Controller Driver * * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com> */ #include <linux/of.h> #include <linux/of_pci.h> #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/pci-acpi.h> #include <linux/pci-ecam.h> #include "../pci.h" /* Device IDs */ #define DEV_LS2K_PCIE_PORT0 0x1a05 #define DEV_LS7A_PCIE_PORT0 0x7a09 #define DEV_LS7A_PCIE_PORT1 0x7a19 #define DEV_LS7A_PCIE_PORT2 0x7a29 #define DEV_LS7A_PCIE_PORT3 0x7a39 #define DEV_LS7A_PCIE_PORT4 0x7a49 #define DEV_LS7A_PCIE_PORT5 0x7a59 #define DEV_LS7A_PCIE_PORT6 0x7a69 #define DEV_LS2K_APB 0x7a02 #define DEV_LS7A_GMAC 0x7a03 #define DEV_LS7A_DC1 0x7a06 #define DEV_LS7A_LPC 0x7a0c #define DEV_LS7A_AHCI 0x7a08 #define DEV_LS7A_CONF 0x7a10 #define DEV_LS7A_GNET 0x7a13 #define DEV_LS7A_EHCI 0x7a14 #define DEV_LS7A_DC2 0x7a36 #define DEV_LS7A_HDMI 0x7a37 #define FLAG_CFG0 BIT(0) #define FLAG_CFG1 BIT(1) #define FLAG_DEV_FIX BIT(2) #define FLAG_DEV_HIDDEN BIT(3) struct loongson_pci_data { u32 flags; struct pci_ops *ops; }; struct loongson_pci { void __iomem *cfg0_base; void __iomem *cfg1_base; struct platform_device *pdev; const struct loongson_pci_data *data; }; /* Fixup wrong class code in PCIe bridges */ static void bridge_class_quirk(struct pci_dev *dev) { dev->class = PCI_CLASS_BRIDGE_PCI_NORMAL; } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT0, bridge_class_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT1, bridge_class_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT2, bridge_class_quirk); static void system_bus_quirk(struct pci_dev *pdev) { /* * The address space consumed by these devices is outside the * resources of the host bridge. */ pdev->mmio_always_on = 1; pdev->non_compliant_bars = 1; } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS2K_APB, system_bus_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_CONF, system_bus_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_LPC, system_bus_quirk); /* * Some Loongson PCIe ports have hardware limitations on their Maximum Read * Request Size. They can't handle anything larger than this. Sane * firmware will set proper MRRS at boot, so we only need no_inc_mrrs for * bridges. However, some MIPS Loongson firmware doesn't set MRRS properly, * so we have to enforce maximum safe MRRS, which is 256 bytes. */ #ifdef CONFIG_MIPS static void loongson_set_min_mrrs_quirk(struct pci_dev *pdev) { struct pci_bus *bus = pdev->bus; struct pci_dev *bridge; static const struct pci_device_id bridge_devids[] = { { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, { 0, }, }; /* look for the matching bridge */ while (!pci_is_root_bus(bus)) { bridge = bus->self; bus = bus->parent; if (pci_match_id(bridge_devids, bridge)) { if (pcie_get_readrq(pdev) > 256) { pci_info(pdev, "limiting MRRS to 256\n"); pcie_set_readrq(pdev, 256); } break; } } } DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_set_min_mrrs_quirk); #endif static void loongson_mrrs_quirk(struct pci_dev *pdev) { struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); bridge->no_inc_mrrs = 1; } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS2K_PCIE_PORT0, loongson_mrrs_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT0, loongson_mrrs_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT1, loongson_mrrs_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT2, loongson_mrrs_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT3, loongson_mrrs_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT4, loongson_mrrs_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT5, loongson_mrrs_quirk); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT6, loongson_mrrs_quirk); static void loongson_pci_pin_quirk(struct pci_dev *pdev) { pdev->pin = 1 + (PCI_FUNC(pdev->devfn) & 3); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_DC1, loongson_pci_pin_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_DC2, loongson_pci_pin_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_GMAC, loongson_pci_pin_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_AHCI, loongson_pci_pin_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_EHCI, loongson_pci_pin_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_GNET, loongson_pci_pin_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_HDMI, loongson_pci_pin_quirk); static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; if (acpi_disabled) return (struct loongson_pci *)(bus->sysdata); cfg = bus->sysdata; return (struct loongson_pci *)(cfg->priv); } static void __iomem *cfg0_map(struct loongson_pci *priv, struct pci_bus *bus, unsigned int devfn, int where) { unsigned long addroff = 0x0; unsigned char busnum = bus->number; if (!pci_is_root_bus(bus)) { addroff |= BIT(24); /* Type 1 Access */ addroff |= (busnum << 16); } addroff |= (devfn << 8) | where; return priv->cfg0_base + addroff; } static void __iomem *cfg1_map(struct loongson_pci *priv, struct pci_bus *bus, unsigned int devfn, int where) { unsigned long addroff = 0x0; unsigned char busnum = bus->number; if (!pci_is_root_bus(bus)) { addroff |= BIT(28); /* Type 1 Access */ addroff |= (busnum << 16); } addroff |= (devfn << 8) | (where & 0xff) | ((where & 0xf00) << 16); return priv->cfg1_base + addroff; } static bool pdev_may_exist(struct pci_bus *bus, unsigned int device, unsigned int function) { return !(pci_is_root_bus(bus) && (device >= 9 && device <= 20) && (function > 0)); } static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { unsigned int device = PCI_SLOT(devfn); unsigned int function = PCI_FUNC(devfn); struct loongson_pci *priv = pci_bus_to_loongson_pci(bus); /* * Do not read more than one device on the bus other than * the host bus. */ if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) { if (!pci_is_root_bus(bus) && (device > 0)) return NULL; } /* Don't access non-existent devices */ if (priv->data->flags & FLAG_DEV_HIDDEN) { if (!pdev_may_exist(bus, device, function)) return NULL; } /* CFG0 can only access standard space */ if (where < PCI_CFG_SPACE_SIZE && priv->cfg0_base) return cfg0_map(priv, bus, devfn, where); /* CFG1 can access extended space */ if (where < PCI_CFG_SPACE_EXP_SIZE && priv->cfg1_base) return cfg1_map(priv, bus, devfn, where); return NULL; } #ifdef CONFIG_OF static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { int irq; u8 val; irq = of_irq_parse_and_map_pci(dev, slot, pin); if (irq > 0) return irq; /* Care i8259 legacy systems */ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &val); /* i8259 only have 15 IRQs */ if (val > 15) return 0; return val; } /* LS2K/LS7A accept 8/16/32-bit PCI config operations */ static struct pci_ops loongson_pci_ops = { .map_bus = pci_loongson_map_bus, .read = pci_generic_config_read, .write = pci_generic_config_write, }; /* RS780/SR5690 only accept 32-bit PCI config operations */ static struct pci_ops loongson_pci_ops32 = { .map_bus = pci_loongson_map_bus, .read = pci_generic_config_read32, .write = pci_generic_config_write32, }; static const struct loongson_pci_data ls2k_pci_data = { .flags = FLAG_CFG1 | FLAG_DEV_FIX | FLAG_DEV_HIDDEN, .ops = &loongson_pci_ops, }; static const struct loongson_pci_data ls7a_pci_data = { .flags = FLAG_CFG1 | FLAG_DEV_FIX | FLAG_DEV_HIDDEN, .ops = &loongson_pci_ops, }; static const struct loongson_pci_data rs780e_pci_data = { .flags = FLAG_CFG0, .ops = &loongson_pci_ops32, }; static const struct of_device_id loongson_pci_of_match[] = { { .compatible = "loongson,ls2k-pci", .data = &ls2k_pci_data, }, { .compatible = "loongson,ls7a-pci", .data = &ls7a_pci_data, }, { .compatible = "loongson,rs780e-pci", .data = &rs780e_pci_data, }, {} }; static int loongson_pci_probe(struct platform_device *pdev) { struct loongson_pci *priv; struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct pci_host_bridge *bridge; struct resource *regs; if (!node) return -ENODEV; bridge = devm_pci_alloc_host_bridge(dev, sizeof(*priv)); if (!bridge) return -ENODEV; priv = pci_host_bridge_priv(bridge); priv->pdev = pdev; priv->data = of_device_get_match_data(dev); if (priv->data->flags & FLAG_CFG0) { regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) dev_err(dev, "missing mem resources for cfg0\n"); else { priv->cfg0_base = devm_pci_remap_cfg_resource(dev, regs); if (IS_ERR(priv->cfg0_base)) return PTR_ERR(priv->cfg0_base); } } if (priv->data->flags & FLAG_CFG1) { regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!regs) dev_info(dev, "missing mem resource for cfg1\n"); else { priv->cfg1_base = devm_pci_remap_cfg_resource(dev, regs); if (IS_ERR(priv->cfg1_base)) priv->cfg1_base = NULL; } } bridge->sysdata = priv; bridge->ops = priv->data->ops; bridge->map_irq = loongson_map_irq; return pci_host_probe(bridge); } static struct platform_driver loongson_pci_driver = { .driver = { .name = "loongson-pci", .of_match_table = loongson_pci_of_match, }, .probe = loongson_pci_probe, }; builtin_platform_driver(loongson_pci_driver); #endif #ifdef CONFIG_ACPI static int loongson_pci_ecam_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; struct loongson_pci *priv; struct loongson_pci_data *data; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; cfg->priv = priv; data->flags = FLAG_CFG1 | FLAG_DEV_HIDDEN; priv->data = data; priv->cfg1_base = cfg->win - (cfg->busr.start << 16); return 0; } const struct pci_ecam_ops loongson_pci_ecam_ops = { .bus_shift = 16, .init = loongson_pci_ecam_init, .pci_ops = { .map_bus = pci_loongson_map_bus, .read = pci_generic_config_read, .write = pci_generic_config_write, } }; #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
You can’t perform that action at this time.