1
0
Files
kernel-49/arch/powerpc/kernel/pci_dn.c
Greg Kroah-Hartman 6c5ed2a7d7 Merge 4.9.331 into android-4.9-q
Changes in 4.9.331
	uas: add no-uas quirk for Hiksemi usb_disk
	usb-storage: Add Hiksemi USB3-FW to IGNORE_UAS
	uas: ignore UAS for Thinkplus chips
	net: usb: qmi_wwan: Add new usb-id for Dell branded EM7455
	ntfs: fix BUG_ON in ntfs_lookup_inode_by_name()
	mmc: moxart: fix 4-bit bus width and remove 8-bit bus width
	mm: prevent page_frag_alloc() from corrupting the memory
	Revert "drm: bridge: analogix/dp: add panel prepare/unprepare in suspend/resume time"
	Input: melfas_mip4 - fix return value check in mip4_probe()
	usbnet: Fix memory leak in usbnet_disconnect()
	nvme: add new line after variable declatation
	nvme: Fix IOC_PR_CLEAR and IOC_PR_RELEASE ioctls for nvme devices
	selftests: Fix the if conditions of in test_extra_filter()
	clk: iproc: Minor tidy up of iproc pll data structures
	clk: iproc: Do not rely on node name for correct PLL setup
	Makefile.extrawarn: Move -Wcast-function-type-strict to W=1
	ARM: fix function graph tracer and unwinder dependencies
	fs: fix UAF/GPF bug in nilfs_mdt_destroy
	dmaengine: xilinx_dma: cleanup for fetching xlnx,num-fstores property
	dmaengine: xilinx_dma: Report error in case of dma_set_mask_and_coherent API failure
	ARM: dts: fix Moxa SDIO 'compatible', remove 'sdhci' misnomer
	net/ieee802154: fix uninit value bug in dgram_sendmsg
	um: Cleanup syscall_handler_t cast in syscalls_32.h
	um: Cleanup compiler warning in arch/x86/um/tls_32.c
	usb: mon: make mmapped memory read only
	USB: serial: ftdi_sio: fix 300 bps rate for SIO
	nilfs2: fix NULL pointer dereference at nilfs_bmap_lookup_at_level()
	nilfs2: fix leak of nilfs_root in case of writer thread creation failure
	nilfs2: replace WARN_ONs by nilfs_error for checkpoint acquisition failure
	ceph: don't truncate file in atomic_open
	random: clamp credited irq bits to maximum mixed
	ALSA: hda: Fix position reporting on Poulsbo
	scsi: stex: Properly zero out the passthrough command structure
	USB: serial: qcserial: add new usb-id for Dell branded EM7455
	random: avoid reading two cache lines on irq randomness
	wifi: mac80211_hwsim: avoid mac80211 warning on bad rate
	random: restore O_NONBLOCK support
	Input: xpad - add supported devices as contributed on github
	Input: xpad - fix wireless 360 controller breaking after suspend
	random: use expired timer rather than wq for mixing fast pool
	ALSA: oss: Fix potential deadlock at unregistration
	ALSA: rawmidi: Drop register_mutex in snd_rawmidi_free()
	ALSA: usb-audio: Fix potential memory leaks
	ALSA: usb-audio: Fix NULL dererence at error path
	iio: dac: ad5593r: Fix i2c read protocol requirements
	fs: dlm: fix race between test_bit() and queue_work()
	fs: dlm: handle -EBUSY first in lock arg validation
	quota: Check next/prev free block number after reading from quota file
	regulator: qcom_rpm: Fix circular deferral regression
	parisc: fbdev/stifb: Align graphics memory size to 4MB
	UM: cpuinfo: Fix a warning for CONFIG_CPUMASK_OFFSTACK
	PCI: Sanitise firmware BAR assignments behind a PCI-PCI bridge
	fbdev: smscufx: Fix use-after-free in ufx_ops_open()
	nilfs2: fix use-after-free bug of struct nilfs_root
	ext4: avoid crash when inline data creation follows DIO write
	ext4: fix null-ptr-deref in ext4_write_info
	ext4: make ext4_lazyinit_thread freezable
	ext4: place buffer head allocation before handle start
	ring-buffer: Allow splice to read previous partially read pages
	ring-buffer: Check pending waiters when doing wake ups as well
	ring-buffer: Fix race between reset page and reading page
	KVM: x86/emulator: Fix handing of POP SS to correctly set interruptibility
	selinux: use "grep -E" instead of "egrep"
	sh: machvec: Use char[] for section boundaries
	wifi: ath10k: add peer map clean up for peer delete in ath10k_sta_state()
	wifi: mac80211: allow bw change during channel switch in mesh
	wifi: rtl8xxxu: tighten bounds checking in rtl8xxxu_read_efuse()
	spi: qup: add missing clk_disable_unprepare on error in spi_qup_resume()
	spi: qup: add missing clk_disable_unprepare on error in spi_qup_pm_resume_runtime()
	wifi: rtl8xxxu: gen2: Fix mistake in path B IQ calibration
	net: fs_enet: Fix wrong check in do_pd_setup
	spi/omap100k:Fix PM disable depth imbalance in omap1_spi100k_probe
	mISDN: fix use-after-free bugs in l1oip timer handlers
	tcp: fix tcp_cwnd_validate() to not forget is_cwnd_limited
	net: rds: don't hold sock lock when cancelling work from rds_tcp_reset_callbacks()
	bnx2x: fix potential memory leak in bnx2x_tpa_stop()
	drm/mipi-dsi: Detach devices when removing the host
	platform/x86: msi-laptop: Fix old-ec check for backlight registering
	mmc: au1xmmc: Fix an error handling path in au1xmmc_probe()
	ASoC: eureka-tlv320: Hold reference returned from of_find_xxx API
	ALSA: dmaengine: increment buffer pointer atomically
	memory: of: Fix refcount leak bug in of_get_ddr_timings()
	soc: qcom: smsm: Fix refcount leak bugs in qcom_smsm_probe()
	soc: qcom: smem_state: Add refcounting for the 'state->of_node'
	ARM: dts: kirkwood: lsxl: fix serial line
	ARM: dts: kirkwood: lsxl: remove first ethernet port
	ARM: Drop CMDLINE_* dependency on ATAGS
	ARM: dts: exynos: fix polarity of VBUS GPIO of Origen
	iio: adc: at91-sama5d2_adc: fix AT91_SAMA5D2_MR_TRACKTIM_MAX
	iio: inkern: only release the device node when done with it
	iio: ABI: Fix wrong format of differential capacitance channel ABI.
	clk: tegra: Fix refcount leak in tegra210_clock_init
	clk: tegra: Fix refcount leak in tegra114_clock_init
	clk: tegra20: Fix refcount leak in tegra20_clock_init
	HSI: omap_ssi: Fix refcount leak in ssi_probe
	HSI: omap_ssi_port: Fix dma_map_sg error check
	media: exynos4-is: fimc-is: Add of_node_put() when breaking out of loop
	tty: xilinx_uartps: Fix the ignore_status
	media: xilinx: vipp: Fix refcount leak in xvip_graph_dma_init
	RDMA/rxe: Fix "kernel NULL pointer dereference" error
	RDMA/rxe: Fix the error caused by qp->sk
	dyndbg: fix module.dyndbg handling
	dyndbg: let query-modname override actual module name
	ata: fix ata_id_sense_reporting_enabled() and ata_id_has_sense_reporting()
	ata: fix ata_id_has_devslp()
	ata: fix ata_id_has_ncq_autosense()
	ata: fix ata_id_has_dipm()
	drivers: serial: jsm: fix some leaks in probe
	firmware: google: Test spinlock on panic path to avoid lockups
	serial: 8250: Fix restoring termios speed after suspend
	mfd: intel_soc_pmic: Fix an error handling path in intel_soc_pmic_i2c_probe()
	mfd: lp8788: Fix an error handling path in lp8788_probe()
	mfd: lp8788: Fix an error handling path in lp8788_irq_init() and lp8788_irq_init()
	mfd: sm501: Add check for platform_driver_register()
	dmaengine: ioat: stop mod_timer from resurrecting deleted timer in __cleanup()
	clk: bcm2835: fix bcm2835_clock_rate_from_divisor declaration
	clk: ti: dra7-atl: Fix reference leak in of_dra7_atl_clk_probe
	powerpc/math_emu/efp: Include module.h
	powerpc/pci_dn: Add missing of_node_put()
	powerpc: Fix SPE Power ISA properties for e500v1 platforms
	iommu/omap: Fix buffer overflow in debugfs
	f2fs: fix race condition on setting FI_NO_EXTENT flag
	ACPI: video: Add Toshiba Satellite/Portege Z830 quirk
	MIPS: BCM47XX: Cast memcmp() of function to (void *)
	powercap: intel_rapl: fix UBSAN shift-out-of-bounds issue
	thermal: intel_powerclamp: Use get_cpu() instead of smp_processor_id() to avoid crash
	openvswitch: Fix double reporting of drops in dropwatch
	openvswitch: Fix overreporting of drops in dropwatch
	tcp: annotate data-race around tcp_md5sig_pool_populated
	xfrm: Update ipcomp_scratches with NULL when freed
	Bluetooth: L2CAP: initialize delayed works at l2cap_chan_create()
	Bluetooth: hci_sysfs: Fix attempting to call device_add multiple times
	can: bcm: check the result of can_send() in bcm_can_tx()
	wifi: rt2x00: don't run Rt5592 IQ calibration on MT7620
	Bluetooth: L2CAP: Fix user-after-free
	r8152: Rate limit overflow messages
	drm: Use size_t type for len variable in drm_copy_field()
	drm: Prevent drm_copy_field() to attempt copying a NULL pointer
	platform/x86: msi-laptop: Change DMI match / alias strings to fix module autoloading
	drm/amdgpu: fix initial connector audio value
	ARM: dts: imx7d-sdb: config the max pressure for tsc2046
	ARM: dts: imx6q: add missing properties for sram
	ARM: dts: imx6dl: add missing properties for sram
	ARM: dts: imx6qp: add missing properties for sram
	ARM: dts: imx6sl: add missing properties for sram
	media: cx88: Fix a null-ptr-deref bug in buffer_prepare()
	scsi: 3w-9xxx: Avoid disabling device if failing to enable it
	HID: roccat: Fix use-after-free in roccat_read()
	usb: host: xhci: Fix potential memory leak in xhci_alloc_stream_info()
	usb: musb: Fix musb_gadget.c rxstate overflow bug
	Revert "usb: storage: Add quirk for Samsung Fit flash"
	usb: idmouse: fix an uninit-value in idmouse_open
	perf intel-pt: Fix segfault in intel_pt_print_info() with uClibc
	net: ieee802154: return -EINVAL for unknown addr type
	net/ieee802154: don't warn zero-sized raw_sendmsg()
	ext4: continue to expand file system when the target size doesn't reach
	inet: fully convert sk->sk_rx_dst to RCU rules
	thermal: intel_powerclamp: Use first online CPU as control_cpu
	gcov: support GCC 12.1 and newer compilers
	Linux 4.9.331

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I105d6215a29d200abe3330f328ce3c2009ba0df9
2022-10-26 16:33:33 +03:00

564 lines
13 KiB
C

/*
* pci_dn.c
*
* Copyright (C) 2001 Todd Inglett, IBM Corporation
*
* PCI manipulation via device_nodes.
*
* 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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/gfp.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
#include <asm/firmware.h>
#include <asm/eeh.h>
/*
* The function is used to find the firmware data of one
* specific PCI device, which is attached to the indicated
* PCI bus. For VFs, their firmware data is linked to that
* one of PF's bridge. For other devices, their firmware
* data is linked to that of their bridge.
*/
static struct pci_dn *pci_bus_to_pdn(struct pci_bus *bus)
{
struct pci_bus *pbus;
struct device_node *dn;
struct pci_dn *pdn;
/*
* We probably have virtual bus which doesn't
* have associated bridge.
*/
pbus = bus;
while (pbus) {
if (pci_is_root_bus(pbus) || pbus->self)
break;
pbus = pbus->parent;
}
/*
* Except virtual bus, all PCI buses should
* have device nodes.
*/
dn = pci_bus_to_OF_node(pbus);
pdn = dn ? PCI_DN(dn) : NULL;
return pdn;
}
struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
int devfn)
{
struct device_node *dn = NULL;
struct pci_dn *parent, *pdn;
struct pci_dev *pdev = NULL;
/* Fast path: fetch from PCI device */
list_for_each_entry(pdev, &bus->devices, bus_list) {
if (pdev->devfn == devfn) {
if (pdev->dev.archdata.pci_data)
return pdev->dev.archdata.pci_data;
dn = pci_device_to_OF_node(pdev);
break;
}
}
/* Fast path: fetch from device node */
pdn = dn ? PCI_DN(dn) : NULL;
if (pdn)
return pdn;
/* Slow path: fetch from firmware data hierarchy */
parent = pci_bus_to_pdn(bus);
if (!parent)
return NULL;
list_for_each_entry(pdn, &parent->child_list, list) {
if (pdn->busno == bus->number &&
pdn->devfn == devfn)
return pdn;
}
return NULL;
}
struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
{
struct device_node *dn;
struct pci_dn *parent, *pdn;
/* Search device directly */
if (pdev->dev.archdata.pci_data)
return pdev->dev.archdata.pci_data;
/* Check device node */
dn = pci_device_to_OF_node(pdev);
pdn = dn ? PCI_DN(dn) : NULL;
if (pdn)
return pdn;
/*
* VFs don't have device nodes. We hook their
* firmware data to PF's bridge.
*/
parent = pci_bus_to_pdn(pdev->bus);
if (!parent)
return NULL;
list_for_each_entry(pdn, &parent->child_list, list) {
if (pdn->busno == pdev->bus->number &&
pdn->devfn == pdev->devfn)
return pdn;
}
return NULL;
}
#ifdef CONFIG_PCI_IOV
static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent,
struct pci_dev *pdev,
int vf_index,
int busno, int devfn)
{
struct pci_dn *pdn;
/* Except PHB, we always have the parent */
if (!parent)
return NULL;
pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
if (!pdn) {
dev_warn(&pdev->dev, "%s: Out of memory!\n", __func__);
return NULL;
}
pdn->phb = parent->phb;
pdn->parent = parent;
pdn->busno = busno;
pdn->devfn = devfn;
#ifdef CONFIG_PPC_POWERNV
pdn->vf_index = vf_index;
pdn->pe_number = IODA_INVALID_PE;
#endif
INIT_LIST_HEAD(&pdn->child_list);
INIT_LIST_HEAD(&pdn->list);
list_add_tail(&pdn->list, &parent->child_list);
/*
* If we already have PCI device instance, lets
* bind them.
*/
if (pdev)
pdev->dev.archdata.pci_data = pdn;
return pdn;
}
#endif
struct pci_dn *add_dev_pci_data(struct pci_dev *pdev)
{
#ifdef CONFIG_PCI_IOV
struct pci_dn *parent, *pdn;
int i;
/* Only support IOV for now */
if (!pdev->is_physfn)
return pci_get_pdn(pdev);
/* Check if VFs have been populated */
pdn = pci_get_pdn(pdev);
if (!pdn || (pdn->flags & PCI_DN_FLAG_IOV_VF))
return NULL;
pdn->flags |= PCI_DN_FLAG_IOV_VF;
parent = pci_bus_to_pdn(pdev->bus);
if (!parent)
return NULL;
for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) {
struct eeh_dev *edev __maybe_unused;
pdn = add_one_dev_pci_data(parent, NULL, i,
pci_iov_virtfn_bus(pdev, i),
pci_iov_virtfn_devfn(pdev, i));
if (!pdn) {
dev_warn(&pdev->dev, "%s: Cannot create firmware data for VF#%d\n",
__func__, i);
return NULL;
}
#ifdef CONFIG_EEH
/* Create the EEH device for the VF */
edev = eeh_dev_init(pdn);
BUG_ON(!edev);
edev->physfn = pdev;
#endif /* CONFIG_EEH */
}
#endif /* CONFIG_PCI_IOV */
return pci_get_pdn(pdev);
}
void remove_dev_pci_data(struct pci_dev *pdev)
{
#ifdef CONFIG_PCI_IOV
struct pci_dn *parent;
struct pci_dn *pdn, *tmp;
int i;
/*
* VF and VF PE are created/released dynamically, so we need to
* bind/unbind them. Otherwise the VF and VF PE would be mismatched
* when re-enabling SR-IOV.
*/
if (pdev->is_virtfn) {
pdn = pci_get_pdn(pdev);
#ifdef CONFIG_PPC_POWERNV
pdn->pe_number = IODA_INVALID_PE;
#endif
return;
}
/* Only support IOV PF for now */
if (!pdev->is_physfn)
return;
/* Check if VFs have been populated */
pdn = pci_get_pdn(pdev);
if (!pdn || !(pdn->flags & PCI_DN_FLAG_IOV_VF))
return;
pdn->flags &= ~PCI_DN_FLAG_IOV_VF;
parent = pci_bus_to_pdn(pdev->bus);
if (!parent)
return;
/*
* We might introduce flag to pci_dn in future
* so that we can release VF's firmware data in
* a batch mode.
*/
for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) {
struct eeh_dev *edev __maybe_unused;
list_for_each_entry_safe(pdn, tmp,
&parent->child_list, list) {
if (pdn->busno != pci_iov_virtfn_bus(pdev, i) ||
pdn->devfn != pci_iov_virtfn_devfn(pdev, i))
continue;
#ifdef CONFIG_EEH
/*
* Release EEH state for this VF. The PCI core
* has already torn down the pci_dev for this VF, but
* we're responsible to removing the eeh_dev since it
* has the same lifetime as the pci_dn that spawned it.
*/
edev = pdn_to_eeh_dev(pdn);
if (edev) {
/*
* We allocate pci_dn's for the totalvfs count,
* but only only the vfs that were activated
* have a configured PE.
*/
if (edev->pe)
eeh_rmv_from_parent_pe(edev);
pdn->edev = NULL;
kfree(edev);
}
#endif /* CONFIG_EEH */
if (!list_empty(&pdn->list))
list_del(&pdn->list);
kfree(pdn);
}
}
#endif /* CONFIG_PCI_IOV */
}
struct pci_dn *pci_add_device_node_info(struct pci_controller *hose,
struct device_node *dn)
{
const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);
const __be32 *regs;
struct device_node *parent;
struct pci_dn *pdn;
#ifdef CONFIG_EEH
struct eeh_dev *edev;
#endif
pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
if (pdn == NULL)
return NULL;
dn->data = pdn;
pdn->node = dn;
pdn->phb = hose;
#ifdef CONFIG_PPC_POWERNV
pdn->pe_number = IODA_INVALID_PE;
#endif
regs = of_get_property(dn, "reg", NULL);
if (regs) {
u32 addr = of_read_number(regs, 1);
/* First register entry is addr (00BBSS00) */
pdn->busno = (addr >> 16) & 0xff;
pdn->devfn = (addr >> 8) & 0xff;
}
/* vendor/device IDs and class code */
regs = of_get_property(dn, "vendor-id", NULL);
pdn->vendor_id = regs ? of_read_number(regs, 1) : 0;
regs = of_get_property(dn, "device-id", NULL);
pdn->device_id = regs ? of_read_number(regs, 1) : 0;
regs = of_get_property(dn, "class-code", NULL);
pdn->class_code = regs ? of_read_number(regs, 1) : 0;
/* Extended config space */
pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1);
/* Create EEH device */
#ifdef CONFIG_EEH
edev = eeh_dev_init(pdn);
if (!edev) {
kfree(pdn);
return NULL;
}
#endif
/* Attach to parent node */
INIT_LIST_HEAD(&pdn->child_list);
INIT_LIST_HEAD(&pdn->list);
parent = of_get_parent(dn);
pdn->parent = parent ? PCI_DN(parent) : NULL;
of_node_put(parent);
if (pdn->parent)
list_add_tail(&pdn->list, &pdn->parent->child_list);
return pdn;
}
EXPORT_SYMBOL_GPL(pci_add_device_node_info);
void pci_remove_device_node_info(struct device_node *dn)
{
struct pci_dn *pdn = dn ? PCI_DN(dn) : NULL;
#ifdef CONFIG_EEH
struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
if (edev)
edev->pdn = NULL;
#endif
if (!pdn)
return;
WARN_ON(!list_empty(&pdn->child_list));
list_del(&pdn->list);
if (pdn->parent)
of_node_put(pdn->parent->node);
dn->data = NULL;
kfree(pdn);
}
EXPORT_SYMBOL_GPL(pci_remove_device_node_info);
/*
* Traverse a device tree stopping each PCI device in the tree.
* This is done depth first. As each node is processed, a "pre"
* function is called and the children are processed recursively.
*
* The "pre" func returns a value. If non-zero is returned from
* the "pre" func, the traversal stops and this value is returned.
* This return value is useful when using traverse as a method of
* finding a device.
*
* NOTE: we do not run the func for devices that do not appear to
* be PCI except for the start node which we assume (this is good
* because the start node is often a phb which may be missing PCI
* properties).
* We use the class-code as an indicator. If we run into
* one of these nodes we also assume its siblings are non-pci for
* performance.
*/
void *pci_traverse_device_nodes(struct device_node *start,
void *(*fn)(struct device_node *, void *),
void *data)
{
struct device_node *dn, *nextdn;
void *ret;
/* We started with a phb, iterate all childs */
for (dn = start->child; dn; dn = nextdn) {
const __be32 *classp;
u32 class = 0;
nextdn = NULL;
classp = of_get_property(dn, "class-code", NULL);
if (classp)
class = of_read_number(classp, 1);
if (fn) {
ret = fn(dn, data);
if (ret)
return ret;
}
/* If we are a PCI bridge, go down */
if (dn->child && ((class >> 8) == PCI_CLASS_BRIDGE_PCI ||
(class >> 8) == PCI_CLASS_BRIDGE_CARDBUS))
/* Depth first...do children */
nextdn = dn->child;
else if (dn->sibling)
/* ok, try next sibling instead. */
nextdn = dn->sibling;
if (!nextdn) {
/* Walk up to next valid sibling. */
do {
dn = dn->parent;
if (dn == start)
return NULL;
} while (dn->sibling == NULL);
nextdn = dn->sibling;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(pci_traverse_device_nodes);
static struct pci_dn *pci_dn_next_one(struct pci_dn *root,
struct pci_dn *pdn)
{
struct list_head *next = pdn->child_list.next;
if (next != &pdn->child_list)
return list_entry(next, struct pci_dn, list);
while (1) {
if (pdn == root)
return NULL;
next = pdn->list.next;
if (next != &pdn->parent->child_list)
break;
pdn = pdn->parent;
}
return list_entry(next, struct pci_dn, list);
}
void *traverse_pci_dn(struct pci_dn *root,
void *(*fn)(struct pci_dn *, void *),
void *data)
{
struct pci_dn *pdn = root;
void *ret;
/* Only scan the child nodes */
for (pdn = pci_dn_next_one(root, pdn); pdn;
pdn = pci_dn_next_one(root, pdn)) {
ret = fn(pdn, data);
if (ret)
return ret;
}
return NULL;
}
static void *add_pdn(struct device_node *dn, void *data)
{
struct pci_controller *hose = data;
struct pci_dn *pdn;
pdn = pci_add_device_node_info(hose, dn);
if (!pdn)
return ERR_PTR(-ENOMEM);
return NULL;
}
/**
* pci_devs_phb_init_dynamic - setup pci devices under this PHB
* phb: pci-to-host bridge (top-level bridge connecting to cpu)
*
* This routine is called both during boot, (before the memory
* subsystem is set up, before kmalloc is valid) and during the
* dynamic lpar operation of adding a PHB to a running system.
*/
void pci_devs_phb_init_dynamic(struct pci_controller *phb)
{
struct device_node *dn = phb->dn;
struct pci_dn *pdn;
/* PHB nodes themselves must not match */
pdn = pci_add_device_node_info(phb, dn);
if (pdn) {
pdn->devfn = pdn->busno = -1;
pdn->vendor_id = pdn->device_id = pdn->class_code = 0;
pdn->phb = phb;
phb->pci_data = pdn;
}
/* Update dn->phb ptrs for new phb and children devices */
pci_traverse_device_nodes(dn, add_pdn, phb);
}
/**
* pci_devs_phb_init - Initialize phbs and pci devs under them.
*
* This routine walks over all phb's (pci-host bridges) on the
* system, and sets up assorted pci-related structures
* (including pci info in the device node structs) for each
* pci device found underneath. This routine runs once,
* early in the boot sequence.
*/
static int __init pci_devs_phb_init(void)
{
struct pci_controller *phb, *tmp;
/* This must be done first so the device nodes have valid pci info! */
list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
pci_devs_phb_init_dynamic(phb);
return 0;
}
core_initcall(pci_devs_phb_init);
static void pci_dev_pdn_setup(struct pci_dev *pdev)
{
struct pci_dn *pdn;
if (pdev->dev.archdata.pci_data)
return;
/* Setup the fast path */
pdn = pci_get_pdn(pdev);
pdev->dev.archdata.pci_data = pdn;
}
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, pci_dev_pdn_setup);