linux/arch/powerpc/platforms/pseries/pci.c

198 lines
4.8 KiB
C
Raw Normal View History

/*
* Copyright (C) 2001 Dave Engebretsen, IBM Corporation
* Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
*
* pSeries specific routines for PCI.
*
* 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/init.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <asm/eeh.h>
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#include <asm/ppc-pci.h>
#include "pseries.h"
#if 0
void pcibios_name_device(struct pci_dev *dev)
{
struct device_node *dn;
/*
* Add IBM loc code (slot) as a prefix to the device names for service
*/
dn = pci_device_to_OF_node(dev);
if (dn) {
const char *loc_code = of_get_property(dn, "ibm,loc-code",
NULL);
if (loc_code) {
int loc_len = strlen(loc_code);
if (loc_len < sizeof(dev->dev.name)) {
memmove(dev->dev.name+loc_len+1, dev->dev.name,
sizeof(dev->dev.name)-loc_len-1);
memcpy(dev->dev.name, loc_code, loc_len);
dev->dev.name[loc_len] = ' ';
dev->dev.name[sizeof(dev->dev.name)-1] = '\0';
}
}
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device);
#endif
#ifdef CONFIG_PCI_IOV
int pseries_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs)
{
/* Allocate PCI data */
add_dev_pci_data(pdev);
return 0;
}
int pseries_pcibios_sriov_disable(struct pci_dev *pdev)
{
/* Release PCI data */
remove_dev_pci_data(pdev);
return 0;
}
#endif
static void __init pSeries_request_regions(void)
{
if (!isa_io_base)
return;
request_region(0x20,0x20,"pic1");
request_region(0xa0,0x20,"pic2");
request_region(0x00,0x20,"dma1");
request_region(0x40,0x20,"timer");
request_region(0x80,0x10,"dma page reg");
request_region(0xc0,0x20,"dma2");
}
void __init pSeries_final_fixup(void)
{
pSeries_request_regions();
eeh_addr_cache_build();
#ifdef CONFIG_PCI_IOV
ppc_md.pcibios_sriov_enable = pseries_pcibios_sriov_enable;
ppc_md.pcibios_sriov_disable = pseries_pcibios_sriov_disable;
#endif
}
/*
* Assume the winbond 82c105 is the IDE controller on a
* p610/p615/p630. We should probably be more careful in case
* someone tries to plug in a similar adapter.
*/
static void fixup_winbond_82c105(struct pci_dev* dev)
{
int i;
unsigned int reg;
if (!machine_is(pseries))
return;
printk("Using INTC for W82c105 IDE controller.\n");
pci_read_config_dword(dev, 0x40, &reg);
/* Enable LEGIRQ to use INTC instead of ISA interrupts */
pci_write_config_dword(dev, 0x40, reg | (1<<11));
for (i = 0; i < DEVICE_COUNT_RESOURCE; ++i) {
/* zap the 2nd function of the winbond chip */
if (dev->resource[i].flags & IORESOURCE_IO
&& dev->bus->number == 0 && dev->devfn == 0x81)
dev->resource[i].flags &= ~IORESOURCE_IO;
[POWERPC] Mark winbond IDE PCI resources with start 0 as unassigned libata calls pci_request_regions to claim PCI BAR 0 - 5 pci_request_regions fails if one of the regions cant be claimed. bar 5 has start == 0, __request_resource will fail. Tested on a p630 in SMP mode with pata_sl82c105 00:03.1 IDE interface: Symphony Labs SL82c105 (rev 05) (prog-if 8f [Master SecP SecO PriP PriO]) Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- Interrupt: pin A routed to IRQ 86 Region 0: I/O ports at 3fd3000f000 [size=8] Region 1: I/O ports at 3fd3000f010 [size=4] Region 2: I/O ports at 3fd3000f020 [size=8] Region 3: I/O ports at 3fd3000f030 [size=4] Region 4: I/O ports at 3fd3000f040 [size=16] Region 5: I/O ports at 3fd30000000 [size=16] 00: ad 10 05 01 41 01 80 02 05 8f 01 01 08 48 80 00 10: 01 f0 00 00 11 f0 00 00 21 f0 00 00 31 f0 00 00 20: 41 f0 00 00 01 00 00 00 00 00 00 00 00 00 00 00 30: 00 00 00 00 00 00 00 00 00 00 00 00 56 01 02 28 40: b3 08 ff 00 09 09 00 00 09 09 00 00 09 09 00 00 50: 09 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70: 00 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 00 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 name "ide" linux,phandle 00d5cdc0 (14011840) assigned-addresses 81001910 00000000 0000f000 00000000 00000008 81001914 00000000 0000f010 00000000 00000004 81001918 00000000 0000f020 00000000 00000008 8100191c 00000000 0000f030 00000000 00000004 81001920 00000000 0000f040 00000000 00000010 81001924 00000000 00000000 00000000 00000010 interrupts 00000003 built-in #size-cells 00000000 #address-cells 00000001 device_type "ide" reg 00001900 00000000 00000000 00000000 00000000 41001910 00000000 00000000 00000000 00000008 41001914 00000000 00000000 00000000 00000004 41001918 00000000 00000000 00000000 00000008 4100191c 00000000 00000000 00000000 00000004 41001920 00000000 00000000 00000000 00000010 41001924 00000000 00000000 00000000 00000010 compatible "pci10ad,105" "pciclass,01018f" ibm,fw-slot-number 00000000 fast-back-to-back devsel-speed 00000001 max-latency 00000028 (40) min-grant 00000002 class-code 0001018f (65935) revision-id 00000005 device-id 00000105 (261) vendor-id 000010ad (4269) ibm,loc-code "U0.1-P1/Q6" Signed-off-by: Olaf Hering <olaf@aepfle.de> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-02-10 21:38:37 +01:00
if (dev->resource[i].start == 0 && dev->resource[i].end) {
dev->resource[i].flags = 0;
dev->resource[i].end = 0;
}
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105,
fixup_winbond_82c105);
int pseries_root_bridge_prepare(struct pci_host_bridge *bridge)
{
struct device_node *dn, *pdn;
struct pci_bus *bus;
u32 pcie_link_speed_stats[2];
int rc;
bus = bridge->bus;
powerpc/pseries: use pci_host_bridge.release_fn() to kfree(phb) This patch leverages 'struct pci_host_bridge' from the PCI subsystem in order to free the pci_controller only after the last reference to its devices is dropped (avoiding an oops in pcibios_release_device() if the last reference is dropped after pcibios_free_controller()). The patch relies on pci_host_bridge.release_fn() (and .release_data), which is called automatically by the PCI subsystem when the root bus is released (i.e., the last reference is dropped). Those fields are set via pci_set_host_bridge_release() (e.g. in the platform-specific implementation of pcibios_root_bridge_prepare()). It introduces the 'pcibios_free_controller_deferred()' .release_fn() and it expects .release_data to hold a pointer to the pci_controller. The function implictly calls 'pcibios_free_controller()', so an user must *NOT* explicitly call it if using the new _deferred() callback. The functionality is enabled for pseries (although it isn't platform specific, and may be used by cxl). Details on not-so-elegant design choices: - Use 'pci_host_bridge.release_data' field as pointer to associated 'struct pci_controller' so *not* to 'pci_bus_to_host(bridge->bus)' in pcibios_free_controller_deferred(). That's because pci_remove_root_bus() sets 'host_bridge->bus = NULL' (so, if the last reference is released after pci_remove_root_bus() runs, which eventually reaches pcibios_free_controller_deferred(), that would hit a null pointer dereference). The cxl/vphb.c code calls pci_remove_root_bus(), and the cxl folks are interested in this fix. Test-case #1 (hold references) # ls -ld /sys/block/sd* | grep -m1 0021:01:00.0 <...> /sys/block/sdaa -> ../devices/pci0021:01/0021:01:00.0/<...> # ls -ld /sys/block/sd* | grep -m1 0021:01:00.1 <...> /sys/block/sdab -> ../devices/pci0021:01/0021:01:00.1/<...> # cat >/dev/sdaa & pid1=$! # cat >/dev/sdab & pid2=$! # drmgr -w 5 -d 1 -c phb -s 'PHB 33' -r Validating PHB DLPAR capability...yes. [ 594.306719] pci_hp_remove_devices: PCI: Removing devices on bus 0021:01 [ 594.306738] pci_hp_remove_devices: Removing 0021:01:00.0... ... [ 598.236381] pci_hp_remove_devices: Removing 0021:01:00.1... ... [ 611.972077] pci_bus 0021:01: busn_res: [bus 01-ff] is released [ 611.972140] rpadlpar_io: slot PHB 33 removed # kill -9 $pid1 # kill -9 $pid2 [ 632.918088] pcibios_free_controller_deferred: domain 33, dynamic 1 Test-case #2 (don't hold references) # drmgr -w 5 -d 1 -c phb -s 'PHB 33' -r Validating PHB DLPAR capability...yes. [ 916.357363] pci_hp_remove_devices: PCI: Removing devices on bus 0021:01 [ 916.357386] pci_hp_remove_devices: Removing 0021:01:00.0... ... [ 920.566527] pci_hp_remove_devices: Removing 0021:01:00.1... ... [ 933.955873] pci_bus 0021:01: busn_res: [bus 01-ff] is released [ 933.955977] pcibios_free_controller_deferred: domain 33, dynamic 1 [ 933.955999] rpadlpar_io: slot PHB 33 removed Suggested-By: Gavin Shan <gwshan@linux.vnet.ibm.com> Signed-off-by: Mauricio Faria de Oliveira <mauricfo@linux.vnet.ibm.com> Reviewed-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> Tested-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> # cxl Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2016-08-11 22:25:40 +02:00
/* Rely on the pcibios_free_controller_deferred() callback. */
pci_set_host_bridge_release(bridge, pcibios_free_controller_deferred,
(void *) pci_bus_to_host(bus));
dn = pcibios_get_phb_of_node(bus);
if (!dn)
return 0;
for (pdn = dn; pdn != NULL; pdn = of_get_next_parent(pdn)) {
rc = of_property_read_u32_array(pdn,
"ibm,pcie-link-speed-stats",
&pcie_link_speed_stats[0], 2);
if (!rc)
break;
}
of_node_put(pdn);
if (rc) {
pr_debug("no ibm,pcie-link-speed-stats property\n");
return 0;
}
switch (pcie_link_speed_stats[0]) {
case 0x01:
bus->max_bus_speed = PCIE_SPEED_2_5GT;
break;
case 0x02:
bus->max_bus_speed = PCIE_SPEED_5_0GT;
break;
case 0x04:
bus->max_bus_speed = PCIE_SPEED_8_0GT;
break;
default:
bus->max_bus_speed = PCI_SPEED_UNKNOWN;
break;
}
switch (pcie_link_speed_stats[1]) {
case 0x01:
bus->cur_bus_speed = PCIE_SPEED_2_5GT;
break;
case 0x02:
bus->cur_bus_speed = PCIE_SPEED_5_0GT;
break;
case 0x04:
bus->cur_bus_speed = PCIE_SPEED_8_0GT;
break;
default:
bus->cur_bus_speed = PCI_SPEED_UNKNOWN;
break;
}
return 0;
}