[PATCH v4 1/2] x86, pci: Reset PCIe devices at boot time
Takao Indoh
indou.takao at jp.fujitsu.com
Wed Nov 7 01:48:45 EST 2012
(2012/10/16 13:23), Takao Indoh wrote:
> (2012/10/16 3:36), Yinghai Lu wrote:
>> On Mon, Oct 15, 2012 at 12:00 AM, Takao Indoh
>> <indou.takao at jp.fujitsu.com> wrote:
>>> This patch resets PCIe devices at boot time by hot reset when
>>> "reset_devices" is specified.
>>
>> how about pci devices that domain_nr is not zero ?
>
> This patch does not support multiple domains yet.
>
>>>
>>> Signed-off-by: Takao Indoh <indou.takao at jp.fujitsu.com>
>>> ---
>>> arch/x86/include/asm/pci-direct.h | 1
>>> arch/x86/kernel/setup.c | 3
>>> arch/x86/pci/early.c | 344 ++++++++++++++++++++++++++++
>>> include/linux/pci.h | 2
>>> init/main.c | 4
>>> 5 files changed, 352 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/arch/x86/include/asm/pci-direct.h b/arch/x86/include/asm/pci-direct.h
>>> index b1e7a45..de30db2 100644
>>> --- a/arch/x86/include/asm/pci-direct.h
>>> +++ b/arch/x86/include/asm/pci-direct.h
>>> @@ -18,4 +18,5 @@ extern int early_pci_allowed(void);
>>> extern unsigned int pci_early_dump_regs;
>>> extern void early_dump_pci_device(u8 bus, u8 slot, u8 func);
>>> extern void early_dump_pci_devices(void);
>>> +extern void early_reset_pcie_devices(void);
>>> #endif /* _ASM_X86_PCI_DIRECT_H */
>>> diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
>>> index a2bb18e..73d3425 100644
>>> --- a/arch/x86/kernel/setup.c
>>> +++ b/arch/x86/kernel/setup.c
>>> @@ -987,6 +987,9 @@ void __init setup_arch(char **cmdline_p)
>>> generic_apic_probe();
>>>
>>> early_quirks();
>>> +#ifdef CONFIG_PCI
>>> + early_reset_pcie_devices();
>>> +#endif
>>>
>>> /*
>>> * Read APIC and some other early information from ACPI tables.
>>> diff --git a/arch/x86/pci/early.c b/arch/x86/pci/early.c
>>> index d1067d5..683b30f 100644
>>> --- a/arch/x86/pci/early.c
>>> +++ b/arch/x86/pci/early.c
>>> @@ -1,5 +1,6 @@
>>> #include <linux/kernel.h>
>>> #include <linux/pci.h>
>>> +#include <linux/bootmem.h>
>>> #include <asm/pci-direct.h>
>>> #include <asm/io.h>
>>> #include <asm/pci_x86.h>
>>> @@ -109,3 +110,346 @@ void early_dump_pci_devices(void)
>>> }
>>> }
>>> }
>>> +
>>> +#define PCI_EXP_SAVE_REGS 7
>>> +#define pcie_cap_has_devctl(type, flags) 1
>>> +#define pcie_cap_has_lnkctl(type, flags) \
>>> + ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
>>> + (type == PCI_EXP_TYPE_ROOT_PORT || \
>>> + type == PCI_EXP_TYPE_ENDPOINT || \
>>> + type == PCI_EXP_TYPE_LEG_END))
>>> +#define pcie_cap_has_sltctl(type, flags) \
>>> + ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
>>> + ((type == PCI_EXP_TYPE_ROOT_PORT) || \
>>> + (type == PCI_EXP_TYPE_DOWNSTREAM && \
>>> + (flags & PCI_EXP_FLAGS_SLOT))))
>>> +#define pcie_cap_has_rtctl(type, flags) \
>>> + ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
>>> + (type == PCI_EXP_TYPE_ROOT_PORT || \
>>> + type == PCI_EXP_TYPE_RC_EC))
>>> +
>>> +struct save_config {
>>> + u32 pci[16];
>>> + u16 pcie[PCI_EXP_SAVE_REGS];
>>> +};
>>> +
>>> +struct pcie_dev {
>>> + int cap; /* position of PCI Express capability */
>>> + int flags; /* PCI_EXP_FLAGS */
>>> + struct save_config save; /* saved configration register */
>>> +};
>>> +
>>> +struct pcie_port {
>>> + struct list_head dev;
>>> + u8 secondary;
>>> + struct pcie_dev child[PCI_MAX_FUNCTIONS];
>>> +};
>>> +
>>> +static LIST_HEAD(device_list);
>>> +static void __init pci_udelay(int loops)
>>> +{
>>> + while (loops--) {
>>> + /* Approximately 1 us */
>>> + native_io_delay();
>>> + }
>>> +}
>>> +
>>> +/* Derived from drivers/pci/pci.c */
>>> +#define PCI_FIND_CAP_TTL 48
>>> +static int __init __pci_find_next_cap_ttl(u8 bus, u8 slot, u8 func,
>>> + u8 pos, int cap, int *ttl)
>>> +{
>>> + u8 id;
>>> +
>>> + while ((*ttl)--) {
>>> + pos = read_pci_config_byte(bus, slot, func, pos);
>>> + if (pos < 0x40)
>>> + break;
>>> + pos &= ~3;
>>> + id = read_pci_config_byte(bus, slot, func,
>>> + pos + PCI_CAP_LIST_ID);
>>> + if (id == 0xff)
>>> + break;
>>> + if (id == cap)
>>> + return pos;
>>> + pos += PCI_CAP_LIST_NEXT;
>>> + }
>>> + return 0;
>>> +}
>>> +
>>> +static int __init __pci_find_next_cap(u8 bus, u8 slot, u8 func, u8 pos, int cap)
>>> +{
>>> + int ttl = PCI_FIND_CAP_TTL;
>>> +
>>> + return __pci_find_next_cap_ttl(bus, slot, func, pos, cap, &ttl);
>>> +}
>>> +
>>> +static int __init __pci_bus_find_cap_start(u8 bus, u8 slot, u8 func,
>>> + u8 hdr_type)
>>> +{
>>> + u16 status;
>>> +
>>> + status = read_pci_config_16(bus, slot, func, PCI_STATUS);
>>> + if (!(status & PCI_STATUS_CAP_LIST))
>>> + return 0;
>>> +
>>> + switch (hdr_type) {
>>> + case PCI_HEADER_TYPE_NORMAL:
>>> + case PCI_HEADER_TYPE_BRIDGE:
>>> + return PCI_CAPABILITY_LIST;
>>> + case PCI_HEADER_TYPE_CARDBUS:
>>> + return PCI_CB_CAPABILITY_LIST;
>>> + default:
>>> + return 0;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int __init early_pci_find_capability(u8 bus, u8 slot, u8 func, int cap)
>>> +{
>>> + int pos;
>>> + u8 type = read_pci_config_byte(bus, slot, func, PCI_HEADER_TYPE);
>>> +
>>> + pos = __pci_bus_find_cap_start(bus, slot, func, type & 0x7f);
>>> + if (pos)
>>> + pos = __pci_find_next_cap(bus, slot, func, pos, cap);
>>> +
>>> + return pos;
>>> +}
>>> +
>>> +static void __init do_reset(u8 bus, u8 slot, u8 func)
>>> +{
>>> + u16 ctrl;
>>> +
>>> + printk(KERN_INFO "pci 0000:%02x:%02x.%d reset\n", bus, slot, func);
>>> +
>>> + /* Assert Secondary Bus Reset */
>>> + ctrl = read_pci_config_16(bus, slot, func, PCI_BRIDGE_CONTROL);
>>> + ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
>>> + write_pci_config_16(bus, slot, func, PCI_BRIDGE_CONTROL, ctrl);
>>> +
>>> + /*
>>> + * PCIe spec requires software to ensure a minimum reset duration
>>> + * (Trst == 1ms). We have here 5ms safety margin because pci_udelay is
>>> + * not precise.
>>> + */
>>> + pci_udelay(5000);
>>> +
>>> + /* De-assert Secondary Bus Reset */
>>> + ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
>>> + write_pci_config_16(bus, slot, func, PCI_BRIDGE_CONTROL, ctrl);
>>> +}
>>> +
>>> +static void __init save_state(unsigned bus, unsigned slot, unsigned func,
>>> + struct pcie_dev *dev)
>>> +{
>>> + int i;
>>> + int pcie, flags, pcie_type;
>>> + struct save_config *save;
>>> +
>>> + pcie = dev->cap;
>>> + flags = dev->flags;
>>> + pcie_type = (flags & PCI_EXP_FLAGS_TYPE) >> 4;
>>> + save = &dev->save;
>>> +
>>> + printk(KERN_INFO "pci 0000:%02x:%02x.%d save state\n", bus, slot, func);
>>> +
>>> + for (i = 0; i < 16; i++)
>>> + save->pci[i] = read_pci_config(bus, slot, func, i * 4);
>>> + i = 0;
>>> + if (pcie_cap_has_devctl(pcie_type, flags))
>>> + save->pcie[i++] = read_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_DEVCTL);
>>> + if (pcie_cap_has_lnkctl(pcie_type, flags))
>>> + save->pcie[i++] = read_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_LNKCTL);
>>> + if (pcie_cap_has_sltctl(pcie_type, flags))
>>> + save->pcie[i++] = read_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_SLTCTL);
>>> + if (pcie_cap_has_rtctl(pcie_type, flags))
>>> + save->pcie[i++] = read_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_RTCTL);
>>> +
>>> + if ((flags & PCI_EXP_FLAGS_VERS) >= 2) {
>>> + save->pcie[i++] = read_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_DEVCTL2);
>>> + save->pcie[i++] = read_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_LNKCTL2);
>>> + save->pcie[i++] = read_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_SLTCTL2);
>>> + }
>>> +}
>>> +
>>> +static void __init restore_state(unsigned bus, unsigned slot, unsigned func,
>>> + struct pcie_dev *dev)
>>> +{
>>> + int i = 0;
>>> + int pcie, flags, pcie_type;
>>> + struct save_config *save;
>>> +
>>> + pcie = dev->cap;
>>> + flags = dev->flags;
>>> + pcie_type = (flags & PCI_EXP_FLAGS_TYPE) >> 4;
>>> + save = &dev->save;
>>> +
>>> + printk(KERN_INFO "pci 0000:%02x:%02x.%d restore state\n",
>>> + bus, slot, func);
>>> +
>>> + if (pcie_cap_has_devctl(pcie_type, flags))
>>> + write_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_DEVCTL, save->pcie[i++]);
>>> + if (pcie_cap_has_lnkctl(pcie_type, flags))
>>> + write_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_LNKCTL, save->pcie[i++]);
>>> + if (pcie_cap_has_sltctl(pcie_type, flags))
>>> + write_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_SLTCTL, save->pcie[i++]);
>>> + if (pcie_cap_has_rtctl(pcie_type, flags))
>>> + write_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_RTCTL, save->pcie[i++]);
>>> +
>>> + if ((flags & PCI_EXP_FLAGS_VERS) >= 2) {
>>> + write_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_DEVCTL2, save->pcie[i++]);
>>> + write_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_LNKCTL2, save->pcie[i++]);
>>> + write_pci_config_16(bus, slot, func,
>>> + pcie + PCI_EXP_SLTCTL2, save->pcie[i++]);
>>> + }
>>> +
>>> + for (i = 15; i >= 0; i--)
>>> + write_pci_config(bus, slot, func, i * 4, save->pci[i]);
>>> +}
>>
>> do you have to pass bus/slot/func and use read/pci_config directly ?
>>
>> I had one patchset that use dummy pci device and reuse existing late quirk code
>> in early_quirk to do usb handoff early.
>>
>> please check
>>
>> git://git.kernel.org/pub/scm/linux/kernel/git/yinghai/linux-yinghai.git
>> for-x86-early-quirk-usb
>>
>> 678a023: x86: usb handoff in early_quirk
>> 2d418d8: pci, usb: Make usb handoff func all take base remapping
>> d9bd1ad: x86, pci: add dummy pci device for early stage
>> de38757: x86: early_quirk check all bus/dev/func in domain 0
>> 325cc7a: make msleep to do mdelay before scheduler is running
>> eec78a4: x86: set percpu cpu_info lpj to default
>> 52ebec4: x86, pci: early dump skip device the same way as later probe code
>>
>> if that could help.
>> you may reuse some later functions that take pci_dev as parameters.
> d9bd1ad looks very useful for my patch. Thanks for the information.
> What is the status of this patch? Already got in tip tree or
> somewhere?
Hi Yinghai,
I'm rewriting my reset code using your dummy pci_dev patch. Do you have
a plan to post it or can I post it with my patches?
Thanks,
Takao Indoh
More information about the kexec
mailing list