PCI changes for the v3.18 merge window:
Enumeration - Check Vendor ID only for Config Request Retry Status (Rajat Jain) - Enable Config Request Retry Status when supported (Rajat Jain) - Add generic domain handling (Catalin Marinas) - Generate uppercase hex for modalias interface class (Ricardo Ribalda Delgado) Resource management - Add missing MEM_64 mask in pci_assign_unassigned_bridge_resources() (Yinghai Lu) - Increase IBM ipr SAS Crocodile BARs to at least system page size (Douglas Lehr) PCI device hotplug - Prevent NULL dereference during pciehp probe (Andreas Noever) - Move _HPP & _HPX handling into core (Bjorn Helgaas) - Apply _HPP to PCIe devices as well as PCI (Bjorn Helgaas) - Apply _HPP/_HPX to display devices (Bjorn Helgaas) - Preserve SERR & PARITY settings when applying _HPP/_HPX (Bjorn Helgaas) - Preserve MPS and MRRS settings when applying _HPP/_HPX (Bjorn Helgaas) - Apply _HPP/_HPX to all devices, not just hot-added ones (Bjorn Helgaas) - Fix wait time in pciehp timeout message (Yinghai Lu) - Add more pciehp Slot Control debug output (Yinghai Lu) - Stop disabling pciehp notifications during init (Yinghai Lu) MSI - Remove arch_msi_check_device() (Alexander Gordeev) - Rename pci_msi_check_device() to pci_msi_supported() (Alexander Gordeev) - Move D0 check into pci_msi_check_device() (Alexander Gordeev) - Remove unused kobject from struct msi_desc (Yijing Wang) - Remove "pos" from the struct msi_desc msi_attrib (Yijing Wang) - Add "msi_bus" sysfs MSI/MSI-X control for endpoints (Yijing Wang) - Use __get_cached_msi_msg() instead of get_cached_msi_msg() (Yijing Wang) - Use __read_msi_msg() instead of read_msi_msg() (Yijing Wang) - Use __write_msi_msg() instead of write_msi_msg() (Yijing Wang) Power management - Drop unused runtime PM support code for PCIe ports (Rafael J. Wysocki) - Allow PCI devices to be put into D3cold during system suspend (Rafael J. Wysocki) AER - Add additional AER error strings (Gong Chen) - Make <linux/aer.h> standalone includable (Thierry Reding) Virtualization - Add ACS quirk for Solarflare SFC9120 & SFC9140 (Alex Williamson) - Add ACS quirk for Intel 10G NICs (Alex Williamson) - Add ACS quirk for AMD A88X southbridge (Marti Raudsepp) - Remove unused pci_find_upstream_pcie_bridge(), pci_get_dma_source() (Alex Williamson) - Add device flag helpers (Ethan Zhao) - Assume all Mellanox devices have broken INTx masking (Gavin Shan) Generic host bridge driver - Fix ioport_map() for !CONFIG_GENERIC_IOMAP (Liviu Dudau) - Add pci_register_io_range() and pci_pio_to_address() (Liviu Dudau) - Define PCI_IOBASE as the base of virtual PCI IO space (Liviu Dudau) - Fix the conversion of IO ranges into IO resources (Liviu Dudau) - Add pci_get_new_domain_nr() and of_get_pci_domain_nr() (Liviu Dudau) - Add support for parsing PCI host bridge resources from DT (Liviu Dudau) - Add pci_remap_iospace() to map bus I/O resources (Liviu Dudau) - Add arm64 architectural support for PCI (Liviu Dudau) APM X-Gene - Add APM X-Gene PCIe driver (Tanmay Inamdar) - Add arm64 DT APM X-Gene PCIe device tree nodes (Tanmay Inamdar) Freescale i.MX6 - Probe in module_init(), not fs_initcall() (Lucas Stach) - Delay enabling reference clock for SS until it stabilizes (Tim Harvey) Marvell MVEBU - Fix uninitialized variable in mvebu_get_tgt_attr() (Thomas Petazzoni) NVIDIA Tegra - Make sure the PCIe PLL is really reset (Eric Yuen) - Add error path tegra_msi_teardown_irq() cleanup (Jisheng Zhang) - Fix extended configuration space mapping (Peter Daifuku) - Implement resource hierarchy (Thierry Reding) - Clear CLKREQ# enable on port disable (Thierry Reding) - Add Tegra124 support (Thierry Reding) ST Microelectronics SPEAr13xx - Pass config resource through reg property (Pratyush Anand) Synopsys DesignWare - Use NULL instead of false (Fabio Estevam) - Parse bus-range property from devicetree (Lucas Stach) - Use pci_create_root_bus() instead of pci_scan_root_bus() (Lucas Stach) - Remove pci_assign_unassigned_resources() (Lucas Stach) - Check private_data validity in single place (Lucas Stach) - Setup and clear exactly one MSI at a time (Lucas Stach) - Remove open-coded bitmap operations (Lucas Stach) - Fix configuration base address when using 'reg' (Minghuan Lian) - Fix IO resource end address calculation (Minghuan Lian) - Rename get_msi_data() to get_msi_addr() (Minghuan Lian) - Add get_msi_data() to pcie_host_ops (Minghuan Lian) - Add support for v3.65 hardware (Murali Karicheri) - Fold struct pcie_port_info into struct pcie_port (Pratyush Anand) TI Keystone - Add TI Keystone PCIe driver (Murali Karicheri) - Limit MRSS for all downstream devices (Murali Karicheri) - Assume controller is already in RC mode (Murali Karicheri) - Set device ID based on SoC to support multiple ports (Murali Karicheri) Xilinx AXI - Add Xilinx AXI PCIe driver (Srikanth Thokala) - Fix xilinx_pcie_assign_msi() return value test (Dan Carpenter) Miscellaneous - Clean up whitespace (Quentin Lambert) - Remove assignments from "if" conditions (Quentin Lambert) - Move PCI_VENDOR_ID_VMWARE to pci_ids.h (Francesco Ruggeri) - x86: Mark DMI tables as initialization data (Mathias Krause) - x86: Move __init annotation to the correct place (Mathias Krause) - x86: Mark constants of pci_mmcfg_nvidia_mcp55() as __initconst (Mathias Krause) - x86: Constify pci_mmcfg_probes[] array (Mathias Krause) - x86: Mark PCI BIOS initialization code as such (Mathias Krause) - Parenthesize PCI_DEVID and PCI_VPD_LRDT_ID parameters (Megan Kamiya) - Remove unnecessary variable in pci_add_dynid() (Tobias Klauser) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUNWmJAAoJEFmIoMA60/r8GncP/3uHRoBrnaF6pv+S1l1p3Fs/ l1kKH91/IuAAU7VJX8pkNybFqx02topWmiVVXAzqvD01PcRLGCLjPbWl5h+y5/Ja CHZH33AwHAmm0kt4BrOSOeHTLJhAigly2zV3P4F8jRIgyaeMoGZ6Ko4tkQUpm21k +ohrOd4cxYkmzzCjKwsZZhKnyRNpae8FmTk3VQBPuN8DbhvFPrqo5/+GeAdSZTdS HZHpfl2HL4095aY7uBVsZqNkjQyl6SnWwjkjLnuI8q3qA3BLgDZE/Jr8F/MNuW1V y01JIjerFWMDFyBIkpg7moYnODy6oP3KvczwYdKGmqsJja+0MQvYhLTwD+R/yTQS SewJA0mL3T3EJEfnFYkCiaIX27xIwk/FxHfaKPN91xgx/QM7xCVZNrU2/dXjhoX1 GqLKxOEaFHhWWTyT5Dj27I0ZcElzFZ3tIwvrHfs8y22oAuAlsAypaUgvUwRfL4CO hOj4ITZa0t041sYWqxCoGAA9Fdp8HMzNKKS5F4mhADz4Ad9v6uPCNv/s/RoxVsbm jhZOtPYJ0/iCA+kNVX563S8Z3VpfPI+7bBjcj2WKdzW+IlICvOKT+kvwL2Tv/rE7 w0hrNsbkgGsYbPldMx7LwCavsUtYFuNj0zoU6vkhP2jk6O2Tn5VXDmjrXH0v3iHI v03vlUtre0bQ26fzDyLQ =4Zv1 -----END PGP SIGNATURE----- Merge tag 'pci-v3.18-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci Pull PCI updates from Bjorn Helgaas: "The interesting things here are: - Turn on Config Request Retry Status Software Visibility. This caused hangs last time, but we included a fix this time. - Rework PCI device configuration to use _HPP/_HPX more aggressively - Allow PCI devices to be put into D3cold during system suspend - Add arm64 PCI support - Add APM X-Gene host bridge driver - Add TI Keystone host bridge driver - Add Xilinx AXI host bridge driver More detailed summary: Enumeration - Check Vendor ID only for Config Request Retry Status (Rajat Jain) - Enable Config Request Retry Status when supported (Rajat Jain) - Add generic domain handling (Catalin Marinas) - Generate uppercase hex for modalias interface class (Ricardo Ribalda Delgado) Resource management - Add missing MEM_64 mask in pci_assign_unassigned_bridge_resources() (Yinghai Lu) - Increase IBM ipr SAS Crocodile BARs to at least system page size (Douglas Lehr) PCI device hotplug - Prevent NULL dereference during pciehp probe (Andreas Noever) - Move _HPP & _HPX handling into core (Bjorn Helgaas) - Apply _HPP to PCIe devices as well as PCI (Bjorn Helgaas) - Apply _HPP/_HPX to display devices (Bjorn Helgaas) - Preserve SERR & PARITY settings when applying _HPP/_HPX (Bjorn Helgaas) - Preserve MPS and MRRS settings when applying _HPP/_HPX (Bjorn Helgaas) - Apply _HPP/_HPX to all devices, not just hot-added ones (Bjorn Helgaas) - Fix wait time in pciehp timeout message (Yinghai Lu) - Add more pciehp Slot Control debug output (Yinghai Lu) - Stop disabling pciehp notifications during init (Yinghai Lu) MSI - Remove arch_msi_check_device() (Alexander Gordeev) - Rename pci_msi_check_device() to pci_msi_supported() (Alexander Gordeev) - Move D0 check into pci_msi_check_device() (Alexander Gordeev) - Remove unused kobject from struct msi_desc (Yijing Wang) - Remove "pos" from the struct msi_desc msi_attrib (Yijing Wang) - Add "msi_bus" sysfs MSI/MSI-X control for endpoints (Yijing Wang) - Use __get_cached_msi_msg() instead of get_cached_msi_msg() (Yijing Wang) - Use __read_msi_msg() instead of read_msi_msg() (Yijing Wang) - Use __write_msi_msg() instead of write_msi_msg() (Yijing Wang) Power management - Drop unused runtime PM support code for PCIe ports (Rafael J. Wysocki) - Allow PCI devices to be put into D3cold during system suspend (Rafael J. Wysocki) AER - Add additional AER error strings (Gong Chen) - Make <linux/aer.h> standalone includable (Thierry Reding) Virtualization - Add ACS quirk for Solarflare SFC9120 & SFC9140 (Alex Williamson) - Add ACS quirk for Intel 10G NICs (Alex Williamson) - Add ACS quirk for AMD A88X southbridge (Marti Raudsepp) - Remove unused pci_find_upstream_pcie_bridge(), pci_get_dma_source() (Alex Williamson) - Add device flag helpers (Ethan Zhao) - Assume all Mellanox devices have broken INTx masking (Gavin Shan) Generic host bridge driver - Fix ioport_map() for !CONFIG_GENERIC_IOMAP (Liviu Dudau) - Add pci_register_io_range() and pci_pio_to_address() (Liviu Dudau) - Define PCI_IOBASE as the base of virtual PCI IO space (Liviu Dudau) - Fix the conversion of IO ranges into IO resources (Liviu Dudau) - Add pci_get_new_domain_nr() and of_get_pci_domain_nr() (Liviu Dudau) - Add support for parsing PCI host bridge resources from DT (Liviu Dudau) - Add pci_remap_iospace() to map bus I/O resources (Liviu Dudau) - Add arm64 architectural support for PCI (Liviu Dudau) APM X-Gene - Add APM X-Gene PCIe driver (Tanmay Inamdar) - Add arm64 DT APM X-Gene PCIe device tree nodes (Tanmay Inamdar) Freescale i.MX6 - Probe in module_init(), not fs_initcall() (Lucas Stach) - Delay enabling reference clock for SS until it stabilizes (Tim Harvey) Marvell MVEBU - Fix uninitialized variable in mvebu_get_tgt_attr() (Thomas Petazzoni) NVIDIA Tegra - Make sure the PCIe PLL is really reset (Eric Yuen) - Add error path tegra_msi_teardown_irq() cleanup (Jisheng Zhang) - Fix extended configuration space mapping (Peter Daifuku) - Implement resource hierarchy (Thierry Reding) - Clear CLKREQ# enable on port disable (Thierry Reding) - Add Tegra124 support (Thierry Reding) ST Microelectronics SPEAr13xx - Pass config resource through reg property (Pratyush Anand) Synopsys DesignWare - Use NULL instead of false (Fabio Estevam) - Parse bus-range property from devicetree (Lucas Stach) - Use pci_create_root_bus() instead of pci_scan_root_bus() (Lucas Stach) - Remove pci_assign_unassigned_resources() (Lucas Stach) - Check private_data validity in single place (Lucas Stach) - Setup and clear exactly one MSI at a time (Lucas Stach) - Remove open-coded bitmap operations (Lucas Stach) - Fix configuration base address when using 'reg' (Minghuan Lian) - Fix IO resource end address calculation (Minghuan Lian) - Rename get_msi_data() to get_msi_addr() (Minghuan Lian) - Add get_msi_data() to pcie_host_ops (Minghuan Lian) - Add support for v3.65 hardware (Murali Karicheri) - Fold struct pcie_port_info into struct pcie_port (Pratyush Anand) TI Keystone - Add TI Keystone PCIe driver (Murali Karicheri) - Limit MRSS for all downstream devices (Murali Karicheri) - Assume controller is already in RC mode (Murali Karicheri) - Set device ID based on SoC to support multiple ports (Murali Karicheri) Xilinx AXI - Add Xilinx AXI PCIe driver (Srikanth Thokala) - Fix xilinx_pcie_assign_msi() return value test (Dan Carpenter) Miscellaneous - Clean up whitespace (Quentin Lambert) - Remove assignments from "if" conditions (Quentin Lambert) - Move PCI_VENDOR_ID_VMWARE to pci_ids.h (Francesco Ruggeri) - x86: Mark DMI tables as initialization data (Mathias Krause) - x86: Move __init annotation to the correct place (Mathias Krause) - x86: Mark constants of pci_mmcfg_nvidia_mcp55() as __initconst (Mathias Krause) - x86: Constify pci_mmcfg_probes[] array (Mathias Krause) - x86: Mark PCI BIOS initialization code as such (Mathias Krause) - Parenthesize PCI_DEVID and PCI_VPD_LRDT_ID parameters (Megan Kamiya) - Remove unnecessary variable in pci_add_dynid() (Tobias Klauser)" * tag 'pci-v3.18-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (109 commits) arm64: dts: Add APM X-Gene PCIe device tree nodes PCI: Add ACS quirk for AMD A88X southbridge devices PCI: xgene: Add APM X-Gene PCIe driver PCI: designware: Remove open-coded bitmap operations PCI/MSI: Remove unnecessary temporary variable PCI/MSI: Use __write_msi_msg() instead of write_msi_msg() MSI/powerpc: Use __read_msi_msg() instead of read_msi_msg() PCI/MSI: Use __get_cached_msi_msg() instead of get_cached_msi_msg() PCI/MSI: Add "msi_bus" sysfs MSI/MSI-X control for endpoints PCI/MSI: Remove "pos" from the struct msi_desc msi_attrib PCI/MSI: Remove unused kobject from struct msi_desc PCI/MSI: Rename pci_msi_check_device() to pci_msi_supported() PCI/MSI: Move D0 check into pci_msi_check_device() PCI/MSI: Remove arch_msi_check_device() irqchip: armada-370-xp: Remove arch_msi_check_device() PCI/MSI/PPC: Remove arch_msi_check_device() arm64: Add architectural support for PCI PCI: Add pci_remap_iospace() to map bus I/O resources of/pci: Add support for parsing PCI host bridge resources from DT of/pci: Add pci_get_new_domain_nr() and of_get_pci_domain_nr() ... Conflicts: arch/arm64/boot/dts/apm-storm.dtsi
This commit is contained in:
commit
80213c03c4
|
@ -65,6 +65,16 @@ Description:
|
|||
force a rescan of all PCI buses in the system, and
|
||||
re-discover previously removed devices.
|
||||
|
||||
What: /sys/bus/pci/devices/.../msi_bus
|
||||
Date: September 2014
|
||||
Contact: Linux PCI developers <linux-pci@vger.kernel.org>
|
||||
Description:
|
||||
Writing a zero value to this attribute disallows MSI and
|
||||
MSI-X for any future drivers of the device. If the device
|
||||
is a bridge, MSI and MSI-X will be disallowed for future
|
||||
drivers of all child devices under the bridge. Drivers
|
||||
must be reloaded for the new setting to take effect.
|
||||
|
||||
What: /sys/bus/pci/devices/.../msi_irqs/
|
||||
Date: September, 2011
|
||||
Contact: Neil Horman <nhorman@tuxdriver.com>
|
||||
|
|
|
@ -23,3 +23,6 @@ Required properties:
|
|||
|
||||
Optional properties:
|
||||
- reset-gpio: gpio pin number of power good signal
|
||||
- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
|
||||
specify this property, to keep backwards compatibility a range of 0x00-0xff
|
||||
is assumed if not present)
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
NVIDIA Tegra PCIe controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra20-pcie" or "nvidia,tegra30-pcie"
|
||||
- compatible: Must be one of:
|
||||
- "nvidia,tegra20-pcie"
|
||||
- "nvidia,tegra30-pcie"
|
||||
- "nvidia,tegra124-pcie"
|
||||
- device_type: Must be "pci"
|
||||
- reg: A list of physical base address and length for each set of controller
|
||||
registers. Must contain an entry for each entry in the reg-names property.
|
||||
|
@ -57,6 +60,11 @@ Required properties:
|
|||
- afi
|
||||
- pcie_x
|
||||
|
||||
Required properties on Tegra124 and later:
|
||||
- phys: Must contain an entry for each entry in phy-names.
|
||||
- phy-names: Must include the following entries:
|
||||
- pcie
|
||||
|
||||
Power supplies for Tegra20:
|
||||
- avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
@ -84,6 +92,21 @@ Power supplies for Tegra30:
|
|||
- avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
||||
Power supplies for Tegra124:
|
||||
- Required:
|
||||
- avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
|
||||
Must supply 3.3 V.
|
||||
- hvdd-pex-pll-e-supply: High-voltage supply for PLLE (shared with USB3).
|
||||
Must supply 3.3 V.
|
||||
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
|
||||
supply 2.8-3.3 V.
|
||||
- avdd-pll-erefe-supply: Power supply for PLLE (shared with USB3). Must
|
||||
supply 1.05 V.
|
||||
|
||||
Root ports are defined as subnodes of the PCIe controller node.
|
||||
|
||||
Required properties:
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
TI Keystone PCIe interface
|
||||
|
||||
Keystone PCI host Controller is based on Designware PCI h/w version 3.65.
|
||||
It shares common functions with PCIe Designware core driver and inherit
|
||||
common properties defined in
|
||||
Documentation/devicetree/bindings/pci/designware-pci.txt
|
||||
|
||||
Please refer to Documentation/devicetree/bindings/pci/designware-pci.txt
|
||||
for the details of Designware DT bindings. Additional properties are
|
||||
described here as well as properties that are not applicable.
|
||||
|
||||
Required Properties:-
|
||||
|
||||
compatibility: "ti,keystone-pcie"
|
||||
reg: index 1 is the base address and length of DW application registers.
|
||||
index 2 is the base address and length of PCI device ID register.
|
||||
|
||||
pcie_msi_intc : Interrupt controller device node for MSI IRQ chip
|
||||
interrupt-cells: should be set to 1
|
||||
interrupt-parent: Parent interrupt controller phandle
|
||||
interrupts: GIC interrupt lines connected to PCI MSI interrupt lines
|
||||
|
||||
Example:
|
||||
pcie_msi_intc: msi-interrupt-controller {
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 31 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 32 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 33 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 34 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 35 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 36 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 37 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
|
||||
pcie_intc: Interrupt controller device node for Legacy IRQ chip
|
||||
interrupt-cells: should be set to 1
|
||||
interrupt-parent: Parent interrupt controller phandle
|
||||
interrupts: GIC interrupt lines connected to PCI Legacy interrupt lines
|
||||
|
||||
Example:
|
||||
pcie_intc: legacy-interrupt-controller {
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <GIC_SPI 26 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 27 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 28 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 29 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
|
||||
Optional properties:-
|
||||
phys: phandle to Generic Keystone SerDes phy for PCI
|
||||
phy-names: name of the Generic Keystine SerDes phy for PCI
|
||||
- If boot loader already does PCI link establishment, then phys and
|
||||
phy-names shouldn't be present.
|
||||
|
||||
Designware DT Properties not applicable for Keystone PCI
|
||||
|
||||
1. pcie_bus clock-names not used. Instead, a phandle to phys is used.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
* AppliedMicro X-Gene PCIe interface
|
||||
|
||||
Required properties:
|
||||
- device_type: set to "pci"
|
||||
- compatible: should contain "apm,xgene-pcie" to identify the core.
|
||||
- reg: A list of physical base address and length for each set of controller
|
||||
registers. Must contain an entry for each entry in the reg-names
|
||||
property.
|
||||
- reg-names: Must include the following entries:
|
||||
"csr": controller configuration registers.
|
||||
"cfg": pcie configuration space registers.
|
||||
- #address-cells: set to <3>
|
||||
- #size-cells: set to <2>
|
||||
- ranges: ranges for the outbound memory, I/O regions.
|
||||
- dma-ranges: ranges for the inbound memory regions.
|
||||
- #interrupt-cells: set to <1>
|
||||
- interrupt-map-mask and interrupt-map: standard PCI properties
|
||||
to define the mapping of the PCIe interface to interrupt
|
||||
numbers.
|
||||
- clocks: from common clock binding: handle to pci clock.
|
||||
|
||||
Optional properties:
|
||||
- status: Either "ok" or "disabled".
|
||||
- dma-coherent: Present if dma operations are coherent
|
||||
|
||||
Example:
|
||||
|
||||
SoC specific DT Entry:
|
||||
|
||||
pcie0: pcie@1f2b0000 {
|
||||
status = "disabled";
|
||||
device_type = "pci";
|
||||
compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
||||
#interrupt-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
#address-cells = <3>;
|
||||
reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */
|
||||
0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
||||
reg-names = "csr", "cfg";
|
||||
ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */
|
||||
0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
|
||||
dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
||||
0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
||||
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
||||
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
|
||||
0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
|
||||
0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
|
||||
0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
|
||||
dma-coherent;
|
||||
clocks = <&pcie0clk 0>;
|
||||
};
|
||||
|
||||
|
||||
Board specific DT Entry:
|
||||
&pcie0 {
|
||||
status = "ok";
|
||||
};
|
|
@ -0,0 +1,62 @@
|
|||
* Xilinx AXI PCIe Root Port Bridge DT description
|
||||
|
||||
Required properties:
|
||||
- #address-cells: Address representation for root ports, set to <3>
|
||||
- #size-cells: Size representation for root ports, set to <2>
|
||||
- #interrupt-cells: specifies the number of cells needed to encode an
|
||||
interrupt source. The value must be 1.
|
||||
- compatible: Should contain "xlnx,axi-pcie-host-1.00.a"
|
||||
- reg: Should contain AXI PCIe registers location and length
|
||||
- device_type: must be "pci"
|
||||
- interrupts: Should contain AXI PCIe interrupt
|
||||
- interrupt-map-mask,
|
||||
interrupt-map: standard PCI properties to define the mapping of the
|
||||
PCI interface to interrupt numbers.
|
||||
- ranges: ranges for the PCI memory regions (I/O space region is not
|
||||
supported by hardware)
|
||||
Please refer to the standard PCI bus binding document for a more
|
||||
detailed explanation
|
||||
|
||||
Optional properties:
|
||||
- bus-range: PCI bus numbers covered
|
||||
|
||||
Interrupt controller child node
|
||||
+++++++++++++++++++++++++++++++
|
||||
Required properties:
|
||||
- interrupt-controller: identifies the node as an interrupt controller
|
||||
- #address-cells: specifies the number of cells needed to encode an
|
||||
address. The value must be 0.
|
||||
- #interrupt-cells: specifies the number of cells needed to encode an
|
||||
interrupt source. The value must be 1.
|
||||
|
||||
NOTE:
|
||||
The core provides a single interrupt for both INTx/MSI messages. So,
|
||||
created a interrupt controller node to support 'interrupt-map' DT
|
||||
functionality. The driver will create an IRQ domain for this map, decode
|
||||
the four INTx interrupts in ISR and route them to this domain.
|
||||
|
||||
|
||||
Example:
|
||||
++++++++
|
||||
|
||||
pci_express: axi-pcie@50000000 {
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "xlnx,axi-pcie-host-1.00.a";
|
||||
reg = < 0x50000000 0x10000000 >;
|
||||
device_type = "pci";
|
||||
interrupts = < 0 52 4 >;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0 0 0 1 &pcie_intc 1>,
|
||||
<0 0 0 2 &pcie_intc 2>,
|
||||
<0 0 0 3 &pcie_intc 3>,
|
||||
<0 0 0 4 &pcie_intc 4>;
|
||||
ranges = < 0x02000000 0 0x60000000 0x60000000 0 0x10000000 >;
|
||||
|
||||
pcie_intc: interrupt-controller {
|
||||
interrupt-controller;
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
}
|
||||
};
|
|
@ -264,8 +264,10 @@ IIO
|
|||
IO region
|
||||
devm_release_mem_region()
|
||||
devm_release_region()
|
||||
devm_release_resource()
|
||||
devm_request_mem_region()
|
||||
devm_request_region()
|
||||
devm_request_resource()
|
||||
|
||||
IOMAP
|
||||
devm_ioport_map()
|
||||
|
|
15
MAINTAINERS
15
MAINTAINERS
|
@ -6939,6 +6939,14 @@ F: include/linux/pci*
|
|||
F: arch/x86/pci/
|
||||
F: arch/x86/kernel/quirks.c
|
||||
|
||||
PCI DRIVER FOR APPLIEDMICRO XGENE
|
||||
M: Tanmay Inamdar <tinamdar@apm.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
L: linux-arm-kernel@lists.infradead.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/pci/xgene-pci.txt
|
||||
F: drivers/pci/host/pci-xgene.c
|
||||
|
||||
PCI DRIVER FOR IMX6
|
||||
M: Richard Zhu <r65037@freescale.com>
|
||||
M: Lucas Stach <l.stach@pengutronix.de>
|
||||
|
@ -6947,6 +6955,13 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
|||
S: Maintained
|
||||
F: drivers/pci/host/*imx6*
|
||||
|
||||
PCI DRIVER FOR TI KEYSTONE
|
||||
M: Murali Karicheri <m-karicheri2@ti.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: drivers/pci/host/*keystone*
|
||||
|
||||
PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
|
||||
M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
M: Jason Cooper <jason@lakedaemon.net>
|
||||
|
|
|
@ -85,7 +85,8 @@
|
|||
|
||||
pcie0: pcie@b1000000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb1000000 0x4000>;
|
||||
reg = <0xb1000000 0x4000>, <0x80000000 0x20000>;
|
||||
reg-names = "dbi", "config";
|
||||
interrupts = <0 68 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 68 0x4>;
|
||||
|
@ -95,15 +96,15 @@
|
|||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0x80000000 0x80000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0x80020000 0 0x00010000 /* downstream I/O */
|
||||
ranges = <0x81000000 0 0 0x80020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0x80030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie1: pcie@b1800000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb1800000 0x4000>;
|
||||
reg = <0xb1800000 0x4000>, <0x90000000 0x20000>;
|
||||
reg-names = "dbi", "config";
|
||||
interrupts = <0 69 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 69 0x4>;
|
||||
|
@ -113,15 +114,15 @@
|
|||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0x90000000 0x90000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0x90020000 0 0x00010000 /* downstream I/O */
|
||||
ranges = <0x81000000 0 0 0x90020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0x90030000 0x90030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie2: pcie@b4000000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb4000000 0x4000>;
|
||||
reg = <0xb4000000 0x4000>, <0xc0000000 0x20000>;
|
||||
reg-names = "dbi", "config";
|
||||
interrupts = <0 70 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 70 0x4>;
|
||||
|
@ -131,8 +132,7 @@
|
|||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0xc0000000 0xc0000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0xc0020000 0 0x00010000 /* downstream I/O */
|
||||
ranges = <0x81000000 0 0 0xc0020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0xc0030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
|
|
@ -50,7 +50,8 @@
|
|||
|
||||
pcie0: pcie@b1000000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb1000000 0x4000>;
|
||||
reg = <0xb1000000 0x4000>, <0x80000000 0x20000>;
|
||||
reg-names = "dbi", "config";
|
||||
interrupts = <0 68 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 68 0x4>;
|
||||
|
@ -60,8 +61,7 @@
|
|||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0x80000000 0x80000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0x80020000 0 0x00010000 /* downstream I/O */
|
||||
ranges = <0x81000000 0 0 0x80020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0x80030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
|
|
@ -178,6 +178,7 @@ static inline void __iomem *__typesafe_io(unsigned long addr)
|
|||
|
||||
/* PCI fixed i/o mapping */
|
||||
#define PCI_IO_VIRT_BASE 0xfee00000
|
||||
#define PCI_IOBASE ((void __iomem *)PCI_IO_VIRT_BASE)
|
||||
|
||||
#if defined(CONFIG_PCI)
|
||||
void pci_ioremap_set_mem_type(int mem_type);
|
||||
|
|
|
@ -660,6 +660,7 @@ static void __init pci_v3_preinit(void)
|
|||
{
|
||||
unsigned long flags;
|
||||
unsigned int temp;
|
||||
phys_addr_t io_address = pci_pio_to_address(io_mem.start);
|
||||
|
||||
pcibios_min_mem = 0x00100000;
|
||||
|
||||
|
@ -701,7 +702,7 @@ static void __init pci_v3_preinit(void)
|
|||
/*
|
||||
* Setup window 2 - PCI IO
|
||||
*/
|
||||
v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_mem.start) |
|
||||
v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_address) |
|
||||
V3_LB_BASE_ENABLE);
|
||||
v3_writew(V3_LB_MAP2, v3_addr_to_lb_map2(0));
|
||||
|
||||
|
@ -742,6 +743,7 @@ static void __init pci_v3_preinit(void)
|
|||
static void __init pci_v3_postinit(void)
|
||||
{
|
||||
unsigned int pci_cmd;
|
||||
phys_addr_t io_address = pci_pio_to_address(io_mem.start);
|
||||
|
||||
pci_cmd = PCI_COMMAND_MEMORY |
|
||||
PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
|
||||
|
@ -758,7 +760,7 @@ static void __init pci_v3_postinit(void)
|
|||
"interrupt: %d\n", ret);
|
||||
#endif
|
||||
|
||||
register_isa_ports(non_mem.start, io_mem.start, 0);
|
||||
register_isa_ports(non_mem.start, io_address, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -867,33 +869,32 @@ static int __init pci_v3_probe(struct platform_device *pdev)
|
|||
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
if (!range.flags) {
|
||||
of_pci_range_to_resource(&range, np, &conf_mem);
|
||||
ret = of_pci_range_to_resource(&range, np, &conf_mem);
|
||||
conf_mem.name = "PCIv3 config";
|
||||
}
|
||||
if (range.flags & IORESOURCE_IO) {
|
||||
of_pci_range_to_resource(&range, np, &io_mem);
|
||||
ret = of_pci_range_to_resource(&range, np, &io_mem);
|
||||
io_mem.name = "PCIv3 I/O";
|
||||
}
|
||||
if ((range.flags & IORESOURCE_MEM) &&
|
||||
!(range.flags & IORESOURCE_PREFETCH)) {
|
||||
non_mem_pci = range.pci_addr;
|
||||
non_mem_pci_sz = range.size;
|
||||
of_pci_range_to_resource(&range, np, &non_mem);
|
||||
ret = of_pci_range_to_resource(&range, np, &non_mem);
|
||||
non_mem.name = "PCIv3 non-prefetched mem";
|
||||
}
|
||||
if ((range.flags & IORESOURCE_MEM) &&
|
||||
(range.flags & IORESOURCE_PREFETCH)) {
|
||||
pre_mem_pci = range.pci_addr;
|
||||
pre_mem_pci_sz = range.size;
|
||||
of_pci_range_to_resource(&range, np, &pre_mem);
|
||||
ret = of_pci_range_to_resource(&range, np, &pre_mem);
|
||||
pre_mem.name = "PCIv3 prefetched mem";
|
||||
}
|
||||
}
|
||||
|
||||
if (!conf_mem.start || !io_mem.start ||
|
||||
!non_mem.start || !pre_mem.start) {
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "missing ranges in device node\n");
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
pci_v3.map_irq = of_irq_parse_and_map_pci;
|
||||
|
|
|
@ -83,7 +83,7 @@ config MMU
|
|||
def_bool y
|
||||
|
||||
config NO_IOPORT_MAP
|
||||
def_bool y
|
||||
def_bool y if !PCI
|
||||
|
||||
config STACKTRACE_SUPPORT
|
||||
def_bool y
|
||||
|
@ -163,6 +163,26 @@ menu "Bus support"
|
|||
config ARM_AMBA
|
||||
bool
|
||||
|
||||
config PCI
|
||||
bool "PCI support"
|
||||
help
|
||||
This feature enables support for PCI bus system. If you say Y
|
||||
here, the kernel will include drivers and infrastructure code
|
||||
to support PCI bus devices.
|
||||
|
||||
config PCI_DOMAINS
|
||||
def_bool PCI
|
||||
|
||||
config PCI_DOMAINS_GENERIC
|
||||
def_bool PCI
|
||||
|
||||
config PCI_SYSCALL
|
||||
def_bool PCI
|
||||
|
||||
source "drivers/pci/Kconfig"
|
||||
source "drivers/pci/pcie/Kconfig"
|
||||
source "drivers/pci/hotplug/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Kernel Features"
|
||||
|
|
|
@ -25,6 +25,14 @@
|
|||
};
|
||||
};
|
||||
|
||||
&pcie0clk {
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
&serial0 {
|
||||
status = "ok";
|
||||
};
|
||||
|
|
|
@ -282,6 +282,171 @@
|
|||
enable-mask = <0x10>;
|
||||
clock-output-names = "rngpkaclk";
|
||||
};
|
||||
|
||||
pcie0clk: pcie0clk@1f2bc000 {
|
||||
status = "disabled";
|
||||
compatible = "apm,xgene-device-clock";
|
||||
#clock-cells = <1>;
|
||||
clocks = <&socplldiv2 0>;
|
||||
reg = <0x0 0x1f2bc000 0x0 0x1000>;
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "pcie0clk";
|
||||
};
|
||||
|
||||
pcie1clk: pcie1clk@1f2cc000 {
|
||||
status = "disabled";
|
||||
compatible = "apm,xgene-device-clock";
|
||||
#clock-cells = <1>;
|
||||
clocks = <&socplldiv2 0>;
|
||||
reg = <0x0 0x1f2cc000 0x0 0x1000>;
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "pcie1clk";
|
||||
};
|
||||
|
||||
pcie2clk: pcie2clk@1f2dc000 {
|
||||
status = "disabled";
|
||||
compatible = "apm,xgene-device-clock";
|
||||
#clock-cells = <1>;
|
||||
clocks = <&socplldiv2 0>;
|
||||
reg = <0x0 0x1f2dc000 0x0 0x1000>;
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "pcie2clk";
|
||||
};
|
||||
|
||||
pcie3clk: pcie3clk@1f50c000 {
|
||||
status = "disabled";
|
||||
compatible = "apm,xgene-device-clock";
|
||||
#clock-cells = <1>;
|
||||
clocks = <&socplldiv2 0>;
|
||||
reg = <0x0 0x1f50c000 0x0 0x1000>;
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "pcie3clk";
|
||||
};
|
||||
|
||||
pcie4clk: pcie4clk@1f51c000 {
|
||||
status = "disabled";
|
||||
compatible = "apm,xgene-device-clock";
|
||||
#clock-cells = <1>;
|
||||
clocks = <&socplldiv2 0>;
|
||||
reg = <0x0 0x1f51c000 0x0 0x1000>;
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "pcie4clk";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0: pcie@1f2b0000 {
|
||||
status = "disabled";
|
||||
device_type = "pci";
|
||||
compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
||||
#interrupt-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
#address-cells = <3>;
|
||||
reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */
|
||||
0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
||||
reg-names = "csr", "cfg";
|
||||
ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */
|
||||
0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
|
||||
dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
||||
0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
||||
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
||||
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
|
||||
0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
|
||||
0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
|
||||
0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
|
||||
dma-coherent;
|
||||
clocks = <&pcie0clk 0>;
|
||||
};
|
||||
|
||||
pcie1: pcie@1f2c0000 {
|
||||
status = "disabled";
|
||||
device_type = "pci";
|
||||
compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
||||
#interrupt-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
#address-cells = <3>;
|
||||
reg = < 0x00 0x1f2c0000 0x0 0x00010000 /* Controller registers */
|
||||
0xd0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
||||
reg-names = "csr", "cfg";
|
||||
ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000 /* io */
|
||||
0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */
|
||||
dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
||||
0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
||||
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
||||
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1
|
||||
0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1
|
||||
0x0 0x0 0x0 0x3 &gic 0x0 0xca 0x1
|
||||
0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
|
||||
dma-coherent;
|
||||
clocks = <&pcie1clk 0>;
|
||||
};
|
||||
|
||||
pcie2: pcie@1f2d0000 {
|
||||
status = "disabled";
|
||||
device_type = "pci";
|
||||
compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
||||
#interrupt-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
#address-cells = <3>;
|
||||
reg = < 0x00 0x1f2d0000 0x0 0x00010000 /* Controller registers */
|
||||
0x90 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
||||
reg-names = "csr", "cfg";
|
||||
ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000 /* io */
|
||||
0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */
|
||||
dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
||||
0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
||||
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
||||
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1
|
||||
0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1
|
||||
0x0 0x0 0x0 0x3 &gic 0x0 0xd0 0x1
|
||||
0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
|
||||
dma-coherent;
|
||||
clocks = <&pcie2clk 0>;
|
||||
};
|
||||
|
||||
pcie3: pcie@1f500000 {
|
||||
status = "disabled";
|
||||
device_type = "pci";
|
||||
compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
||||
#interrupt-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
#address-cells = <3>;
|
||||
reg = < 0x00 0x1f500000 0x0 0x00010000 /* Controller registers */
|
||||
0xa0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
||||
reg-names = "csr", "cfg";
|
||||
ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000 /* io */
|
||||
0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem */
|
||||
dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
||||
0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
||||
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
||||
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1
|
||||
0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1
|
||||
0x0 0x0 0x0 0x3 &gic 0x0 0xd6 0x1
|
||||
0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
|
||||
dma-coherent;
|
||||
clocks = <&pcie3clk 0>;
|
||||
};
|
||||
|
||||
pcie4: pcie@1f510000 {
|
||||
status = "disabled";
|
||||
device_type = "pci";
|
||||
compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
||||
#interrupt-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
#address-cells = <3>;
|
||||
reg = < 0x00 0x1f510000 0x0 0x00010000 /* Controller registers */
|
||||
0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */
|
||||
reg-names = "csr", "cfg";
|
||||
ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000 /* io */
|
||||
0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */
|
||||
dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
||||
0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
||||
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
||||
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1
|
||||
0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1
|
||||
0x0 0x0 0x0 0x3 &gic 0x0 0xdc 0x1
|
||||
0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
|
||||
dma-coherent;
|
||||
clocks = <&pcie4clk 0>;
|
||||
};
|
||||
|
||||
serial0: serial@1c020000 {
|
||||
|
|
|
@ -29,6 +29,7 @@ generic-y += mman.h
|
|||
generic-y += msgbuf.h
|
||||
generic-y += mutex.h
|
||||
generic-y += pci.h
|
||||
generic-y += pci-bridge.h
|
||||
generic-y += poll.h
|
||||
generic-y += preempt.h
|
||||
generic-y += resource.h
|
||||
|
|
|
@ -121,7 +121,8 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
|
|||
/*
|
||||
* I/O port access primitives.
|
||||
*/
|
||||
#define IO_SPACE_LIMIT 0xffff
|
||||
#define arch_has_dev_port() (1)
|
||||
#define IO_SPACE_LIMIT (SZ_32M - 1)
|
||||
#define PCI_IOBASE ((void __iomem *)(MODULES_VADDR - SZ_32M))
|
||||
|
||||
static inline u8 inb(unsigned long addr)
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef __ASM_PCI_H
|
||||
#define __ASM_PCI_H
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm-generic/pci-bridge.h>
|
||||
#include <asm-generic/pci-dma-compat.h>
|
||||
|
||||
#define PCIBIOS_MIN_IO 0x1000
|
||||
#define PCIBIOS_MIN_MEM 0
|
||||
|
||||
/*
|
||||
* Set to 1 if the kernel should re-assign all PCI bus numbers
|
||||
*/
|
||||
#define pcibios_assign_all_busses() \
|
||||
(pci_has_flag(PCI_REASSIGN_ALL_BUS))
|
||||
|
||||
/*
|
||||
* PCI address space differs from physical memory address space
|
||||
*/
|
||||
#define PCI_DMA_BUS_IS_PHYS (0)
|
||||
|
||||
extern int isa_dma_bridge_buggy;
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static inline int pci_proc_domain(struct pci_bus *bus)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* __ASM_PCI_H */
|
|
@ -301,6 +301,8 @@ static inline int has_transparent_hugepage(void)
|
|||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
|
||||
#define pgprot_writecombine(prot) \
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
|
||||
#define pgprot_device(prot) \
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
|
||||
#define __HAVE_PHYS_MEM_ACCESS_PROT
|
||||
struct file;
|
||||
extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
|
||||
|
|
|
@ -30,6 +30,7 @@ arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o
|
|||
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
||||
arm64-obj-$(CONFIG_KGDB) += kgdb.o
|
||||
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
|
||||
arm64-obj-$(CONFIG_PCI) += pci.o
|
||||
|
||||
obj-y += $(arm64-obj-y) vdso/
|
||||
obj-m += $(arm64-obj-m)
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Code borrowed from powerpc/kernel/pci-common.c
|
||||
*
|
||||
* Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
|
||||
* Copyright (C) 2014 ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/pci-bridge.h>
|
||||
|
||||
/*
|
||||
* Called after each bus is probed, but before its children are examined
|
||||
*/
|
||||
void pcibios_fixup_bus(struct pci_bus *bus)
|
||||
{
|
||||
/* nothing to do, expected to be removed in the future */
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't have to worry about legacy ISA devices, so nothing to do here
|
||||
*/
|
||||
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
return res->start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to assign the IRQ number from DT when adding a new device
|
||||
*/
|
||||
int pcibios_add_device(struct pci_dev *dev)
|
||||
{
|
||||
dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||
static bool dt_domain_found = false;
|
||||
|
||||
void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
|
||||
{
|
||||
int domain = of_get_pci_domain_nr(parent->of_node);
|
||||
|
||||
if (domain >= 0) {
|
||||
dt_domain_found = true;
|
||||
} else if (dt_domain_found == true) {
|
||||
dev_err(parent, "Node %s is missing \"linux,pci-domain\" property in DT\n",
|
||||
parent->of_node->full_name);
|
||||
return;
|
||||
} else {
|
||||
domain = pci_get_new_domain_nr();
|
||||
}
|
||||
|
||||
bus->domain_nr = domain;
|
||||
}
|
||||
#endif
|
|
@ -23,7 +23,7 @@ static int ia64_set_msi_irq_affinity(struct irq_data *idata,
|
|||
if (irq_prepare_move(irq, cpu))
|
||||
return -1;
|
||||
|
||||
get_cached_msi_msg(irq, &msg);
|
||||
__get_cached_msi_msg(idata->msi_desc, &msg);
|
||||
|
||||
addr = msg.address_lo;
|
||||
addr &= MSI_ADDR_DEST_ID_MASK;
|
||||
|
|
|
@ -175,7 +175,7 @@ static int sn_set_msi_irq_affinity(struct irq_data *data,
|
|||
* Release XIO resources for the old MSI PCI address
|
||||
*/
|
||||
|
||||
get_cached_msi_msg(irq, &msg);
|
||||
__get_cached_msi_msg(data->msi_desc, &msg);
|
||||
sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
|
||||
pdev = sn_pdev->pdi_linux_pcidev;
|
||||
provider = SN_PCIDEV_BUSPROVIDER(pdev);
|
||||
|
|
|
@ -73,8 +73,7 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
|
|||
* wants. Most devices only want 1, which will give
|
||||
* configured_private_bits and request_private_bits equal 0.
|
||||
*/
|
||||
pci_read_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
|
||||
&control);
|
||||
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
|
||||
|
||||
/*
|
||||
* If the number of private bits has been configured then use
|
||||
|
@ -176,8 +175,7 @@ msi_irq_allocated:
|
|||
/* Update the number of IRQs the device has available to it */
|
||||
control &= ~PCI_MSI_FLAGS_QSIZE;
|
||||
control |= request_private_bits << 4;
|
||||
pci_write_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
|
||||
control);
|
||||
pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
|
||||
|
||||
irq_set_msi_desc(irq, desc);
|
||||
write_msi_msg(irq, &msg);
|
||||
|
|
|
@ -136,8 +136,6 @@ struct machdep_calls {
|
|||
int (*pci_setup_phb)(struct pci_controller *host);
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
int (*msi_check_device)(struct pci_dev* dev,
|
||||
int nvec, int type);
|
||||
int (*setup_msi_irqs)(struct pci_dev *dev,
|
||||
int nvec, int type);
|
||||
void (*teardown_msi_irqs)(struct pci_dev *dev);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include <asm/machdep.h>
|
||||
|
||||
int arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
|
||||
int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
|
||||
{
|
||||
if (!ppc_md.setup_msi_irqs || !ppc_md.teardown_msi_irqs) {
|
||||
pr_debug("msi: Platform doesn't provide MSI callbacks.\n");
|
||||
|
@ -24,16 +24,6 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
|
|||
if (type == PCI_CAP_ID_MSI && nvec > 1)
|
||||
return 1;
|
||||
|
||||
if (ppc_md.msi_check_device) {
|
||||
pr_debug("msi: Using platform check routine.\n");
|
||||
return ppc_md.msi_check_device(dev, nvec, type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
|
||||
{
|
||||
return ppc_md.setup_msi_irqs(dev, nvec, type);
|
||||
}
|
||||
|
||||
|
|
|
@ -199,14 +199,6 @@ out_error:
|
|||
return msic;
|
||||
}
|
||||
|
||||
static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
|
||||
{
|
||||
if (!find_msi_translator(dev))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
|
||||
{
|
||||
struct device_node *dn;
|
||||
|
@ -416,7 +408,6 @@ static int axon_msi_probe(struct platform_device *device)
|
|||
|
||||
ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
|
||||
ppc_md.msi_check_device = axon_msi_check_device;
|
||||
|
||||
axon_msi_debug_setup(dn, msic);
|
||||
|
||||
|
|
|
@ -46,29 +46,21 @@
|
|||
//#define cfg_dbg(fmt...) printk(fmt)
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type)
|
||||
{
|
||||
struct pci_controller *hose = pci_bus_to_host(pdev->bus);
|
||||
struct pnv_phb *phb = hose->private_data;
|
||||
struct pci_dn *pdn = pci_get_pdn(pdev);
|
||||
|
||||
if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
|
||||
return -ENODEV;
|
||||
|
||||
return (phb && phb->msi_bmp.bitmap) ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
struct pci_controller *hose = pci_bus_to_host(pdev->bus);
|
||||
struct pnv_phb *phb = hose->private_data;
|
||||
struct pci_dn *pdn = pci_get_pdn(pdev);
|
||||
struct msi_desc *entry;
|
||||
struct msi_msg msg;
|
||||
int hwirq;
|
||||
unsigned int virq;
|
||||
int rc;
|
||||
|
||||
if (WARN_ON(!phb))
|
||||
if (WARN_ON(!phb) || !phb->msi_bmp.bitmap)
|
||||
return -ENODEV;
|
||||
|
||||
if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
|
||||
return -ENODEV;
|
||||
|
||||
list_for_each_entry(entry, &pdev->msi_list, list) {
|
||||
|
@ -860,7 +852,6 @@ void __init pnv_pci_init(void)
|
|||
|
||||
/* Configure MSIs */
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
ppc_md.msi_check_device = pnv_msi_check_device;
|
||||
ppc_md.setup_msi_irqs = pnv_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
|
||||
#endif
|
||||
|
|
|
@ -336,26 +336,6 @@ out:
|
|||
return request;
|
||||
}
|
||||
|
||||
static int rtas_msi_check_device(struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
int quota, rc;
|
||||
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
rc = check_req_msix(pdev, nvec);
|
||||
else
|
||||
rc = check_req_msi(pdev, nvec);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
quota = msi_quota_for_device(pdev, nvec);
|
||||
|
||||
if (quota && quota < nvec)
|
||||
return quota;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_msix_entries(struct pci_dev *pdev)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
|
@ -397,15 +377,24 @@ static void rtas_hack_32bit_msi_gen2(struct pci_dev *pdev)
|
|||
static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
|
||||
{
|
||||
struct pci_dn *pdn;
|
||||
int hwirq, virq, i, rc;
|
||||
int hwirq, virq, i, quota, rc;
|
||||
struct msi_desc *entry;
|
||||
struct msi_msg msg;
|
||||
int nvec = nvec_in;
|
||||
int use_32bit_msi_hack = 0;
|
||||
|
||||
pdn = pci_get_pdn(pdev);
|
||||
if (!pdn)
|
||||
return -ENODEV;
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
rc = check_req_msix(pdev, nvec);
|
||||
else
|
||||
rc = check_req_msi(pdev, nvec);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
quota = msi_quota_for_device(pdev, nvec);
|
||||
|
||||
if (quota && quota < nvec)
|
||||
return quota;
|
||||
|
||||
if (type == PCI_CAP_ID_MSIX && check_msix_entries(pdev))
|
||||
return -EINVAL;
|
||||
|
@ -416,12 +405,14 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
|
|||
*/
|
||||
if (type == PCI_CAP_ID_MSIX) {
|
||||
int m = roundup_pow_of_two(nvec);
|
||||
int quota = msi_quota_for_device(pdev, m);
|
||||
quota = msi_quota_for_device(pdev, m);
|
||||
|
||||
if (quota >= m)
|
||||
nvec = m;
|
||||
}
|
||||
|
||||
pdn = pci_get_pdn(pdev);
|
||||
|
||||
/*
|
||||
* Try the new more explicit firmware interface, if that fails fall
|
||||
* back to the old interface. The old interface is known to never
|
||||
|
@ -485,7 +476,7 @@ again:
|
|||
irq_set_msi_desc(virq, entry);
|
||||
|
||||
/* Read config space back so we can restore after reset */
|
||||
read_msi_msg(virq, &msg);
|
||||
__read_msi_msg(entry, &msg);
|
||||
entry->msg = msg;
|
||||
}
|
||||
|
||||
|
@ -526,7 +517,6 @@ static int rtas_msi_init(void)
|
|||
WARN_ON(ppc_md.setup_msi_irqs);
|
||||
ppc_md.setup_msi_irqs = rtas_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = rtas_teardown_msi_irqs;
|
||||
ppc_md.msi_check_device = rtas_msi_check_device;
|
||||
|
||||
WARN_ON(ppc_md.pci_irq_fixup);
|
||||
ppc_md.pci_irq_fixup = rtas_msi_pci_irq_fixup;
|
||||
|
|
|
@ -109,14 +109,6 @@ static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("fslmsi: MSI-X untested, trying anyway.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_teardown_msi_irqs(struct pci_dev *pdev)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
|
@ -173,6 +165,9 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
|||
struct msi_msg msg;
|
||||
struct fsl_msi *msi_data;
|
||||
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("fslmsi: MSI-X untested, trying anyway.\n");
|
||||
|
||||
/*
|
||||
* If the PCI node has an fsl,msi property, then we need to use it
|
||||
* to find the specific MSI.
|
||||
|
@ -527,7 +522,6 @@ static int fsl_of_msi_probe(struct platform_device *dev)
|
|||
if (!ppc_md.setup_msi_irqs) {
|
||||
ppc_md.setup_msi_irqs = fsl_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs;
|
||||
ppc_md.msi_check_device = fsl_msi_check_device;
|
||||
} else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) {
|
||||
dev_err(&dev->dev, "Different MSI driver already installed!\n");
|
||||
err = -ENODEV;
|
||||
|
|
|
@ -63,14 +63,6 @@ static struct irq_chip mpic_pasemi_msi_chip = {
|
|||
.name = "PASEMI-MSI",
|
||||
};
|
||||
|
||||
static int pasemi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("pasemi_msi: MSI-X untested, trying anyway\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
|
@ -97,6 +89,8 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
|||
struct msi_msg msg;
|
||||
int hwirq;
|
||||
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("pasemi_msi: MSI-X untested, trying anyway\n");
|
||||
pr_debug("pasemi_msi_setup_msi_irqs, pdev %p nvec %d type %d\n",
|
||||
pdev, nvec, type);
|
||||
|
||||
|
@ -169,7 +163,6 @@ int mpic_pasemi_msi_init(struct mpic *mpic)
|
|||
WARN_ON(ppc_md.setup_msi_irqs);
|
||||
ppc_md.setup_msi_irqs = pasemi_msi_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = pasemi_msi_teardown_msi_irqs;
|
||||
ppc_md.msi_check_device = pasemi_msi_check_device;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -105,22 +105,6 @@ static u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int u3msi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("u3msi: MSI-X untested, trying anyway.\n");
|
||||
|
||||
/* If we can't find a magic address then MSI ain't gonna work */
|
||||
if (find_ht_magic_addr(pdev, 0) == 0 &&
|
||||
find_u4_magic_addr(pdev, 0) == 0) {
|
||||
pr_debug("u3msi: no magic address found for %s\n",
|
||||
pci_name(pdev));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void u3msi_teardown_msi_irqs(struct pci_dev *pdev)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
|
@ -146,6 +130,17 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
|||
u64 addr;
|
||||
int hwirq;
|
||||
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("u3msi: MSI-X untested, trying anyway.\n");
|
||||
|
||||
/* If we can't find a magic address then MSI ain't gonna work */
|
||||
if (find_ht_magic_addr(pdev, 0) == 0 &&
|
||||
find_u4_magic_addr(pdev, 0) == 0) {
|
||||
pr_debug("u3msi: no magic address found for %s\n",
|
||||
pci_name(pdev));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
list_for_each_entry(entry, &pdev->msi_list, list) {
|
||||
hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap, 1);
|
||||
if (hwirq < 0) {
|
||||
|
@ -202,7 +197,6 @@ int mpic_u3msi_init(struct mpic *mpic)
|
|||
WARN_ON(ppc_md.setup_msi_irqs);
|
||||
ppc_md.setup_msi_irqs = u3msi_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = u3msi_teardown_msi_irqs;
|
||||
ppc_md.msi_check_device = u3msi_msi_check_device;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,12 @@ static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
|
|||
int irq, hwirq;
|
||||
u64 addr;
|
||||
|
||||
/* We don't support MSI-X */
|
||||
if (type == PCI_CAP_ID_MSIX) {
|
||||
pr_debug("%s: MSI-X not supported.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_for_each_entry(entry, &dev->msi_list, list) {
|
||||
irq = msi_bitmap_alloc_hwirqs(&ppc4xx_hsta_msi.bmp, 1);
|
||||
if (irq < 0) {
|
||||
|
@ -117,17 +123,6 @@ static void hsta_teardown_msi_irqs(struct pci_dev *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static int hsta_msi_check_device(struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
/* We don't support MSI-X */
|
||||
if (type == PCI_CAP_ID_MSIX) {
|
||||
pr_debug("%s: MSI-X not supported.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hsta_msi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -178,7 +173,6 @@ static int hsta_msi_probe(struct platform_device *pdev)
|
|||
|
||||
ppc_md.setup_msi_irqs = hsta_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = hsta_teardown_msi_irqs;
|
||||
ppc_md.msi_check_device = hsta_msi_check_device;
|
||||
return 0;
|
||||
|
||||
out2:
|
||||
|
|
|
@ -85,8 +85,12 @@ static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
|
|||
struct msi_desc *entry;
|
||||
struct ppc4xx_msi *msi_data = &ppc4xx_msi;
|
||||
|
||||
msi_data->msi_virqs = kmalloc((msi_irqs) * sizeof(int),
|
||||
GFP_KERNEL);
|
||||
dev_dbg(&dev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
|
||||
__func__, nvec, type);
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
|
||||
|
||||
msi_data->msi_virqs = kmalloc((msi_irqs) * sizeof(int), GFP_KERNEL);
|
||||
if (!msi_data->msi_virqs)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -134,16 +138,6 @@ void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
dev_dbg(&pdev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
|
||||
__func__, nvec, type);
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppc4xx_setup_pcieh_hw(struct platform_device *dev,
|
||||
struct resource res, struct ppc4xx_msi *msi)
|
||||
{
|
||||
|
@ -259,7 +253,6 @@ static int ppc4xx_msi_probe(struct platform_device *dev)
|
|||
|
||||
ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
|
||||
ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
|
||||
ppc_md.msi_check_device = ppc4xx_msi_check_device;
|
||||
return err;
|
||||
|
||||
error_out:
|
||||
|
|
|
@ -81,14 +81,14 @@ struct pci_ops pci_root_ops = {
|
|||
*/
|
||||
DEFINE_RAW_SPINLOCK(pci_config_lock);
|
||||
|
||||
static int can_skip_ioresource_align(const struct dmi_system_id *d)
|
||||
static int __init can_skip_ioresource_align(const struct dmi_system_id *d)
|
||||
{
|
||||
pci_probe |= PCI_CAN_SKIP_ISA_ALIGN;
|
||||
printk(KERN_INFO "PCI: %s detected, can skip ISA alignment\n", d->ident);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id can_skip_pciprobe_dmi_table[] = {
|
||||
static const struct dmi_system_id can_skip_pciprobe_dmi_table[] __initconst = {
|
||||
/*
|
||||
* Systems where PCI IO resource ISA alignment can be skipped
|
||||
* when the ISA enable bit in the bridge control is not set
|
||||
|
@ -186,7 +186,7 @@ void pcibios_remove_bus(struct pci_bus *bus)
|
|||
* on the kernel command line (which was parsed earlier).
|
||||
*/
|
||||
|
||||
static int set_bf_sort(const struct dmi_system_id *d)
|
||||
static int __init set_bf_sort(const struct dmi_system_id *d)
|
||||
{
|
||||
if (pci_bf_sort == pci_bf_sort_default) {
|
||||
pci_bf_sort = pci_dmi_bf;
|
||||
|
@ -195,7 +195,7 @@ static int set_bf_sort(const struct dmi_system_id *d)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void read_dmi_type_b1(const struct dmi_header *dm,
|
||||
static void __init read_dmi_type_b1(const struct dmi_header *dm,
|
||||
void *private_data)
|
||||
{
|
||||
u8 *d = (u8 *)dm + 4;
|
||||
|
@ -217,7 +217,7 @@ static void read_dmi_type_b1(const struct dmi_header *dm,
|
|||
}
|
||||
}
|
||||
|
||||
static int find_sort_method(const struct dmi_system_id *d)
|
||||
static int __init find_sort_method(const struct dmi_system_id *d)
|
||||
{
|
||||
dmi_walk(read_dmi_type_b1, NULL);
|
||||
|
||||
|
@ -232,7 +232,7 @@ static int find_sort_method(const struct dmi_system_id *d)
|
|||
* Enable renumbering of PCI bus# ranges to reach all PCI busses (Cardbus)
|
||||
*/
|
||||
#ifdef __i386__
|
||||
static int assign_all_busses(const struct dmi_system_id *d)
|
||||
static int __init assign_all_busses(const struct dmi_system_id *d)
|
||||
{
|
||||
pci_probe |= PCI_ASSIGN_ALL_BUSSES;
|
||||
printk(KERN_INFO "%s detected: enabling PCI bus# renumbering"
|
||||
|
@ -241,7 +241,7 @@ static int assign_all_busses(const struct dmi_system_id *d)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int set_scan_all(const struct dmi_system_id *d)
|
||||
static int __init set_scan_all(const struct dmi_system_id *d)
|
||||
{
|
||||
printk(KERN_INFO "PCI: %s detected, enabling pci=pcie_scan_all\n",
|
||||
d->ident);
|
||||
|
@ -249,7 +249,7 @@ static int set_scan_all(const struct dmi_system_id *d)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id pciprobe_dmi_table[] = {
|
||||
static const struct dmi_system_id pciprobe_dmi_table[] __initconst = {
|
||||
#ifdef __i386__
|
||||
/*
|
||||
* Laptops which need pci=assign-busses to see Cardbus cards
|
||||
|
|
|
@ -31,7 +31,7 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
|
|||
|
||||
LIST_HEAD(pci_mmcfg_list);
|
||||
|
||||
static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
|
||||
static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
|
||||
{
|
||||
if (cfg->res.parent)
|
||||
release_resource(&cfg->res);
|
||||
|
@ -39,7 +39,7 @@ static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
|
|||
kfree(cfg);
|
||||
}
|
||||
|
||||
static __init void free_all_mmcfg(void)
|
||||
static void __init free_all_mmcfg(void)
|
||||
{
|
||||
struct pci_mmcfg_region *cfg, *tmp;
|
||||
|
||||
|
@ -93,7 +93,7 @@ static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
|
|||
return new;
|
||||
}
|
||||
|
||||
static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
|
||||
static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
|
||||
int end, u64 addr)
|
||||
{
|
||||
struct pci_mmcfg_region *new;
|
||||
|
@ -125,7 +125,7 @@ struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static const char __init *pci_mmcfg_e7520(void)
|
||||
static const char *__init pci_mmcfg_e7520(void)
|
||||
{
|
||||
u32 win;
|
||||
raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win);
|
||||
|
@ -140,7 +140,7 @@ static const char __init *pci_mmcfg_e7520(void)
|
|||
return "Intel Corporation E7520 Memory Controller Hub";
|
||||
}
|
||||
|
||||
static const char __init *pci_mmcfg_intel_945(void)
|
||||
static const char *__init pci_mmcfg_intel_945(void)
|
||||
{
|
||||
u32 pciexbar, mask = 0, len = 0;
|
||||
|
||||
|
@ -184,7 +184,7 @@ static const char __init *pci_mmcfg_intel_945(void)
|
|||
return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
|
||||
}
|
||||
|
||||
static const char __init *pci_mmcfg_amd_fam10h(void)
|
||||
static const char *__init pci_mmcfg_amd_fam10h(void)
|
||||
{
|
||||
u32 low, high, address;
|
||||
u64 base, msr;
|
||||
|
@ -235,21 +235,25 @@ static const char __init *pci_mmcfg_amd_fam10h(void)
|
|||
}
|
||||
|
||||
static bool __initdata mcp55_checked;
|
||||
static const char __init *pci_mmcfg_nvidia_mcp55(void)
|
||||
static const char *__init pci_mmcfg_nvidia_mcp55(void)
|
||||
{
|
||||
int bus;
|
||||
int mcp55_mmconf_found = 0;
|
||||
|
||||
static const u32 extcfg_regnum = 0x90;
|
||||
static const u32 extcfg_regsize = 4;
|
||||
static const u32 extcfg_enable_mask = 1<<31;
|
||||
static const u32 extcfg_start_mask = 0xff<<16;
|
||||
static const int extcfg_start_shift = 16;
|
||||
static const u32 extcfg_size_mask = 0x3<<28;
|
||||
static const int extcfg_size_shift = 28;
|
||||
static const int extcfg_sizebus[] = {0x100, 0x80, 0x40, 0x20};
|
||||
static const u32 extcfg_base_mask[] = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff};
|
||||
static const int extcfg_base_lshift = 25;
|
||||
static const u32 extcfg_regnum __initconst = 0x90;
|
||||
static const u32 extcfg_regsize __initconst = 4;
|
||||
static const u32 extcfg_enable_mask __initconst = 1 << 31;
|
||||
static const u32 extcfg_start_mask __initconst = 0xff << 16;
|
||||
static const int extcfg_start_shift __initconst = 16;
|
||||
static const u32 extcfg_size_mask __initconst = 0x3 << 28;
|
||||
static const int extcfg_size_shift __initconst = 28;
|
||||
static const int extcfg_sizebus[] __initconst = {
|
||||
0x100, 0x80, 0x40, 0x20
|
||||
};
|
||||
static const u32 extcfg_base_mask[] __initconst = {
|
||||
0x7ff8, 0x7ffc, 0x7ffe, 0x7fff
|
||||
};
|
||||
static const int extcfg_base_lshift __initconst = 25;
|
||||
|
||||
/*
|
||||
* do check if amd fam10h already took over
|
||||
|
@ -302,7 +306,7 @@ struct pci_mmcfg_hostbridge_probe {
|
|||
const char *(*probe)(void);
|
||||
};
|
||||
|
||||
static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = {
|
||||
static const struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initconst = {
|
||||
{ 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 },
|
||||
{ 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
|
||||
|
|
|
@ -79,13 +79,13 @@ union bios32 {
|
|||
static struct {
|
||||
unsigned long address;
|
||||
unsigned short segment;
|
||||
} bios32_indirect = { 0, __KERNEL_CS };
|
||||
} bios32_indirect __initdata = { 0, __KERNEL_CS };
|
||||
|
||||
/*
|
||||
* Returns the entry point for the given service, NULL on error
|
||||
*/
|
||||
|
||||
static unsigned long bios32_service(unsigned long service)
|
||||
static unsigned long __init bios32_service(unsigned long service)
|
||||
{
|
||||
unsigned char return_code; /* %al */
|
||||
unsigned long address; /* %ebx */
|
||||
|
@ -124,7 +124,7 @@ static struct {
|
|||
|
||||
static int pci_bios_present;
|
||||
|
||||
static int check_pcibios(void)
|
||||
static int __init check_pcibios(void)
|
||||
{
|
||||
u32 signature, eax, ebx, ecx;
|
||||
u8 status, major_ver, minor_ver, hw_mech;
|
||||
|
@ -312,7 +312,7 @@ static const struct pci_raw_ops pci_bios_access = {
|
|||
* Try to find PCI BIOS.
|
||||
*/
|
||||
|
||||
static const struct pci_raw_ops *pci_find_bios(void)
|
||||
static const struct pci_raw_ops *__init pci_find_bios(void)
|
||||
{
|
||||
union bios32 *check;
|
||||
unsigned char sum;
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
/*
|
||||
* PCI device IDs.
|
||||
*/
|
||||
#define PCI_VENDOR_ID_VMWARE 0x15AD
|
||||
#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
|
||||
|
||||
/*
|
||||
|
|
|
@ -136,6 +136,10 @@ static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
|
|||
struct msi_msg msg;
|
||||
int virq, hwirq;
|
||||
|
||||
/* We support MSI, but not MSI-X */
|
||||
if (desc->msi_attrib.is_msix)
|
||||
return -EINVAL;
|
||||
|
||||
hwirq = armada_370_xp_alloc_msi();
|
||||
if (hwirq < 0)
|
||||
return hwirq;
|
||||
|
@ -166,15 +170,6 @@ static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
|
|||
armada_370_xp_free_msi(hwirq);
|
||||
}
|
||||
|
||||
static int armada_370_xp_check_msi_device(struct msi_chip *chip, struct pci_dev *dev,
|
||||
int nvec, int type)
|
||||
{
|
||||
/* We support MSI, but not MSI-X */
|
||||
if (type == PCI_CAP_ID_MSI)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct irq_chip armada_370_xp_msi_irq_chip = {
|
||||
.name = "armada_370_xp_msi_irq",
|
||||
.irq_enable = unmask_msi_irq,
|
||||
|
@ -213,7 +208,6 @@ static int armada_370_xp_msi_init(struct device_node *node,
|
|||
|
||||
msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
|
||||
msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
|
||||
msi_chip->check_device = armada_370_xp_check_msi_device;
|
||||
msi_chip->of_node = node;
|
||||
|
||||
armada_370_xp_msi_domain =
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "vmci_driver.h"
|
||||
#include "vmci_event.h"
|
||||
|
||||
#define PCI_VENDOR_ID_VMWARE 0x15AD
|
||||
#define PCI_DEVICE_ID_VMWARE_VMCI 0x0740
|
||||
|
||||
#define VMCI_UTIL_NUM_RESOURCES 1
|
||||
|
|
|
@ -117,7 +117,6 @@ enum {
|
|||
/*
|
||||
* PCI vendor and device IDs.
|
||||
*/
|
||||
#define PCI_VENDOR_ID_VMWARE 0x15AD
|
||||
#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0
|
||||
#define MAX_ETHERNET_CARDS 10
|
||||
#define MAX_PCI_PASSTHRU_DEVICE 6
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
/* Max address size we deal with */
|
||||
|
@ -293,6 +295,51 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
|
||||
|
||||
/*
|
||||
* of_pci_range_to_resource - Create a resource from an of_pci_range
|
||||
* @range: the PCI range that describes the resource
|
||||
* @np: device node where the range belongs to
|
||||
* @res: pointer to a valid resource that will be updated to
|
||||
* reflect the values contained in the range.
|
||||
*
|
||||
* Returns EINVAL if the range cannot be converted to resource.
|
||||
*
|
||||
* Note that if the range is an IO range, the resource will be converted
|
||||
* using pci_address_to_pio() which can fail if it is called too early or
|
||||
* if the range cannot be matched to any host bridge IO space (our case here).
|
||||
* To guard against that we try to register the IO range first.
|
||||
* If that fails we know that pci_address_to_pio() will do too.
|
||||
*/
|
||||
int of_pci_range_to_resource(struct of_pci_range *range,
|
||||
struct device_node *np, struct resource *res)
|
||||
{
|
||||
int err;
|
||||
res->flags = range->flags;
|
||||
res->parent = res->child = res->sibling = NULL;
|
||||
res->name = np->full_name;
|
||||
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
unsigned long port;
|
||||
err = pci_register_io_range(range->cpu_addr, range->size);
|
||||
if (err)
|
||||
goto invalid_range;
|
||||
port = pci_address_to_pio(range->cpu_addr);
|
||||
if (port == (unsigned long)-1) {
|
||||
err = -EINVAL;
|
||||
goto invalid_range;
|
||||
}
|
||||
res->start = port;
|
||||
} else {
|
||||
res->start = range->cpu_addr;
|
||||
}
|
||||
res->end = res->start + range->size - 1;
|
||||
return 0;
|
||||
|
||||
invalid_range:
|
||||
res->start = (resource_size_t)OF_BAD_ADDR;
|
||||
res->end = (resource_size_t)OF_BAD_ADDR;
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
/*
|
||||
|
@ -601,12 +648,119 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
|
|||
}
|
||||
EXPORT_SYMBOL(of_get_address);
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range {
|
||||
struct list_head list;
|
||||
phys_addr_t start;
|
||||
resource_size_t size;
|
||||
};
|
||||
|
||||
static LIST_HEAD(io_range_list);
|
||||
static DEFINE_SPINLOCK(io_range_lock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Record the PCI IO range (expressed as CPU physical address + size).
|
||||
* Return a negative value if an error has occured, zero otherwise
|
||||
*/
|
||||
int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
/* check if the range hasn't been previously recorded */
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (addr >= range->start && addr + size <= range->start + size) {
|
||||
/* range already registered, bail out */
|
||||
goto end_register;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
|
||||
/* range not registed yet, check for available space */
|
||||
if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
|
||||
/* if it's too big check if 64K space can be reserved */
|
||||
if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
|
||||
err = -E2BIG;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
size = SZ_64K;
|
||||
pr_warn("Requested IO range too big, new size set to 64K\n");
|
||||
}
|
||||
|
||||
/* add the range to the list */
|
||||
range = kzalloc(sizeof(*range), GFP_KERNEL);
|
||||
if (!range) {
|
||||
err = -ENOMEM;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
range->start = addr;
|
||||
range->size = size;
|
||||
|
||||
list_add_tail(&range->list, &io_range_list);
|
||||
|
||||
end_register:
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
phys_addr_t pci_pio_to_address(unsigned long pio)
|
||||
{
|
||||
phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
if (pio > IO_SPACE_LIMIT)
|
||||
return address;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (pio >= allocated_size && pio < allocated_size + range->size) {
|
||||
address = range->start + pio - allocated_size;
|
||||
break;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
unsigned long __weak pci_address_to_pio(phys_addr_t address)
|
||||
{
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *res;
|
||||
resource_size_t offset = 0;
|
||||
unsigned long addr = -1;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(res, &io_range_list, list) {
|
||||
if (address >= res->start && address < res->start + res->size) {
|
||||
addr = res->start - address + offset;
|
||||
break;
|
||||
}
|
||||
offset += res->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
|
||||
return addr;
|
||||
#else
|
||||
if (address > IO_SPACE_LIMIT)
|
||||
return (unsigned long)-1;
|
||||
|
||||
return (unsigned long) address;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __of_address_to_resource(struct device_node *dev,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static inline int __of_pci_pci_compare(struct device_node *node,
|
||||
unsigned int data)
|
||||
|
@ -89,6 +91,146 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_parse_bus_range);
|
||||
|
||||
/**
|
||||
* This function will try to obtain the host bridge domain number by
|
||||
* finding a property called "linux,pci-domain" of the given device node.
|
||||
*
|
||||
* @node: device tree node with the domain information
|
||||
*
|
||||
* Returns the associated domain number from DT in the range [0-0xffff], or
|
||||
* a negative value if the required property is not found.
|
||||
*/
|
||||
int of_get_pci_domain_nr(struct device_node *node)
|
||||
{
|
||||
const __be32 *value;
|
||||
int len;
|
||||
u16 domain;
|
||||
|
||||
value = of_get_property(node, "linux,pci-domain", &len);
|
||||
if (!value || len < sizeof(*value))
|
||||
return -EINVAL;
|
||||
|
||||
domain = (u16)be32_to_cpup(value);
|
||||
|
||||
return domain;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
|
||||
|
||||
#if defined(CONFIG_OF_ADDRESS)
|
||||
/**
|
||||
* of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT
|
||||
* @dev: device node of the host bridge having the range property
|
||||
* @busno: bus number associated with the bridge root bus
|
||||
* @bus_max: maximum number of buses for this bridge
|
||||
* @resources: list where the range of resources will be added after DT parsing
|
||||
* @io_base: pointer to a variable that will contain on return the physical
|
||||
* address for the start of the I/O range. Can be NULL if the caller doesn't
|
||||
* expect IO ranges to be present in the device tree.
|
||||
*
|
||||
* It is the caller's job to free the @resources list.
|
||||
*
|
||||
* This function will parse the "ranges" property of a PCI host bridge device
|
||||
* node and setup the resource mapping based on its content. It is expected
|
||||
* that the property conforms with the Power ePAPR document.
|
||||
*
|
||||
* It returns zero if the range parsing has been successful or a standard error
|
||||
* value if it failed.
|
||||
*/
|
||||
int of_pci_get_host_bridge_resources(struct device_node *dev,
|
||||
unsigned char busno, unsigned char bus_max,
|
||||
struct list_head *resources, resource_size_t *io_base)
|
||||
{
|
||||
struct resource *res;
|
||||
struct resource *bus_range;
|
||||
struct of_pci_range range;
|
||||
struct of_pci_range_parser parser;
|
||||
char range_type[4];
|
||||
int err;
|
||||
|
||||
if (io_base)
|
||||
*io_base = (resource_size_t)OF_BAD_ADDR;
|
||||
|
||||
bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
|
||||
if (!bus_range)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_info("PCI host bridge %s ranges:\n", dev->full_name);
|
||||
|
||||
err = of_pci_parse_bus_range(dev, bus_range);
|
||||
if (err) {
|
||||
bus_range->start = busno;
|
||||
bus_range->end = bus_max;
|
||||
bus_range->flags = IORESOURCE_BUS;
|
||||
pr_info(" No bus range found for %s, using %pR\n",
|
||||
dev->full_name, bus_range);
|
||||
} else {
|
||||
if (bus_range->end > bus_range->start + bus_max)
|
||||
bus_range->end = bus_range->start + bus_max;
|
||||
}
|
||||
pci_add_resource(resources, bus_range);
|
||||
|
||||
/* Check for ranges property */
|
||||
err = of_pci_range_parser_init(&parser, dev);
|
||||
if (err)
|
||||
goto parse_failed;
|
||||
|
||||
pr_debug("Parsing ranges property...\n");
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
/* Read next ranges element */
|
||||
if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
|
||||
snprintf(range_type, 4, " IO");
|
||||
else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
|
||||
snprintf(range_type, 4, "MEM");
|
||||
else
|
||||
snprintf(range_type, 4, "err");
|
||||
pr_info(" %s %#010llx..%#010llx -> %#010llx\n", range_type,
|
||||
range.cpu_addr, range.cpu_addr + range.size - 1,
|
||||
range.pci_addr);
|
||||
|
||||
/*
|
||||
* If we failed translation or got a zero-sized region
|
||||
* then skip this range
|
||||
*/
|
||||
if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
|
||||
continue;
|
||||
|
||||
res = kzalloc(sizeof(struct resource), GFP_KERNEL);
|
||||
if (!res) {
|
||||
err = -ENOMEM;
|
||||
goto parse_failed;
|
||||
}
|
||||
|
||||
err = of_pci_range_to_resource(&range, dev, res);
|
||||
if (err)
|
||||
goto conversion_failed;
|
||||
|
||||
if (resource_type(res) == IORESOURCE_IO) {
|
||||
if (!io_base) {
|
||||
pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
|
||||
dev->full_name);
|
||||
err = -EINVAL;
|
||||
goto conversion_failed;
|
||||
}
|
||||
if (*io_base != (resource_size_t)OF_BAD_ADDR)
|
||||
pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
|
||||
dev->full_name);
|
||||
*io_base = range.cpu_addr;
|
||||
}
|
||||
|
||||
pci_add_resource_offset(resources, res, res->start - range.pci_addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
conversion_failed:
|
||||
kfree(res);
|
||||
parse_failed:
|
||||
pci_free_resource_list(resources);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
|
||||
#endif /* CONFIG_OF_ADDRESS */
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
|
||||
static LIST_HEAD(of_pci_msi_chip_list);
|
||||
|
|
|
@ -63,4 +63,32 @@ config PCIE_SPEAR13XX
|
|||
help
|
||||
Say Y here if you want PCIe support on SPEAr13XX SoCs.
|
||||
|
||||
config PCI_KEYSTONE
|
||||
bool "TI Keystone PCIe controller"
|
||||
depends on ARCH_KEYSTONE
|
||||
select PCIE_DW
|
||||
select PCIEPORTBUS
|
||||
help
|
||||
Say Y here if you want to enable PCI controller support on Keystone
|
||||
SoCs. The PCI controller on Keystone is based on Designware hardware
|
||||
and therefore the driver re-uses the Designware core functions to
|
||||
implement the driver.
|
||||
|
||||
config PCIE_XILINX
|
||||
bool "Xilinx AXI PCIe host bridge support"
|
||||
depends on ARCH_ZYNQ
|
||||
help
|
||||
Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
|
||||
Host Bridge driver.
|
||||
|
||||
config PCI_XGENE
|
||||
bool "X-Gene PCIe controller"
|
||||
depends on ARCH_XGENE
|
||||
depends on OF
|
||||
select PCIEPORTBUS
|
||||
help
|
||||
Say Y here if you want internal PCI support on APM X-Gene SoC.
|
||||
There are 5 internal PCIe ports available. Each port is GEN3 capable
|
||||
and have varied lanes from x1 to x8.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -8,3 +8,6 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
|
|||
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
|
||||
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
|
||||
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
|
||||
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
|
||||
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
|
||||
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
|
||||
|
|
|
@ -257,11 +257,6 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
|
|||
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
|
||||
int ret;
|
||||
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
|
||||
|
||||
ret = clk_prepare_enable(imx6_pcie->pcie_phy);
|
||||
if (ret) {
|
||||
dev_err(pp->dev, "unable to enable pcie_phy clock\n");
|
||||
|
@ -283,6 +278,12 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
|
|||
/* allow the clocks to stabilize */
|
||||
usleep_range(200, 500);
|
||||
|
||||
/* power up core phy and enable ref clock */
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
|
||||
|
||||
/* Some boards don't have PCIe reset GPIO. */
|
||||
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
|
||||
gpio_set_value(imx6_pcie->reset_gpio, 0);
|
||||
|
@ -647,7 +648,7 @@ static int __init imx6_pcie_init(void)
|
|||
{
|
||||
return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
|
||||
}
|
||||
fs_initcall(imx6_pcie_init);
|
||||
module_init(imx6_pcie_init);
|
||||
|
||||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver");
|
||||
|
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* Designware application register space functions for Keystone PCI controller
|
||||
*
|
||||
* Copyright (C) 2013-2014 Texas Instruments., Ltd.
|
||||
* http://www.ti.com
|
||||
*
|
||||
* Author: Murali Karicheri <m-karicheri2@ti.com>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "pcie-designware.h"
|
||||
#include "pci-keystone.h"
|
||||
|
||||
/* Application register defines */
|
||||
#define LTSSM_EN_VAL 1
|
||||
#define LTSSM_STATE_MASK 0x1f
|
||||
#define LTSSM_STATE_L0 0x11
|
||||
#define DBI_CS2_EN_VAL 0x20
|
||||
#define OB_XLAT_EN_VAL 2
|
||||
|
||||
/* Application registers */
|
||||
#define CMD_STATUS 0x004
|
||||
#define CFG_SETUP 0x008
|
||||
#define OB_SIZE 0x030
|
||||
#define CFG_PCIM_WIN_SZ_IDX 3
|
||||
#define CFG_PCIM_WIN_CNT 32
|
||||
#define SPACE0_REMOTE_CFG_OFFSET 0x1000
|
||||
#define OB_OFFSET_INDEX(n) (0x200 + (8 * n))
|
||||
#define OB_OFFSET_HI(n) (0x204 + (8 * n))
|
||||
|
||||
/* IRQ register defines */
|
||||
#define IRQ_EOI 0x050
|
||||
#define IRQ_STATUS 0x184
|
||||
#define IRQ_ENABLE_SET 0x188
|
||||
#define IRQ_ENABLE_CLR 0x18c
|
||||
|
||||
#define MSI_IRQ 0x054
|
||||
#define MSI0_IRQ_STATUS 0x104
|
||||
#define MSI0_IRQ_ENABLE_SET 0x108
|
||||
#define MSI0_IRQ_ENABLE_CLR 0x10c
|
||||
#define IRQ_STATUS 0x184
|
||||
#define MSI_IRQ_OFFSET 4
|
||||
|
||||
/* Config space registers */
|
||||
#define DEBUG0 0x728
|
||||
|
||||
#define to_keystone_pcie(x) container_of(x, struct keystone_pcie, pp)
|
||||
|
||||
static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
|
||||
{
|
||||
return sys->private_data;
|
||||
}
|
||||
|
||||
static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset,
|
||||
u32 *bit_pos)
|
||||
{
|
||||
*reg_offset = offset % 8;
|
||||
*bit_pos = offset >> 3;
|
||||
}
|
||||
|
||||
u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
|
||||
return ks_pcie->app.start + MSI_IRQ;
|
||||
}
|
||||
|
||||
void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
|
||||
{
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
u32 pending, vector;
|
||||
int src, virq;
|
||||
|
||||
pending = readl(ks_pcie->va_app_base + MSI0_IRQ_STATUS + (offset << 4));
|
||||
|
||||
/*
|
||||
* MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit
|
||||
* shows 1, 9, 17, 25 and so forth
|
||||
*/
|
||||
for (src = 0; src < 4; src++) {
|
||||
if (BIT(src) & pending) {
|
||||
vector = offset + (src << 3);
|
||||
virq = irq_linear_revmap(pp->irq_domain, vector);
|
||||
dev_dbg(pp->dev, "irq: bit %d, vector %d, virq %d\n",
|
||||
src, vector, virq);
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ks_dw_pcie_msi_irq_ack(struct irq_data *d)
|
||||
{
|
||||
u32 offset, reg_offset, bit_pos;
|
||||
struct keystone_pcie *ks_pcie;
|
||||
unsigned int irq = d->irq;
|
||||
struct msi_desc *msi;
|
||||
struct pcie_port *pp;
|
||||
|
||||
msi = irq_get_msi_desc(irq);
|
||||
pp = sys_to_pcie(msi->dev->bus->sysdata);
|
||||
ks_pcie = to_keystone_pcie(pp);
|
||||
offset = irq - irq_linear_revmap(pp->irq_domain, 0);
|
||||
update_reg_offset_bit_pos(offset, ®_offset, &bit_pos);
|
||||
|
||||
writel(BIT(bit_pos),
|
||||
ks_pcie->va_app_base + MSI0_IRQ_STATUS + (reg_offset << 4));
|
||||
writel(reg_offset + MSI_IRQ_OFFSET, ks_pcie->va_app_base + IRQ_EOI);
|
||||
}
|
||||
|
||||
void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
|
||||
{
|
||||
u32 reg_offset, bit_pos;
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
|
||||
update_reg_offset_bit_pos(irq, ®_offset, &bit_pos);
|
||||
writel(BIT(bit_pos),
|
||||
ks_pcie->va_app_base + MSI0_IRQ_ENABLE_SET + (reg_offset << 4));
|
||||
}
|
||||
|
||||
void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
|
||||
{
|
||||
u32 reg_offset, bit_pos;
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
|
||||
update_reg_offset_bit_pos(irq, ®_offset, &bit_pos);
|
||||
writel(BIT(bit_pos),
|
||||
ks_pcie->va_app_base + MSI0_IRQ_ENABLE_CLR + (reg_offset << 4));
|
||||
}
|
||||
|
||||
static void ks_dw_pcie_msi_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie;
|
||||
unsigned int irq = d->irq;
|
||||
struct msi_desc *msi;
|
||||
struct pcie_port *pp;
|
||||
u32 offset;
|
||||
|
||||
msi = irq_get_msi_desc(irq);
|
||||
pp = sys_to_pcie(msi->dev->bus->sysdata);
|
||||
ks_pcie = to_keystone_pcie(pp);
|
||||
offset = irq - irq_linear_revmap(pp->irq_domain, 0);
|
||||
|
||||
/* Mask the end point if PVM implemented */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
if (msi->msi_attrib.maskbit)
|
||||
mask_msi_irq(d);
|
||||
}
|
||||
|
||||
ks_dw_pcie_msi_clear_irq(pp, offset);
|
||||
}
|
||||
|
||||
static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie;
|
||||
unsigned int irq = d->irq;
|
||||
struct msi_desc *msi;
|
||||
struct pcie_port *pp;
|
||||
u32 offset;
|
||||
|
||||
msi = irq_get_msi_desc(irq);
|
||||
pp = sys_to_pcie(msi->dev->bus->sysdata);
|
||||
ks_pcie = to_keystone_pcie(pp);
|
||||
offset = irq - irq_linear_revmap(pp->irq_domain, 0);
|
||||
|
||||
/* Mask the end point if PVM implemented */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
if (msi->msi_attrib.maskbit)
|
||||
unmask_msi_irq(d);
|
||||
}
|
||||
|
||||
ks_dw_pcie_msi_set_irq(pp, offset);
|
||||
}
|
||||
|
||||
static struct irq_chip ks_dw_pcie_msi_irq_chip = {
|
||||
.name = "Keystone-PCIe-MSI-IRQ",
|
||||
.irq_ack = ks_dw_pcie_msi_irq_ack,
|
||||
.irq_mask = ks_dw_pcie_msi_irq_mask,
|
||||
.irq_unmask = ks_dw_pcie_msi_irq_unmask,
|
||||
};
|
||||
|
||||
static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip,
|
||||
handle_level_irq);
|
||||
irq_set_chip_data(irq, domain->host_data);
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = {
|
||||
.map = ks_dw_pcie_msi_map,
|
||||
};
|
||||
|
||||
int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_chip *chip)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
int i;
|
||||
|
||||
pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np,
|
||||
MAX_MSI_IRQS,
|
||||
&ks_dw_pcie_msi_domain_ops,
|
||||
chip);
|
||||
if (!pp->irq_domain) {
|
||||
dev_err(pp->dev, "irq domain init failed\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_MSI_IRQS; i++)
|
||||
irq_create_mapping(pp->irq_domain, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_LEGACY_IRQS; i++)
|
||||
writel(0x1, ks_pcie->va_app_base + IRQ_ENABLE_SET + (i << 4));
|
||||
}
|
||||
|
||||
void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset)
|
||||
{
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
u32 pending;
|
||||
int virq;
|
||||
|
||||
pending = readl(ks_pcie->va_app_base + IRQ_STATUS + (offset << 4));
|
||||
|
||||
if (BIT(0) & pending) {
|
||||
virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset);
|
||||
dev_dbg(pp->dev, ": irq: irq_offset %d, virq %d\n", offset,
|
||||
virq);
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
|
||||
/* EOI the INTx interrupt */
|
||||
writel(offset, ks_pcie->va_app_base + IRQ_EOI);
|
||||
}
|
||||
|
||||
static void ks_dw_pcie_ack_legacy_irq(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void ks_dw_pcie_mask_legacy_irq(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void ks_dw_pcie_unmask_legacy_irq(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static struct irq_chip ks_dw_pcie_legacy_irq_chip = {
|
||||
.name = "Keystone-PCI-Legacy-IRQ",
|
||||
.irq_ack = ks_dw_pcie_ack_legacy_irq,
|
||||
.irq_mask = ks_dw_pcie_mask_legacy_irq,
|
||||
.irq_unmask = ks_dw_pcie_unmask_legacy_irq,
|
||||
};
|
||||
|
||||
static int ks_dw_pcie_init_legacy_irq_map(struct irq_domain *d,
|
||||
unsigned int irq, irq_hw_number_t hw_irq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &ks_dw_pcie_legacy_irq_chip,
|
||||
handle_level_irq);
|
||||
irq_set_chip_data(irq, d->host_data);
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops ks_dw_pcie_legacy_irq_domain_ops = {
|
||||
.map = ks_dw_pcie_init_legacy_irq_map,
|
||||
.xlate = irq_domain_xlate_onetwocell,
|
||||
};
|
||||
|
||||
/**
|
||||
* ks_dw_pcie_set_dbi_mode() - Set DBI mode to access overlaid BAR mask
|
||||
* registers
|
||||
*
|
||||
* Since modification of dbi_cs2 involves different clock domain, read the
|
||||
* status back to ensure the transition is complete.
|
||||
*/
|
||||
static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
writel(DBI_CS2_EN_VAL | readl(reg_virt + CMD_STATUS),
|
||||
reg_virt + CMD_STATUS);
|
||||
|
||||
do {
|
||||
val = readl(reg_virt + CMD_STATUS);
|
||||
} while (!(val & DBI_CS2_EN_VAL));
|
||||
}
|
||||
|
||||
/**
|
||||
* ks_dw_pcie_clear_dbi_mode() - Disable DBI mode
|
||||
*
|
||||
* Since modification of dbi_cs2 involves different clock domain, read the
|
||||
* status back to ensure the transition is complete.
|
||||
*/
|
||||
static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
writel(~DBI_CS2_EN_VAL & readl(reg_virt + CMD_STATUS),
|
||||
reg_virt + CMD_STATUS);
|
||||
|
||||
do {
|
||||
val = readl(reg_virt + CMD_STATUS);
|
||||
} while (val & DBI_CS2_EN_VAL);
|
||||
}
|
||||
|
||||
void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
|
||||
{
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
u32 start = pp->mem.start, end = pp->mem.end;
|
||||
int i, tr_size;
|
||||
|
||||
/* Disable BARs for inbound access */
|
||||
ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
|
||||
writel(0, pp->dbi_base + PCI_BASE_ADDRESS_0);
|
||||
writel(0, pp->dbi_base + PCI_BASE_ADDRESS_1);
|
||||
ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
|
||||
|
||||
/* Set outbound translation size per window division */
|
||||
writel(CFG_PCIM_WIN_SZ_IDX & 0x7, ks_pcie->va_app_base + OB_SIZE);
|
||||
|
||||
tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M;
|
||||
|
||||
/* Using Direct 1:1 mapping of RC <-> PCI memory space */
|
||||
for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) {
|
||||
writel(start | 1, ks_pcie->va_app_base + OB_OFFSET_INDEX(i));
|
||||
writel(0, ks_pcie->va_app_base + OB_OFFSET_HI(i));
|
||||
start += tr_size;
|
||||
}
|
||||
|
||||
/* Enable OB translation */
|
||||
writel(OB_XLAT_EN_VAL | readl(ks_pcie->va_app_base + CMD_STATUS),
|
||||
ks_pcie->va_app_base + CMD_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* ks_pcie_cfg_setup() - Set up configuration space address for a device
|
||||
*
|
||||
* @ks_pcie: ptr to keystone_pcie structure
|
||||
* @bus: Bus number the device is residing on
|
||||
* @devfn: device, function number info
|
||||
*
|
||||
* Forms and returns the address of configuration space mapped in PCIESS
|
||||
* address space 0. Also configures CFG_SETUP for remote configuration space
|
||||
* access.
|
||||
*
|
||||
* The address space has two regions to access configuration - local and remote.
|
||||
* We access local region for bus 0 (as RC is attached on bus 0) and remote
|
||||
* region for others with TYPE 1 access when bus > 1. As for device on bus = 1,
|
||||
* we will do TYPE 0 access as it will be on our secondary bus (logical).
|
||||
* CFG_SETUP is needed only for remote configuration access.
|
||||
*/
|
||||
static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus,
|
||||
unsigned int devfn)
|
||||
{
|
||||
u8 device = PCI_SLOT(devfn), function = PCI_FUNC(devfn);
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
u32 regval;
|
||||
|
||||
if (bus == 0)
|
||||
return pp->dbi_base;
|
||||
|
||||
regval = (bus << 16) | (device << 8) | function;
|
||||
|
||||
/*
|
||||
* Since Bus#1 will be a virtual bus, we need to have TYPE0
|
||||
* access only.
|
||||
* TYPE 1
|
||||
*/
|
||||
if (bus != 1)
|
||||
regval |= BIT(24);
|
||||
|
||||
writel(regval, ks_pcie->va_app_base + CFG_SETUP);
|
||||
return pp->va_cfg0_base;
|
||||
}
|
||||
|
||||
int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
unsigned int devfn, int where, int size, u32 *val)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
u8 bus_num = bus->number;
|
||||
void __iomem *addr;
|
||||
|
||||
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
|
||||
|
||||
return dw_pcie_cfg_read(addr + (where & ~0x3), where, size, val);
|
||||
}
|
||||
|
||||
int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
unsigned int devfn, int where, int size, u32 val)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
u8 bus_num = bus->number;
|
||||
void __iomem *addr;
|
||||
|
||||
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
|
||||
|
||||
return dw_pcie_cfg_write(addr + (where & ~0x3), where, size, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* ks_dw_pcie_v3_65_scan_bus() - keystone scan_bus post initialization
|
||||
*
|
||||
* This sets BAR0 to enable inbound access for MSI_IRQ register
|
||||
*/
|
||||
void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
|
||||
/* Configure and set up BAR0 */
|
||||
ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
|
||||
|
||||
/* Enable BAR0 */
|
||||
writel(1, pp->dbi_base + PCI_BASE_ADDRESS_0);
|
||||
writel(SZ_4K - 1, pp->dbi_base + PCI_BASE_ADDRESS_0);
|
||||
|
||||
ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
|
||||
|
||||
/*
|
||||
* For BAR0, just setting bus address for inbound writes (MSI) should
|
||||
* be sufficient. Use physical address to avoid any conflicts.
|
||||
*/
|
||||
writel(ks_pcie->app.start, pp->dbi_base + PCI_BASE_ADDRESS_0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ks_dw_pcie_link_up() - Check if link up
|
||||
*/
|
||||
int ks_dw_pcie_link_up(struct pcie_port *pp)
|
||||
{
|
||||
u32 val = readl(pp->dbi_base + DEBUG0);
|
||||
|
||||
return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
|
||||
}
|
||||
|
||||
void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Disable Link training */
|
||||
val = readl(ks_pcie->va_app_base + CMD_STATUS);
|
||||
val &= ~LTSSM_EN_VAL;
|
||||
writel(LTSSM_EN_VAL | val, ks_pcie->va_app_base + CMD_STATUS);
|
||||
|
||||
/* Initiate Link Training */
|
||||
val = readl(ks_pcie->va_app_base + CMD_STATUS);
|
||||
writel(LTSSM_EN_VAL | val, ks_pcie->va_app_base + CMD_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* ks_dw_pcie_host_init() - initialize host for v3_65 dw hardware
|
||||
*
|
||||
* Ioremap the register resources, initialize legacy irq domain
|
||||
* and call dw_pcie_v3_65_host_init() API to initialize the Keystone
|
||||
* PCI host controller.
|
||||
*/
|
||||
int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
|
||||
struct device_node *msi_intc_np)
|
||||
{
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
struct platform_device *pdev = to_platform_device(pp->dev);
|
||||
struct resource *res;
|
||||
|
||||
/* Index 0 is the config reg. space address */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pp->dbi_base = devm_ioremap_resource(pp->dev, res);
|
||||
if (IS_ERR(pp->dbi_base))
|
||||
return PTR_ERR(pp->dbi_base);
|
||||
|
||||
/*
|
||||
* We set these same and is used in pcie rd/wr_other_conf
|
||||
* functions
|
||||
*/
|
||||
pp->va_cfg0_base = pp->dbi_base + SPACE0_REMOTE_CFG_OFFSET;
|
||||
pp->va_cfg1_base = pp->va_cfg0_base;
|
||||
|
||||
/* Index 1 is the application reg. space address */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
ks_pcie->app = *res;
|
||||
ks_pcie->va_app_base = devm_ioremap_resource(pp->dev, res);
|
||||
if (IS_ERR(ks_pcie->va_app_base))
|
||||
return PTR_ERR(ks_pcie->va_app_base);
|
||||
|
||||
/* Create legacy IRQ domain */
|
||||
ks_pcie->legacy_irq_domain =
|
||||
irq_domain_add_linear(ks_pcie->legacy_intc_np,
|
||||
MAX_LEGACY_IRQS,
|
||||
&ks_dw_pcie_legacy_irq_domain_ops,
|
||||
NULL);
|
||||
if (!ks_pcie->legacy_irq_domain) {
|
||||
dev_err(pp->dev, "Failed to add irq domain for legacy irqs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return dw_pcie_host_init(pp);
|
||||
}
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* PCIe host controller driver for Texas Instruments Keystone SoCs
|
||||
*
|
||||
* Copyright (C) 2013-2014 Texas Instruments., Ltd.
|
||||
* http://www.ti.com
|
||||
*
|
||||
* Author: Murali Karicheri <m-karicheri2@ti.com>
|
||||
* Implementation based on pci-exynos.c and pcie-designware.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/signal.h>
|
||||
|
||||
#include "pcie-designware.h"
|
||||
#include "pci-keystone.h"
|
||||
|
||||
#define DRIVER_NAME "keystone-pcie"
|
||||
|
||||
/* driver specific constants */
|
||||
#define MAX_MSI_HOST_IRQS 8
|
||||
#define MAX_LEGACY_HOST_IRQS 4
|
||||
|
||||
/* DEV_STAT_CTRL */
|
||||
#define PCIE_CAP_BASE 0x70
|
||||
|
||||
/* PCIE controller device IDs */
|
||||
#define PCIE_RC_K2HK 0xb008
|
||||
#define PCIE_RC_K2E 0xb009
|
||||
#define PCIE_RC_K2L 0xb00a
|
||||
|
||||
#define to_keystone_pcie(x) container_of(x, struct keystone_pcie, pp)
|
||||
|
||||
static void quirk_limit_mrrs(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_bus *bus = dev->bus;
|
||||
struct pci_dev *bridge = bus->self;
|
||||
static const struct pci_device_id rc_pci_devids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2HK),
|
||||
.class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2E),
|
||||
.class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2L),
|
||||
.class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
if (pci_is_root_bus(bus))
|
||||
return;
|
||||
|
||||
/* look for the host bridge */
|
||||
while (!pci_is_root_bus(bus)) {
|
||||
bridge = bus->self;
|
||||
bus = bus->parent;
|
||||
}
|
||||
|
||||
if (bridge) {
|
||||
/*
|
||||
* Keystone PCI controller has a h/w limitation of
|
||||
* 256 bytes maximum read request size. It can't handle
|
||||
* anything higher than this. So force this limit on
|
||||
* all downstream devices.
|
||||
*/
|
||||
if (pci_match_id(rc_pci_devids, bridge)) {
|
||||
if (pcie_get_readrq(dev) > 256) {
|
||||
dev_info(&dev->dev, "limiting MRRS to 256\n");
|
||||
pcie_set_readrq(dev, 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs);
|
||||
|
||||
static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
|
||||
{
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
int count = 200;
|
||||
|
||||
dw_pcie_setup_rc(pp);
|
||||
|
||||
if (dw_pcie_link_up(pp)) {
|
||||
dev_err(pp->dev, "Link already up\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ks_dw_pcie_initiate_link_train(ks_pcie);
|
||||
/* check if the link is up or not */
|
||||
while (!dw_pcie_link_up(pp)) {
|
||||
usleep_range(100, 1000);
|
||||
if (--count) {
|
||||
ks_dw_pcie_initiate_link_train(ks_pcie);
|
||||
continue;
|
||||
}
|
||||
dev_err(pp->dev, "phy link never came up\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ks_pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
|
||||
u32 offset = irq - ks_pcie->msi_host_irqs[0];
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
|
||||
dev_dbg(pp->dev, "ks_pci_msi_irq_handler, irq %d\n", irq);
|
||||
|
||||
/*
|
||||
* The chained irq handler installation would have replaced normal
|
||||
* interrupt driver handler so we need to take care of mask/unmask and
|
||||
* ack operation.
|
||||
*/
|
||||
chained_irq_enter(chip, desc);
|
||||
ks_dw_pcie_handle_msi_irq(ks_pcie, offset);
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* ks_pcie_legacy_irq_handler() - Handle legacy interrupt
|
||||
* @irq: IRQ line for legacy interrupts
|
||||
* @desc: Pointer to irq descriptor
|
||||
*
|
||||
* Traverse through pending legacy interrupts and invoke handler for each. Also
|
||||
* takes care of interrupt controller level mask/ack operation.
|
||||
*/
|
||||
static void ks_pcie_legacy_irq_handler(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0];
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
|
||||
dev_dbg(pp->dev, ": Handling legacy irq %d\n", irq);
|
||||
|
||||
/*
|
||||
* The chained irq handler installation would have replaced normal
|
||||
* interrupt driver handler so we need to take care of mask/unmask and
|
||||
* ack operation.
|
||||
*/
|
||||
chained_irq_enter(chip, desc);
|
||||
ks_dw_pcie_handle_legacy_irq(ks_pcie, irq_offset);
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
|
||||
char *controller, int *num_irqs)
|
||||
{
|
||||
int temp, max_host_irqs, legacy = 1, *host_irqs, ret = -EINVAL;
|
||||
struct device *dev = ks_pcie->pp.dev;
|
||||
struct device_node *np_pcie = dev->of_node, **np_temp;
|
||||
|
||||
if (!strcmp(controller, "msi-interrupt-controller"))
|
||||
legacy = 0;
|
||||
|
||||
if (legacy) {
|
||||
np_temp = &ks_pcie->legacy_intc_np;
|
||||
max_host_irqs = MAX_LEGACY_HOST_IRQS;
|
||||
host_irqs = &ks_pcie->legacy_host_irqs[0];
|
||||
} else {
|
||||
np_temp = &ks_pcie->msi_intc_np;
|
||||
max_host_irqs = MAX_MSI_HOST_IRQS;
|
||||
host_irqs = &ks_pcie->msi_host_irqs[0];
|
||||
}
|
||||
|
||||
/* interrupt controller is in a child node */
|
||||
*np_temp = of_find_node_by_name(np_pcie, controller);
|
||||
if (!(*np_temp)) {
|
||||
dev_err(dev, "Node for %s is absent\n", controller);
|
||||
goto out;
|
||||
}
|
||||
temp = of_irq_count(*np_temp);
|
||||
if (!temp)
|
||||
goto out;
|
||||
if (temp > max_host_irqs)
|
||||
dev_warn(dev, "Too many %s interrupts defined %u\n",
|
||||
(legacy ? "legacy" : "MSI"), temp);
|
||||
|
||||
/*
|
||||
* support upto max_host_irqs. In dt from index 0 to 3 (legacy) or 0 to
|
||||
* 7 (MSI)
|
||||
*/
|
||||
for (temp = 0; temp < max_host_irqs; temp++) {
|
||||
host_irqs[temp] = irq_of_parse_and_map(*np_temp, temp);
|
||||
if (host_irqs[temp] < 0)
|
||||
break;
|
||||
}
|
||||
if (temp) {
|
||||
*num_irqs = temp;
|
||||
ret = 0;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Legacy IRQ */
|
||||
for (i = 0; i < ks_pcie->num_legacy_host_irqs; i++) {
|
||||
irq_set_handler_data(ks_pcie->legacy_host_irqs[i], ks_pcie);
|
||||
irq_set_chained_handler(ks_pcie->legacy_host_irqs[i],
|
||||
ks_pcie_legacy_irq_handler);
|
||||
}
|
||||
ks_dw_pcie_enable_legacy_irqs(ks_pcie);
|
||||
|
||||
/* MSI IRQ */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
for (i = 0; i < ks_pcie->num_msi_host_irqs; i++) {
|
||||
irq_set_chained_handler(ks_pcie->msi_host_irqs[i],
|
||||
ks_pcie_msi_irq_handler);
|
||||
irq_set_handler_data(ks_pcie->msi_host_irqs[i],
|
||||
ks_pcie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When a PCI device does not exist during config cycles, keystone host gets a
|
||||
* bus error instead of returning 0xffffffff. This handler always returns 0
|
||||
* for this kind of faults.
|
||||
*/
|
||||
static int keystone_pcie_fault(unsigned long addr, unsigned int fsr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long instr = *(unsigned long *) instruction_pointer(regs);
|
||||
|
||||
if ((instr & 0x0e100090) == 0x00100090) {
|
||||
int reg = (instr >> 12) & 15;
|
||||
|
||||
regs->uregs[reg] = -1;
|
||||
regs->ARM_pc += 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init ks_pcie_host_init(struct pcie_port *pp)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
u32 val;
|
||||
|
||||
ks_pcie_establish_link(ks_pcie);
|
||||
ks_dw_pcie_setup_rc_app_regs(ks_pcie);
|
||||
ks_pcie_setup_interrupts(ks_pcie);
|
||||
writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8),
|
||||
pp->dbi_base + PCI_IO_BASE);
|
||||
|
||||
/* update the Vendor ID */
|
||||
writew(ks_pcie->device_id, pp->dbi_base + PCI_DEVICE_ID);
|
||||
|
||||
/* update the DEV_STAT_CTRL to publish right mrrs */
|
||||
val = readl(pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
|
||||
val &= ~PCI_EXP_DEVCTL_READRQ;
|
||||
/* set the mrrs to 256 bytes */
|
||||
val |= BIT(12);
|
||||
writel(val, pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
|
||||
|
||||
/*
|
||||
* PCIe access errors that result into OCP errors are caught by ARM as
|
||||
* "External aborts"
|
||||
*/
|
||||
hook_fault_code(17, keystone_pcie_fault, SIGBUS, 0,
|
||||
"Asynchronous external abort");
|
||||
}
|
||||
|
||||
static struct pcie_host_ops keystone_pcie_host_ops = {
|
||||
.rd_other_conf = ks_dw_pcie_rd_other_conf,
|
||||
.wr_other_conf = ks_dw_pcie_wr_other_conf,
|
||||
.link_up = ks_dw_pcie_link_up,
|
||||
.host_init = ks_pcie_host_init,
|
||||
.msi_set_irq = ks_dw_pcie_msi_set_irq,
|
||||
.msi_clear_irq = ks_dw_pcie_msi_clear_irq,
|
||||
.get_msi_addr = ks_dw_pcie_get_msi_addr,
|
||||
.msi_host_init = ks_dw_pcie_msi_host_init,
|
||||
.scan_bus = ks_dw_pcie_v3_65_scan_bus,
|
||||
};
|
||||
|
||||
static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
int ret;
|
||||
|
||||
ret = ks_pcie_get_irq_controller_info(ks_pcie,
|
||||
"legacy-interrupt-controller",
|
||||
&ks_pcie->num_legacy_host_irqs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
ret = ks_pcie_get_irq_controller_info(ks_pcie,
|
||||
"msi-interrupt-controller",
|
||||
&ks_pcie->num_msi_host_irqs);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &keystone_pcie_host_ops;
|
||||
ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id ks_pcie_of_match[] = {
|
||||
{
|
||||
.type = "pci",
|
||||
.compatible = "ti,keystone-pcie",
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ks_pcie_of_match);
|
||||
|
||||
static int __exit ks_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(ks_pcie->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ks_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct keystone_pcie *ks_pcie;
|
||||
struct pcie_port *pp;
|
||||
struct resource *res;
|
||||
void __iomem *reg_p;
|
||||
struct phy *phy;
|
||||
int ret = 0;
|
||||
|
||||
ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie),
|
||||
GFP_KERNEL);
|
||||
if (!ks_pcie) {
|
||||
dev_err(dev, "no memory for keystone pcie\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
pp = &ks_pcie->pp;
|
||||
|
||||
/* initialize SerDes Phy if present */
|
||||
phy = devm_phy_get(dev, "pcie-phy");
|
||||
if (!IS_ERR_OR_NULL(phy)) {
|
||||
ret = phy_init(phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* index 2 is to read PCI DEVICE_ID */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
reg_p = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(reg_p))
|
||||
return PTR_ERR(reg_p);
|
||||
ks_pcie->device_id = readl(reg_p) >> 16;
|
||||
devm_iounmap(dev, reg_p);
|
||||
devm_release_mem_region(dev, res->start, resource_size(res));
|
||||
|
||||
pp->dev = dev;
|
||||
platform_set_drvdata(pdev, ks_pcie);
|
||||
ks_pcie->clk = devm_clk_get(dev, "pcie");
|
||||
if (IS_ERR(ks_pcie->clk)) {
|
||||
dev_err(dev, "Failed to get pcie rc clock\n");
|
||||
return PTR_ERR(ks_pcie->clk);
|
||||
}
|
||||
ret = clk_prepare_enable(ks_pcie->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ks_add_pcie_port(ks_pcie, pdev);
|
||||
if (ret < 0)
|
||||
goto fail_clk;
|
||||
|
||||
return 0;
|
||||
fail_clk:
|
||||
clk_disable_unprepare(ks_pcie->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver ks_pcie_driver __refdata = {
|
||||
.probe = ks_pcie_probe,
|
||||
.remove = __exit_p(ks_pcie_remove),
|
||||
.driver = {
|
||||
.name = "keystone-pcie",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(ks_pcie_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ks_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");
|
||||
MODULE_DESCRIPTION("Keystone PCIe host controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Keystone PCI Controller's common includes
|
||||
*
|
||||
* Copyright (C) 2013-2014 Texas Instruments., Ltd.
|
||||
* http://www.ti.com
|
||||
*
|
||||
* Author: Murali Karicheri <m-karicheri2@ti.com>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define MAX_LEGACY_IRQS 4
|
||||
#define MAX_MSI_HOST_IRQS 8
|
||||
#define MAX_LEGACY_HOST_IRQS 4
|
||||
|
||||
struct keystone_pcie {
|
||||
struct clk *clk;
|
||||
struct pcie_port pp;
|
||||
/* PCI Device ID */
|
||||
u32 device_id;
|
||||
int num_legacy_host_irqs;
|
||||
int legacy_host_irqs[MAX_LEGACY_HOST_IRQS];
|
||||
struct device_node *legacy_intc_np;
|
||||
|
||||
int num_msi_host_irqs;
|
||||
int msi_host_irqs[MAX_MSI_HOST_IRQS];
|
||||
struct device_node *msi_intc_np;
|
||||
struct irq_domain *legacy_irq_domain;
|
||||
|
||||
/* Application register space */
|
||||
void __iomem *va_app_base;
|
||||
struct resource app;
|
||||
};
|
||||
|
||||
/* Keystone DW specific MSI controller APIs/definitions */
|
||||
void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset);
|
||||
u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
|
||||
|
||||
/* Keystone specific PCI controller APIs */
|
||||
void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
|
||||
void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset);
|
||||
int ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
|
||||
struct device_node *msi_intc_np);
|
||||
int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
unsigned int devfn, int where, int size, u32 val);
|
||||
int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
unsigned int devfn, int where, int size, u32 *val);
|
||||
void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie);
|
||||
int ks_dw_pcie_link_up(struct pcie_port *pp);
|
||||
void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie);
|
||||
void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq);
|
||||
void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq);
|
||||
void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp);
|
||||
int ks_dw_pcie_msi_host_init(struct pcie_port *pp,
|
||||
struct msi_chip *chip);
|
|
@ -873,7 +873,7 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
|
|||
rangesz = pna + na + ns;
|
||||
nranges = rlen / sizeof(__be32) / rangesz;
|
||||
|
||||
for (i = 0; i < nranges; i++) {
|
||||
for (i = 0; i < nranges; i++, range += rangesz) {
|
||||
u32 flags = of_read_number(range, 1);
|
||||
u32 slot = of_read_number(range + 1, 1);
|
||||
u64 cpuaddr = of_read_number(range + na, pna);
|
||||
|
@ -883,14 +883,14 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
|
|||
rtype = IORESOURCE_IO;
|
||||
else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32)
|
||||
rtype = IORESOURCE_MEM;
|
||||
else
|
||||
continue;
|
||||
|
||||
if (slot == PCI_SLOT(devfn) && type == rtype) {
|
||||
*tgt = DT_CPUADDR_TO_TARGET(cpuaddr);
|
||||
*attr = DT_CPUADDR_TO_ATTR(cpuaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
range += rangesz;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
|
@ -115,13 +116,20 @@
|
|||
|
||||
#define AFI_INTR_CODE 0xb8
|
||||
#define AFI_INTR_CODE_MASK 0xf
|
||||
#define AFI_INTR_AXI_SLAVE_ERROR 1
|
||||
#define AFI_INTR_AXI_DECODE_ERROR 2
|
||||
#define AFI_INTR_INI_SLAVE_ERROR 1
|
||||
#define AFI_INTR_INI_DECODE_ERROR 2
|
||||
#define AFI_INTR_TARGET_ABORT 3
|
||||
#define AFI_INTR_MASTER_ABORT 4
|
||||
#define AFI_INTR_INVALID_WRITE 5
|
||||
#define AFI_INTR_LEGACY 6
|
||||
#define AFI_INTR_FPCI_DECODE_ERROR 7
|
||||
#define AFI_INTR_AXI_DECODE_ERROR 8
|
||||
#define AFI_INTR_FPCI_TIMEOUT 9
|
||||
#define AFI_INTR_PE_PRSNT_SENSE 10
|
||||
#define AFI_INTR_PE_CLKREQ_SENSE 11
|
||||
#define AFI_INTR_CLKCLAMP_SENSE 12
|
||||
#define AFI_INTR_RDY4PD_SENSE 13
|
||||
#define AFI_INTR_P2P_ERROR 14
|
||||
|
||||
#define AFI_INTR_SIGNATURE 0xbc
|
||||
#define AFI_UPPER_FPCI_ADDRESS 0xc0
|
||||
|
@ -152,8 +160,10 @@
|
|||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20)
|
||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20)
|
||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20)
|
||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20)
|
||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
|
||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20)
|
||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20)
|
||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20)
|
||||
|
||||
#define AFI_FUSE 0x104
|
||||
|
@ -165,12 +175,21 @@
|
|||
#define AFI_PEX_CTRL_RST (1 << 0)
|
||||
#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1)
|
||||
#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
|
||||
#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4)
|
||||
|
||||
#define AFI_PLLE_CONTROL 0x160
|
||||
#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9)
|
||||
#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1)
|
||||
|
||||
#define AFI_PEXBIAS_CTRL_0 0x168
|
||||
|
||||
#define RP_VEND_XP 0x00000F00
|
||||
#define RP_VEND_XP_DL_UP (1 << 30)
|
||||
|
||||
#define RP_PRIV_MISC 0x00000FE0
|
||||
#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0)
|
||||
#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0)
|
||||
|
||||
#define RP_LINK_CONTROL_STATUS 0x00000090
|
||||
#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000
|
||||
#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000
|
||||
|
@ -197,6 +216,7 @@
|
|||
|
||||
#define PADS_REFCLK_CFG0 0x000000C8
|
||||
#define PADS_REFCLK_CFG1 0x000000CC
|
||||
#define PADS_REFCLK_BIAS 0x000000D0
|
||||
|
||||
/*
|
||||
* Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit
|
||||
|
@ -236,6 +256,7 @@ struct tegra_pcie_soc_data {
|
|||
bool has_pex_bias_ctrl;
|
||||
bool has_intr_prsnt_sense;
|
||||
bool has_cml_clk;
|
||||
bool has_gen2;
|
||||
};
|
||||
|
||||
static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
|
||||
|
@ -253,6 +274,7 @@ struct tegra_pcie {
|
|||
struct list_head buses;
|
||||
struct resource *cs;
|
||||
|
||||
struct resource all;
|
||||
struct resource io;
|
||||
struct resource mem;
|
||||
struct resource prefetch;
|
||||
|
@ -267,6 +289,8 @@ struct tegra_pcie {
|
|||
struct reset_control *afi_rst;
|
||||
struct reset_control *pcie_xrst;
|
||||
|
||||
struct phy *phy;
|
||||
|
||||
struct tegra_msi msi;
|
||||
|
||||
struct list_head ports;
|
||||
|
@ -382,7 +406,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
|
|||
for (i = 0; i < 16; i++) {
|
||||
unsigned long virt = (unsigned long)bus->area->addr +
|
||||
i * SZ_64K;
|
||||
phys_addr_t phys = cs + i * SZ_1M + busnr * SZ_64K;
|
||||
phys_addr_t phys = cs + i * SZ_16M + busnr * SZ_64K;
|
||||
|
||||
err = ioremap_page_range(virt, virt + SZ_64K, phys, prot);
|
||||
if (err < 0) {
|
||||
|
@ -561,6 +585,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
|
|||
if (soc->has_pex_clkreq_en)
|
||||
value |= AFI_PEX_CTRL_CLKREQ_EN;
|
||||
|
||||
value |= AFI_PEX_CTRL_OVERRIDE_EN;
|
||||
|
||||
afi_writel(port->pcie, value, ctrl);
|
||||
|
||||
tegra_pcie_port_reset(port);
|
||||
|
@ -568,6 +594,7 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
|
|||
|
||||
static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
|
||||
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
|
||||
unsigned long value;
|
||||
|
||||
|
@ -578,6 +605,10 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
|
|||
|
||||
/* disable reference clock */
|
||||
value = afi_readl(port->pcie, ctrl);
|
||||
|
||||
if (soc->has_pex_clkreq_en)
|
||||
value &= ~AFI_PEX_CTRL_CLKREQ_EN;
|
||||
|
||||
value &= ~AFI_PEX_CTRL_REFCLK_EN;
|
||||
afi_writel(port->pcie, value, ctrl);
|
||||
}
|
||||
|
@ -626,13 +657,25 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
|
|||
static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
{
|
||||
struct tegra_pcie *pcie = sys_to_pcie(sys);
|
||||
int err;
|
||||
phys_addr_t io_start;
|
||||
|
||||
err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = devm_request_resource(pcie->dev, &pcie->all, &pcie->prefetch);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
io_start = pci_pio_to_address(pcie->io.start);
|
||||
|
||||
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
|
||||
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
|
||||
sys->mem_offset);
|
||||
pci_add_resource(&sys->resources, &pcie->busn);
|
||||
|
||||
pci_ioremap_io(nr * SZ_64K, pcie->io.start);
|
||||
pci_ioremap_io(nr * SZ_64K, io_start);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -684,9 +727,15 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
|
|||
"Target abort",
|
||||
"Master abort",
|
||||
"Invalid write",
|
||||
"Legacy interrupt",
|
||||
"Response decoding error",
|
||||
"AXI response decoding error",
|
||||
"Transaction timeout",
|
||||
"Slot present pin change",
|
||||
"Slot clock request change",
|
||||
"TMS clock ramp change",
|
||||
"TMS ready for power down",
|
||||
"Peer2Peer error",
|
||||
};
|
||||
struct tegra_pcie *pcie = arg;
|
||||
u32 code, signature;
|
||||
|
@ -737,6 +786,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
|
|||
static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
|
||||
{
|
||||
u32 fpci_bar, size, axi_address;
|
||||
phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
|
||||
|
||||
/* Bar 0: type 1 extended configuration space */
|
||||
fpci_bar = 0xfe100000;
|
||||
|
@ -749,7 +799,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
|
|||
/* Bar 1: downstream IO bar */
|
||||
fpci_bar = 0xfdfc0000;
|
||||
size = resource_size(&pcie->io);
|
||||
axi_address = pcie->io.start;
|
||||
axi_address = io_start;
|
||||
afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
|
||||
afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
|
||||
afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
|
||||
|
@ -792,30 +842,27 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
|
|||
afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
|
||||
}
|
||||
|
||||
static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
||||
static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
struct tegra_pcie_port *port;
|
||||
unsigned int timeout;
|
||||
unsigned long value;
|
||||
u32 value;
|
||||
|
||||
/* power down PCIe slot clock bias pad */
|
||||
if (soc->has_pex_bias_ctrl)
|
||||
afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
|
||||
timeout = jiffies + msecs_to_jiffies(timeout);
|
||||
|
||||
/* configure mode and disable all ports */
|
||||
value = afi_readl(pcie, AFI_PCIE_CONFIG);
|
||||
value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
|
||||
value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
|
||||
while (time_before(jiffies, timeout)) {
|
||||
value = pads_readl(pcie, soc->pads_pll_ctl);
|
||||
if (value & PADS_PLL_CTL_LOCKDET)
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each_entry(port, &pcie->ports, list)
|
||||
value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
afi_writel(pcie, value, AFI_PCIE_CONFIG);
|
||||
|
||||
value = afi_readl(pcie, AFI_FUSE);
|
||||
value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
|
||||
afi_writel(pcie, value, AFI_FUSE);
|
||||
static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
/* initialize internal PHY, enable up to 16 PCIE lanes */
|
||||
pads_writel(pcie, 0x0, PADS_CTL_SEL);
|
||||
|
@ -834,6 +881,13 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
|||
value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
|
||||
pads_writel(pcie, value, soc->pads_pll_ctl);
|
||||
|
||||
/* reset PLL */
|
||||
value = pads_readl(pcie, soc->pads_pll_ctl);
|
||||
value &= ~PADS_PLL_CTL_RST_B4SM;
|
||||
pads_writel(pcie, value, soc->pads_pll_ctl);
|
||||
|
||||
usleep_range(20, 100);
|
||||
|
||||
/* take PLL out of reset */
|
||||
value = pads_readl(pcie, soc->pads_pll_ctl);
|
||||
value |= PADS_PLL_CTL_RST_B4SM;
|
||||
|
@ -846,15 +900,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
|||
pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1);
|
||||
|
||||
/* wait for the PLL to lock */
|
||||
timeout = 300;
|
||||
do {
|
||||
value = pads_readl(pcie, soc->pads_pll_ctl);
|
||||
usleep_range(1000, 2000);
|
||||
if (--timeout == 0) {
|
||||
pr_err("Tegra PCIe error: timeout waiting for PLL\n");
|
||||
return -EBUSY;
|
||||
err = tegra_pcie_pll_wait(pcie, 500);
|
||||
if (err < 0) {
|
||||
dev_err(pcie->dev, "PLL failed to lock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
} while (!(value & PADS_PLL_CTL_LOCKDET));
|
||||
|
||||
/* turn off IDDQ override */
|
||||
value = pads_readl(pcie, PADS_CTL);
|
||||
|
@ -866,6 +916,58 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
|||
value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
|
||||
pads_writel(pcie, value, PADS_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
struct tegra_pcie_port *port;
|
||||
unsigned long value;
|
||||
int err;
|
||||
|
||||
/* enable PLL power down */
|
||||
if (pcie->phy) {
|
||||
value = afi_readl(pcie, AFI_PLLE_CONTROL);
|
||||
value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
|
||||
value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
|
||||
afi_writel(pcie, value, AFI_PLLE_CONTROL);
|
||||
}
|
||||
|
||||
/* power down PCIe slot clock bias pad */
|
||||
if (soc->has_pex_bias_ctrl)
|
||||
afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
|
||||
|
||||
/* configure mode and disable all ports */
|
||||
value = afi_readl(pcie, AFI_PCIE_CONFIG);
|
||||
value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
|
||||
value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
|
||||
|
||||
list_for_each_entry(port, &pcie->ports, list)
|
||||
value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
|
||||
|
||||
afi_writel(pcie, value, AFI_PCIE_CONFIG);
|
||||
|
||||
if (soc->has_gen2) {
|
||||
value = afi_readl(pcie, AFI_FUSE);
|
||||
value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
|
||||
afi_writel(pcie, value, AFI_FUSE);
|
||||
} else {
|
||||
value = afi_readl(pcie, AFI_FUSE);
|
||||
value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
|
||||
afi_writel(pcie, value, AFI_FUSE);
|
||||
}
|
||||
|
||||
if (!pcie->phy)
|
||||
err = tegra_pcie_phy_enable(pcie);
|
||||
else
|
||||
err = phy_power_on(pcie->phy);
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(pcie->dev, "failed to power on PHY: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* take the PCIe interface module out of reset */
|
||||
reset_control_deassert(pcie->pcie_xrst);
|
||||
|
||||
|
@ -899,6 +1001,10 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
|||
|
||||
/* TODO: disable and unprepare clocks? */
|
||||
|
||||
err = phy_power_off(pcie->phy);
|
||||
if (err < 0)
|
||||
dev_warn(pcie->dev, "failed to power off PHY: %d\n", err);
|
||||
|
||||
reset_control_assert(pcie->pcie_xrst);
|
||||
reset_control_assert(pcie->afi_rst);
|
||||
reset_control_assert(pcie->pex_rst);
|
||||
|
@ -1020,6 +1126,19 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
|
|||
return err;
|
||||
}
|
||||
|
||||
pcie->phy = devm_phy_optional_get(pcie->dev, "pcie");
|
||||
if (IS_ERR(pcie->phy)) {
|
||||
err = PTR_ERR(pcie->phy);
|
||||
dev_err(&pdev->dev, "failed to get PHY: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = phy_init(pcie->phy);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tegra_pcie_power_on(pcie);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to power up: %d\n", err);
|
||||
|
@ -1078,10 +1197,17 @@ poweroff:
|
|||
|
||||
static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (pcie->irq > 0)
|
||||
free_irq(pcie->irq, pcie);
|
||||
|
||||
tegra_pcie_power_off(pcie);
|
||||
|
||||
err = phy_exit(pcie->phy);
|
||||
if (err < 0)
|
||||
dev_err(pcie->dev, "failed to teardown PHY: %d\n", err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1170,8 +1296,10 @@ static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
|
|||
return hwirq;
|
||||
|
||||
irq = irq_create_mapping(msi->domain, hwirq);
|
||||
if (!irq)
|
||||
if (!irq) {
|
||||
tegra_msi_free(msi, hwirq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_set_msi_desc(irq, desc);
|
||||
|
||||
|
@ -1189,8 +1317,10 @@ static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
|
|||
{
|
||||
struct tegra_msi *msi = to_tegra_msi(chip);
|
||||
struct irq_data *d = irq_get_irq_data(irq);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
tegra_msi_free(msi, d->hwirq);
|
||||
irq_dispose_mapping(irq);
|
||||
tegra_msi_free(msi, hwirq);
|
||||
}
|
||||
|
||||
static struct irq_chip tegra_msi_irq_chip = {
|
||||
|
@ -1327,7 +1457,19 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
|
|||
{
|
||||
struct device_node *np = pcie->dev->of_node;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
|
||||
if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
|
||||
switch (lanes) {
|
||||
case 0x0000104:
|
||||
dev_info(pcie->dev, "4x1, 1x1 configuration\n");
|
||||
*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1;
|
||||
return 0;
|
||||
|
||||
case 0x0000102:
|
||||
dev_info(pcie->dev, "2x1, 1x1 configuration\n");
|
||||
*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1;
|
||||
return 0;
|
||||
}
|
||||
} else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
|
||||
switch (lanes) {
|
||||
case 0x00000204:
|
||||
dev_info(pcie->dev, "4x1, 2x1 configuration\n");
|
||||
|
@ -1435,7 +1577,23 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
|
|||
struct device_node *np = pcie->dev->of_node;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
|
||||
if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
|
||||
pcie->num_supplies = 7;
|
||||
|
||||
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
|
||||
sizeof(*pcie->supplies),
|
||||
GFP_KERNEL);
|
||||
if (!pcie->supplies)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->supplies[i++].supply = "avddio-pex";
|
||||
pcie->supplies[i++].supply = "dvddio-pex";
|
||||
pcie->supplies[i++].supply = "avdd-pex-pll";
|
||||
pcie->supplies[i++].supply = "hvdd-pex";
|
||||
pcie->supplies[i++].supply = "hvdd-pex-pll-e";
|
||||
pcie->supplies[i++].supply = "vddio-pex-ctl";
|
||||
pcie->supplies[i++].supply = "avdd-pll-erefe";
|
||||
} else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
|
||||
bool need_pexa = false, need_pexb = false;
|
||||
|
||||
/* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */
|
||||
|
@ -1514,32 +1672,50 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
|||
struct resource res;
|
||||
int err;
|
||||
|
||||
memset(&pcie->all, 0, sizeof(pcie->all));
|
||||
pcie->all.flags = IORESOURCE_MEM;
|
||||
pcie->all.name = np->full_name;
|
||||
pcie->all.start = ~0;
|
||||
pcie->all.end = 0;
|
||||
|
||||
if (of_pci_range_parser_init(&parser, np)) {
|
||||
dev_err(pcie->dev, "missing \"ranges\" property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
of_pci_range_to_resource(&range, np, &res);
|
||||
err = of_pci_range_to_resource(&range, np, &res);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (res.flags & IORESOURCE_TYPE_BITS) {
|
||||
case IORESOURCE_IO:
|
||||
memcpy(&pcie->io, &res, sizeof(res));
|
||||
pcie->io.name = "I/O";
|
||||
pcie->io.name = np->full_name;
|
||||
break;
|
||||
|
||||
case IORESOURCE_MEM:
|
||||
if (res.flags & IORESOURCE_PREFETCH) {
|
||||
memcpy(&pcie->prefetch, &res, sizeof(res));
|
||||
pcie->prefetch.name = "PREFETCH";
|
||||
pcie->prefetch.name = "prefetchable";
|
||||
} else {
|
||||
memcpy(&pcie->mem, &res, sizeof(res));
|
||||
pcie->mem.name = "MEM";
|
||||
pcie->mem.name = "non-prefetchable";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (res.start <= pcie->all.start)
|
||||
pcie->all.start = res.start;
|
||||
|
||||
if (res.end >= pcie->all.end)
|
||||
pcie->all.end = res.end;
|
||||
}
|
||||
|
||||
err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->all);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = of_pci_parse_bus_range(np, &pcie->busn);
|
||||
if (err < 0) {
|
||||
dev_err(pcie->dev, "failed to parse ranges property: %d\n",
|
||||
|
@ -1641,6 +1817,12 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
|
|||
unsigned int retries = 3;
|
||||
unsigned long value;
|
||||
|
||||
/* override presence detection */
|
||||
value = readl(port->base + RP_PRIV_MISC);
|
||||
value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT;
|
||||
value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT;
|
||||
writel(value, port->base + RP_PRIV_MISC);
|
||||
|
||||
do {
|
||||
unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
|
||||
|
||||
|
@ -1721,6 +1903,7 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
|
|||
.has_pex_bias_ctrl = false,
|
||||
.has_intr_prsnt_sense = false,
|
||||
.has_cml_clk = false,
|
||||
.has_gen2 = false,
|
||||
};
|
||||
|
||||
static const struct tegra_pcie_soc_data tegra30_pcie_data = {
|
||||
|
@ -1732,9 +1915,23 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
|
|||
.has_pex_bias_ctrl = true,
|
||||
.has_intr_prsnt_sense = true,
|
||||
.has_cml_clk = true,
|
||||
.has_gen2 = false,
|
||||
};
|
||||
|
||||
static const struct tegra_pcie_soc_data tegra124_pcie_data = {
|
||||
.num_ports = 2,
|
||||
.msi_base_shift = 8,
|
||||
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
|
||||
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
|
||||
.has_pex_clkreq_en = true,
|
||||
.has_pex_bias_ctrl = true,
|
||||
.has_intr_prsnt_sense = true,
|
||||
.has_cml_clk = true,
|
||||
.has_gen2 = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_pcie_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data },
|
||||
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
|
||||
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
|
||||
{ },
|
||||
|
|
|
@ -0,0 +1,659 @@
|
|||
/**
|
||||
* APM X-Gene PCIe Driver
|
||||
*
|
||||
* Copyright (c) 2014 Applied Micro Circuits Corporation.
|
||||
*
|
||||
* Author: Tanmay Inamdar <tinamdar@apm.com>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include <linux/clk-private.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define PCIECORE_CTLANDSTATUS 0x50
|
||||
#define PIM1_1L 0x80
|
||||
#define IBAR2 0x98
|
||||
#define IR2MSK 0x9c
|
||||
#define PIM2_1L 0xa0
|
||||
#define IBAR3L 0xb4
|
||||
#define IR3MSKL 0xbc
|
||||
#define PIM3_1L 0xc4
|
||||
#define OMR1BARL 0x100
|
||||
#define OMR2BARL 0x118
|
||||
#define OMR3BARL 0x130
|
||||
#define CFGBARL 0x154
|
||||
#define CFGBARH 0x158
|
||||
#define CFGCTL 0x15c
|
||||
#define RTDID 0x160
|
||||
#define BRIDGE_CFG_0 0x2000
|
||||
#define BRIDGE_CFG_4 0x2010
|
||||
#define BRIDGE_STATUS_0 0x2600
|
||||
|
||||
#define LINK_UP_MASK 0x00000100
|
||||
#define AXI_EP_CFG_ACCESS 0x10000
|
||||
#define EN_COHERENCY 0xF0000000
|
||||
#define EN_REG 0x00000001
|
||||
#define OB_LO_IO 0x00000002
|
||||
#define XGENE_PCIE_VENDORID 0x10E8
|
||||
#define XGENE_PCIE_DEVICEID 0xE004
|
||||
#define SZ_1T (SZ_1G*1024ULL)
|
||||
#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe)
|
||||
|
||||
struct xgene_pcie_port {
|
||||
struct device_node *node;
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
void __iomem *csr_base;
|
||||
void __iomem *cfg_base;
|
||||
unsigned long cfg_addr;
|
||||
bool link_up;
|
||||
};
|
||||
|
||||
static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
|
||||
{
|
||||
return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
|
||||
}
|
||||
|
||||
/* PCIe Configuration Out/In */
|
||||
static inline void xgene_pcie_cfg_out32(void __iomem *addr, int offset, u32 val)
|
||||
{
|
||||
writel(val, addr + offset);
|
||||
}
|
||||
|
||||
static inline void xgene_pcie_cfg_out16(void __iomem *addr, int offset, u16 val)
|
||||
{
|
||||
u32 val32 = readl(addr + (offset & ~0x3));
|
||||
|
||||
switch (offset & 0x3) {
|
||||
case 2:
|
||||
val32 &= ~0xFFFF0000;
|
||||
val32 |= (u32)val << 16;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
val32 &= ~0xFFFF;
|
||||
val32 |= val;
|
||||
break;
|
||||
}
|
||||
writel(val32, addr + (offset & ~0x3));
|
||||
}
|
||||
|
||||
static inline void xgene_pcie_cfg_out8(void __iomem *addr, int offset, u8 val)
|
||||
{
|
||||
u32 val32 = readl(addr + (offset & ~0x3));
|
||||
|
||||
switch (offset & 0x3) {
|
||||
case 0:
|
||||
val32 &= ~0xFF;
|
||||
val32 |= val;
|
||||
break;
|
||||
case 1:
|
||||
val32 &= ~0xFF00;
|
||||
val32 |= (u32)val << 8;
|
||||
break;
|
||||
case 2:
|
||||
val32 &= ~0xFF0000;
|
||||
val32 |= (u32)val << 16;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
val32 &= ~0xFF000000;
|
||||
val32 |= (u32)val << 24;
|
||||
break;
|
||||
}
|
||||
writel(val32, addr + (offset & ~0x3));
|
||||
}
|
||||
|
||||
static inline void xgene_pcie_cfg_in32(void __iomem *addr, int offset, u32 *val)
|
||||
{
|
||||
*val = readl(addr + offset);
|
||||
}
|
||||
|
||||
static inline void xgene_pcie_cfg_in16(void __iomem *addr, int offset, u32 *val)
|
||||
{
|
||||
*val = readl(addr + (offset & ~0x3));
|
||||
|
||||
switch (offset & 0x3) {
|
||||
case 2:
|
||||
*val >>= 16;
|
||||
break;
|
||||
}
|
||||
|
||||
*val &= 0xFFFF;
|
||||
}
|
||||
|
||||
static inline void xgene_pcie_cfg_in8(void __iomem *addr, int offset, u32 *val)
|
||||
{
|
||||
*val = readl(addr + (offset & ~0x3));
|
||||
|
||||
switch (offset & 0x3) {
|
||||
case 3:
|
||||
*val = *val >> 24;
|
||||
break;
|
||||
case 2:
|
||||
*val = *val >> 16;
|
||||
break;
|
||||
case 1:
|
||||
*val = *val >> 8;
|
||||
break;
|
||||
}
|
||||
*val &= 0xFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* When the address bit [17:16] is 2'b01, the Configuration access will be
|
||||
* treated as Type 1 and it will be forwarded to external PCIe device.
|
||||
*/
|
||||
static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
|
||||
{
|
||||
struct xgene_pcie_port *port = bus->sysdata;
|
||||
|
||||
if (bus->number >= (bus->primary + 1))
|
||||
return port->cfg_base + AXI_EP_CFG_ACCESS;
|
||||
|
||||
return port->cfg_base;
|
||||
}
|
||||
|
||||
/*
|
||||
* For Configuration request, RTDID register is used as Bus Number,
|
||||
* Device Number and Function number of the header fields.
|
||||
*/
|
||||
static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
|
||||
{
|
||||
struct xgene_pcie_port *port = bus->sysdata;
|
||||
unsigned int b, d, f;
|
||||
u32 rtdid_val = 0;
|
||||
|
||||
b = bus->number;
|
||||
d = PCI_SLOT(devfn);
|
||||
f = PCI_FUNC(devfn);
|
||||
|
||||
if (!pci_is_root_bus(bus))
|
||||
rtdid_val = (b << 8) | (d << 3) | f;
|
||||
|
||||
writel(rtdid_val, port->csr_base + RTDID);
|
||||
/* read the register back to ensure flush */
|
||||
readl(port->csr_base + RTDID);
|
||||
}
|
||||
|
||||
/*
|
||||
* X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
|
||||
* the translation from PCI bus to native BUS. Entire DDR region
|
||||
* is mapped into PCIe space using these registers, so it can be
|
||||
* reached by DMA from EP devices. The BAR0/1 of bridge should be
|
||||
* hidden during enumeration to avoid the sizing and resource allocation
|
||||
* by PCIe core.
|
||||
*/
|
||||
static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
|
||||
{
|
||||
if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
|
||||
(offset == PCI_BASE_ADDRESS_1)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int offset, int len, u32 *val)
|
||||
{
|
||||
struct xgene_pcie_port *port = bus->sysdata;
|
||||
void __iomem *addr;
|
||||
|
||||
if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
if (xgene_pcie_hide_rc_bars(bus, offset)) {
|
||||
*val = 0;
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
xgene_pcie_set_rtdid_reg(bus, devfn);
|
||||
addr = xgene_pcie_get_cfg_base(bus);
|
||||
switch (len) {
|
||||
case 1:
|
||||
xgene_pcie_cfg_in8(addr, offset, val);
|
||||
break;
|
||||
case 2:
|
||||
xgene_pcie_cfg_in16(addr, offset, val);
|
||||
break;
|
||||
default:
|
||||
xgene_pcie_cfg_in32(addr, offset, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int xgene_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int offset, int len, u32 val)
|
||||
{
|
||||
struct xgene_pcie_port *port = bus->sysdata;
|
||||
void __iomem *addr;
|
||||
|
||||
if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
if (xgene_pcie_hide_rc_bars(bus, offset))
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
|
||||
xgene_pcie_set_rtdid_reg(bus, devfn);
|
||||
addr = xgene_pcie_get_cfg_base(bus);
|
||||
switch (len) {
|
||||
case 1:
|
||||
xgene_pcie_cfg_out8(addr, offset, (u8)val);
|
||||
break;
|
||||
case 2:
|
||||
xgene_pcie_cfg_out16(addr, offset, (u16)val);
|
||||
break;
|
||||
default:
|
||||
xgene_pcie_cfg_out32(addr, offset, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static struct pci_ops xgene_pcie_ops = {
|
||||
.read = xgene_pcie_read_config,
|
||||
.write = xgene_pcie_write_config
|
||||
};
|
||||
|
||||
static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
|
||||
u32 flags, u64 size)
|
||||
{
|
||||
u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags;
|
||||
u32 val32 = 0;
|
||||
u32 val;
|
||||
|
||||
val32 = readl(csr_base + addr);
|
||||
val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16);
|
||||
writel(val, csr_base + addr);
|
||||
|
||||
val32 = readl(csr_base + addr + 0x04);
|
||||
val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16);
|
||||
writel(val, csr_base + addr + 0x04);
|
||||
|
||||
val32 = readl(csr_base + addr + 0x04);
|
||||
val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16);
|
||||
writel(val, csr_base + addr + 0x04);
|
||||
|
||||
val32 = readl(csr_base + addr + 0x08);
|
||||
val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16);
|
||||
writel(val, csr_base + addr + 0x08);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void xgene_pcie_linkup(struct xgene_pcie_port *port,
|
||||
u32 *lanes, u32 *speed)
|
||||
{
|
||||
void __iomem *csr_base = port->csr_base;
|
||||
u32 val32;
|
||||
|
||||
port->link_up = false;
|
||||
val32 = readl(csr_base + PCIECORE_CTLANDSTATUS);
|
||||
if (val32 & LINK_UP_MASK) {
|
||||
port->link_up = true;
|
||||
*speed = PIPE_PHY_RATE_RD(val32);
|
||||
val32 = readl(csr_base + BRIDGE_STATUS_0);
|
||||
*lanes = val32 >> 26;
|
||||
}
|
||||
}
|
||||
|
||||
static int xgene_pcie_init_port(struct xgene_pcie_port *port)
|
||||
{
|
||||
int rc;
|
||||
|
||||
port->clk = clk_get(port->dev, NULL);
|
||||
if (IS_ERR(port->clk)) {
|
||||
dev_err(port->dev, "clock not available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = clk_prepare_enable(port->clk);
|
||||
if (rc) {
|
||||
dev_err(port->dev, "clock enable failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pcie_map_reg(struct xgene_pcie_port *port,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
|
||||
port->csr_base = devm_ioremap_resource(port->dev, res);
|
||||
if (IS_ERR(port->csr_base))
|
||||
return PTR_ERR(port->csr_base);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
|
||||
port->cfg_base = devm_ioremap_resource(port->dev, res);
|
||||
if (IS_ERR(port->cfg_base))
|
||||
return PTR_ERR(port->cfg_base);
|
||||
port->cfg_addr = res->start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
|
||||
struct resource *res, u32 offset,
|
||||
u64 cpu_addr, u64 pci_addr)
|
||||
{
|
||||
void __iomem *base = port->csr_base + offset;
|
||||
resource_size_t size = resource_size(res);
|
||||
u64 restype = resource_type(res);
|
||||
u64 mask = 0;
|
||||
u32 min_size;
|
||||
u32 flag = EN_REG;
|
||||
|
||||
if (restype == IORESOURCE_MEM) {
|
||||
min_size = SZ_128M;
|
||||
} else {
|
||||
min_size = 128;
|
||||
flag |= OB_LO_IO;
|
||||
}
|
||||
|
||||
if (size >= min_size)
|
||||
mask = ~(size - 1) | flag;
|
||||
else
|
||||
dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n",
|
||||
(u64)size, min_size);
|
||||
|
||||
writel(lower_32_bits(cpu_addr), base);
|
||||
writel(upper_32_bits(cpu_addr), base + 0x04);
|
||||
writel(lower_32_bits(mask), base + 0x08);
|
||||
writel(upper_32_bits(mask), base + 0x0c);
|
||||
writel(lower_32_bits(pci_addr), base + 0x10);
|
||||
writel(upper_32_bits(pci_addr), base + 0x14);
|
||||
}
|
||||
|
||||
static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr)
|
||||
{
|
||||
writel(lower_32_bits(addr), csr_base + CFGBARL);
|
||||
writel(upper_32_bits(addr), csr_base + CFGBARH);
|
||||
writel(EN_REG, csr_base + CFGCTL);
|
||||
}
|
||||
|
||||
static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
|
||||
struct list_head *res,
|
||||
resource_size_t io_base)
|
||||
{
|
||||
struct pci_host_bridge_window *window;
|
||||
struct device *dev = port->dev;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(window, res, list) {
|
||||
struct resource *res = window->res;
|
||||
u64 restype = resource_type(res);
|
||||
|
||||
dev_dbg(port->dev, "%pR\n", res);
|
||||
|
||||
switch (restype) {
|
||||
case IORESOURCE_IO:
|
||||
xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base,
|
||||
res->start - window->offset);
|
||||
ret = pci_remap_iospace(res, io_base);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case IORESOURCE_MEM:
|
||||
xgene_pcie_setup_ob_reg(port, res, OMR1BARL, res->start,
|
||||
res->start - window->offset);
|
||||
break;
|
||||
case IORESOURCE_BUS:
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid resource %pR\n", res);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size)
|
||||
{
|
||||
writel(lower_32_bits(pim), addr);
|
||||
writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04);
|
||||
writel(lower_32_bits(size), addr + 0x10);
|
||||
writel(upper_32_bits(size), addr + 0x14);
|
||||
}
|
||||
|
||||
/*
|
||||
* X-Gene PCIe support maximum 3 inbound memory regions
|
||||
* This function helps to select a region based on size of region
|
||||
*/
|
||||
static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size)
|
||||
{
|
||||
if ((size > 4) && (size < SZ_16M) && !(*ib_reg_mask & (1 << 1))) {
|
||||
*ib_reg_mask |= (1 << 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((size > SZ_1K) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 0))) {
|
||||
*ib_reg_mask |= (1 << 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((size > SZ_1M) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 2))) {
|
||||
*ib_reg_mask |= (1 << 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
|
||||
struct of_pci_range *range, u8 *ib_reg_mask)
|
||||
{
|
||||
void __iomem *csr_base = port->csr_base;
|
||||
void __iomem *cfg_base = port->cfg_base;
|
||||
void *bar_addr;
|
||||
void *pim_addr;
|
||||
u64 cpu_addr = range->cpu_addr;
|
||||
u64 pci_addr = range->pci_addr;
|
||||
u64 size = range->size;
|
||||
u64 mask = ~(size - 1) | EN_REG;
|
||||
u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64;
|
||||
u32 bar_low;
|
||||
int region;
|
||||
|
||||
region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size);
|
||||
if (region < 0) {
|
||||
dev_warn(port->dev, "invalid pcie dma-range config\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (range->flags & IORESOURCE_PREFETCH)
|
||||
flags |= PCI_BASE_ADDRESS_MEM_PREFETCH;
|
||||
|
||||
bar_low = pcie_bar_low_val((u32)cpu_addr, flags);
|
||||
switch (region) {
|
||||
case 0:
|
||||
xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size);
|
||||
bar_addr = cfg_base + PCI_BASE_ADDRESS_0;
|
||||
writel(bar_low, bar_addr);
|
||||
writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
|
||||
pim_addr = csr_base + PIM1_1L;
|
||||
break;
|
||||
case 1:
|
||||
bar_addr = csr_base + IBAR2;
|
||||
writel(bar_low, bar_addr);
|
||||
writel(lower_32_bits(mask), csr_base + IR2MSK);
|
||||
pim_addr = csr_base + PIM2_1L;
|
||||
break;
|
||||
case 2:
|
||||
bar_addr = csr_base + IBAR3L;
|
||||
writel(bar_low, bar_addr);
|
||||
writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
|
||||
writel(lower_32_bits(mask), csr_base + IR3MSKL);
|
||||
writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4);
|
||||
pim_addr = csr_base + PIM3_1L;
|
||||
break;
|
||||
}
|
||||
|
||||
xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1));
|
||||
}
|
||||
|
||||
static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
|
||||
struct device_node *node)
|
||||
{
|
||||
const int na = 3, ns = 2;
|
||||
int rlen;
|
||||
|
||||
parser->node = node;
|
||||
parser->pna = of_n_addr_cells(node);
|
||||
parser->np = parser->pna + na + ns;
|
||||
|
||||
parser->range = of_get_property(node, "dma-ranges", &rlen);
|
||||
if (!parser->range)
|
||||
return -ENOENT;
|
||||
parser->end = parser->range + rlen / sizeof(__be32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port)
|
||||
{
|
||||
struct device_node *np = port->node;
|
||||
struct of_pci_range range;
|
||||
struct of_pci_range_parser parser;
|
||||
struct device *dev = port->dev;
|
||||
u8 ib_reg_mask = 0;
|
||||
|
||||
if (pci_dma_range_parser_init(&parser, np)) {
|
||||
dev_err(dev, "missing dma-ranges property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get the dma-ranges from DT */
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
u64 end = range.cpu_addr + range.size - 1;
|
||||
|
||||
dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
|
||||
range.flags, range.cpu_addr, end, range.pci_addr);
|
||||
xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* clear BAR configuration which was done by firmware */
|
||||
static void xgene_pcie_clear_config(struct xgene_pcie_port *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = PIM1_1L; i <= CFGCTL; i += 4)
|
||||
writel(0x0, port->csr_base + i);
|
||||
}
|
||||
|
||||
static int xgene_pcie_setup(struct xgene_pcie_port *port,
|
||||
struct list_head *res,
|
||||
resource_size_t io_base)
|
||||
{
|
||||
u32 val, lanes = 0, speed = 0;
|
||||
int ret;
|
||||
|
||||
xgene_pcie_clear_config(port);
|
||||
|
||||
/* setup the vendor and device IDs correctly */
|
||||
val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID;
|
||||
writel(val, port->csr_base + BRIDGE_CFG_0);
|
||||
|
||||
ret = xgene_pcie_map_ranges(port, res, io_base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = xgene_pcie_parse_map_dma_ranges(port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xgene_pcie_linkup(port, &lanes, &speed);
|
||||
if (!port->link_up)
|
||||
dev_info(port->dev, "(rc) link down\n");
|
||||
else
|
||||
dev_info(port->dev, "(rc) x%d gen-%d link up\n",
|
||||
lanes, speed + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pcie_probe_bridge(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *dn = pdev->dev.of_node;
|
||||
struct xgene_pcie_port *port;
|
||||
resource_size_t iobase = 0;
|
||||
struct pci_bus *bus;
|
||||
int ret;
|
||||
LIST_HEAD(res);
|
||||
|
||||
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
port->node = of_node_get(pdev->dev.of_node);
|
||||
port->dev = &pdev->dev;
|
||||
|
||||
ret = xgene_pcie_map_reg(port, pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = xgene_pcie_init_port(port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_pci_get_host_bridge_resources(dn, 0, 0xff, &res, &iobase);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = xgene_pcie_setup(port, &res, iobase);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res);
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id xgene_pcie_match_table[] = {
|
||||
{.compatible = "apm,xgene-pcie",},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver xgene_pcie_driver = {
|
||||
.driver = {
|
||||
.name = "xgene-pcie",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(xgene_pcie_match_table),
|
||||
},
|
||||
.probe = xgene_pcie_probe_bridge,
|
||||
};
|
||||
module_platform_driver(xgene_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Tanmay Inamdar <tinamdar@apm.com>");
|
||||
MODULE_DESCRIPTION("APM X-Gene PCIe driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -73,6 +73,8 @@ static unsigned long global_io_offset;
|
|||
|
||||
static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
|
||||
{
|
||||
BUG_ON(!sys->private_data);
|
||||
|
||||
return sys->private_data;
|
||||
}
|
||||
|
||||
|
@ -194,30 +196,6 @@ void dw_pcie_msi_init(struct pcie_port *pp)
|
|||
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
|
||||
}
|
||||
|
||||
static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0)
|
||||
{
|
||||
int flag = 1;
|
||||
|
||||
do {
|
||||
pos = find_next_zero_bit(pp->msi_irq_in_use,
|
||||
MAX_MSI_IRQS, pos);
|
||||
/*if you have reached to the end then get out from here.*/
|
||||
if (pos == MAX_MSI_IRQS)
|
||||
return -ENOSPC;
|
||||
/*
|
||||
* Check if this position is at correct offset.nvec is always a
|
||||
* power of two. pos0 must be nvec bit aligned.
|
||||
*/
|
||||
if (pos % msgvec)
|
||||
pos += msgvec - (pos % msgvec);
|
||||
else
|
||||
flag = 0;
|
||||
} while (flag);
|
||||
|
||||
*pos0 = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
|
||||
{
|
||||
unsigned int res, bit, val;
|
||||
|
@ -236,13 +214,14 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base,
|
|||
|
||||
for (i = 0; i < nvec; i++) {
|
||||
irq_set_msi_desc_off(irq_base, i, NULL);
|
||||
clear_bit(pos + i, pp->msi_irq_in_use);
|
||||
/* Disable corresponding interrupt on MSI controller */
|
||||
if (pp->ops->msi_clear_irq)
|
||||
pp->ops->msi_clear_irq(pp, pos + i);
|
||||
else
|
||||
dw_pcie_msi_clear_irq(pp, pos + i);
|
||||
}
|
||||
|
||||
bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec));
|
||||
}
|
||||
|
||||
static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
|
||||
|
@ -258,31 +237,13 @@ static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
|
|||
|
||||
static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
|
||||
{
|
||||
int irq, pos0, pos1, i;
|
||||
int irq, pos0, i;
|
||||
struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata);
|
||||
|
||||
if (!pp) {
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pos0 = find_first_zero_bit(pp->msi_irq_in_use,
|
||||
MAX_MSI_IRQS);
|
||||
if (pos0 % no_irqs) {
|
||||
if (find_valid_pos0(pp, no_irqs, pos0, &pos0))
|
||||
pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS,
|
||||
order_base_2(no_irqs));
|
||||
if (pos0 < 0)
|
||||
goto no_valid_irq;
|
||||
}
|
||||
if (no_irqs > 1) {
|
||||
pos1 = find_next_bit(pp->msi_irq_in_use,
|
||||
MAX_MSI_IRQS, pos0);
|
||||
/* there must be nvec number of consecutive free bits */
|
||||
while ((pos1 - pos0) < no_irqs) {
|
||||
if (find_valid_pos0(pp, no_irqs, pos1, &pos0))
|
||||
goto no_valid_irq;
|
||||
pos1 = find_next_bit(pp->msi_irq_in_use,
|
||||
MAX_MSI_IRQS, pos0);
|
||||
}
|
||||
}
|
||||
|
||||
irq = irq_find_mapping(pp->irq_domain, pos0);
|
||||
if (!irq)
|
||||
|
@ -300,7 +261,6 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
|
|||
clear_irq_range(pp, irq, i, pos0);
|
||||
goto no_valid_irq;
|
||||
}
|
||||
set_bit(pos0 + i, pp->msi_irq_in_use);
|
||||
/*Enable corresponding interrupt in MSI interrupt controller */
|
||||
if (pp->ops->msi_set_irq)
|
||||
pp->ops->msi_set_irq(pp, pos0 + i);
|
||||
|
@ -316,69 +276,28 @@ no_valid_irq:
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static void clear_irq(unsigned int irq)
|
||||
{
|
||||
unsigned int pos, nvec;
|
||||
struct msi_desc *msi;
|
||||
struct pcie_port *pp;
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
|
||||
/* get the port structure */
|
||||
msi = irq_data_get_msi(data);
|
||||
pp = sys_to_pcie(msi->dev->bus->sysdata);
|
||||
if (!pp) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
/* undo what was done in assign_irq */
|
||||
pos = data->hwirq;
|
||||
nvec = 1 << msi->msi_attrib.multiple;
|
||||
|
||||
clear_irq_range(pp, irq, nvec, pos);
|
||||
|
||||
/* all irqs cleared; reset attributes */
|
||||
msi->irq = 0;
|
||||
msi->msi_attrib.multiple = 0;
|
||||
}
|
||||
|
||||
static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
|
||||
struct msi_desc *desc)
|
||||
{
|
||||
int irq, pos, msgvec;
|
||||
u16 msg_ctr;
|
||||
int irq, pos;
|
||||
struct msi_msg msg;
|
||||
struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
|
||||
|
||||
if (!pp) {
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS,
|
||||
&msg_ctr);
|
||||
msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4;
|
||||
if (msgvec == 0)
|
||||
msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1;
|
||||
if (msgvec > 5)
|
||||
msgvec = 0;
|
||||
|
||||
irq = assign_irq((1 << msgvec), desc, &pos);
|
||||
irq = assign_irq(1, desc, &pos);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
/*
|
||||
* write_msi_msg() will update PCI_MSI_FLAGS so there is
|
||||
* no need to explicitly call pci_write_config_word().
|
||||
*/
|
||||
desc->msi_attrib.multiple = msgvec;
|
||||
|
||||
if (pp->ops->get_msi_data)
|
||||
msg.address_lo = pp->ops->get_msi_data(pp);
|
||||
if (pp->ops->get_msi_addr)
|
||||
msg.address_lo = pp->ops->get_msi_addr(pp);
|
||||
else
|
||||
msg.address_lo = virt_to_phys((void *)pp->msi_data);
|
||||
msg.address_hi = 0x0;
|
||||
|
||||
if (pp->ops->get_msi_data)
|
||||
msg.data = pp->ops->get_msi_data(pp, pos);
|
||||
else
|
||||
msg.data = pos;
|
||||
|
||||
write_msi_msg(irq, &msg);
|
||||
|
||||
return 0;
|
||||
|
@ -386,7 +305,11 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
|
|||
|
||||
static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
|
||||
{
|
||||
clear_irq(irq);
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
struct msi_desc *msi = irq_data_get_msi(data);
|
||||
struct pcie_port *pp = sys_to_pcie(msi->dev->bus->sysdata);
|
||||
|
||||
clear_irq_range(pp, irq, 1, data->hwirq);
|
||||
}
|
||||
|
||||
static struct msi_chip dw_pcie_msi_chip = {
|
||||
|
@ -425,7 +348,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
struct resource *cfg_res;
|
||||
u32 val, na, ns;
|
||||
const __be32 *addrp;
|
||||
int i, index;
|
||||
int i, index, ret;
|
||||
|
||||
/* Find the address cell size and the number of cells in order to get
|
||||
* the untranslated address.
|
||||
|
@ -435,16 +358,16 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
|
||||
cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
|
||||
if (cfg_res) {
|
||||
pp->config.cfg0_size = resource_size(cfg_res)/2;
|
||||
pp->config.cfg1_size = resource_size(cfg_res)/2;
|
||||
pp->cfg0_size = resource_size(cfg_res)/2;
|
||||
pp->cfg1_size = resource_size(cfg_res)/2;
|
||||
pp->cfg0_base = cfg_res->start;
|
||||
pp->cfg1_base = cfg_res->start + pp->config.cfg0_size;
|
||||
pp->cfg1_base = cfg_res->start + pp->cfg0_size;
|
||||
|
||||
/* Find the untranslated configuration space address */
|
||||
index = of_property_match_string(np, "reg-names", "config");
|
||||
addrp = of_get_address(np, index, false, false);
|
||||
addrp = of_get_address(np, index, NULL, NULL);
|
||||
pp->cfg0_mod_base = of_read_number(addrp, ns);
|
||||
pp->cfg1_mod_base = pp->cfg0_mod_base + pp->config.cfg0_size;
|
||||
pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size;
|
||||
} else {
|
||||
dev_err(pp->dev, "missing *config* reg space\n");
|
||||
}
|
||||
|
@ -466,9 +389,9 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
pp->io.end = min_t(resource_size_t,
|
||||
IO_SPACE_LIMIT,
|
||||
range.pci_addr + range.size
|
||||
+ global_io_offset);
|
||||
pp->config.io_size = resource_size(&pp->io);
|
||||
pp->config.io_bus_addr = range.pci_addr;
|
||||
+ global_io_offset - 1);
|
||||
pp->io_size = resource_size(&pp->io);
|
||||
pp->io_bus_addr = range.pci_addr;
|
||||
pp->io_base = range.cpu_addr;
|
||||
|
||||
/* Find the untranslated IO space address */
|
||||
|
@ -478,8 +401,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
if (restype == IORESOURCE_MEM) {
|
||||
of_pci_range_to_resource(&range, np, &pp->mem);
|
||||
pp->mem.name = "MEM";
|
||||
pp->config.mem_size = resource_size(&pp->mem);
|
||||
pp->config.mem_bus_addr = range.pci_addr;
|
||||
pp->mem_size = resource_size(&pp->mem);
|
||||
pp->mem_bus_addr = range.pci_addr;
|
||||
|
||||
/* Find the untranslated MEM space address */
|
||||
pp->mem_mod_base = of_read_number(parser.range -
|
||||
|
@ -487,19 +410,29 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
}
|
||||
if (restype == 0) {
|
||||
of_pci_range_to_resource(&range, np, &pp->cfg);
|
||||
pp->config.cfg0_size = resource_size(&pp->cfg)/2;
|
||||
pp->config.cfg1_size = resource_size(&pp->cfg)/2;
|
||||
pp->cfg0_size = resource_size(&pp->cfg)/2;
|
||||
pp->cfg1_size = resource_size(&pp->cfg)/2;
|
||||
pp->cfg0_base = pp->cfg.start;
|
||||
pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size;
|
||||
pp->cfg1_base = pp->cfg.start + pp->cfg0_size;
|
||||
|
||||
/* Find the untranslated configuration space address */
|
||||
pp->cfg0_mod_base = of_read_number(parser.range -
|
||||
parser.np + na, ns);
|
||||
pp->cfg1_mod_base = pp->cfg0_mod_base +
|
||||
pp->config.cfg0_size;
|
||||
pp->cfg0_size;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_pci_parse_bus_range(np, &pp->busn);
|
||||
if (ret < 0) {
|
||||
pp->busn.name = np->name;
|
||||
pp->busn.start = 0;
|
||||
pp->busn.end = 0xff;
|
||||
pp->busn.flags = IORESOURCE_BUS;
|
||||
dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n",
|
||||
ret, &pp->busn);
|
||||
}
|
||||
|
||||
if (!pp->dbi_base) {
|
||||
pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start,
|
||||
resource_size(&pp->cfg));
|
||||
|
@ -511,18 +444,23 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
|
||||
pp->mem_base = pp->mem.start;
|
||||
|
||||
if (!pp->va_cfg0_base) {
|
||||
pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
|
||||
pp->config.cfg0_size);
|
||||
pp->cfg0_size);
|
||||
if (!pp->va_cfg0_base) {
|
||||
dev_err(pp->dev, "error with ioremap in function\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pp->va_cfg1_base) {
|
||||
pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
|
||||
pp->config.cfg1_size);
|
||||
pp->cfg1_size);
|
||||
if (!pp->va_cfg1_base) {
|
||||
dev_err(pp->dev, "error with ioremap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
|
||||
dev_err(pp->dev, "Failed to parse the number of lanes\n");
|
||||
|
@ -530,6 +468,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
if (!pp->ops->msi_host_init) {
|
||||
pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
|
||||
MAX_MSI_IRQS, &msi_domain_ops,
|
||||
&dw_pcie_msi_chip);
|
||||
|
@ -540,6 +479,11 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
|
||||
for (i = 0; i < MAX_MSI_IRQS; i++)
|
||||
irq_create_mapping(pp->irq_domain, i);
|
||||
} else {
|
||||
ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pp->ops->host_init)
|
||||
|
@ -558,7 +502,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
|
|||
dw_pci.private_data = (void **)&pp;
|
||||
|
||||
pci_common_init_dev(pp->dev, &dw_pci);
|
||||
pci_assign_unassigned_resources();
|
||||
#ifdef CONFIG_PCI_DOMAINS
|
||||
dw_pci.domain++;
|
||||
#endif
|
||||
|
@ -573,7 +516,7 @@ static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
|
|||
PCIE_ATU_VIEWPORT);
|
||||
dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE);
|
||||
dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE);
|
||||
dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->config.cfg0_size - 1,
|
||||
dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1,
|
||||
PCIE_ATU_LIMIT);
|
||||
dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
|
||||
|
@ -589,7 +532,7 @@ static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
|
|||
dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1);
|
||||
dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE);
|
||||
dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE);
|
||||
dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->config.cfg1_size - 1,
|
||||
dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1,
|
||||
PCIE_ATU_LIMIT);
|
||||
dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
|
||||
|
@ -604,10 +547,10 @@ static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
|
|||
dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1);
|
||||
dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE);
|
||||
dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE);
|
||||
dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->config.mem_size - 1,
|
||||
dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1,
|
||||
PCIE_ATU_LIMIT);
|
||||
dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr),
|
||||
dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr),
|
||||
PCIE_ATU_UPPER_TARGET);
|
||||
dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
|
||||
}
|
||||
|
@ -620,10 +563,10 @@ static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
|
|||
dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1);
|
||||
dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE);
|
||||
dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE);
|
||||
dw_pcie_writel_rc(pp, pp->io_mod_base + pp->config.io_size - 1,
|
||||
dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1,
|
||||
PCIE_ATU_LIMIT);
|
||||
dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr),
|
||||
dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr),
|
||||
PCIE_ATU_UPPER_TARGET);
|
||||
dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
|
||||
}
|
||||
|
@ -707,11 +650,6 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
|||
struct pcie_port *pp = sys_to_pcie(bus->sysdata);
|
||||
int ret;
|
||||
|
||||
if (!pp) {
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
|
||||
*val = 0xffffffff;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
@ -736,11 +674,6 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
|
|||
struct pcie_port *pp = sys_to_pcie(bus->sysdata);
|
||||
int ret;
|
||||
|
||||
if (!pp) {
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
|
@ -768,19 +701,17 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys)
|
|||
|
||||
pp = sys_to_pcie(sys);
|
||||
|
||||
if (!pp)
|
||||
return 0;
|
||||
|
||||
if (global_io_offset < SZ_1M && pp->config.io_size > 0) {
|
||||
sys->io_offset = global_io_offset - pp->config.io_bus_addr;
|
||||
if (global_io_offset < SZ_1M && pp->io_size > 0) {
|
||||
sys->io_offset = global_io_offset - pp->io_bus_addr;
|
||||
pci_ioremap_io(global_io_offset, pp->io_base);
|
||||
global_io_offset += SZ_64K;
|
||||
pci_add_resource_offset(&sys->resources, &pp->io,
|
||||
sys->io_offset);
|
||||
}
|
||||
|
||||
sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr;
|
||||
sys->mem_offset = pp->mem.start - pp->mem_bus_addr;
|
||||
pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
|
||||
pci_add_resource(&sys->resources, &pp->busn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -790,14 +721,16 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
|
|||
struct pci_bus *bus;
|
||||
struct pcie_port *pp = sys_to_pcie(sys);
|
||||
|
||||
if (pp) {
|
||||
pp->root_bus_nr = sys->busnr;
|
||||
bus = pci_scan_root_bus(pp->dev, sys->busnr, &dw_pcie_ops,
|
||||
sys, &sys->resources);
|
||||
} else {
|
||||
bus = NULL;
|
||||
BUG();
|
||||
}
|
||||
bus = pci_create_root_bus(pp->dev, sys->busnr,
|
||||
&dw_pcie_ops, sys, &sys->resources);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
|
||||
pci_scan_child_bus(bus);
|
||||
|
||||
if (bus && pp->ops->scan_bus)
|
||||
pp->ops->scan_bus(pp);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
@ -833,7 +766,6 @@ static struct hw_pci dw_pci = {
|
|||
|
||||
void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
{
|
||||
struct pcie_port_info *config = &pp->config;
|
||||
u32 val;
|
||||
u32 membase;
|
||||
u32 memlimit;
|
||||
|
@ -888,7 +820,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
|||
|
||||
/* setup memory base, memory limit */
|
||||
membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
|
||||
memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000;
|
||||
memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000;
|
||||
val = memlimit | membase;
|
||||
dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
|
||||
|
||||
|
|
|
@ -14,15 +14,6 @@
|
|||
#ifndef _PCIE_DESIGNWARE_H
|
||||
#define _PCIE_DESIGNWARE_H
|
||||
|
||||
struct pcie_port_info {
|
||||
u32 cfg0_size;
|
||||
u32 cfg1_size;
|
||||
u32 io_size;
|
||||
u32 mem_size;
|
||||
phys_addr_t io_bus_addr;
|
||||
phys_addr_t mem_bus_addr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Maximum number of MSI IRQs can be 256 per controller. But keep
|
||||
* it 32 as of now. Probably we will never need more than 32. If needed,
|
||||
|
@ -38,17 +29,23 @@ struct pcie_port {
|
|||
u64 cfg0_base;
|
||||
u64 cfg0_mod_base;
|
||||
void __iomem *va_cfg0_base;
|
||||
u32 cfg0_size;
|
||||
u64 cfg1_base;
|
||||
u64 cfg1_mod_base;
|
||||
void __iomem *va_cfg1_base;
|
||||
u32 cfg1_size;
|
||||
u64 io_base;
|
||||
u64 io_mod_base;
|
||||
phys_addr_t io_bus_addr;
|
||||
u32 io_size;
|
||||
u64 mem_base;
|
||||
u64 mem_mod_base;
|
||||
phys_addr_t mem_bus_addr;
|
||||
u32 mem_size;
|
||||
struct resource cfg;
|
||||
struct resource io;
|
||||
struct resource mem;
|
||||
struct pcie_port_info config;
|
||||
struct resource busn;
|
||||
int irq;
|
||||
u32 lanes;
|
||||
struct pcie_host_ops *ops;
|
||||
|
@ -73,7 +70,10 @@ struct pcie_host_ops {
|
|||
void (*host_init)(struct pcie_port *pp);
|
||||
void (*msi_set_irq)(struct pcie_port *pp, int irq);
|
||||
void (*msi_clear_irq)(struct pcie_port *pp, int irq);
|
||||
u32 (*get_msi_data)(struct pcie_port *pp);
|
||||
u32 (*get_msi_addr)(struct pcie_port *pp);
|
||||
u32 (*get_msi_data)(struct pcie_port *pp, int pos);
|
||||
void (*scan_bus)(struct pcie_port *pp);
|
||||
int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip);
|
||||
};
|
||||
|
||||
int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
|
||||
|
|
|
@ -323,6 +323,7 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
|
|||
|
||||
/* Setup PCIe address space mappings for each resource */
|
||||
resource_size_t size;
|
||||
resource_size_t res_start;
|
||||
u32 mask;
|
||||
|
||||
rcar_pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win));
|
||||
|
@ -335,8 +336,13 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
|
|||
mask = (roundup_pow_of_two(size) / SZ_128) - 1;
|
||||
rcar_pci_write_reg(pcie, mask << 7, PCIEPAMR(win));
|
||||
|
||||
rcar_pci_write_reg(pcie, upper_32_bits(res->start), PCIEPARH(win));
|
||||
rcar_pci_write_reg(pcie, lower_32_bits(res->start), PCIEPARL(win));
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
res_start = pci_pio_to_address(res->start);
|
||||
else
|
||||
res_start = res->start;
|
||||
|
||||
rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPARH(win));
|
||||
rcar_pci_write_reg(pcie, lower_32_bits(res_start), PCIEPARL(win));
|
||||
|
||||
/* First resource is for IO */
|
||||
mask = PAR_ENABLE;
|
||||
|
@ -363,9 +369,10 @@ static int rcar_pcie_setup(int nr, struct pci_sys_data *sys)
|
|||
|
||||
rcar_pcie_setup_window(i, pcie);
|
||||
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
pci_ioremap_io(nr * SZ_64K, res->start);
|
||||
else
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
phys_addr_t io_start = pci_pio_to_address(res->start);
|
||||
pci_ioremap_io(nr * SZ_64K, io_start);
|
||||
} else
|
||||
pci_add_resource(&sys->resources, res);
|
||||
}
|
||||
pci_add_resource(&sys->resources, &pcie->busn);
|
||||
|
@ -935,8 +942,10 @@ static int rcar_pcie_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
of_pci_range_to_resource(&range, pdev->dev.of_node,
|
||||
err = of_pci_range_to_resource(&range, pdev->dev.of_node,
|
||||
&pcie->res[win++]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (win > RCAR_PCI_MAX_RESOURCES)
|
||||
break;
|
||||
|
|
|
@ -340,7 +340,7 @@ static int __init spear13xx_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
pp->dev = dev;
|
||||
|
||||
dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
|
||||
pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
|
||||
if (IS_ERR(pp->dbi_base)) {
|
||||
dev_err(dev, "couldn't remap dbi base %p\n", dbi_base);
|
||||
|
|
|
@ -0,0 +1,970 @@
|
|||
/*
|
||||
* PCIe host controller driver for Xilinx AXI PCIe Bridge
|
||||
*
|
||||
* Copyright (c) 2012 - 2014 Xilinx, Inc.
|
||||
*
|
||||
* Based on the Tegra PCIe driver
|
||||
*
|
||||
* Bits taken from Synopsys Designware Host controller driver and
|
||||
* ARM PCI Host generic driver.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define XILINX_PCIE_REG_BIR 0x00000130
|
||||
#define XILINX_PCIE_REG_IDR 0x00000138
|
||||
#define XILINX_PCIE_REG_IMR 0x0000013c
|
||||
#define XILINX_PCIE_REG_PSCR 0x00000144
|
||||
#define XILINX_PCIE_REG_RPSC 0x00000148
|
||||
#define XILINX_PCIE_REG_MSIBASE1 0x0000014c
|
||||
#define XILINX_PCIE_REG_MSIBASE2 0x00000150
|
||||
#define XILINX_PCIE_REG_RPEFR 0x00000154
|
||||
#define XILINX_PCIE_REG_RPIFR1 0x00000158
|
||||
#define XILINX_PCIE_REG_RPIFR2 0x0000015c
|
||||
|
||||
/* Interrupt registers definitions */
|
||||
#define XILINX_PCIE_INTR_LINK_DOWN BIT(0)
|
||||
#define XILINX_PCIE_INTR_ECRC_ERR BIT(1)
|
||||
#define XILINX_PCIE_INTR_STR_ERR BIT(2)
|
||||
#define XILINX_PCIE_INTR_HOT_RESET BIT(3)
|
||||
#define XILINX_PCIE_INTR_CFG_TIMEOUT BIT(8)
|
||||
#define XILINX_PCIE_INTR_CORRECTABLE BIT(9)
|
||||
#define XILINX_PCIE_INTR_NONFATAL BIT(10)
|
||||
#define XILINX_PCIE_INTR_FATAL BIT(11)
|
||||
#define XILINX_PCIE_INTR_INTX BIT(16)
|
||||
#define XILINX_PCIE_INTR_MSI BIT(17)
|
||||
#define XILINX_PCIE_INTR_SLV_UNSUPP BIT(20)
|
||||
#define XILINX_PCIE_INTR_SLV_UNEXP BIT(21)
|
||||
#define XILINX_PCIE_INTR_SLV_COMPL BIT(22)
|
||||
#define XILINX_PCIE_INTR_SLV_ERRP BIT(23)
|
||||
#define XILINX_PCIE_INTR_SLV_CMPABT BIT(24)
|
||||
#define XILINX_PCIE_INTR_SLV_ILLBUR BIT(25)
|
||||
#define XILINX_PCIE_INTR_MST_DECERR BIT(26)
|
||||
#define XILINX_PCIE_INTR_MST_SLVERR BIT(27)
|
||||
#define XILINX_PCIE_INTR_MST_ERRP BIT(28)
|
||||
#define XILINX_PCIE_IMR_ALL_MASK 0x1FF30FED
|
||||
#define XILINX_PCIE_IDR_ALL_MASK 0xFFFFFFFF
|
||||
|
||||
/* Root Port Error FIFO Read Register definitions */
|
||||
#define XILINX_PCIE_RPEFR_ERR_VALID BIT(18)
|
||||
#define XILINX_PCIE_RPEFR_REQ_ID GENMASK(15, 0)
|
||||
#define XILINX_PCIE_RPEFR_ALL_MASK 0xFFFFFFFF
|
||||
|
||||
/* Root Port Interrupt FIFO Read Register 1 definitions */
|
||||
#define XILINX_PCIE_RPIFR1_INTR_VALID BIT(31)
|
||||
#define XILINX_PCIE_RPIFR1_MSI_INTR BIT(30)
|
||||
#define XILINX_PCIE_RPIFR1_INTR_MASK GENMASK(28, 27)
|
||||
#define XILINX_PCIE_RPIFR1_ALL_MASK 0xFFFFFFFF
|
||||
#define XILINX_PCIE_RPIFR1_INTR_SHIFT 27
|
||||
|
||||
/* Bridge Info Register definitions */
|
||||
#define XILINX_PCIE_BIR_ECAM_SZ_MASK GENMASK(18, 16)
|
||||
#define XILINX_PCIE_BIR_ECAM_SZ_SHIFT 16
|
||||
|
||||
/* Root Port Interrupt FIFO Read Register 2 definitions */
|
||||
#define XILINX_PCIE_RPIFR2_MSG_DATA GENMASK(15, 0)
|
||||
|
||||
/* Root Port Status/control Register definitions */
|
||||
#define XILINX_PCIE_REG_RPSC_BEN BIT(0)
|
||||
|
||||
/* Phy Status/Control Register definitions */
|
||||
#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11)
|
||||
|
||||
/* ECAM definitions */
|
||||
#define ECAM_BUS_NUM_SHIFT 20
|
||||
#define ECAM_DEV_NUM_SHIFT 12
|
||||
|
||||
/* Number of MSI IRQs */
|
||||
#define XILINX_NUM_MSI_IRQS 128
|
||||
|
||||
/* Number of Memory Resources */
|
||||
#define XILINX_MAX_NUM_RESOURCES 3
|
||||
|
||||
/**
|
||||
* struct xilinx_pcie_port - PCIe port information
|
||||
* @reg_base: IO Mapped Register Base
|
||||
* @irq: Interrupt number
|
||||
* @msi_pages: MSI pages
|
||||
* @root_busno: Root Bus number
|
||||
* @dev: Device pointer
|
||||
* @irq_domain: IRQ domain pointer
|
||||
* @bus_range: Bus range
|
||||
* @resources: Bus Resources
|
||||
*/
|
||||
struct xilinx_pcie_port {
|
||||
void __iomem *reg_base;
|
||||
u32 irq;
|
||||
unsigned long msi_pages;
|
||||
u8 root_busno;
|
||||
struct device *dev;
|
||||
struct irq_domain *irq_domain;
|
||||
struct resource bus_range;
|
||||
struct list_head resources;
|
||||
};
|
||||
|
||||
static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS);
|
||||
|
||||
static inline struct xilinx_pcie_port *sys_to_pcie(struct pci_sys_data *sys)
|
||||
{
|
||||
return sys->private_data;
|
||||
}
|
||||
|
||||
static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg)
|
||||
{
|
||||
return readl(port->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline void pcie_write(struct xilinx_pcie_port *port, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, port->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline bool xilinx_pcie_link_is_up(struct xilinx_pcie_port *port)
|
||||
{
|
||||
return (pcie_read(port, XILINX_PCIE_REG_PSCR) &
|
||||
XILINX_PCIE_REG_PSCR_LNKUP) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_clear_err_interrupts - Clear Error Interrupts
|
||||
* @port: PCIe port information
|
||||
*/
|
||||
static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port)
|
||||
{
|
||||
u32 val = pcie_read(port, XILINX_PCIE_REG_RPEFR);
|
||||
|
||||
if (val & XILINX_PCIE_RPEFR_ERR_VALID) {
|
||||
dev_dbg(port->dev, "Requester ID %d\n",
|
||||
val & XILINX_PCIE_RPEFR_REQ_ID);
|
||||
pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK,
|
||||
XILINX_PCIE_REG_RPEFR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_valid_device - Check if a valid device is present on bus
|
||||
* @bus: PCI Bus structure
|
||||
* @devfn: device/function
|
||||
*
|
||||
* Return: 'true' on success and 'false' if invalid device is found
|
||||
*/
|
||||
static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
|
||||
{
|
||||
struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
|
||||
|
||||
/* Check if link is up when trying to access downstream ports */
|
||||
if (bus->number != port->root_busno)
|
||||
if (!xilinx_pcie_link_is_up(port))
|
||||
return false;
|
||||
|
||||
/* Only one device down on each root port */
|
||||
if (bus->number == port->root_busno && devfn > 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Do not read more than one device on the bus directly attached
|
||||
* to RC.
|
||||
*/
|
||||
if (bus->primary == port->root_busno && devfn > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_config_base - Get configuration base
|
||||
* @bus: PCI Bus structure
|
||||
* @devfn: Device/function
|
||||
* @where: Offset from base
|
||||
*
|
||||
* Return: Base address of the configuration space needed to be
|
||||
* accessed.
|
||||
*/
|
||||
static void __iomem *xilinx_pcie_config_base(struct pci_bus *bus,
|
||||
unsigned int devfn, int where)
|
||||
{
|
||||
struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
|
||||
int relbus;
|
||||
|
||||
relbus = (bus->number << ECAM_BUS_NUM_SHIFT) |
|
||||
(devfn << ECAM_DEV_NUM_SHIFT);
|
||||
|
||||
return port->reg_base + relbus + where;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_read_config - Read configuration space
|
||||
* @bus: PCI Bus structure
|
||||
* @devfn: Device/function
|
||||
* @where: Offset from base
|
||||
* @size: Byte/word/dword
|
||||
* @val: Value to be read
|
||||
*
|
||||
* Return: PCIBIOS_SUCCESSFUL on success
|
||||
* PCIBIOS_DEVICE_NOT_FOUND on failure
|
||||
*/
|
||||
static int xilinx_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
void __iomem *addr;
|
||||
|
||||
if (!xilinx_pcie_valid_device(bus, devfn)) {
|
||||
*val = 0xFFFFFFFF;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
addr = xilinx_pcie_config_base(bus, devfn, where);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
*val = readb(addr);
|
||||
break;
|
||||
case 2:
|
||||
*val = readw(addr);
|
||||
break;
|
||||
default:
|
||||
*val = readl(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_write_config - Write configuration space
|
||||
* @bus: PCI Bus structure
|
||||
* @devfn: Device/function
|
||||
* @where: Offset from base
|
||||
* @size: Byte/word/dword
|
||||
* @val: Value to be written to device
|
||||
*
|
||||
* Return: PCIBIOS_SUCCESSFUL on success
|
||||
* PCIBIOS_DEVICE_NOT_FOUND on failure
|
||||
*/
|
||||
static int xilinx_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
void __iomem *addr;
|
||||
|
||||
if (!xilinx_pcie_valid_device(bus, devfn))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
addr = xilinx_pcie_config_base(bus, devfn, where);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
writeb(val, addr);
|
||||
break;
|
||||
case 2:
|
||||
writew(val, addr);
|
||||
break;
|
||||
default:
|
||||
writel(val, addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/* PCIe operations */
|
||||
static struct pci_ops xilinx_pcie_ops = {
|
||||
.read = xilinx_pcie_read_config,
|
||||
.write = xilinx_pcie_write_config,
|
||||
};
|
||||
|
||||
/* MSI functions */
|
||||
|
||||
/**
|
||||
* xilinx_pcie_destroy_msi - Free MSI number
|
||||
* @irq: IRQ to be freed
|
||||
*/
|
||||
static void xilinx_pcie_destroy_msi(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
struct msi_desc *msi;
|
||||
struct xilinx_pcie_port *port;
|
||||
|
||||
desc = irq_to_desc(irq);
|
||||
msi = irq_desc_get_msi_desc(desc);
|
||||
port = sys_to_pcie(msi->dev->bus->sysdata);
|
||||
|
||||
if (!test_bit(irq, msi_irq_in_use))
|
||||
dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
|
||||
else
|
||||
clear_bit(irq, msi_irq_in_use);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_assign_msi - Allocate MSI number
|
||||
* @port: PCIe port structure
|
||||
*
|
||||
* Return: A valid IRQ on success and error value on failure.
|
||||
*/
|
||||
static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port)
|
||||
{
|
||||
int pos;
|
||||
|
||||
pos = find_first_zero_bit(msi_irq_in_use, XILINX_NUM_MSI_IRQS);
|
||||
if (pos < XILINX_NUM_MSI_IRQS)
|
||||
set_bit(pos, msi_irq_in_use);
|
||||
else
|
||||
return -ENOSPC;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_msi_teardown_irq - Destroy the MSI
|
||||
* @chip: MSI Chip descriptor
|
||||
* @irq: MSI IRQ to destroy
|
||||
*/
|
||||
static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
|
||||
{
|
||||
xilinx_pcie_destroy_msi(irq);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_msi_setup_irq - Setup MSI request
|
||||
* @chip: MSI chip pointer
|
||||
* @pdev: PCIe device pointer
|
||||
* @desc: MSI descriptor pointer
|
||||
*
|
||||
* Return: '0' on success and error value on failure
|
||||
*/
|
||||
static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip,
|
||||
struct pci_dev *pdev,
|
||||
struct msi_desc *desc)
|
||||
{
|
||||
struct xilinx_pcie_port *port = sys_to_pcie(pdev->bus->sysdata);
|
||||
unsigned int irq;
|
||||
int hwirq;
|
||||
struct msi_msg msg;
|
||||
phys_addr_t msg_addr;
|
||||
|
||||
hwirq = xilinx_pcie_assign_msi(port);
|
||||
if (hwirq < 0)
|
||||
return hwirq;
|
||||
|
||||
irq = irq_create_mapping(port->irq_domain, hwirq);
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
|
||||
irq_set_msi_desc(irq, desc);
|
||||
|
||||
msg_addr = virt_to_phys((void *)port->msi_pages);
|
||||
|
||||
msg.address_hi = 0;
|
||||
msg.address_lo = msg_addr;
|
||||
msg.data = irq;
|
||||
|
||||
write_msi_msg(irq, &msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MSI Chip Descriptor */
|
||||
static struct msi_chip xilinx_pcie_msi_chip = {
|
||||
.setup_irq = xilinx_pcie_msi_setup_irq,
|
||||
.teardown_irq = xilinx_msi_teardown_irq,
|
||||
};
|
||||
|
||||
/* HW Interrupt Chip Descriptor */
|
||||
static struct irq_chip xilinx_msi_irq_chip = {
|
||||
.name = "Xilinx PCIe MSI",
|
||||
.irq_enable = unmask_msi_irq,
|
||||
.irq_disable = mask_msi_irq,
|
||||
.irq_mask = mask_msi_irq,
|
||||
.irq_unmask = unmask_msi_irq,
|
||||
};
|
||||
|
||||
/**
|
||||
* xilinx_pcie_msi_map - Set the handler for the MSI and mark IRQ as valid
|
||||
* @domain: IRQ domain
|
||||
* @irq: Virtual IRQ number
|
||||
* @hwirq: HW interrupt number
|
||||
*
|
||||
* Return: Always returns 0.
|
||||
*/
|
||||
static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq);
|
||||
irq_set_chip_data(irq, domain->host_data);
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* IRQ Domain operations */
|
||||
static const struct irq_domain_ops msi_domain_ops = {
|
||||
.map = xilinx_pcie_msi_map,
|
||||
};
|
||||
|
||||
/**
|
||||
* xilinx_pcie_enable_msi - Enable MSI support
|
||||
* @port: PCIe port information
|
||||
*/
|
||||
static void xilinx_pcie_enable_msi(struct xilinx_pcie_port *port)
|
||||
{
|
||||
phys_addr_t msg_addr;
|
||||
|
||||
port->msi_pages = __get_free_pages(GFP_KERNEL, 0);
|
||||
msg_addr = virt_to_phys((void *)port->msi_pages);
|
||||
pcie_write(port, 0x0, XILINX_PCIE_REG_MSIBASE1);
|
||||
pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_add_bus - Add MSI chip info to PCIe bus
|
||||
* @bus: PCIe bus
|
||||
*/
|
||||
static void xilinx_pcie_add_bus(struct pci_bus *bus)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
|
||||
|
||||
xilinx_pcie_msi_chip.dev = port->dev;
|
||||
bus->msi = &xilinx_pcie_msi_chip;
|
||||
}
|
||||
}
|
||||
|
||||
/* INTx Functions */
|
||||
|
||||
/**
|
||||
* xilinx_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid
|
||||
* @domain: IRQ domain
|
||||
* @irq: Virtual IRQ number
|
||||
* @hwirq: HW interrupt number
|
||||
*
|
||||
* Return: Always returns 0.
|
||||
*/
|
||||
static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
|
||||
irq_set_chip_data(irq, domain->host_data);
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* INTx IRQ Domain operations */
|
||||
static const struct irq_domain_ops intx_domain_ops = {
|
||||
.map = xilinx_pcie_intx_map,
|
||||
};
|
||||
|
||||
/* PCIe HW Functions */
|
||||
|
||||
/**
|
||||
* xilinx_pcie_intr_handler - Interrupt Service Handler
|
||||
* @irq: IRQ number
|
||||
* @data: PCIe port information
|
||||
*
|
||||
* Return: IRQ_HANDLED on success and IRQ_NONE on failure
|
||||
*/
|
||||
static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
|
||||
{
|
||||
struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data;
|
||||
u32 val, mask, status, msi_data;
|
||||
|
||||
/* Read interrupt decode and mask registers */
|
||||
val = pcie_read(port, XILINX_PCIE_REG_IDR);
|
||||
mask = pcie_read(port, XILINX_PCIE_REG_IMR);
|
||||
|
||||
status = val & mask;
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (status & XILINX_PCIE_INTR_LINK_DOWN)
|
||||
dev_warn(port->dev, "Link Down\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_ECRC_ERR)
|
||||
dev_warn(port->dev, "ECRC failed\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_STR_ERR)
|
||||
dev_warn(port->dev, "Streaming error\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_HOT_RESET)
|
||||
dev_info(port->dev, "Hot reset\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_CFG_TIMEOUT)
|
||||
dev_warn(port->dev, "ECAM access timeout\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_CORRECTABLE) {
|
||||
dev_warn(port->dev, "Correctable error message\n");
|
||||
xilinx_pcie_clear_err_interrupts(port);
|
||||
}
|
||||
|
||||
if (status & XILINX_PCIE_INTR_NONFATAL) {
|
||||
dev_warn(port->dev, "Non fatal error message\n");
|
||||
xilinx_pcie_clear_err_interrupts(port);
|
||||
}
|
||||
|
||||
if (status & XILINX_PCIE_INTR_FATAL) {
|
||||
dev_warn(port->dev, "Fatal error message\n");
|
||||
xilinx_pcie_clear_err_interrupts(port);
|
||||
}
|
||||
|
||||
if (status & XILINX_PCIE_INTR_INTX) {
|
||||
/* INTx interrupt received */
|
||||
val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);
|
||||
|
||||
/* Check whether interrupt valid */
|
||||
if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
|
||||
dev_warn(port->dev, "RP Intr FIFO1 read error\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Clear interrupt FIFO register 1 */
|
||||
pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK,
|
||||
XILINX_PCIE_REG_RPIFR1);
|
||||
|
||||
/* Handle INTx Interrupt */
|
||||
val = ((val & XILINX_PCIE_RPIFR1_INTR_MASK) >>
|
||||
XILINX_PCIE_RPIFR1_INTR_SHIFT) + 1;
|
||||
generic_handle_irq(irq_find_mapping(port->irq_domain, val));
|
||||
}
|
||||
|
||||
if (status & XILINX_PCIE_INTR_MSI) {
|
||||
/* MSI Interrupt */
|
||||
val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);
|
||||
|
||||
if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
|
||||
dev_warn(port->dev, "RP Intr FIFO1 read error\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (val & XILINX_PCIE_RPIFR1_MSI_INTR) {
|
||||
msi_data = pcie_read(port, XILINX_PCIE_REG_RPIFR2) &
|
||||
XILINX_PCIE_RPIFR2_MSG_DATA;
|
||||
|
||||
/* Clear interrupt FIFO register 1 */
|
||||
pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK,
|
||||
XILINX_PCIE_REG_RPIFR1);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
/* Handle MSI Interrupt */
|
||||
generic_handle_irq(msi_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status & XILINX_PCIE_INTR_SLV_UNSUPP)
|
||||
dev_warn(port->dev, "Slave unsupported request\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_SLV_UNEXP)
|
||||
dev_warn(port->dev, "Slave unexpected completion\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_SLV_COMPL)
|
||||
dev_warn(port->dev, "Slave completion timeout\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_SLV_ERRP)
|
||||
dev_warn(port->dev, "Slave Error Poison\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_SLV_CMPABT)
|
||||
dev_warn(port->dev, "Slave Completer Abort\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_SLV_ILLBUR)
|
||||
dev_warn(port->dev, "Slave Illegal Burst\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_MST_DECERR)
|
||||
dev_warn(port->dev, "Master decode error\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_MST_SLVERR)
|
||||
dev_warn(port->dev, "Master slave error\n");
|
||||
|
||||
if (status & XILINX_PCIE_INTR_MST_ERRP)
|
||||
dev_warn(port->dev, "Master error poison\n");
|
||||
|
||||
/* Clear the Interrupt Decode register */
|
||||
pcie_write(port, status, XILINX_PCIE_REG_IDR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_free_irq_domain - Free IRQ domain
|
||||
* @port: PCIe port information
|
||||
*/
|
||||
static void xilinx_pcie_free_irq_domain(struct xilinx_pcie_port *port)
|
||||
{
|
||||
int i;
|
||||
u32 irq, num_irqs;
|
||||
|
||||
/* Free IRQ Domain */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
|
||||
free_pages(port->msi_pages, 0);
|
||||
|
||||
num_irqs = XILINX_NUM_MSI_IRQS;
|
||||
} else {
|
||||
/* INTx */
|
||||
num_irqs = 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_irqs; i++) {
|
||||
irq = irq_find_mapping(port->irq_domain, i);
|
||||
if (irq > 0)
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
|
||||
irq_domain_remove(port->irq_domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_init_irq_domain - Initialize IRQ domain
|
||||
* @port: PCIe port information
|
||||
*
|
||||
* Return: '0' on success and error value on failure
|
||||
*/
|
||||
static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *pcie_intc_node;
|
||||
|
||||
/* Setup INTx */
|
||||
pcie_intc_node = of_get_next_child(node, NULL);
|
||||
if (!pcie_intc_node) {
|
||||
dev_err(dev, "No PCIe Intc node found\n");
|
||||
return PTR_ERR(pcie_intc_node);
|
||||
}
|
||||
|
||||
port->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
|
||||
&intx_domain_ops,
|
||||
port);
|
||||
if (!port->irq_domain) {
|
||||
dev_err(dev, "Failed to get a INTx IRQ domain\n");
|
||||
return PTR_ERR(port->irq_domain);
|
||||
}
|
||||
|
||||
/* Setup MSI */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
port->irq_domain = irq_domain_add_linear(node,
|
||||
XILINX_NUM_MSI_IRQS,
|
||||
&msi_domain_ops,
|
||||
&xilinx_pcie_msi_chip);
|
||||
if (!port->irq_domain) {
|
||||
dev_err(dev, "Failed to get a MSI IRQ domain\n");
|
||||
return PTR_ERR(port->irq_domain);
|
||||
}
|
||||
|
||||
xilinx_pcie_enable_msi(port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_init_port - Initialize hardware
|
||||
* @port: PCIe port information
|
||||
*/
|
||||
static void xilinx_pcie_init_port(struct xilinx_pcie_port *port)
|
||||
{
|
||||
if (xilinx_pcie_link_is_up(port))
|
||||
dev_info(port->dev, "PCIe Link is UP\n");
|
||||
else
|
||||
dev_info(port->dev, "PCIe Link is DOWN\n");
|
||||
|
||||
/* Disable all interrupts */
|
||||
pcie_write(port, ~XILINX_PCIE_IDR_ALL_MASK,
|
||||
XILINX_PCIE_REG_IMR);
|
||||
|
||||
/* Clear pending interrupts */
|
||||
pcie_write(port, pcie_read(port, XILINX_PCIE_REG_IDR) &
|
||||
XILINX_PCIE_IMR_ALL_MASK,
|
||||
XILINX_PCIE_REG_IDR);
|
||||
|
||||
/* Enable all interrupts */
|
||||
pcie_write(port, XILINX_PCIE_IMR_ALL_MASK, XILINX_PCIE_REG_IMR);
|
||||
|
||||
/* Enable the Bridge enable bit */
|
||||
pcie_write(port, pcie_read(port, XILINX_PCIE_REG_RPSC) |
|
||||
XILINX_PCIE_REG_RPSC_BEN,
|
||||
XILINX_PCIE_REG_RPSC);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_setup - Setup memory resources
|
||||
* @nr: Bus number
|
||||
* @sys: Per controller structure
|
||||
*
|
||||
* Return: '1' on success and error value on failure
|
||||
*/
|
||||
static int xilinx_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
{
|
||||
struct xilinx_pcie_port *port = sys_to_pcie(sys);
|
||||
|
||||
list_splice_init(&port->resources, &sys->resources);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_scan_bus - Scan PCIe bus for devices
|
||||
* @nr: Bus number
|
||||
* @sys: Per controller structure
|
||||
*
|
||||
* Return: Valid Bus pointer on success and NULL on failure
|
||||
*/
|
||||
static struct pci_bus *xilinx_pcie_scan_bus(int nr, struct pci_sys_data *sys)
|
||||
{
|
||||
struct xilinx_pcie_port *port = sys_to_pcie(sys);
|
||||
struct pci_bus *bus;
|
||||
|
||||
port->root_busno = sys->busnr;
|
||||
bus = pci_scan_root_bus(port->dev, sys->busnr, &xilinx_pcie_ops,
|
||||
sys, &sys->resources);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_parse_and_add_res - Add resources by parsing ranges
|
||||
* @port: PCIe port information
|
||||
*
|
||||
* Return: '0' on success and error value on failure
|
||||
*/
|
||||
static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct resource *mem;
|
||||
resource_size_t offset;
|
||||
struct of_pci_range_parser parser;
|
||||
struct of_pci_range range;
|
||||
struct pci_host_bridge_window *win;
|
||||
int err = 0, mem_resno = 0;
|
||||
|
||||
/* Get the ranges */
|
||||
if (of_pci_range_parser_init(&parser, node)) {
|
||||
dev_err(dev, "missing \"ranges\" property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse the ranges and add the resources found to the list */
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
|
||||
if (mem_resno >= XILINX_MAX_NUM_RESOURCES) {
|
||||
dev_err(dev, "Maximum memory resources exceeded\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mem = devm_kmalloc(dev, sizeof(*mem), GFP_KERNEL);
|
||||
if (!mem) {
|
||||
err = -ENOMEM;
|
||||
goto free_resources;
|
||||
}
|
||||
|
||||
of_pci_range_to_resource(&range, node, mem);
|
||||
|
||||
switch (mem->flags & IORESOURCE_TYPE_BITS) {
|
||||
case IORESOURCE_MEM:
|
||||
offset = range.cpu_addr - range.pci_addr;
|
||||
mem_resno++;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
dev_warn(dev, "Invalid resource found %pR\n", mem);
|
||||
continue;
|
||||
}
|
||||
|
||||
err = request_resource(&iomem_resource, mem);
|
||||
if (err)
|
||||
goto free_resources;
|
||||
|
||||
pci_add_resource_offset(&port->resources, mem, offset);
|
||||
}
|
||||
|
||||
/* Get the bus range */
|
||||
if (of_pci_parse_bus_range(node, &port->bus_range)) {
|
||||
u32 val = pcie_read(port, XILINX_PCIE_REG_BIR);
|
||||
u8 last;
|
||||
|
||||
last = (val & XILINX_PCIE_BIR_ECAM_SZ_MASK) >>
|
||||
XILINX_PCIE_BIR_ECAM_SZ_SHIFT;
|
||||
|
||||
port->bus_range = (struct resource) {
|
||||
.name = node->name,
|
||||
.start = 0,
|
||||
.end = last,
|
||||
.flags = IORESOURCE_BUS,
|
||||
};
|
||||
}
|
||||
|
||||
/* Register bus resource */
|
||||
pci_add_resource(&port->resources, &port->bus_range);
|
||||
|
||||
return 0;
|
||||
|
||||
free_resources:
|
||||
release_child_resources(&iomem_resource);
|
||||
list_for_each_entry(win, &port->resources, list)
|
||||
devm_kfree(dev, win->res);
|
||||
pci_free_resource_list(&port->resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_parse_dt - Parse Device tree
|
||||
* @port: PCIe port information
|
||||
*
|
||||
* Return: '0' on success and error value on failure
|
||||
*/
|
||||
static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct resource regs;
|
||||
const char *type;
|
||||
int err;
|
||||
|
||||
type = of_get_property(node, "device_type", NULL);
|
||||
if (!type || strcmp(type, "pci")) {
|
||||
dev_err(dev, "invalid \"device_type\" %s\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = of_address_to_resource(node, 0, ®s);
|
||||
if (err) {
|
||||
dev_err(dev, "missing \"reg\" property\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
port->reg_base = devm_ioremap_resource(dev, ®s);
|
||||
if (IS_ERR(port->reg_base))
|
||||
return PTR_ERR(port->reg_base);
|
||||
|
||||
port->irq = irq_of_parse_and_map(node, 0);
|
||||
err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler,
|
||||
IRQF_SHARED, "xilinx-pcie", port);
|
||||
if (err) {
|
||||
dev_err(dev, "unable to request irq %d\n", port->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_probe - Probe function
|
||||
* @pdev: Platform device pointer
|
||||
*
|
||||
* Return: '0' on success and error value on failure
|
||||
*/
|
||||
static int xilinx_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct xilinx_pcie_port *port;
|
||||
struct hw_pci hw;
|
||||
struct device *dev = &pdev->dev;
|
||||
int err;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
port->dev = dev;
|
||||
|
||||
err = xilinx_pcie_parse_dt(port);
|
||||
if (err) {
|
||||
dev_err(dev, "Parsing DT failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
xilinx_pcie_init_port(port);
|
||||
|
||||
err = xilinx_pcie_init_irq_domain(port);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed creating IRQ Domain\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse PCI ranges, configuration bus range and
|
||||
* request their resources
|
||||
*/
|
||||
INIT_LIST_HEAD(&port->resources);
|
||||
err = xilinx_pcie_parse_and_add_res(port);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed adding resources\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, port);
|
||||
|
||||
/* Register the device */
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
hw = (struct hw_pci) {
|
||||
.nr_controllers = 1,
|
||||
.private_data = (void **)&port,
|
||||
.setup = xilinx_pcie_setup,
|
||||
.map_irq = of_irq_parse_and_map_pci,
|
||||
.add_bus = xilinx_pcie_add_bus,
|
||||
.scan = xilinx_pcie_scan_bus,
|
||||
.ops = &xilinx_pcie_ops,
|
||||
};
|
||||
pci_common_init_dev(dev, &hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_remove - Remove function
|
||||
* @pdev: Platform device pointer
|
||||
*
|
||||
* Return: '0' always
|
||||
*/
|
||||
static int xilinx_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xilinx_pcie_port *port = platform_get_drvdata(pdev);
|
||||
|
||||
xilinx_pcie_free_irq_domain(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id xilinx_pcie_of_match[] = {
|
||||
{ .compatible = "xlnx,axi-pcie-host-1.00.a", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver xilinx_pcie_driver = {
|
||||
.driver = {
|
||||
.name = "xilinx-pcie",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = xilinx_pcie_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = xilinx_pcie_probe,
|
||||
.remove = xilinx_pcie_remove,
|
||||
};
|
||||
module_platform_driver(xilinx_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Xilinx Inc");
|
||||
MODULE_DESCRIPTION("Xilinx AXI PCIe driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -24,7 +24,7 @@ obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o
|
|||
|
||||
obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
|
||||
|
||||
pci_hotplug-objs := pci_hotplug_core.o pcihp_slot.o
|
||||
pci_hotplug-objs := pci_hotplug_core.o
|
||||
|
||||
ifdef CONFIG_HOTPLUG_PCI_CPCI
|
||||
pci_hotplug-objs += cpci_hotplug_core.o \
|
||||
|
|
|
@ -46,215 +46,6 @@
|
|||
|
||||
static bool debug_acpi;
|
||||
|
||||
static acpi_status
|
||||
decode_type0_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 6)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 6; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t0 = &hpx->type0_data;
|
||||
hpx->t0->revision = revision;
|
||||
hpx->t0->cache_line_size = fields[2].integer.value;
|
||||
hpx->t0->latency_timer = fields[3].integer.value;
|
||||
hpx->t0->enable_serr = fields[4].integer.value;
|
||||
hpx->t0->enable_perr = fields[5].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 0 Revision %d record not supported\n",
|
||||
__func__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
decode_type1_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 5)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 5; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t1 = &hpx->type1_data;
|
||||
hpx->t1->revision = revision;
|
||||
hpx->t1->max_mem_read = fields[2].integer.value;
|
||||
hpx->t1->avg_max_split = fields[3].integer.value;
|
||||
hpx->t1->tot_max_split = fields[4].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 1 Revision %d record not supported\n",
|
||||
__func__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
decode_type2_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 18)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 18; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t2 = &hpx->type2_data;
|
||||
hpx->t2->revision = revision;
|
||||
hpx->t2->unc_err_mask_and = fields[2].integer.value;
|
||||
hpx->t2->unc_err_mask_or = fields[3].integer.value;
|
||||
hpx->t2->unc_err_sever_and = fields[4].integer.value;
|
||||
hpx->t2->unc_err_sever_or = fields[5].integer.value;
|
||||
hpx->t2->cor_err_mask_and = fields[6].integer.value;
|
||||
hpx->t2->cor_err_mask_or = fields[7].integer.value;
|
||||
hpx->t2->adv_err_cap_and = fields[8].integer.value;
|
||||
hpx->t2->adv_err_cap_or = fields[9].integer.value;
|
||||
hpx->t2->pci_exp_devctl_and = fields[10].integer.value;
|
||||
hpx->t2->pci_exp_devctl_or = fields[11].integer.value;
|
||||
hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value;
|
||||
hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value;
|
||||
hpx->t2->sec_unc_err_sever_and = fields[14].integer.value;
|
||||
hpx->t2->sec_unc_err_sever_or = fields[15].integer.value;
|
||||
hpx->t2->sec_unc_err_mask_and = fields[16].integer.value;
|
||||
hpx->t2->sec_unc_err_mask_or = fields[17].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 2 Revision %d record not supported\n",
|
||||
__func__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
union acpi_object *package, *record, *fields;
|
||||
u32 type;
|
||||
int i;
|
||||
|
||||
/* Clear the return buffer with zeros */
|
||||
memset(hpx, 0, sizeof(struct hotplug_params));
|
||||
|
||||
status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
package = (union acpi_object *)buffer.pointer;
|
||||
if (package->type != ACPI_TYPE_PACKAGE) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < package->package.count; i++) {
|
||||
record = &package->package.elements[i];
|
||||
if (record->type != ACPI_TYPE_PACKAGE) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fields = record->package.elements;
|
||||
if (fields[0].type != ACPI_TYPE_INTEGER ||
|
||||
fields[1].type != ACPI_TYPE_INTEGER) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
type = fields[0].integer.value;
|
||||
switch (type) {
|
||||
case 0:
|
||||
status = decode_type0_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
case 1:
|
||||
status = decode_type1_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
case 2:
|
||||
status = decode_type2_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: Type %d record not supported\n",
|
||||
__func__, type);
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
kfree(buffer.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *package, *fields;
|
||||
int i;
|
||||
|
||||
memset(hpp, 0, sizeof(struct hotplug_params));
|
||||
|
||||
status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
package = (union acpi_object *) buffer.pointer;
|
||||
if (package->type != ACPI_TYPE_PACKAGE ||
|
||||
package->package.count != 4) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fields = package->package.elements;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
hpp->t0 = &hpp->type0_data;
|
||||
hpp->t0->revision = 1;
|
||||
hpp->t0->cache_line_size = fields[0].integer.value;
|
||||
hpp->t0->latency_timer = fields[1].integer.value;
|
||||
hpp->t0->enable_serr = fields[2].integer.value;
|
||||
hpp->t0->enable_perr = fields[3].integer.value;
|
||||
|
||||
exit:
|
||||
kfree(buffer.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* acpi_run_oshp - get control of hotplug from the firmware
|
||||
*
|
||||
* @handle - the handle of the hotplug controller.
|
||||
|
@ -283,48 +74,6 @@ static acpi_status acpi_run_oshp(acpi_handle handle)
|
|||
return status;
|
||||
}
|
||||
|
||||
/* pci_get_hp_params
|
||||
*
|
||||
* @dev - the pci_dev for which we want parameters
|
||||
* @hpp - allocated by the caller
|
||||
*/
|
||||
int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
|
||||
{
|
||||
acpi_status status;
|
||||
acpi_handle handle, phandle;
|
||||
struct pci_bus *pbus;
|
||||
|
||||
handle = NULL;
|
||||
for (pbus = dev->bus; pbus; pbus = pbus->parent) {
|
||||
handle = acpi_pci_get_bridge_handle(pbus);
|
||||
if (handle)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* _HPP settings apply to all child buses, until another _HPP is
|
||||
* encountered. If we don't find an _HPP for the input pci dev,
|
||||
* look for it in the parent device scope since that would apply to
|
||||
* this pci dev.
|
||||
*/
|
||||
while (handle) {
|
||||
status = acpi_run_hpx(handle, hpp);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return 0;
|
||||
status = acpi_run_hpp(handle, hpp);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return 0;
|
||||
if (acpi_is_root_bridge(handle))
|
||||
break;
|
||||
status = acpi_get_parent(handle, &phandle);
|
||||
if (ACPI_FAILURE(status))
|
||||
break;
|
||||
handle = phandle;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_get_hp_params);
|
||||
|
||||
/**
|
||||
* acpi_get_hp_hw_control_from_firmware
|
||||
* @dev: the pci_dev of the bridge that has a hotplug controller
|
||||
|
@ -433,7 +182,8 @@ int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle)
|
|||
{
|
||||
acpi_handle bridge_handle, parent_handle;
|
||||
|
||||
if (!(bridge_handle = acpi_pci_get_bridge_handle(pbus)))
|
||||
bridge_handle = acpi_pci_get_bridge_handle(pbus);
|
||||
if (!bridge_handle)
|
||||
return 0;
|
||||
if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle))))
|
||||
return 0;
|
||||
|
|
|
@ -61,7 +61,6 @@ static DEFINE_MUTEX(bridge_mutex);
|
|||
static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type);
|
||||
static void acpiphp_post_dock_fixup(struct acpi_device *adev);
|
||||
static void acpiphp_sanitize_bus(struct pci_bus *bus);
|
||||
static void acpiphp_set_hpp_values(struct pci_bus *bus);
|
||||
static void hotplug_event(u32 type, struct acpiphp_context *context);
|
||||
static void free_bridge(struct kref *kref);
|
||||
|
||||
|
@ -510,7 +509,7 @@ static void enable_slot(struct acpiphp_slot *slot)
|
|||
__pci_bus_assign_resources(bus, &add_list, NULL);
|
||||
|
||||
acpiphp_sanitize_bus(bus);
|
||||
acpiphp_set_hpp_values(bus);
|
||||
pcie_bus_configure_settings(bus);
|
||||
acpiphp_set_acpi_region(slot);
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
|
@ -698,14 +697,6 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
|
|||
}
|
||||
}
|
||||
|
||||
static void acpiphp_set_hpp_values(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
pci_configure_slot(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove devices for which we could not assign resources, call
|
||||
* arch specific code to fix-up the bus
|
||||
|
|
|
@ -125,7 +125,8 @@ disable_slot(struct hotplug_slot *hotplug_slot)
|
|||
|
||||
/* Unconfigure device */
|
||||
dbg("%s - unconfiguring slot %s", __func__, slot_name(slot));
|
||||
if ((retval = cpci_unconfigure_slot(slot))) {
|
||||
retval = cpci_unconfigure_slot(slot);
|
||||
if (retval) {
|
||||
err("%s - could not unconfigure slot %s",
|
||||
__func__, slot_name(slot));
|
||||
goto disable_error;
|
||||
|
@ -141,9 +142,11 @@ disable_slot(struct hotplug_slot *hotplug_slot)
|
|||
}
|
||||
cpci_led_on(slot);
|
||||
|
||||
if (controller->ops->set_power)
|
||||
if ((retval = controller->ops->set_power(slot, 0)))
|
||||
if (controller->ops->set_power) {
|
||||
retval = controller->ops->set_power(slot, 0);
|
||||
if (retval)
|
||||
goto disable_error;
|
||||
}
|
||||
|
||||
if (update_adapter_status(slot->hotplug_slot, 0))
|
||||
warn("failure to update adapter file");
|
||||
|
@ -467,9 +470,9 @@ check_slots(void)
|
|||
__func__, slot_name(slot), hs_csr);
|
||||
|
||||
if (!slot->extracting) {
|
||||
if (update_latch_status(slot->hotplug_slot, 0)) {
|
||||
if (update_latch_status(slot->hotplug_slot, 0))
|
||||
warn("failure to update latch file");
|
||||
}
|
||||
|
||||
slot->extracting = 1;
|
||||
atomic_inc(&extracting);
|
||||
}
|
||||
|
|
|
@ -182,9 +182,9 @@ static int zt5550_hc_enable_irq(void)
|
|||
{
|
||||
u8 reg;
|
||||
|
||||
if(hc_dev == NULL) {
|
||||
if (hc_dev == NULL)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = readb(csr_int_mask);
|
||||
reg = reg & ~ENUM_INT_MASK;
|
||||
writeb(reg, csr_int_mask);
|
||||
|
@ -195,9 +195,8 @@ static int zt5550_hc_disable_irq(void)
|
|||
{
|
||||
u8 reg;
|
||||
|
||||
if(hc_dev == NULL) {
|
||||
if (hc_dev == NULL)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = readb(csr_int_mask);
|
||||
reg = reg | ENUM_INT_MASK;
|
||||
|
@ -210,9 +209,9 @@ static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id
|
|||
int status;
|
||||
|
||||
status = zt5550_hc_config(pdev);
|
||||
if(status != 0) {
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
dbg("returned from zt5550_hc_config");
|
||||
|
||||
memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller));
|
||||
|
@ -238,8 +237,9 @@ static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id
|
|||
dbg("registered controller");
|
||||
|
||||
/* Look for first device matching cPCI bus's bridge vendor and device IDs */
|
||||
if(!(bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
|
||||
PCI_DEVICE_ID_DEC_21154, NULL))) {
|
||||
bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
|
||||
PCI_DEVICE_ID_DEC_21154, NULL);
|
||||
if (!bus0_dev) {
|
||||
status = -ENODEV;
|
||||
goto init_register_error;
|
||||
}
|
||||
|
|
|
@ -1096,9 +1096,8 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
|
||||
/* initialize our threads if they haven't already been started up */
|
||||
rc = one_time_init();
|
||||
if (rc) {
|
||||
if (rc)
|
||||
goto err_free_bus;
|
||||
}
|
||||
|
||||
dbg("pdev = %p\n", pdev);
|
||||
dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0));
|
||||
|
|
|
@ -705,9 +705,8 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz
|
|||
if (temp == max) {
|
||||
*head = max->next;
|
||||
} else {
|
||||
while (temp && temp->next != max) {
|
||||
while (temp && temp->next != max)
|
||||
temp = temp->next;
|
||||
}
|
||||
|
||||
if (temp)
|
||||
temp->next = max->next;
|
||||
|
@ -903,9 +902,8 @@ irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
|
|||
/*
|
||||
* Check to see if it was our interrupt
|
||||
*/
|
||||
if (!(misc & 0x000C)) {
|
||||
if (!(misc & 0x000C))
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (misc & 0x0004) {
|
||||
/*
|
||||
|
@ -2006,9 +2004,8 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
|
|||
/* Check to see if the interlock is closed */
|
||||
tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
|
||||
|
||||
if (tempdword & (0x01 << hp_slot)) {
|
||||
if (tempdword & (0x01 << hp_slot))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (func->is_a_board) {
|
||||
rc = board_replaced(func, ctrl);
|
||||
|
@ -2070,9 +2067,8 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
|
|||
}
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
if (rc)
|
||||
dbg("%s: rc = %d\n", __func__, rc);
|
||||
}
|
||||
|
||||
if (p_slot)
|
||||
update_slot_info(ctrl, p_slot);
|
||||
|
@ -2095,9 +2091,8 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
|
|||
device = func->device;
|
||||
func = cpqhp_slot_find(ctrl->bus, device, index++);
|
||||
p_slot = cpqhp_find_slot(ctrl, device);
|
||||
if (p_slot) {
|
||||
if (p_slot)
|
||||
physical_slot = p_slot->number;
|
||||
}
|
||||
|
||||
/* Make sure there are no video controllers here */
|
||||
while (func && !rc) {
|
||||
|
|
|
@ -204,9 +204,8 @@ static int load_HRT (void __iomem *rom_start)
|
|||
u8 temp_byte = 0xFF;
|
||||
u32 rc;
|
||||
|
||||
if (!check_for_compaq_ROM(rom_start)) {
|
||||
if (!check_for_compaq_ROM(rom_start))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
available = 1024;
|
||||
|
||||
|
@ -250,9 +249,8 @@ static u32 store_HRT (void __iomem *rom_start)
|
|||
|
||||
available = 1024;
|
||||
|
||||
if (!check_for_compaq_ROM(rom_start)) {
|
||||
if (!check_for_compaq_ROM(rom_start))
|
||||
return(1);
|
||||
}
|
||||
|
||||
buffer = (u32*) evbuffer;
|
||||
|
||||
|
@ -427,9 +425,9 @@ static u32 store_HRT (void __iomem *rom_start)
|
|||
|
||||
void compaq_nvram_init (void __iomem *rom_start)
|
||||
{
|
||||
if (rom_start) {
|
||||
if (rom_start)
|
||||
compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
|
||||
}
|
||||
|
||||
dbg("int15 entry = %p\n", compaq_int15_entry_point);
|
||||
|
||||
/* initialize our int15 lock */
|
||||
|
@ -661,10 +659,9 @@ int compaq_nvram_store (void __iomem *rom_start)
|
|||
|
||||
if (evbuffer_init) {
|
||||
rc = store_HRT(rom_start);
|
||||
if (rc) {
|
||||
if (rc)
|
||||
err(msg_unable_to_save);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -1023,7 +1023,8 @@ static int enable_slot(struct hotplug_slot *hs)
|
|||
debug("ENABLING SLOT........\n");
|
||||
slot_cur = hs->private;
|
||||
|
||||
if ((rc = validate(slot_cur, ENABLE))) {
|
||||
rc = validate(slot_cur, ENABLE);
|
||||
if (rc) {
|
||||
err("validate function failed\n");
|
||||
goto error_nopower;
|
||||
}
|
||||
|
@ -1199,9 +1200,8 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
|
|||
|
||||
debug("DISABLING SLOT...\n");
|
||||
|
||||
if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) {
|
||||
if ((slot_cur == NULL) || (slot_cur->ctrl == NULL))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
flag = slot_cur->flag;
|
||||
slot_cur->flag = 1;
|
||||
|
@ -1336,17 +1336,20 @@ static int __init ibmphp_init(void)
|
|||
for (i = 0; i < 16; i++)
|
||||
irqs[i] = 0;
|
||||
|
||||
if ((rc = ibmphp_access_ebda()))
|
||||
rc = ibmphp_access_ebda();
|
||||
if (rc)
|
||||
goto error;
|
||||
debug("after ibmphp_access_ebda()\n");
|
||||
|
||||
if ((rc = ibmphp_rsrc_init()))
|
||||
rc = ibmphp_rsrc_init();
|
||||
if (rc)
|
||||
goto error;
|
||||
debug("AFTER Resource & EBDA INITIALIZATIONS\n");
|
||||
|
||||
max_slots = get_max_slots();
|
||||
|
||||
if ((rc = ibmphp_register_pci()))
|
||||
rc = ibmphp_register_pci();
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
if (init_ops()) {
|
||||
|
@ -1355,9 +1358,9 @@ static int __init ibmphp_init(void)
|
|||
}
|
||||
|
||||
ibmphp_print_test();
|
||||
if ((rc = ibmphp_hpc_start_poll_thread())) {
|
||||
rc = ibmphp_hpc_start_poll_thread();
|
||||
if (rc)
|
||||
goto error;
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
|
|
|
@ -215,9 +215,8 @@ static void __init print_ebda_hpc (void)
|
|||
debug ("%s - cap of the slot: %x\n", __func__, hpc_ptr->slots[index].slot_cap);
|
||||
}
|
||||
|
||||
for (index = 0; index < hpc_ptr->bus_count; index++) {
|
||||
for (index = 0; index < hpc_ptr->bus_count; index++)
|
||||
debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __func__, hpc_ptr->buses[index].bus_num);
|
||||
}
|
||||
|
||||
debug ("%s - type of hpc: %x\n", __func__, hpc_ptr->ctlr_type);
|
||||
switch (hpc_ptr->ctlr_type) {
|
||||
|
|
|
@ -997,9 +997,8 @@ static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
|
|||
rc = ibmphp_do_disable_slot (pslot);
|
||||
}
|
||||
|
||||
if (update || disable) {
|
||||
if (update || disable)
|
||||
ibmphp_update_slot_info (pslot);
|
||||
}
|
||||
|
||||
debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __func__, rc, disable, update);
|
||||
|
||||
|
|
|
@ -145,7 +145,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
|
|||
case PCI_HEADER_TYPE_NORMAL:
|
||||
debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class);
|
||||
assign_alt_irq (cur_func, class_code);
|
||||
if ((rc = configure_device (cur_func)) < 0) {
|
||||
rc = configure_device(cur_func);
|
||||
if (rc < 0) {
|
||||
/* We need to do this in case some other BARs were properly inserted */
|
||||
err ("was not able to configure devfunc %x on bus %x.\n",
|
||||
cur_func->device, cur_func->busno);
|
||||
|
@ -157,7 +158,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
|
|||
break;
|
||||
case PCI_HEADER_TYPE_MULTIDEVICE:
|
||||
assign_alt_irq (cur_func, class_code);
|
||||
if ((rc = configure_device (cur_func)) < 0) {
|
||||
rc = configure_device(cur_func);
|
||||
if (rc < 0) {
|
||||
/* We need to do this in case some other BARs were properly inserted */
|
||||
err ("was not able to configure devfunc %x on bus %x...bailing out\n",
|
||||
cur_func->device, cur_func->busno);
|
||||
|
|
|
@ -224,7 +224,8 @@ int __init ibmphp_rsrc_init (void)
|
|||
if ((curr->rsrc_type & RESTYPE) == MMASK) {
|
||||
/* no bus structure exists in place yet */
|
||||
if (list_empty (&gbuses)) {
|
||||
if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
|
||||
rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
list_add_tail (&newbus->bus_list, &gbuses);
|
||||
debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
|
||||
|
@ -237,7 +238,8 @@ int __init ibmphp_rsrc_init (void)
|
|||
return rc;
|
||||
} else {
|
||||
/* went through all the buses and didn't find ours, need to create a new bus node */
|
||||
if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
|
||||
rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
list_add_tail (&newbus->bus_list, &gbuses);
|
||||
|
@ -248,7 +250,8 @@ int __init ibmphp_rsrc_init (void)
|
|||
/* prefetchable memory */
|
||||
if (list_empty (&gbuses)) {
|
||||
/* no bus structure exists in place yet */
|
||||
if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
|
||||
rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
list_add_tail (&newbus->bus_list, &gbuses);
|
||||
debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
|
||||
|
@ -261,7 +264,8 @@ int __init ibmphp_rsrc_init (void)
|
|||
return rc;
|
||||
} else {
|
||||
/* went through all the buses and didn't find ours, need to create a new bus node */
|
||||
if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
|
||||
rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
list_add_tail (&newbus->bus_list, &gbuses);
|
||||
debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
|
||||
|
@ -271,7 +275,8 @@ int __init ibmphp_rsrc_init (void)
|
|||
/* IO */
|
||||
if (list_empty (&gbuses)) {
|
||||
/* no bus structure exists in place yet */
|
||||
if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
|
||||
rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
list_add_tail (&newbus->bus_list, &gbuses);
|
||||
debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
|
||||
|
@ -283,7 +288,8 @@ int __init ibmphp_rsrc_init (void)
|
|||
return rc;
|
||||
} else {
|
||||
/* went through all the buses and didn't find ours, need to create a new bus node */
|
||||
if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
|
||||
rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
list_add_tail (&newbus->bus_list, &gbuses);
|
||||
debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
|
||||
|
@ -1038,7 +1044,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
|
|||
/* found our range */
|
||||
if (!res_prev) {
|
||||
/* first time in the loop */
|
||||
if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
|
||||
len_tmp = res_cur->start - 1 - range->start;
|
||||
|
||||
if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
|
||||
debug ("len_tmp = %x\n", len_tmp);
|
||||
|
||||
if ((len_tmp < len_cur) || (len_cur == 0)) {
|
||||
|
@ -1078,7 +1086,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
|
|||
}
|
||||
if (!res_cur->next) {
|
||||
/* last device on the range */
|
||||
if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) {
|
||||
len_tmp = range->end - (res_cur->end + 1);
|
||||
|
||||
if ((range->end != res_cur->end) && (len_tmp >= res->len)) {
|
||||
debug ("len_tmp = %x\n", len_tmp);
|
||||
if ((len_tmp < len_cur) || (len_cur == 0)) {
|
||||
|
||||
|
@ -1117,8 +1127,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
|
|||
if (res_prev) {
|
||||
if (res_prev->rangeno != res_cur->rangeno) {
|
||||
/* 1st device on this range */
|
||||
if ((res_cur->start != range->start) &&
|
||||
((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
|
||||
len_tmp = res_cur->start - 1 - range->start;
|
||||
|
||||
if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
|
||||
if ((len_tmp < len_cur) || (len_cur == 0)) {
|
||||
if ((range->start % tmp_divide) == 0) {
|
||||
/* just perfect, starting address is divisible by length */
|
||||
|
@ -1153,7 +1164,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
|
|||
}
|
||||
} else {
|
||||
/* in the same range */
|
||||
if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) {
|
||||
len_tmp = res_cur->start - 1 - res_prev->end - 1;
|
||||
|
||||
if (len_tmp >= res->len) {
|
||||
if ((len_tmp < len_cur) || (len_cur == 0)) {
|
||||
if (((res_prev->end + 1) % tmp_divide) == 0) {
|
||||
/* just perfect, starting address's divisible by length */
|
||||
|
@ -1212,7 +1225,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
|
|||
break;
|
||||
}
|
||||
while (range) {
|
||||
if ((len_tmp = range->end - range->start) >= res->len) {
|
||||
len_tmp = range->end - range->start;
|
||||
|
||||
if (len_tmp >= res->len) {
|
||||
if ((len_tmp < len_cur) || (len_cur == 0)) {
|
||||
if ((range->start % tmp_divide) == 0) {
|
||||
/* just perfect, starting address's divisible by length */
|
||||
|
@ -1276,7 +1291,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
|
|||
break;
|
||||
}
|
||||
while (range) {
|
||||
if ((len_tmp = range->end - range->start) >= res->len) {
|
||||
len_tmp = range->end - range->start;
|
||||
|
||||
if (len_tmp >= res->len) {
|
||||
if ((len_tmp < len_cur) || (len_cur == 0)) {
|
||||
if ((range->start % tmp_divide) == 0) {
|
||||
/* just perfect, starting address's divisible by length */
|
||||
|
|
|
@ -92,7 +92,7 @@ struct controller {
|
|||
struct slot *slot;
|
||||
wait_queue_head_t queue; /* sleep & wake process */
|
||||
u32 slot_cap;
|
||||
u32 slot_ctrl;
|
||||
u16 slot_ctrl;
|
||||
struct timer_list poll_timer;
|
||||
unsigned long cmd_started; /* jiffies */
|
||||
unsigned int cmd_busy:1;
|
||||
|
|
|
@ -262,6 +262,13 @@ static int pciehp_probe(struct pcie_device *dev)
|
|||
goto err_out_none;
|
||||
}
|
||||
|
||||
if (!dev->port->subordinate) {
|
||||
/* Can happen if we run out of bus numbers during probe */
|
||||
dev_err(&dev->device,
|
||||
"Hotplug bridge without secondary bus, ignoring\n");
|
||||
goto err_out_none;
|
||||
}
|
||||
|
||||
ctrl = pcie_init(dev);
|
||||
if (!ctrl) {
|
||||
dev_err(&dev->device, "Controller initialization failed\n");
|
||||
|
|
|
@ -171,9 +171,9 @@ static void pcie_wait_cmd(struct controller *ctrl)
|
|||
* interrupts.
|
||||
*/
|
||||
if (!rc)
|
||||
ctrl_info(ctrl, "Timeout on hotplug command %#010x (issued %u msec ago)\n",
|
||||
ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n",
|
||||
ctrl->slot_ctrl,
|
||||
jiffies_to_msecs(now - ctrl->cmd_started));
|
||||
jiffies_to_msecs(jiffies - ctrl->cmd_started));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -422,9 +422,9 @@ void pciehp_set_attention_status(struct slot *slot, u8 value)
|
|||
default:
|
||||
return;
|
||||
}
|
||||
pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
|
||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
|
||||
pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
|
||||
}
|
||||
|
||||
void pciehp_green_led_on(struct slot *slot)
|
||||
|
@ -614,6 +614,8 @@ void pcie_enable_notification(struct controller *ctrl)
|
|||
PCI_EXP_SLTCTL_DLLSCE);
|
||||
|
||||
pcie_write_cmd(ctrl, cmd, mask);
|
||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
|
||||
}
|
||||
|
||||
static void pcie_disable_notification(struct controller *ctrl)
|
||||
|
@ -625,6 +627,8 @@ static void pcie_disable_notification(struct controller *ctrl)
|
|||
PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
|
||||
PCI_EXP_SLTCTL_DLLSCE);
|
||||
pcie_write_cmd(ctrl, 0, mask);
|
||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -652,6 +656,8 @@ int pciehp_reset_slot(struct slot *slot, int probe)
|
|||
stat_mask |= PCI_EXP_SLTSTA_DLLSC;
|
||||
|
||||
pcie_write_cmd(ctrl, 0, ctrl_mask);
|
||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
|
||||
if (pciehp_poll_mode)
|
||||
del_timer_sync(&ctrl->poll_timer);
|
||||
|
||||
|
@ -659,6 +665,8 @@ int pciehp_reset_slot(struct slot *slot, int probe)
|
|||
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
|
||||
pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
|
||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
|
||||
if (pciehp_poll_mode)
|
||||
int_poll_timeout(ctrl->poll_timer.data);
|
||||
|
||||
|
@ -797,9 +805,6 @@ struct controller *pcie_init(struct pcie_device *dev)
|
|||
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
|
||||
PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
|
||||
|
||||
/* Disable software notification */
|
||||
pcie_disable_notification(ctrl);
|
||||
|
||||
ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n",
|
||||
(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
|
||||
FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
|
||||
|
|
|
@ -65,14 +65,7 @@ int pciehp_configure_device(struct slot *p_slot)
|
|||
pci_hp_add_bridge(dev);
|
||||
|
||||
pci_assign_unassigned_bridge_resources(bridge);
|
||||
|
||||
list_for_each_entry(dev, &parent->devices, bus_list) {
|
||||
if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
|
||||
continue;
|
||||
|
||||
pci_configure_slot(dev);
|
||||
}
|
||||
|
||||
pcie_bus_configure_settings(parent);
|
||||
pci_bus_add_devices(parent);
|
||||
|
||||
out:
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
||||
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2001 IBM Corp.
|
||||
* Copyright (C) 2003-2004 Intel Corporation
|
||||
* (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
||||
* NON INFRINGEMENT. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
|
||||
static struct hpp_type0 pci_default_type0 = {
|
||||
.revision = 1,
|
||||
.cache_line_size = 8,
|
||||
.latency_timer = 0x40,
|
||||
.enable_serr = 0,
|
||||
.enable_perr = 0,
|
||||
};
|
||||
|
||||
static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
|
||||
{
|
||||
u16 pci_cmd, pci_bctl;
|
||||
|
||||
if (!hpp) {
|
||||
/*
|
||||
* Perhaps we *should* use default settings for PCIe, but
|
||||
* pciehp didn't, so we won't either.
|
||||
*/
|
||||
if (pci_is_pcie(dev))
|
||||
return;
|
||||
hpp = &pci_default_type0;
|
||||
}
|
||||
|
||||
if (hpp->revision > 1) {
|
||||
dev_warn(&dev->dev,
|
||||
"PCI settings rev %d not supported; using defaults\n",
|
||||
hpp->revision);
|
||||
hpp = &pci_default_type0;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
|
||||
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
|
||||
if (hpp->enable_serr)
|
||||
pci_cmd |= PCI_COMMAND_SERR;
|
||||
else
|
||||
pci_cmd &= ~PCI_COMMAND_SERR;
|
||||
if (hpp->enable_perr)
|
||||
pci_cmd |= PCI_COMMAND_PARITY;
|
||||
else
|
||||
pci_cmd &= ~PCI_COMMAND_PARITY;
|
||||
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
|
||||
|
||||
/* Program bridge control value */
|
||||
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
||||
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
|
||||
hpp->latency_timer);
|
||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
|
||||
if (hpp->enable_serr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_SERR;
|
||||
else
|
||||
pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
|
||||
if (hpp->enable_perr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
|
||||
else
|
||||
pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
|
||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
|
||||
}
|
||||
}
|
||||
|
||||
static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
|
||||
{
|
||||
if (hpp)
|
||||
dev_warn(&dev->dev, "PCI-X settings not supported\n");
|
||||
}
|
||||
|
||||
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
|
||||
{
|
||||
int pos;
|
||||
u32 reg32;
|
||||
|
||||
if (!hpp)
|
||||
return;
|
||||
|
||||
if (hpp->revision > 1) {
|
||||
dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
|
||||
hpp->revision);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize Device Control Register */
|
||||
pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
|
||||
~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
|
||||
|
||||
/* Initialize Link Control Register */
|
||||
if (dev->subordinate)
|
||||
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
|
||||
~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
|
||||
|
||||
/* Find Advanced Error Reporting Enhanced Capability */
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/* Initialize Uncorrectable Error Mask Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®32);
|
||||
reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
|
||||
|
||||
/* Initialize Uncorrectable Error Severity Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®32);
|
||||
reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
|
||||
|
||||
/* Initialize Correctable Error Mask Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®32);
|
||||
reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
|
||||
|
||||
/* Initialize Advanced Error Capabilities and Control Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
|
||||
reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
|
||||
|
||||
/*
|
||||
* FIXME: The following two registers are not supported yet.
|
||||
*
|
||||
* o Secondary Uncorrectable Error Severity Register
|
||||
* o Secondary Uncorrectable Error Mask Register
|
||||
*/
|
||||
}
|
||||
|
||||
void pci_configure_slot(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *cdev;
|
||||
struct hotplug_params hpp;
|
||||
|
||||
if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
|
||||
(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
|
||||
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
|
||||
return;
|
||||
|
||||
pcie_bus_configure_settings(dev->bus);
|
||||
|
||||
memset(&hpp, 0, sizeof(hpp));
|
||||
pci_get_hp_params(dev, &hpp);
|
||||
|
||||
program_hpp_type2(dev, hpp.t2);
|
||||
program_hpp_type1(dev, hpp.t1);
|
||||
program_hpp_type0(dev, hpp.t0);
|
||||
|
||||
if (dev->subordinate) {
|
||||
list_for_each_entry(cdev, &dev->subordinate->devices,
|
||||
bus_list)
|
||||
pci_configure_slot(cdev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_configure_slot);
|
|
@ -195,7 +195,8 @@ static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
|
|||
int rc = 0;
|
||||
|
||||
ctrl_dbg(ctrl, "Change speed to %d\n", speed);
|
||||
if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
|
||||
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed);
|
||||
if (rc) {
|
||||
ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
|
||||
__func__);
|
||||
return WRONG_BUS_FREQUENCY;
|
||||
|
@ -261,14 +262,16 @@ static int board_added(struct slot *p_slot)
|
|||
}
|
||||
|
||||
if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
|
||||
if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
|
||||
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz);
|
||||
if (rc) {
|
||||
ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
|
||||
__func__);
|
||||
return WRONG_BUS_FREQUENCY;
|
||||
}
|
||||
|
||||
/* turn on board, blink green LED, turn off Amber LED */
|
||||
if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
|
||||
rc = p_slot->hpc_ops->slot_enable(p_slot);
|
||||
if (rc) {
|
||||
ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
@ -296,7 +299,8 @@ static int board_added(struct slot *p_slot)
|
|||
return rc;
|
||||
|
||||
/* turn on board, blink green LED, turn off Amber LED */
|
||||
if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
|
||||
rc = p_slot->hpc_ops->slot_enable(p_slot);
|
||||
if (rc) {
|
||||
ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -466,7 +466,8 @@ static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
|
|||
u8 m66_cap = !!(slot_reg & MHZ66_CAP);
|
||||
u8 pi, pcix_cap;
|
||||
|
||||
if ((retval = hpc_get_prog_int(slot, &pi)))
|
||||
retval = hpc_get_prog_int(slot, &pi);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
switch (pi) {
|
||||
|
|
|
@ -69,13 +69,7 @@ int shpchp_configure_device(struct slot *p_slot)
|
|||
}
|
||||
|
||||
pci_assign_unassigned_bridge_resources(bridge);
|
||||
|
||||
list_for_each_entry(dev, &parent->devices, bus_list) {
|
||||
if (PCI_SLOT(dev->devfn) != p_slot->device)
|
||||
continue;
|
||||
pci_configure_slot(dev);
|
||||
}
|
||||
|
||||
pcie_bus_configure_settings(parent);
|
||||
pci_bus_add_devices(parent);
|
||||
|
||||
out:
|
||||
|
|
|
@ -633,7 +633,7 @@ int pci_vfs_assigned(struct pci_dev *dev)
|
|||
* our dev as the physical function and the assigned bit is set
|
||||
*/
|
||||
if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
|
||||
(vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED))
|
||||
pci_is_dev_assigned(vfdev))
|
||||
vfs_assigned++;
|
||||
|
||||
vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
|
||||
|
|
|
@ -56,16 +56,6 @@ void __weak arch_teardown_msi_irq(unsigned int irq)
|
|||
chip->teardown_irq(chip, irq);
|
||||
}
|
||||
|
||||
int __weak arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
|
||||
{
|
||||
struct msi_chip *chip = dev->bus->msi;
|
||||
|
||||
if (!chip || !chip->check_device)
|
||||
return 0;
|
||||
|
||||
return chip->check_device(chip, dev, nvec, type);
|
||||
}
|
||||
|
||||
int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
|
@ -130,7 +120,7 @@ static void default_restore_msi_irq(struct pci_dev *dev, int irq)
|
|||
}
|
||||
|
||||
if (entry)
|
||||
write_msi_msg(irq, &entry->msg);
|
||||
__write_msi_msg(entry, &entry->msg);
|
||||
}
|
||||
|
||||
void __weak arch_restore_msi_irqs(struct pci_dev *dev)
|
||||
|
@ -384,17 +374,6 @@ static void free_msi_irqs(struct pci_dev *dev)
|
|||
iounmap(entry->mask_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Its possible that we get into this path
|
||||
* When populate_msi_sysfs fails, which means the entries
|
||||
* were not registered with sysfs. In that case don't
|
||||
* unregister them.
|
||||
*/
|
||||
if (entry->kobj.parent) {
|
||||
kobject_del(&entry->kobj);
|
||||
kobject_put(&entry->kobj);
|
||||
}
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
@ -595,7 +574,6 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev)
|
|||
entry->msi_attrib.entry_nr = 0;
|
||||
entry->msi_attrib.maskbit = !!(control & PCI_MSI_FLAGS_MASKBIT);
|
||||
entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
|
||||
entry->msi_attrib.pos = dev->msi_cap;
|
||||
entry->msi_attrib.multi_cap = (control & PCI_MSI_FLAGS_QMASK) >> 1;
|
||||
|
||||
if (control & PCI_MSI_FLAGS_64BIT)
|
||||
|
@ -699,7 +677,6 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
|
|||
entry->msi_attrib.is_64 = 1;
|
||||
entry->msi_attrib.entry_nr = entries[i].entry;
|
||||
entry->msi_attrib.default_irq = dev->irq;
|
||||
entry->msi_attrib.pos = dev->msix_cap;
|
||||
entry->mask_base = base;
|
||||
|
||||
list_add_tail(&entry->list, &dev->msi_list);
|
||||
|
@ -806,23 +783,24 @@ out_free:
|
|||
}
|
||||
|
||||
/**
|
||||
* pci_msi_check_device - check whether MSI may be enabled on a device
|
||||
* pci_msi_supported - check whether MSI may be enabled on a device
|
||||
* @dev: pointer to the pci_dev data structure of MSI device function
|
||||
* @nvec: how many MSIs have been requested ?
|
||||
* @type: are we checking for MSI or MSI-X ?
|
||||
*
|
||||
* Look at global flags, the device itself, and its parent buses
|
||||
* to determine if MSI/-X are supported for the device. If MSI/-X is
|
||||
* supported return 0, else return an error code.
|
||||
* supported return 1, else return 0.
|
||||
**/
|
||||
static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
|
||||
static int pci_msi_supported(struct pci_dev *dev, int nvec)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
int ret;
|
||||
|
||||
/* MSI must be globally enabled and supported by the device */
|
||||
if (!pci_msi_enable || !dev || dev->no_msi)
|
||||
return -EINVAL;
|
||||
if (!pci_msi_enable)
|
||||
return 0;
|
||||
|
||||
if (!dev || dev->no_msi || dev->current_state != PCI_D0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* You can't ask to have 0 or less MSIs configured.
|
||||
|
@ -830,7 +808,7 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
|
|||
* b) the list manipulation code assumes nvec >= 1.
|
||||
*/
|
||||
if (nvec < 1)
|
||||
return -ERANGE;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Any bridge which does NOT route MSI transactions from its
|
||||
|
@ -841,13 +819,9 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
|
|||
*/
|
||||
for (bus = dev->bus; bus; bus = bus->parent)
|
||||
if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
|
||||
return -EINVAL;
|
||||
|
||||
ret = arch_msi_check_device(dev, nvec, type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -946,15 +920,14 @@ EXPORT_SYMBOL(pci_msix_vec_count);
|
|||
**/
|
||||
int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
|
||||
{
|
||||
int status, nr_entries;
|
||||
int nr_entries;
|
||||
int i, j;
|
||||
|
||||
if (!entries || !dev->msix_cap || dev->current_state != PCI_D0)
|
||||
if (!pci_msi_supported(dev, nvec))
|
||||
return -EINVAL;
|
||||
|
||||
status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX);
|
||||
if (status)
|
||||
return status;
|
||||
if (!entries)
|
||||
return -EINVAL;
|
||||
|
||||
nr_entries = pci_msix_vec_count(dev);
|
||||
if (nr_entries < 0)
|
||||
|
@ -978,8 +951,7 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
|
|||
dev_info(&dev->dev, "can't enable MSI-X (MSI IRQ already assigned)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
status = msix_capability_init(dev, entries, nvec);
|
||||
return status;
|
||||
return msix_capability_init(dev, entries, nvec);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_enable_msix);
|
||||
|
||||
|
@ -1062,7 +1034,7 @@ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
|
|||
int nvec;
|
||||
int rc;
|
||||
|
||||
if (dev->current_state != PCI_D0)
|
||||
if (!pci_msi_supported(dev, minvec))
|
||||
return -EINVAL;
|
||||
|
||||
WARN_ON(!!dev->msi_enabled);
|
||||
|
@ -1085,17 +1057,6 @@ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
|
|||
else if (nvec > maxvec)
|
||||
nvec = maxvec;
|
||||
|
||||
do {
|
||||
rc = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
} else if (rc > 0) {
|
||||
if (rc < minvec)
|
||||
return -ENOSPC;
|
||||
nvec = rc;
|
||||
}
|
||||
} while (rc);
|
||||
|
||||
do {
|
||||
rc = msi_capability_init(dev, nvec);
|
||||
if (rc < 0) {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci-aspm.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
|
@ -17,6 +18,267 @@
|
|||
#include <linux/pm_qos.h>
|
||||
#include "pci.h"
|
||||
|
||||
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
|
||||
{
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
unsigned long long mcfg_addr;
|
||||
|
||||
if (handle)
|
||||
status = acpi_evaluate_integer(handle, METHOD_NAME__CBA,
|
||||
NULL, &mcfg_addr);
|
||||
if (ACPI_FAILURE(status))
|
||||
return 0;
|
||||
|
||||
return (phys_addr_t)mcfg_addr;
|
||||
}
|
||||
|
||||
static acpi_status decode_type0_hpx_record(union acpi_object *record,
|
||||
struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 6)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 6; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t0 = &hpx->type0_data;
|
||||
hpx->t0->revision = revision;
|
||||
hpx->t0->cache_line_size = fields[2].integer.value;
|
||||
hpx->t0->latency_timer = fields[3].integer.value;
|
||||
hpx->t0->enable_serr = fields[4].integer.value;
|
||||
hpx->t0->enable_perr = fields[5].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 0 Revision %d record not supported\n",
|
||||
__func__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status decode_type1_hpx_record(union acpi_object *record,
|
||||
struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 5)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 5; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t1 = &hpx->type1_data;
|
||||
hpx->t1->revision = revision;
|
||||
hpx->t1->max_mem_read = fields[2].integer.value;
|
||||
hpx->t1->avg_max_split = fields[3].integer.value;
|
||||
hpx->t1->tot_max_split = fields[4].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 1 Revision %d record not supported\n",
|
||||
__func__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status decode_type2_hpx_record(union acpi_object *record,
|
||||
struct hotplug_params *hpx)
|
||||
{
|
||||
int i;
|
||||
union acpi_object *fields = record->package.elements;
|
||||
u32 revision = fields[1].integer.value;
|
||||
|
||||
switch (revision) {
|
||||
case 1:
|
||||
if (record->package.count != 18)
|
||||
return AE_ERROR;
|
||||
for (i = 2; i < 18; i++)
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER)
|
||||
return AE_ERROR;
|
||||
hpx->t2 = &hpx->type2_data;
|
||||
hpx->t2->revision = revision;
|
||||
hpx->t2->unc_err_mask_and = fields[2].integer.value;
|
||||
hpx->t2->unc_err_mask_or = fields[3].integer.value;
|
||||
hpx->t2->unc_err_sever_and = fields[4].integer.value;
|
||||
hpx->t2->unc_err_sever_or = fields[5].integer.value;
|
||||
hpx->t2->cor_err_mask_and = fields[6].integer.value;
|
||||
hpx->t2->cor_err_mask_or = fields[7].integer.value;
|
||||
hpx->t2->adv_err_cap_and = fields[8].integer.value;
|
||||
hpx->t2->adv_err_cap_or = fields[9].integer.value;
|
||||
hpx->t2->pci_exp_devctl_and = fields[10].integer.value;
|
||||
hpx->t2->pci_exp_devctl_or = fields[11].integer.value;
|
||||
hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value;
|
||||
hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value;
|
||||
hpx->t2->sec_unc_err_sever_and = fields[14].integer.value;
|
||||
hpx->t2->sec_unc_err_sever_or = fields[15].integer.value;
|
||||
hpx->t2->sec_unc_err_mask_and = fields[16].integer.value;
|
||||
hpx->t2->sec_unc_err_mask_or = fields[17].integer.value;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"%s: Type 2 Revision %d record not supported\n",
|
||||
__func__, revision);
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
union acpi_object *package, *record, *fields;
|
||||
u32 type;
|
||||
int i;
|
||||
|
||||
/* Clear the return buffer with zeros */
|
||||
memset(hpx, 0, sizeof(struct hotplug_params));
|
||||
|
||||
status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
package = (union acpi_object *)buffer.pointer;
|
||||
if (package->type != ACPI_TYPE_PACKAGE) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < package->package.count; i++) {
|
||||
record = &package->package.elements[i];
|
||||
if (record->type != ACPI_TYPE_PACKAGE) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fields = record->package.elements;
|
||||
if (fields[0].type != ACPI_TYPE_INTEGER ||
|
||||
fields[1].type != ACPI_TYPE_INTEGER) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
type = fields[0].integer.value;
|
||||
switch (type) {
|
||||
case 0:
|
||||
status = decode_type0_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
case 1:
|
||||
status = decode_type1_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
case 2:
|
||||
status = decode_type2_hpx_record(record, hpx);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto exit;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: Type %d record not supported\n",
|
||||
__func__, type);
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
kfree(buffer.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *package, *fields;
|
||||
int i;
|
||||
|
||||
memset(hpp, 0, sizeof(struct hotplug_params));
|
||||
|
||||
status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
package = (union acpi_object *) buffer.pointer;
|
||||
if (package->type != ACPI_TYPE_PACKAGE ||
|
||||
package->package.count != 4) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fields = package->package.elements;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (fields[i].type != ACPI_TYPE_INTEGER) {
|
||||
status = AE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
hpp->t0 = &hpp->type0_data;
|
||||
hpp->t0->revision = 1;
|
||||
hpp->t0->cache_line_size = fields[0].integer.value;
|
||||
hpp->t0->latency_timer = fields[1].integer.value;
|
||||
hpp->t0->enable_serr = fields[2].integer.value;
|
||||
hpp->t0->enable_perr = fields[3].integer.value;
|
||||
|
||||
exit:
|
||||
kfree(buffer.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* pci_get_hp_params
|
||||
*
|
||||
* @dev - the pci_dev for which we want parameters
|
||||
* @hpp - allocated by the caller
|
||||
*/
|
||||
int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
|
||||
{
|
||||
acpi_status status;
|
||||
acpi_handle handle, phandle;
|
||||
struct pci_bus *pbus;
|
||||
|
||||
handle = NULL;
|
||||
for (pbus = dev->bus; pbus; pbus = pbus->parent) {
|
||||
handle = acpi_pci_get_bridge_handle(pbus);
|
||||
if (handle)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* _HPP settings apply to all child buses, until another _HPP is
|
||||
* encountered. If we don't find an _HPP for the input pci dev,
|
||||
* look for it in the parent device scope since that would apply to
|
||||
* this pci dev.
|
||||
*/
|
||||
while (handle) {
|
||||
status = acpi_run_hpx(handle, hpp);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return 0;
|
||||
status = acpi_run_hpp(handle, hpp);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return 0;
|
||||
if (acpi_is_root_bridge(handle))
|
||||
break;
|
||||
status = acpi_get_parent(handle, &phandle);
|
||||
if (ACPI_FAILURE(status))
|
||||
break;
|
||||
handle = phandle;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_get_hp_params);
|
||||
|
||||
/**
|
||||
* pci_acpi_wake_bus - Root bus wakeup notification fork function.
|
||||
* @work: Work item to handle.
|
||||
|
@ -84,20 +346,6 @@ acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
|
|||
return acpi_add_pm_notifier(dev, &pci_dev->dev, pci_acpi_wake_dev);
|
||||
}
|
||||
|
||||
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
|
||||
{
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
unsigned long long mcfg_addr;
|
||||
|
||||
if (handle)
|
||||
status = acpi_evaluate_integer(handle, METHOD_NAME__CBA,
|
||||
NULL, &mcfg_addr);
|
||||
if (ACPI_FAILURE(status))
|
||||
return 0;
|
||||
|
||||
return (phys_addr_t)mcfg_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* _SxD returns the D-state with the highest power
|
||||
* (lowest D-state number) supported in the S-state "x".
|
||||
|
|
|
@ -55,7 +55,6 @@ int pci_add_dynid(struct pci_driver *drv,
|
|||
unsigned long driver_data)
|
||||
{
|
||||
struct pci_dynid *dynid;
|
||||
int retval;
|
||||
|
||||
dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
|
||||
if (!dynid)
|
||||
|
@ -73,9 +72,7 @@ int pci_add_dynid(struct pci_driver *drv,
|
|||
list_add_tail(&dynid->node, &drv->dynids.list);
|
||||
spin_unlock(&drv->dynids.lock);
|
||||
|
||||
retval = driver_attach(&drv->driver);
|
||||
|
||||
return retval;
|
||||
return driver_attach(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_add_dynid);
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
|||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
||||
return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x\n",
|
||||
return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X\n",
|
||||
pci_dev->vendor, pci_dev->device,
|
||||
pci_dev->subsystem_vendor, pci_dev->subsystem_device,
|
||||
(u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
|
||||
|
@ -250,46 +250,45 @@ static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr,
|
|||
char *buf)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pci_bus *subordinate = pdev->subordinate;
|
||||
|
||||
if (!pdev->subordinate)
|
||||
return 0;
|
||||
|
||||
return sprintf(buf, "%u\n",
|
||||
!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
|
||||
return sprintf(buf, "%u\n", subordinate ?
|
||||
!(subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI)
|
||||
: !pdev->no_msi);
|
||||
}
|
||||
|
||||
static ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pci_bus *subordinate = pdev->subordinate;
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 0, &val) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Bad things may happen if the no_msi flag is changed
|
||||
* while drivers are loaded.
|
||||
*/
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
* Maybe devices without subordinate buses shouldn't have this
|
||||
* attribute in the first place?
|
||||
* "no_msi" and "bus_flags" only affect what happens when a driver
|
||||
* requests MSI or MSI-X. They don't affect any drivers that have
|
||||
* already requested MSI or MSI-X.
|
||||
*/
|
||||
if (!pdev->subordinate)
|
||||
if (!subordinate) {
|
||||
pdev->no_msi = !val;
|
||||
dev_info(&pdev->dev, "MSI/MSI-X %s for future drivers\n",
|
||||
val ? "allowed" : "disallowed");
|
||||
return count;
|
||||
|
||||
/* Is the flag going to change, or keep the value it already had? */
|
||||
if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^
|
||||
!!val) {
|
||||
pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI;
|
||||
|
||||
dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI, bad things could happen\n",
|
||||
val ? "" : " not");
|
||||
}
|
||||
|
||||
if (val)
|
||||
subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
|
||||
else
|
||||
subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
|
||||
|
||||
dev_info(&subordinate->dev, "MSI/MSI-X %s for future drivers of devices on this bus\n",
|
||||
val ? "allowed" : "disallowed");
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(msi_bus);
|
||||
|
|
|
@ -1003,12 +1003,19 @@ int pci_save_state(struct pci_dev *dev)
|
|||
for (i = 0; i < 16; i++)
|
||||
pci_read_config_dword(dev, i * 4, &dev->saved_config_space[i]);
|
||||
dev->state_saved = true;
|
||||
if ((i = pci_save_pcie_state(dev)) != 0)
|
||||
|
||||
i = pci_save_pcie_state(dev);
|
||||
if (i != 0)
|
||||
return i;
|
||||
if ((i = pci_save_pcix_state(dev)) != 0)
|
||||
|
||||
i = pci_save_pcix_state(dev);
|
||||
if (i != 0)
|
||||
return i;
|
||||
if ((i = pci_save_vc_state(dev)) != 0)
|
||||
|
||||
i = pci_save_vc_state(dev);
|
||||
if (i != 0)
|
||||
return i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_save_state);
|
||||
|
@ -1907,10 +1914,6 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
|
|||
if (target_state == PCI_POWER_ERROR)
|
||||
return -EIO;
|
||||
|
||||
/* D3cold during system suspend/hibernate is not supported */
|
||||
if (target_state > PCI_D3hot)
|
||||
target_state = PCI_D3hot;
|
||||
|
||||
pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));
|
||||
|
||||
error = pci_set_power_state(dev, target_state);
|
||||
|
@ -2704,6 +2707,37 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
|
|||
}
|
||||
EXPORT_SYMBOL(pci_request_regions_exclusive);
|
||||
|
||||
/**
|
||||
* pci_remap_iospace - Remap the memory mapped I/O space
|
||||
* @res: Resource describing the I/O space
|
||||
* @phys_addr: physical address of range to be mapped
|
||||
*
|
||||
* Remap the memory mapped I/O space described by the @res
|
||||
* and the CPU physical address @phys_addr into virtual address space.
|
||||
* Only architectures that have memory mapped IO functions defined
|
||||
* (and the PCI_IOBASE value defined) should call this function.
|
||||
*/
|
||||
int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
|
||||
{
|
||||
#if defined(PCI_IOBASE) && defined(CONFIG_MMU)
|
||||
unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start;
|
||||
|
||||
if (!(res->flags & IORESOURCE_IO))
|
||||
return -EINVAL;
|
||||
|
||||
if (res->end > IO_SPACE_LIMIT)
|
||||
return -EINVAL;
|
||||
|
||||
return ioremap_page_range(vaddr, vaddr + resource_size(res), phys_addr,
|
||||
pgprot_device(PAGE_KERNEL));
|
||||
#else
|
||||
/* this architecture does not have memory mapped I/O space,
|
||||
so this function should never be called */
|
||||
WARN_ONCE(1, "This architecture does not support memory mapped I/O\n");
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __pci_set_master(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
u16 old_cmd, cmd;
|
||||
|
@ -4406,6 +4440,15 @@ static void pci_no_domains(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_DOMAINS
|
||||
static atomic_t __domain_nr = ATOMIC_INIT(-1);
|
||||
|
||||
int pci_get_new_domain_nr(void)
|
||||
{
|
||||
return atomic_inc_return(&__domain_nr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* pci_ext_cfg_avail - can we access extended PCI config space?
|
||||
*
|
||||
|
|
|
@ -89,15 +89,17 @@ static const char *aer_correctable_error_string[] = {
|
|||
NULL,
|
||||
"Replay Timer Timeout", /* Bit Position 12 */
|
||||
"Advisory Non-Fatal", /* Bit Position 13 */
|
||||
"Corrected Internal Error", /* Bit Position 14 */
|
||||
"Header Log Overflow", /* Bit Position 15 */
|
||||
};
|
||||
|
||||
static const char *aer_uncorrectable_error_string[] = {
|
||||
NULL,
|
||||
"Undefined", /* Bit Position 0 */
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"Data Link Protocol", /* Bit Position 4 */
|
||||
NULL,
|
||||
"Surprise Down Error", /* Bit Position 5 */
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
@ -113,6 +115,11 @@ static const char *aer_uncorrectable_error_string[] = {
|
|||
"Malformed TLP", /* Bit Position 18 */
|
||||
"ECRC", /* Bit Position 19 */
|
||||
"Unsupported Request", /* Bit Position 20 */
|
||||
"ACS Violation", /* Bit Position 21 */
|
||||
"Uncorrectable Internal Error", /* Bit Position 22 */
|
||||
"MC Blocked TLP", /* Bit Position 23 */
|
||||
"AtomicOp Egress Blocked", /* Bit Position 24 */
|
||||
"TLP Prefix Blocked Error", /* Bit Position 25 */
|
||||
};
|
||||
|
||||
static const char *aer_agent_string[] = {
|
||||
|
|
|
@ -93,77 +93,6 @@ static int pcie_port_resume_noirq(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
struct d3cold_info {
|
||||
bool no_d3cold;
|
||||
unsigned int d3cold_delay;
|
||||
};
|
||||
|
||||
static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data)
|
||||
{
|
||||
struct d3cold_info *info = data;
|
||||
|
||||
info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay,
|
||||
info->d3cold_delay);
|
||||
if (pdev->no_d3cold)
|
||||
info->no_d3cold = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcie_port_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct d3cold_info d3cold_info = {
|
||||
.no_d3cold = false,
|
||||
.d3cold_delay = PCI_PM_D3_WAIT,
|
||||
};
|
||||
|
||||
/*
|
||||
* If any subordinate device disable D3cold, we should not put
|
||||
* the port into D3cold. The D3cold delay of port should be
|
||||
* the max of that of all subordinate devices.
|
||||
*/
|
||||
pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info);
|
||||
pdev->no_d3cold = d3cold_info.no_d3cold;
|
||||
pdev->d3cold_delay = d3cold_info.d3cold_delay;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcie_port_runtime_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_dev_pme_poll(struct pci_dev *pdev, void *data)
|
||||
{
|
||||
bool *pme_poll = data;
|
||||
|
||||
if (pdev->pme_poll)
|
||||
*pme_poll = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcie_port_runtime_idle(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
bool pme_poll = false;
|
||||
|
||||
/*
|
||||
* If any subordinate device needs pme poll, we should keep
|
||||
* the port in D0, because we need port in D0 to poll it.
|
||||
*/
|
||||
pci_walk_bus(pdev->subordinate, pci_dev_pme_poll, &pme_poll);
|
||||
/* Delay for a short while to prevent too frequent suspend/resume */
|
||||
if (!pme_poll)
|
||||
pm_schedule_suspend(dev, 10);
|
||||
return -EBUSY;
|
||||
}
|
||||
#else
|
||||
#define pcie_port_runtime_suspend NULL
|
||||
#define pcie_port_runtime_resume NULL
|
||||
#define pcie_port_runtime_idle NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops pcie_portdrv_pm_ops = {
|
||||
.suspend = pcie_port_device_suspend,
|
||||
.resume = pcie_port_device_resume,
|
||||
|
@ -172,9 +101,6 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
|
|||
.poweroff = pcie_port_device_suspend,
|
||||
.restore = pcie_port_device_resume,
|
||||
.resume_noirq = pcie_port_resume_noirq,
|
||||
.runtime_suspend = pcie_port_runtime_suspend,
|
||||
.runtime_resume = pcie_port_runtime_resume,
|
||||
.runtime_idle = pcie_port_runtime_idle,
|
||||
};
|
||||
|
||||
#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpumask.h>
|
||||
|
@ -485,7 +486,7 @@ void pci_read_bridge_bases(struct pci_bus *child)
|
|||
}
|
||||
}
|
||||
|
||||
static struct pci_bus *pci_alloc_bus(void)
|
||||
static struct pci_bus *pci_alloc_bus(struct pci_bus *parent)
|
||||
{
|
||||
struct pci_bus *b;
|
||||
|
||||
|
@ -500,6 +501,10 @@ static struct pci_bus *pci_alloc_bus(void)
|
|||
INIT_LIST_HEAD(&b->resources);
|
||||
b->max_bus_speed = PCI_SPEED_UNKNOWN;
|
||||
b->cur_bus_speed = PCI_SPEED_UNKNOWN;
|
||||
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||
if (parent)
|
||||
b->domain_nr = parent->domain_nr;
|
||||
#endif
|
||||
return b;
|
||||
}
|
||||
|
||||
|
@ -671,7 +676,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
|
|||
/*
|
||||
* Allocate a new bus, and inherit stuff from the parent..
|
||||
*/
|
||||
child = pci_alloc_bus();
|
||||
child = pci_alloc_bus(parent);
|
||||
if (!child)
|
||||
return NULL;
|
||||
|
||||
|
@ -740,6 +745,17 @@ struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev,
|
|||
}
|
||||
EXPORT_SYMBOL(pci_add_new_bus);
|
||||
|
||||
static void pci_enable_crs(struct pci_dev *pdev)
|
||||
{
|
||||
u16 root_cap = 0;
|
||||
|
||||
/* Enable CRS Software Visibility if supported */
|
||||
pcie_capability_read_word(pdev, PCI_EXP_RTCAP, &root_cap);
|
||||
if (root_cap & PCI_EXP_RTCAP_CRSVIS)
|
||||
pcie_capability_set_word(pdev, PCI_EXP_RTCTL,
|
||||
PCI_EXP_RTCTL_CRSSVE);
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's a bridge, configure it and scan the bus behind it.
|
||||
* For CardBus bridges, we don't scan behind as the devices will
|
||||
|
@ -787,6 +803,8 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
|
|||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
|
||||
bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
|
||||
|
||||
pci_enable_crs(dev);
|
||||
|
||||
if ((secondary || subordinate) && !pcibios_assign_all_busses() &&
|
||||
!is_cardbus && !broken) {
|
||||
unsigned int cmax;
|
||||
|
@ -1226,6 +1244,137 @@ int pci_setup_device(struct pci_dev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct hpp_type0 pci_default_type0 = {
|
||||
.revision = 1,
|
||||
.cache_line_size = 8,
|
||||
.latency_timer = 0x40,
|
||||
.enable_serr = 0,
|
||||
.enable_perr = 0,
|
||||
};
|
||||
|
||||
static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
|
||||
{
|
||||
u16 pci_cmd, pci_bctl;
|
||||
|
||||
if (!hpp)
|
||||
hpp = &pci_default_type0;
|
||||
|
||||
if (hpp->revision > 1) {
|
||||
dev_warn(&dev->dev,
|
||||
"PCI settings rev %d not supported; using defaults\n",
|
||||
hpp->revision);
|
||||
hpp = &pci_default_type0;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
|
||||
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
|
||||
if (hpp->enable_serr)
|
||||
pci_cmd |= PCI_COMMAND_SERR;
|
||||
if (hpp->enable_perr)
|
||||
pci_cmd |= PCI_COMMAND_PARITY;
|
||||
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
|
||||
|
||||
/* Program bridge control value */
|
||||
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
||||
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
|
||||
hpp->latency_timer);
|
||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
|
||||
if (hpp->enable_serr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_SERR;
|
||||
if (hpp->enable_perr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
|
||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
|
||||
}
|
||||
}
|
||||
|
||||
static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
|
||||
{
|
||||
if (hpp)
|
||||
dev_warn(&dev->dev, "PCI-X settings not supported\n");
|
||||
}
|
||||
|
||||
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
|
||||
{
|
||||
int pos;
|
||||
u32 reg32;
|
||||
|
||||
if (!hpp)
|
||||
return;
|
||||
|
||||
if (hpp->revision > 1) {
|
||||
dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
|
||||
hpp->revision);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow _HPX to change MPS or MRRS settings. We manage
|
||||
* those to make sure they're consistent with the rest of the
|
||||
* platform.
|
||||
*/
|
||||
hpp->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
|
||||
PCI_EXP_DEVCTL_READRQ;
|
||||
hpp->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
|
||||
PCI_EXP_DEVCTL_READRQ);
|
||||
|
||||
/* Initialize Device Control Register */
|
||||
pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
|
||||
~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
|
||||
|
||||
/* Initialize Link Control Register */
|
||||
if (dev->subordinate)
|
||||
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
|
||||
~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
|
||||
|
||||
/* Find Advanced Error Reporting Enhanced Capability */
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/* Initialize Uncorrectable Error Mask Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®32);
|
||||
reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
|
||||
|
||||
/* Initialize Uncorrectable Error Severity Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®32);
|
||||
reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
|
||||
|
||||
/* Initialize Correctable Error Mask Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®32);
|
||||
reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
|
||||
|
||||
/* Initialize Advanced Error Capabilities and Control Register */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
|
||||
reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
|
||||
|
||||
/*
|
||||
* FIXME: The following two registers are not supported yet.
|
||||
*
|
||||
* o Secondary Uncorrectable Error Severity Register
|
||||
* o Secondary Uncorrectable Error Mask Register
|
||||
*/
|
||||
}
|
||||
|
||||
static void pci_configure_device(struct pci_dev *dev)
|
||||
{
|
||||
struct hotplug_params hpp;
|
||||
int ret;
|
||||
|
||||
memset(&hpp, 0, sizeof(hpp));
|
||||
ret = pci_get_hp_params(dev, &hpp);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
program_hpp_type2(dev, hpp.t2);
|
||||
program_hpp_type1(dev, hpp.t1);
|
||||
program_hpp_type0(dev, hpp.t0);
|
||||
}
|
||||
|
||||
static void pci_release_capabilities(struct pci_dev *dev)
|
||||
{
|
||||
pci_vpd_release(dev);
|
||||
|
@ -1282,8 +1431,13 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
|
|||
*l == 0x0000ffff || *l == 0xffff0000)
|
||||
return false;
|
||||
|
||||
/* Configuration request Retry Status */
|
||||
while (*l == 0xffff0001) {
|
||||
/*
|
||||
* Configuration Request Retry Status. Some root ports return the
|
||||
* actual device ID instead of the synthetic ID (0xFFFF) required
|
||||
* by the PCIe spec. Ignore the device ID and only check for
|
||||
* (vendor id == 1).
|
||||
*/
|
||||
while ((*l & 0xffff) == 0x0001) {
|
||||
if (!crs_timeout)
|
||||
return false;
|
||||
|
||||
|
@ -1363,6 +1517,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
|
|||
{
|
||||
int ret;
|
||||
|
||||
pci_configure_device(dev);
|
||||
|
||||
device_initialize(&dev->dev);
|
||||
dev->dev.release = pci_release_dev;
|
||||
|
||||
|
@ -1751,13 +1907,14 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
|
|||
char bus_addr[64];
|
||||
char *fmt;
|
||||
|
||||
b = pci_alloc_bus();
|
||||
b = pci_alloc_bus(NULL);
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
b->sysdata = sysdata;
|
||||
b->ops = ops;
|
||||
b->number = b->busn_res.start = bus;
|
||||
pci_bus_assign_domain_nr(b, parent);
|
||||
b2 = pci_find_bus(pci_domain_nr(b), bus);
|
||||
if (b2) {
|
||||
/* If we already got to this bus through a different bridge, ignore it */
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/dma.h> /* isa_dma_bridge_buggy */
|
||||
#include "pci.h"
|
||||
|
||||
|
@ -287,6 +288,25 @@ static void quirk_citrine(struct pci_dev *dev)
|
|||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, quirk_citrine);
|
||||
|
||||
/* On IBM Crocodile ipr SAS adapters, expand BAR to system page size */
|
||||
static void quirk_extend_bar_to_page(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_STD_RESOURCE_END; i++) {
|
||||
struct resource *r = &dev->resource[i];
|
||||
|
||||
if (r->flags & IORESOURCE_MEM && resource_size(r) < PAGE_SIZE) {
|
||||
r->end = PAGE_SIZE - 1;
|
||||
r->start = 0;
|
||||
r->flags |= IORESOURCE_UNSET;
|
||||
dev_info(&dev->dev, "expanded BAR %d to page size: %pR\n",
|
||||
i, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, 0x034a, quirk_extend_bar_to_page);
|
||||
|
||||
/*
|
||||
* S3 868 and 968 chips report region size equal to 32M, but they decode 64M.
|
||||
* If it's needed, re-allocate the region.
|
||||
|
@ -2985,6 +3005,8 @@ DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
|
|||
*/
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169,
|
||||
quirk_broken_intx_masking);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
|
||||
quirk_broken_intx_masking);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/*
|
||||
|
@ -3512,57 +3534,6 @@ DECLARE_PCI_FIXUP_HEADER(0x1283, 0x8892, quirk_use_pcie_bridge_dma_alias);
|
|||
/* Intel 82801, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c49 */
|
||||
DECLARE_PCI_FIXUP_HEADER(0x8086, 0x244e, quirk_use_pcie_bridge_dma_alias);
|
||||
|
||||
static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
|
||||
{
|
||||
if (!PCI_FUNC(dev->devfn))
|
||||
return pci_dev_get(dev);
|
||||
|
||||
return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
|
||||
}
|
||||
|
||||
static const struct pci_dev_dma_source {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
struct pci_dev *(*dma_source)(struct pci_dev *dev);
|
||||
} pci_dev_dma_source[] = {
|
||||
/*
|
||||
* https://bugzilla.redhat.com/show_bug.cgi?id=605888
|
||||
*
|
||||
* Some Ricoh devices use the function 0 source ID for DMA on
|
||||
* other functions of a multifunction device. The DMA devices
|
||||
* is therefore function 0, which will have implications of the
|
||||
* iommu grouping of these devices.
|
||||
*/
|
||||
{ PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source },
|
||||
{ PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source },
|
||||
{ PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source },
|
||||
{ PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* IOMMUs with isolation capabilities need to be programmed with the
|
||||
* correct source ID of a device. In most cases, the source ID matches
|
||||
* the device doing the DMA, but sometimes hardware is broken and will
|
||||
* tag the DMA as being sourced from a different device. This function
|
||||
* allows that translation. Note that the reference count of the
|
||||
* returned device is incremented on all paths.
|
||||
*/
|
||||
struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
|
||||
{
|
||||
const struct pci_dev_dma_source *i;
|
||||
|
||||
for (i = pci_dev_dma_source; i->dma_source; i++) {
|
||||
if ((i->vendor == dev->vendor ||
|
||||
i->vendor == (u16)PCI_ANY_ID) &&
|
||||
(i->device == dev->device ||
|
||||
i->device == (u16)PCI_ANY_ID))
|
||||
return i->dma_source(dev);
|
||||
}
|
||||
|
||||
return pci_dev_get(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* AMD has indicated that the devices below do not support peer-to-peer
|
||||
* in any system where they are found in the southbridge with an AMD
|
||||
|
@ -3582,6 +3553,11 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
|
|||
* 1002:439d SB7x0/SB8x0/SB9x0 LPC host controller
|
||||
* 1002:4384 SBx00 PCI to PCI Bridge
|
||||
* 1002:4399 SB7x0/SB8x0/SB9x0 USB OHCI2 Controller
|
||||
*
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=81841#c15
|
||||
*
|
||||
* 1022:780f [AMD] FCH PCI Bridge
|
||||
* 1022:7809 [AMD] FCH USB OHCI Controller
|
||||
*/
|
||||
static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
|
||||
{
|
||||
|
@ -3664,6 +3640,23 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
|
|||
return acs_flags & ~flags ? 0 : 1;
|
||||
}
|
||||
|
||||
static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
|
||||
{
|
||||
/*
|
||||
* SV, TB, and UF are not relevant to multifunction endpoints.
|
||||
*
|
||||
* Multifunction devices are only required to implement RR, CR, and DT
|
||||
* in their ACS capability if they support peer-to-peer transactions.
|
||||
* Devices matching this quirk have been verified by the vendor to not
|
||||
* perform peer-to-peer with other functions, allowing us to mask out
|
||||
* these bits as if they were unimplemented in the ACS capability.
|
||||
*/
|
||||
acs_flags &= ~(PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR |
|
||||
PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT);
|
||||
|
||||
return acs_flags ? 0 : 1;
|
||||
}
|
||||
|
||||
static const struct pci_dev_acs_enabled {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
|
@ -3675,6 +3668,30 @@ static const struct pci_dev_acs_enabled {
|
|||
{ PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs },
|
||||
{ PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs },
|
||||
{ PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs },
|
||||
{ PCI_VENDOR_ID_AMD, 0x780f, pci_quirk_amd_sb_acs },
|
||||
{ PCI_VENDOR_ID_AMD, 0x7809, pci_quirk_amd_sb_acs },
|
||||
{ PCI_VENDOR_ID_SOLARFLARE, 0x0903, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_SOLARFLARE, 0x0923, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10C6, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10DB, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10DD, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10E1, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10F1, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10F7, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10F8, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10F9, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10FA, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10FB, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x10FC, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x1507, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x1514, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x151C, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x1529, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x152A, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x154D, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x154F, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x1551, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, 0x1558, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
|
||||
{ 0 }
|
||||
};
|
||||
|
|
|
@ -103,40 +103,6 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* find the upstream PCIe-to-PCI bridge of a PCI device
|
||||
* if the device is PCIE, return NULL
|
||||
* if the device isn't connected to a PCIe bridge (that is its parent is a
|
||||
* legacy PCI bridge and the bridge is directly connected to bus 0), return its
|
||||
* parent
|
||||
*/
|
||||
struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_dev *tmp = NULL;
|
||||
|
||||
if (pci_is_pcie(pdev))
|
||||
return NULL;
|
||||
while (1) {
|
||||
if (pci_is_root_bus(pdev->bus))
|
||||
break;
|
||||
pdev = pdev->bus->self;
|
||||
/* a p2p bridge */
|
||||
if (!pci_is_pcie(pdev)) {
|
||||
tmp = pdev;
|
||||
continue;
|
||||
}
|
||||
/* PCI device should connect to a PCIe bridge */
|
||||
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {
|
||||
/* Busted hardware? */
|
||||
WARN_ON_ONCE(1);
|
||||
return NULL;
|
||||
}
|
||||
return pdev;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
|
||||
{
|
||||
struct pci_bus *child;
|
||||
|
|
|
@ -1652,7 +1652,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
|
|||
struct pci_dev_resource *fail_res;
|
||||
int retval;
|
||||
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
|
||||
IORESOURCE_PREFETCH;
|
||||
IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
|
||||
|
||||
again:
|
||||
__pci_bus_size_bridges(parent, &add_list);
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */
|
||||
|
||||
#define PCI_VENDOR_ID_VMWARE 0x15AD
|
||||
#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0
|
||||
|
||||
/*
|
||||
|
|
|
@ -727,7 +727,7 @@ static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm)
|
|||
p_setd(perm, 0, ALL_VIRT, NO_WRITE);
|
||||
|
||||
/* Writable bits mask */
|
||||
mask = PCI_ERR_UNC_TRAIN | /* Training */
|
||||
mask = PCI_ERR_UNC_UND | /* Undefined */
|
||||
PCI_ERR_UNC_DLP | /* Data Link Protocol */
|
||||
PCI_ERR_UNC_SURPDN | /* Surprise Down */
|
||||
PCI_ERR_UNC_POISON_TLP | /* Poisoned TLP */
|
||||
|
|
|
@ -133,7 +133,7 @@ static void pcistub_device_release(struct kref *kref)
|
|||
xen_pcibk_config_free_dyn_fields(dev);
|
||||
xen_pcibk_config_free_dev(dev);
|
||||
|
||||
dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
|
||||
pci_clear_dev_assigned(dev);
|
||||
pci_dev_put(dev);
|
||||
|
||||
kfree(psdev);
|
||||
|
@ -413,7 +413,7 @@ static int pcistub_init_device(struct pci_dev *dev)
|
|||
dev_dbg(&dev->dev, "reset device\n");
|
||||
xen_pcibk_reset_device(dev);
|
||||
|
||||
dev->dev_flags |= PCI_DEV_FLAGS_ASSIGNED;
|
||||
pci_set_dev_assigned(dev);
|
||||
return 0;
|
||||
|
||||
config_release:
|
||||
|
|
|
@ -331,7 +331,7 @@ static inline void iounmap(void __iomem *addr)
|
|||
#ifndef CONFIG_GENERIC_IOMAP
|
||||
static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
|
||||
{
|
||||
return (void __iomem *) port;
|
||||
return PCI_IOBASE + (port & IO_SPACE_LIMIT);
|
||||
}
|
||||
|
||||
static inline void ioport_unmap(void __iomem *p)
|
||||
|
|
|
@ -249,6 +249,10 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
|
|||
#define pgprot_writecombine pgprot_noncached
|
||||
#endif
|
||||
|
||||
#ifndef pgprot_device
|
||||
#define pgprot_device pgprot_noncached
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When walking page tables, get the address of the next boundary,
|
||||
* or the end address of the range if that comes earlier. Although no
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#ifndef _AER_H_
|
||||
#define _AER_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define AER_NONFATAL 0
|
||||
#define AER_FATAL 1
|
||||
#define AER_CORRECTABLE 2
|
||||
|
|
|
@ -215,6 +215,11 @@ static inline int __deprecated check_region(resource_size_t s,
|
|||
|
||||
/* Wrappers for managed devices */
|
||||
struct device;
|
||||
|
||||
extern int devm_request_resource(struct device *dev, struct resource *root,
|
||||
struct resource *new);
|
||||
extern void devm_release_resource(struct device *dev, struct resource *new);
|
||||
|
||||
#define devm_request_region(dev,start,n,name) \
|
||||
__devm_request_region(dev, &ioport_resource, (start), (n), (name))
|
||||
#define devm_request_mem_region(dev,start,n,name) \
|
||||
|
|
|
@ -29,7 +29,6 @@ struct msi_desc {
|
|||
__u8 multi_cap : 3; /* log2 num of messages supported */
|
||||
__u8 maskbit : 1; /* mask-pending bit supported ? */
|
||||
__u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */
|
||||
__u8 pos; /* Location of the msi capability */
|
||||
__u16 entry_nr; /* specific enabled entry */
|
||||
unsigned default_irq; /* default pre-assigned irq */
|
||||
} msi_attrib;
|
||||
|
@ -47,8 +46,6 @@ struct msi_desc {
|
|||
|
||||
/* Last set MSI message */
|
||||
struct msi_msg msg;
|
||||
|
||||
struct kobject kobj;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -60,7 +57,6 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc);
|
|||
void arch_teardown_msi_irq(unsigned int irq);
|
||||
int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
|
||||
void arch_teardown_msi_irqs(struct pci_dev *dev);
|
||||
int arch_msi_check_device(struct pci_dev* dev, int nvec, int type);
|
||||
void arch_restore_msi_irqs(struct pci_dev *dev);
|
||||
|
||||
void default_teardown_msi_irqs(struct pci_dev *dev);
|
||||
|
@ -77,8 +73,6 @@ struct msi_chip {
|
|||
int (*setup_irq)(struct msi_chip *chip, struct pci_dev *dev,
|
||||
struct msi_desc *desc);
|
||||
void (*teardown_irq)(struct msi_chip *chip, unsigned int irq);
|
||||
int (*check_device)(struct msi_chip *chip, struct pci_dev *dev,
|
||||
int nvec, int type);
|
||||
};
|
||||
|
||||
#endif /* LINUX_MSI_H */
|
||||
|
|
|
@ -23,17 +23,6 @@ struct of_pci_range {
|
|||
#define for_each_of_pci_range(parser, range) \
|
||||
for (; of_pci_range_parser_one(parser, range);)
|
||||
|
||||
static inline void of_pci_range_to_resource(struct of_pci_range *range,
|
||||
struct device_node *np,
|
||||
struct resource *res)
|
||||
{
|
||||
res->flags = range->flags;
|
||||
res->start = range->cpu_addr;
|
||||
res->end = range->cpu_addr + range->size - 1;
|
||||
res->parent = res->child = res->sibling = NULL;
|
||||
res->name = np->full_name;
|
||||
}
|
||||
|
||||
/* Translate a DMA address from device space to CPU space */
|
||||
extern u64 of_translate_dma_address(struct device_node *dev,
|
||||
const __be32 *in_addr);
|
||||
|
@ -55,7 +44,9 @@ extern void __iomem *of_iomap(struct device_node *device, int index);
|
|||
extern const __be32 *of_get_address(struct device_node *dev, int index,
|
||||
u64 *size, unsigned int *flags);
|
||||
|
||||
extern int pci_register_io_range(phys_addr_t addr, resource_size_t size);
|
||||
extern unsigned long pci_address_to_pio(phys_addr_t addr);
|
||||
extern phys_addr_t pci_pio_to_address(unsigned long pio);
|
||||
|
||||
extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
||||
struct device_node *node);
|
||||
|
@ -80,6 +71,11 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline phys_addr_t pci_pio_to_address(unsigned long pio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
||||
struct device_node *node)
|
||||
{
|
||||
|
@ -138,6 +134,9 @@ extern const __be32 *of_get_pci_address(struct device_node *dev, int bar_no,
|
|||
u64 *size, unsigned int *flags);
|
||||
extern int of_pci_address_to_resource(struct device_node *dev, int bar,
|
||||
struct resource *r);
|
||||
extern int of_pci_range_to_resource(struct of_pci_range *range,
|
||||
struct device_node *np,
|
||||
struct resource *res);
|
||||
#else /* CONFIG_OF_ADDRESS && CONFIG_PCI */
|
||||
static inline int of_pci_address_to_resource(struct device_node *dev, int bar,
|
||||
struct resource *r)
|
||||
|
@ -150,6 +149,12 @@ static inline const __be32 *of_get_pci_address(struct device_node *dev,
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline int of_pci_range_to_resource(struct of_pci_range *range,
|
||||
struct device_node *np,
|
||||
struct resource *res)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */
|
||||
|
||||
#endif /* __OF_ADDRESS_H */
|
||||
|
|
|
@ -15,6 +15,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,
|
|||
int of_pci_get_devfn(struct device_node *np);
|
||||
int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
|
||||
int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
|
||||
int of_get_pci_domain_nr(struct device_node *node);
|
||||
#else
|
||||
static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
|
||||
{
|
||||
|
@ -43,6 +44,18 @@ of_pci_parse_bus_range(struct device_node *node, struct resource *res)
|
|||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
of_get_pci_domain_nr(struct device_node *node)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_ADDRESS)
|
||||
int of_pci_get_host_bridge_resources(struct device_node *dev,
|
||||
unsigned char busno, unsigned char bus_max,
|
||||
struct list_head *resources, resource_size_t *io_base);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue