1736759efSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * PCI Express PCI Hot Plug Driver
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1995,2001 Compaq Computer Corporation
61da177e4SLinus Torvalds * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
71da177e4SLinus Torvalds * Copyright (C) 2001 IBM Corp.
81da177e4SLinus Torvalds * Copyright (C) 2003-2004 Intel Corporation
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * All rights reserved.
111da177e4SLinus Torvalds *
128cf4c195SKristen Accardi * Send feedback to <greg@kroah.com>,<kristen.c.accardi@intel.com>
131da177e4SLinus Torvalds */
141da177e4SLinus Torvalds
1594dbc956SFrederick Lawler #define dev_fmt(fmt) "pciehp: " fmt
1694dbc956SFrederick Lawler
170b382546SStuart Hayes #include <linux/dmi.h>
181da177e4SLinus Torvalds #include <linux/kernel.h>
191da177e4SLinus Torvalds #include <linux/types.h>
20de25968cSTim Schmielau #include <linux/jiffies.h>
21ec07a447SLukas Wunner #include <linux/kthread.h>
221da177e4SLinus Torvalds #include <linux/pci.h>
236b08c385SLukas Wunner #include <linux/pm_runtime.h>
245d1b8c9eSAndrew Morton #include <linux/interrupt.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
265d1b8c9eSAndrew Morton
271da177e4SLinus Torvalds #include "../pci.h"
281da177e4SLinus Torvalds #include "pciehp.h"
291da177e4SLinus Torvalds
300b382546SStuart Hayes static const struct dmi_system_id inband_presence_disabled_dmi_table[] = {
310b382546SStuart Hayes /*
320b382546SStuart Hayes * Match all Dell systems, as some Dell systems have inband
330b382546SStuart Hayes * presence disabled on NVMe slots (but don't support the bit to
340b382546SStuart Hayes * report it). Setting inband presence disabled should have no
350b382546SStuart Hayes * negative effect, except on broken hotplug slots that never
360b382546SStuart Hayes * assert presence detect--and those will still work, they will
370b382546SStuart Hayes * just have a bit of extra delay before being probed.
380b382546SStuart Hayes */
390b382546SStuart Hayes {
400b382546SStuart Hayes .ident = "Dell System",
410b382546SStuart Hayes .matches = {
420b382546SStuart Hayes DMI_MATCH(DMI_OEM_STRING, "Dell System"),
430b382546SStuart Hayes },
440b382546SStuart Hayes },
450b382546SStuart Hayes {}
460b382546SStuart Hayes };
470b382546SStuart Hayes
ctrl_dev(struct controller * ctrl)48cd84d340SBjorn Helgaas static inline struct pci_dev *ctrl_dev(struct controller *ctrl)
49a0f018daSKenji Kaneshige {
50cd84d340SBjorn Helgaas return ctrl->pcie->port;
51a0f018daSKenji Kaneshige }
521da177e4SLinus Torvalds
537b4ce26bSLukas Wunner static irqreturn_t pciehp_isr(int irq, void *dev_id);
547b4ce26bSLukas Wunner static irqreturn_t pciehp_ist(int irq, void *dev_id);
55ec07a447SLukas Wunner static int pciehp_poll(void *data);
561da177e4SLinus Torvalds
pciehp_request_irq(struct controller * ctrl)572aeeef11SKenji Kaneshige static inline int pciehp_request_irq(struct controller *ctrl)
582aeeef11SKenji Kaneshige {
59f7a10e32SKenji Kaneshige int retval, irq = ctrl->pcie->irq;
602aeeef11SKenji Kaneshige
612aeeef11SKenji Kaneshige if (pciehp_poll_mode) {
62ec07a447SLukas Wunner ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl,
63ec07a447SLukas Wunner "pciehp_poll-%s",
645790a9c7SLukas Wunner slot_name(ctrl));
65ec07a447SLukas Wunner return PTR_ERR_OR_ZERO(ctrl->poll_thread);
662aeeef11SKenji Kaneshige }
672aeeef11SKenji Kaneshige
682aeeef11SKenji Kaneshige /* Installs the interrupt handler */
697b4ce26bSLukas Wunner retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist,
70e07ca82aSBjorn Helgaas IRQF_SHARED, "pciehp", ctrl);
712aeeef11SKenji Kaneshige if (retval)
727f2feec1STaku Izumi ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
737f2feec1STaku Izumi irq);
742aeeef11SKenji Kaneshige return retval;
752aeeef11SKenji Kaneshige }
762aeeef11SKenji Kaneshige
pciehp_free_irq(struct controller * ctrl)772aeeef11SKenji Kaneshige static inline void pciehp_free_irq(struct controller *ctrl)
782aeeef11SKenji Kaneshige {
792aeeef11SKenji Kaneshige if (pciehp_poll_mode)
80ec07a447SLukas Wunner kthread_stop(ctrl->poll_thread);
812aeeef11SKenji Kaneshige else
82f7a10e32SKenji Kaneshige free_irq(ctrl->pcie->irq, ctrl);
832aeeef11SKenji Kaneshige }
842aeeef11SKenji Kaneshige
pcie_poll_cmd(struct controller * ctrl,int timeout)8540b96083SBjorn Helgaas static int pcie_poll_cmd(struct controller *ctrl, int timeout)
866592e02aSKenji Kaneshige {
87cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
886592e02aSKenji Kaneshige u16 slot_status;
896592e02aSKenji Kaneshige
90b94ec12dSAndy Shevchenko do {
911a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
92a3b0f10dSNaveen Naidu if (PCI_POSSIBLE_ERROR(slot_status)) {
931469d17dSJarod Wilson ctrl_info(ctrl, "%s: no response from device\n",
941469d17dSJarod Wilson __func__);
951469d17dSJarod Wilson return 0;
961469d17dSJarod Wilson }
971469d17dSJarod Wilson
981a84b99cSBjorn Helgaas if (slot_status & PCI_EXP_SLTSTA_CC) {
99cd84d340SBjorn Helgaas pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
100cd84d340SBjorn Helgaas PCI_EXP_SLTSTA_CC);
10192912b17SLiguang Zhang ctrl->cmd_busy = 0;
10292912b17SLiguang Zhang smp_mb();
103820943b6SKenji Kaneshige return 1;
104820943b6SKenji Kaneshige }
10566618badSKenji Kaneshige msleep(10);
10666618badSKenji Kaneshige timeout -= 10;
107b94ec12dSAndy Shevchenko } while (timeout >= 0);
1086592e02aSKenji Kaneshige return 0; /* timeout */
1096592e02aSKenji Kaneshige }
1106592e02aSKenji Kaneshige
pcie_wait_cmd(struct controller * ctrl)1114283c70eSBjorn Helgaas static void pcie_wait_cmd(struct controller *ctrl)
11244ef4cefSKenji Kaneshige {
113262303feSKenji Kaneshige unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
11440b96083SBjorn Helgaas unsigned long duration = msecs_to_jiffies(msecs);
11540b96083SBjorn Helgaas unsigned long cmd_timeout = ctrl->cmd_started + duration;
11640b96083SBjorn Helgaas unsigned long now, timeout;
117262303feSKenji Kaneshige int rc;
11844ef4cefSKenji Kaneshige
1194283c70eSBjorn Helgaas /*
1204283c70eSBjorn Helgaas * If the controller does not generate notifications for command
1214283c70eSBjorn Helgaas * completions, we never need to wait between writes.
1224283c70eSBjorn Helgaas */
1236c1a32e0SRajat Jain if (NO_CMD_CMPL(ctrl))
1244283c70eSBjorn Helgaas return;
1254283c70eSBjorn Helgaas
1264283c70eSBjorn Helgaas if (!ctrl->cmd_busy)
1274283c70eSBjorn Helgaas return;
1284283c70eSBjorn Helgaas
12940b96083SBjorn Helgaas /*
13040b96083SBjorn Helgaas * Even if the command has already timed out, we want to call
13140b96083SBjorn Helgaas * pcie_poll_cmd() so it can clear PCI_EXP_SLTSTA_CC.
13240b96083SBjorn Helgaas */
13340b96083SBjorn Helgaas now = jiffies;
13440b96083SBjorn Helgaas if (time_before_eq(cmd_timeout, now))
13540b96083SBjorn Helgaas timeout = 1;
13640b96083SBjorn Helgaas else
13740b96083SBjorn Helgaas timeout = cmd_timeout - now;
13840b96083SBjorn Helgaas
1394283c70eSBjorn Helgaas if (ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE &&
1404283c70eSBjorn Helgaas ctrl->slot_ctrl & PCI_EXP_SLTCTL_CCIE)
141d737bdc1SKenji Kaneshige rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout);
1424283c70eSBjorn Helgaas else
1437cbeb9f9SYinghai Lu rc = pcie_poll_cmd(ctrl, jiffies_to_msecs(timeout));
14440b96083SBjorn Helgaas
145262303feSKenji Kaneshige if (!rc)
146d537a3abSBjorn Helgaas ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n",
14740b96083SBjorn Helgaas ctrl->slot_ctrl,
148d433889cSYinghai Lu jiffies_to_msecs(jiffies - ctrl->cmd_started));
14944ef4cefSKenji Kaneshige }
15044ef4cefSKenji Kaneshige
151d22b3621SBjorn Helgaas #define CC_ERRATUM_MASK (PCI_EXP_SLTCTL_PCC | \
152d22b3621SBjorn Helgaas PCI_EXP_SLTCTL_PIC | \
153d22b3621SBjorn Helgaas PCI_EXP_SLTCTL_AIC | \
154d22b3621SBjorn Helgaas PCI_EXP_SLTCTL_EIC)
155d22b3621SBjorn Helgaas
pcie_do_write_cmd(struct controller * ctrl,u16 cmd,u16 mask,bool wait)156a5dd4b4bSAlex Williamson static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
157a5dd4b4bSAlex Williamson u16 mask, bool wait)
1581da177e4SLinus Torvalds {
159cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
160d22b3621SBjorn Helgaas u16 slot_ctrl_orig, slot_ctrl;
1611da177e4SLinus Torvalds
16244ef4cefSKenji Kaneshige mutex_lock(&ctrl->ctrl_lock);
16344ef4cefSKenji Kaneshige
164a5dd4b4bSAlex Williamson /*
165a5dd4b4bSAlex Williamson * Always wait for any previous command that might still be in progress
166a5dd4b4bSAlex Williamson */
1673461a068SBjorn Helgaas pcie_wait_cmd(ctrl);
1683461a068SBjorn Helgaas
1691a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
170a3b0f10dSNaveen Naidu if (PCI_POSSIBLE_ERROR(slot_ctrl)) {
1711469d17dSJarod Wilson ctrl_info(ctrl, "%s: no response from device\n", __func__);
1721469d17dSJarod Wilson goto out;
1731469d17dSJarod Wilson }
1741469d17dSJarod Wilson
175d22b3621SBjorn Helgaas slot_ctrl_orig = slot_ctrl;
176f4778364SKenji Kaneshige slot_ctrl &= ~mask;
177b7aa1f16SKenji Kaneshige slot_ctrl |= (cmd & mask);
178f4778364SKenji Kaneshige ctrl->cmd_busy = 1;
1792d32a9aeSKenji Kaneshige smp_mb();
18025bd879eSMika Westerberg ctrl->slot_ctrl = slot_ctrl;
1811a84b99cSBjorn Helgaas pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl);
18240b96083SBjorn Helgaas ctrl->cmd_started = jiffies;
183f4778364SKenji Kaneshige
184a5dd4b4bSAlex Williamson /*
185d22b3621SBjorn Helgaas * Controllers with the Intel CF118 and similar errata advertise
186d22b3621SBjorn Helgaas * Command Completed support, but they only set Command Completed
187d22b3621SBjorn Helgaas * if we change the "Control" bits for power, power indicator,
188d22b3621SBjorn Helgaas * attention indicator, or interlock. If we only change the
189d22b3621SBjorn Helgaas * "Enable" bits, they never set the Command Completed bit.
190d22b3621SBjorn Helgaas */
191d22b3621SBjorn Helgaas if (pdev->broken_cmd_compl &&
192d22b3621SBjorn Helgaas (slot_ctrl_orig & CC_ERRATUM_MASK) == (slot_ctrl & CC_ERRATUM_MASK))
193d22b3621SBjorn Helgaas ctrl->cmd_busy = 0;
194d22b3621SBjorn Helgaas
195d22b3621SBjorn Helgaas /*
196a5dd4b4bSAlex Williamson * Optionally wait for the hardware to be ready for a new command,
197a5dd4b4bSAlex Williamson * indicating completion of the above issued command.
198a5dd4b4bSAlex Williamson */
199a5dd4b4bSAlex Williamson if (wait)
200a5dd4b4bSAlex Williamson pcie_wait_cmd(ctrl);
201a5dd4b4bSAlex Williamson
2021469d17dSJarod Wilson out:
20344ef4cefSKenji Kaneshige mutex_unlock(&ctrl->ctrl_lock);
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds
206a5dd4b4bSAlex Williamson /**
207a5dd4b4bSAlex Williamson * pcie_write_cmd - Issue controller command
208a5dd4b4bSAlex Williamson * @ctrl: controller to which the command is issued
209a5dd4b4bSAlex Williamson * @cmd: command value written to slot control register
210a5dd4b4bSAlex Williamson * @mask: bitmask of slot control register to be modified
211a5dd4b4bSAlex Williamson */
pcie_write_cmd(struct controller * ctrl,u16 cmd,u16 mask)212a5dd4b4bSAlex Williamson static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
213a5dd4b4bSAlex Williamson {
214a5dd4b4bSAlex Williamson pcie_do_write_cmd(ctrl, cmd, mask, true);
215a5dd4b4bSAlex Williamson }
216a5dd4b4bSAlex Williamson
217a5dd4b4bSAlex Williamson /* Same as above without waiting for the hardware to latch */
pcie_write_cmd_nowait(struct controller * ctrl,u16 cmd,u16 mask)218a5dd4b4bSAlex Williamson static void pcie_write_cmd_nowait(struct controller *ctrl, u16 cmd, u16 mask)
219a5dd4b4bSAlex Williamson {
220a5dd4b4bSAlex Williamson pcie_do_write_cmd(ctrl, cmd, mask, false);
221a5dd4b4bSAlex Williamson }
222a5dd4b4bSAlex Williamson
22387d0f2a5SMika Westerberg /**
22487d0f2a5SMika Westerberg * pciehp_check_link_active() - Is the link active
22587d0f2a5SMika Westerberg * @ctrl: PCIe hotplug controller
22687d0f2a5SMika Westerberg *
22787d0f2a5SMika Westerberg * Check whether the downstream link is currently active. Note it is
22887d0f2a5SMika Westerberg * possible that the card is removed immediately after this so the
22987d0f2a5SMika Westerberg * caller may need to take it into account.
23087d0f2a5SMika Westerberg *
23187d0f2a5SMika Westerberg * If the hotplug controller itself is not available anymore returns
23287d0f2a5SMika Westerberg * %-ENODEV.
23387d0f2a5SMika Westerberg */
pciehp_check_link_active(struct controller * ctrl)23487d0f2a5SMika Westerberg int pciehp_check_link_active(struct controller *ctrl)
235f18e9625SKenji Kaneshige {
236cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
2374e2ce405SYinghai Lu u16 lnk_status;
23887d0f2a5SMika Westerberg int ret;
239f18e9625SKenji Kaneshige
24087d0f2a5SMika Westerberg ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
241a3b0f10dSNaveen Naidu if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status))
24287d0f2a5SMika Westerberg return -ENODEV;
24387d0f2a5SMika Westerberg
2444e2ce405SYinghai Lu ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
2454e2ce405SYinghai Lu ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
2464e2ce405SYinghai Lu
2474e2ce405SYinghai Lu return ret;
248f18e9625SKenji Kaneshige }
249f18e9625SKenji Kaneshige
pci_bus_check_dev(struct pci_bus * bus,int devfn)2502f5d8e4fSYinghai Lu static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
2512f5d8e4fSYinghai Lu {
2522f5d8e4fSYinghai Lu u32 l;
2532f5d8e4fSYinghai Lu int count = 0;
2542f5d8e4fSYinghai Lu int delay = 1000, step = 20;
2552f5d8e4fSYinghai Lu bool found = false;
2562f5d8e4fSYinghai Lu
2572f5d8e4fSYinghai Lu do {
2582f5d8e4fSYinghai Lu found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0);
2592f5d8e4fSYinghai Lu count++;
2602f5d8e4fSYinghai Lu
2612f5d8e4fSYinghai Lu if (found)
2622f5d8e4fSYinghai Lu break;
2632f5d8e4fSYinghai Lu
2642f5d8e4fSYinghai Lu msleep(step);
2652f5d8e4fSYinghai Lu delay -= step;
2662f5d8e4fSYinghai Lu } while (delay > 0);
2672f5d8e4fSYinghai Lu
2687e696b8aSBjorn Helgaas if (count > 1)
269742ee16bSFrederick Lawler pr_debug("pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n",
2702f5d8e4fSYinghai Lu pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
2712f5d8e4fSYinghai Lu PCI_FUNC(devfn), count, step, l);
2722f5d8e4fSYinghai Lu
2732f5d8e4fSYinghai Lu return found;
2742f5d8e4fSYinghai Lu }
2752f5d8e4fSYinghai Lu
pcie_wait_for_presence(struct pci_dev * pdev)276f496648bSAlexandru Gagniuc static void pcie_wait_for_presence(struct pci_dev *pdev)
277f496648bSAlexandru Gagniuc {
278f496648bSAlexandru Gagniuc int timeout = 1250;
279f496648bSAlexandru Gagniuc u16 slot_status;
280f496648bSAlexandru Gagniuc
281f496648bSAlexandru Gagniuc do {
282f496648bSAlexandru Gagniuc pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
283f496648bSAlexandru Gagniuc if (slot_status & PCI_EXP_SLTSTA_PDS)
284f496648bSAlexandru Gagniuc return;
285f496648bSAlexandru Gagniuc msleep(10);
286f496648bSAlexandru Gagniuc timeout -= 10;
287f496648bSAlexandru Gagniuc } while (timeout > 0);
288f496648bSAlexandru Gagniuc }
289f496648bSAlexandru Gagniuc
pciehp_check_link_status(struct controller * ctrl)29082a9e79eSKenji Kaneshige int pciehp_check_link_status(struct controller *ctrl)
2911da177e4SLinus Torvalds {
292cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
2931a84b99cSBjorn Helgaas bool found;
2941da177e4SLinus Torvalds u16 lnk_status;
2951da177e4SLinus Torvalds
2968a614499SLukas Wunner if (!pcie_wait_for_link(pdev, true)) {
2978a614499SLukas Wunner ctrl_info(ctrl, "Slot(%s): No link\n", slot_name(ctrl));
298f0157160SKeith Busch return -1;
2998a614499SLukas Wunner }
300f18e9625SKenji Kaneshige
301f496648bSAlexandru Gagniuc if (ctrl->inband_presence_disabled)
302f496648bSAlexandru Gagniuc pcie_wait_for_presence(pdev);
303f496648bSAlexandru Gagniuc
3042f5d8e4fSYinghai Lu found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
3052f5d8e4fSYinghai Lu PCI_DEVFN(0, 0));
3060027cb3eSKenji Kaneshige
3076c35a1acSLukas Wunner /* ignore link or presence changes up to this point */
3086c35a1acSLukas Wunner if (found)
3096c35a1acSLukas Wunner atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
3106c35a1acSLukas Wunner &ctrl->pending_events);
3116c35a1acSLukas Wunner
3121a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
3137f2feec1STaku Izumi ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
314322162a7SKenji Kaneshige if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
315322162a7SKenji Kaneshige !(lnk_status & PCI_EXP_LNKSTA_NLW)) {
3168a614499SLukas Wunner ctrl_info(ctrl, "Slot(%s): Cannot train link: status %#06x\n",
3178a614499SLukas Wunner slot_name(ctrl), lnk_status);
3181a84b99cSBjorn Helgaas return -1;
3191da177e4SLinus Torvalds }
3201da177e4SLinus Torvalds
321fdbd3ce9SYinghai Lu pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status);
322fdbd3ce9SYinghai Lu
3238a614499SLukas Wunner if (!found) {
3248a614499SLukas Wunner ctrl_info(ctrl, "Slot(%s): No device found\n",
3258a614499SLukas Wunner slot_name(ctrl));
3261a84b99cSBjorn Helgaas return -1;
3278a614499SLukas Wunner }
3282f5d8e4fSYinghai Lu
3291a84b99cSBjorn Helgaas return 0;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds
__pciehp_link_set(struct controller * ctrl,bool enable)3327f822999SYinghai Lu static int __pciehp_link_set(struct controller *ctrl, bool enable)
3337f822999SYinghai Lu {
334cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
3357f822999SYinghai Lu
336*5f75f96cSIlpo Järvinen pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
337*5f75f96cSIlpo Järvinen PCI_EXP_LNKCTL_LD,
338*5f75f96cSIlpo Järvinen enable ? 0 : PCI_EXP_LNKCTL_LD);
3397f822999SYinghai Lu
3401a84b99cSBjorn Helgaas return 0;
3417f822999SYinghai Lu }
3427f822999SYinghai Lu
pciehp_link_enable(struct controller * ctrl)3437f822999SYinghai Lu static int pciehp_link_enable(struct controller *ctrl)
3447f822999SYinghai Lu {
3457f822999SYinghai Lu return __pciehp_link_set(ctrl, true);
3467f822999SYinghai Lu }
3477f822999SYinghai Lu
pciehp_get_raw_indicator_status(struct hotplug_slot * hotplug_slot,u8 * status)348576243b3SKeith Busch int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
349576243b3SKeith Busch u8 *status)
350576243b3SKeith Busch {
351125450f8SLukas Wunner struct controller *ctrl = to_ctrl(hotplug_slot);
3525790a9c7SLukas Wunner struct pci_dev *pdev = ctrl_dev(ctrl);
353576243b3SKeith Busch u16 slot_ctrl;
354576243b3SKeith Busch
3554417aa45SLukas Wunner pci_config_pm_runtime_get(pdev);
356576243b3SKeith Busch pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
3574417aa45SLukas Wunner pci_config_pm_runtime_put(pdev);
358576243b3SKeith Busch *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6;
359576243b3SKeith Busch return 0;
360576243b3SKeith Busch }
361576243b3SKeith Busch
pciehp_get_attention_status(struct hotplug_slot * hotplug_slot,u8 * status)362eee6e273SLukas Wunner int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status)
3631da177e4SLinus Torvalds {
364125450f8SLukas Wunner struct controller *ctrl = to_ctrl(hotplug_slot);
365cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
3661da177e4SLinus Torvalds u16 slot_ctrl;
3671da177e4SLinus Torvalds
3684417aa45SLukas Wunner pci_config_pm_runtime_get(pdev);
3691a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
3704417aa45SLukas Wunner pci_config_pm_runtime_put(pdev);
3711518c17aSKenji Kaneshige ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__,
3721518c17aSKenji Kaneshige pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
3731da177e4SLinus Torvalds
374e7b4f0d7SBjorn Helgaas switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) {
375e7b4f0d7SBjorn Helgaas case PCI_EXP_SLTCTL_ATTN_IND_ON:
3761da177e4SLinus Torvalds *status = 1; /* On */
3771da177e4SLinus Torvalds break;
378e7b4f0d7SBjorn Helgaas case PCI_EXP_SLTCTL_ATTN_IND_BLINK:
3791da177e4SLinus Torvalds *status = 2; /* Blink */
3801da177e4SLinus Torvalds break;
381e7b4f0d7SBjorn Helgaas case PCI_EXP_SLTCTL_ATTN_IND_OFF:
3821da177e4SLinus Torvalds *status = 0; /* Off */
3831da177e4SLinus Torvalds break;
3841da177e4SLinus Torvalds default:
3851da177e4SLinus Torvalds *status = 0xFF;
3861da177e4SLinus Torvalds break;
3871da177e4SLinus Torvalds }
388eee6e273SLukas Wunner
389eee6e273SLukas Wunner return 0;
3901da177e4SLinus Torvalds }
3911da177e4SLinus Torvalds
pciehp_get_power_status(struct controller * ctrl,u8 * status)3925790a9c7SLukas Wunner void pciehp_get_power_status(struct controller *ctrl, u8 *status)
3931da177e4SLinus Torvalds {
394cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
3951da177e4SLinus Torvalds u16 slot_ctrl;
3961da177e4SLinus Torvalds
3971a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
3981518c17aSKenji Kaneshige ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__,
3991518c17aSKenji Kaneshige pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
4001da177e4SLinus Torvalds
401e7b4f0d7SBjorn Helgaas switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) {
402e7b4f0d7SBjorn Helgaas case PCI_EXP_SLTCTL_PWR_ON:
403e7b4f0d7SBjorn Helgaas *status = 1; /* On */
4041da177e4SLinus Torvalds break;
405e7b4f0d7SBjorn Helgaas case PCI_EXP_SLTCTL_PWR_OFF:
406e7b4f0d7SBjorn Helgaas *status = 0; /* Off */
4071da177e4SLinus Torvalds break;
4081da177e4SLinus Torvalds default:
4091da177e4SLinus Torvalds *status = 0xFF;
4101da177e4SLinus Torvalds break;
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds
pciehp_get_latch_status(struct controller * ctrl,u8 * status)4145790a9c7SLukas Wunner void pciehp_get_latch_status(struct controller *ctrl, u8 *status)
4151da177e4SLinus Torvalds {
4165790a9c7SLukas Wunner struct pci_dev *pdev = ctrl_dev(ctrl);
4171da177e4SLinus Torvalds u16 slot_status;
4181da177e4SLinus Torvalds
4191a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
420322162a7SKenji Kaneshige *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS);
4211da177e4SLinus Torvalds }
4221da177e4SLinus Torvalds
42387d0f2a5SMika Westerberg /**
42487d0f2a5SMika Westerberg * pciehp_card_present() - Is the card present
42587d0f2a5SMika Westerberg * @ctrl: PCIe hotplug controller
42687d0f2a5SMika Westerberg *
42787d0f2a5SMika Westerberg * Function checks whether the card is currently present in the slot and
42887d0f2a5SMika Westerberg * in that case returns true. Note it is possible that the card is
42987d0f2a5SMika Westerberg * removed immediately after the check so the caller may need to take
43087d0f2a5SMika Westerberg * this into account.
43187d0f2a5SMika Westerberg *
43287d0f2a5SMika Westerberg * It the hotplug controller itself is not available anymore returns
43387d0f2a5SMika Westerberg * %-ENODEV.
43487d0f2a5SMika Westerberg */
pciehp_card_present(struct controller * ctrl)43587d0f2a5SMika Westerberg int pciehp_card_present(struct controller *ctrl)
4361da177e4SLinus Torvalds {
43780696f99SLukas Wunner struct pci_dev *pdev = ctrl_dev(ctrl);
4381da177e4SLinus Torvalds u16 slot_status;
43987d0f2a5SMika Westerberg int ret;
4401da177e4SLinus Torvalds
44187d0f2a5SMika Westerberg ret = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
442a3b0f10dSNaveen Naidu if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(slot_status))
44387d0f2a5SMika Westerberg return -ENODEV;
44487d0f2a5SMika Westerberg
44587d0f2a5SMika Westerberg return !!(slot_status & PCI_EXP_SLTSTA_PDS);
44680696f99SLukas Wunner }
44780696f99SLukas Wunner
44880696f99SLukas Wunner /**
44980696f99SLukas Wunner * pciehp_card_present_or_link_active() - whether given slot is occupied
45080696f99SLukas Wunner * @ctrl: PCIe hotplug controller
45180696f99SLukas Wunner *
45280696f99SLukas Wunner * Unlike pciehp_card_present(), which determines presence solely from the
45380696f99SLukas Wunner * Presence Detect State bit, this helper also returns true if the Link Active
45480696f99SLukas Wunner * bit is set. This is a concession to broken hotplug ports which hardwire
45580696f99SLukas Wunner * Presence Detect State to zero, such as Wilocity's [1ae9:0200].
45687d0f2a5SMika Westerberg *
45787d0f2a5SMika Westerberg * Returns: %1 if the slot is occupied and %0 if it is not. If the hotplug
45887d0f2a5SMika Westerberg * port is not present anymore returns %-ENODEV.
45980696f99SLukas Wunner */
pciehp_card_present_or_link_active(struct controller * ctrl)46087d0f2a5SMika Westerberg int pciehp_card_present_or_link_active(struct controller *ctrl)
46180696f99SLukas Wunner {
46287d0f2a5SMika Westerberg int ret;
46387d0f2a5SMika Westerberg
46487d0f2a5SMika Westerberg ret = pciehp_card_present(ctrl);
46587d0f2a5SMika Westerberg if (ret)
46687d0f2a5SMika Westerberg return ret;
46787d0f2a5SMika Westerberg
46887d0f2a5SMika Westerberg return pciehp_check_link_active(ctrl);
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds
pciehp_query_power_fault(struct controller * ctrl)4715790a9c7SLukas Wunner int pciehp_query_power_fault(struct controller *ctrl)
4721da177e4SLinus Torvalds {
4735790a9c7SLukas Wunner struct pci_dev *pdev = ctrl_dev(ctrl);
4741da177e4SLinus Torvalds u16 slot_status;
4751da177e4SLinus Torvalds
4761a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
477322162a7SKenji Kaneshige return !!(slot_status & PCI_EXP_SLTSTA_PFD);
4781da177e4SLinus Torvalds }
4791da177e4SLinus Torvalds
pciehp_set_raw_indicator_status(struct hotplug_slot * hotplug_slot,u8 status)480576243b3SKeith Busch int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
481576243b3SKeith Busch u8 status)
482576243b3SKeith Busch {
483125450f8SLukas Wunner struct controller *ctrl = to_ctrl(hotplug_slot);
4844417aa45SLukas Wunner struct pci_dev *pdev = ctrl_dev(ctrl);
485576243b3SKeith Busch
4864417aa45SLukas Wunner pci_config_pm_runtime_get(pdev);
487576243b3SKeith Busch pcie_write_cmd_nowait(ctrl, status << 6,
488576243b3SKeith Busch PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC);
4894417aa45SLukas Wunner pci_config_pm_runtime_put(pdev);
490576243b3SKeith Busch return 0;
491576243b3SKeith Busch }
492576243b3SKeith Busch
493688033f5SDenis Efremov /**
494688033f5SDenis Efremov * pciehp_set_indicators() - set attention indicator, power indicator, or both
495688033f5SDenis Efremov * @ctrl: PCIe hotplug controller
496688033f5SDenis Efremov * @pwr: one of:
497688033f5SDenis Efremov * PCI_EXP_SLTCTL_PWR_IND_ON
498688033f5SDenis Efremov * PCI_EXP_SLTCTL_PWR_IND_BLINK
499688033f5SDenis Efremov * PCI_EXP_SLTCTL_PWR_IND_OFF
500688033f5SDenis Efremov * @attn: one of:
501688033f5SDenis Efremov * PCI_EXP_SLTCTL_ATTN_IND_ON
502688033f5SDenis Efremov * PCI_EXP_SLTCTL_ATTN_IND_BLINK
503688033f5SDenis Efremov * PCI_EXP_SLTCTL_ATTN_IND_OFF
504688033f5SDenis Efremov *
505688033f5SDenis Efremov * Either @pwr or @attn can also be INDICATOR_NOOP to leave that indicator
506688033f5SDenis Efremov * unchanged.
507688033f5SDenis Efremov */
pciehp_set_indicators(struct controller * ctrl,int pwr,int attn)508688033f5SDenis Efremov void pciehp_set_indicators(struct controller *ctrl, int pwr, int attn)
509688033f5SDenis Efremov {
510688033f5SDenis Efremov u16 cmd = 0, mask = 0;
511688033f5SDenis Efremov
512688033f5SDenis Efremov if (PWR_LED(ctrl) && pwr != INDICATOR_NOOP) {
513688033f5SDenis Efremov cmd |= (pwr & PCI_EXP_SLTCTL_PIC);
514688033f5SDenis Efremov mask |= PCI_EXP_SLTCTL_PIC;
515688033f5SDenis Efremov }
516688033f5SDenis Efremov
517688033f5SDenis Efremov if (ATTN_LED(ctrl) && attn != INDICATOR_NOOP) {
518688033f5SDenis Efremov cmd |= (attn & PCI_EXP_SLTCTL_AIC);
519688033f5SDenis Efremov mask |= PCI_EXP_SLTCTL_AIC;
520688033f5SDenis Efremov }
521688033f5SDenis Efremov
522688033f5SDenis Efremov if (cmd) {
523688033f5SDenis Efremov pcie_write_cmd_nowait(ctrl, cmd, mask);
524688033f5SDenis Efremov ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
525688033f5SDenis Efremov pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
526688033f5SDenis Efremov }
527688033f5SDenis Efremov }
528688033f5SDenis Efremov
pciehp_power_on_slot(struct controller * ctrl)5295790a9c7SLukas Wunner int pciehp_power_on_slot(struct controller *ctrl)
5301da177e4SLinus Torvalds {
531cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
532f4778364SKenji Kaneshige u16 slot_status;
5331a84b99cSBjorn Helgaas int retval;
5341da177e4SLinus Torvalds
53534fb6bf9SKeith Busch /* Clear power-fault bit from previous power failures */
5361a84b99cSBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
5372f2ed41cSBjorn Helgaas if (slot_status & PCI_EXP_SLTSTA_PFD)
5382f2ed41cSBjorn Helgaas pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
5392f2ed41cSBjorn Helgaas PCI_EXP_SLTSTA_PFD);
5405651c48cSKenji Kaneshige ctrl->power_fault_detected = 0;
541a0f018daSKenji Kaneshige
542e7b4f0d7SBjorn Helgaas pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC);
5431518c17aSKenji Kaneshige ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
544e7b4f0d7SBjorn Helgaas pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
545e7b4f0d7SBjorn Helgaas PCI_EXP_SLTCTL_PWR_ON);
5461da177e4SLinus Torvalds
5472debd928SYinghai Lu retval = pciehp_link_enable(ctrl);
5482debd928SYinghai Lu if (retval)
5492debd928SYinghai Lu ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__);
5502debd928SYinghai Lu
5511da177e4SLinus Torvalds return retval;
5521da177e4SLinus Torvalds }
5531da177e4SLinus Torvalds
pciehp_power_off_slot(struct controller * ctrl)5545790a9c7SLukas Wunner void pciehp_power_off_slot(struct controller *ctrl)
5551da177e4SLinus Torvalds {
556e7b4f0d7SBjorn Helgaas pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
5571518c17aSKenji Kaneshige ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
558e7b4f0d7SBjorn Helgaas pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
559e7b4f0d7SBjorn Helgaas PCI_EXP_SLTCTL_PWR_OFF);
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds
pciehp_ignore_dpc_link_change(struct controller * ctrl,struct pci_dev * pdev,int irq)562a97396c6SLukas Wunner static void pciehp_ignore_dpc_link_change(struct controller *ctrl,
563a97396c6SLukas Wunner struct pci_dev *pdev, int irq)
564a97396c6SLukas Wunner {
565a97396c6SLukas Wunner /*
566a97396c6SLukas Wunner * Ignore link changes which occurred while waiting for DPC recovery.
567a97396c6SLukas Wunner * Could be several if DPC triggered multiple times consecutively.
568a97396c6SLukas Wunner */
569a97396c6SLukas Wunner synchronize_hardirq(irq);
570a97396c6SLukas Wunner atomic_and(~PCI_EXP_SLTSTA_DLLSC, &ctrl->pending_events);
571a97396c6SLukas Wunner if (pciehp_poll_mode)
572a97396c6SLukas Wunner pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
573a97396c6SLukas Wunner PCI_EXP_SLTSTA_DLLSC);
574a97396c6SLukas Wunner ctrl_info(ctrl, "Slot(%s): Link Down/Up ignored (recovered by DPC)\n",
575a97396c6SLukas Wunner slot_name(ctrl));
576a97396c6SLukas Wunner
577a97396c6SLukas Wunner /*
578a97396c6SLukas Wunner * If the link is unexpectedly down after successful recovery,
579a97396c6SLukas Wunner * the corresponding link change may have been ignored above.
580a97396c6SLukas Wunner * Synthesize it to ensure that it is acted on.
581a97396c6SLukas Wunner */
582085a9f43SHans de Goede down_read_nested(&ctrl->reset_lock, ctrl->depth);
583a97396c6SLukas Wunner if (!pciehp_check_link_active(ctrl))
584a97396c6SLukas Wunner pciehp_request(ctrl, PCI_EXP_SLTSTA_DLLSC);
585a97396c6SLukas Wunner up_read(&ctrl->reset_lock);
586a97396c6SLukas Wunner }
587a97396c6SLukas Wunner
pciehp_isr(int irq,void * dev_id)588fad214b0SMayurkumar Patel static irqreturn_t pciehp_isr(int irq, void *dev_id)
5891da177e4SLinus Torvalds {
59048fe3915SKenji Kaneshige struct controller *ctrl = (struct controller *)dev_id;
591cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
5926b08c385SLukas Wunner struct device *parent = pdev->dev.parent;
5938edf5332SStuart Hayes u16 status, events = 0;
5941da177e4SLinus Torvalds
5957b4ce26bSLukas Wunner /*
596720d6a67SMika Westerberg * Interrupts only occur in D3hot or shallower and only if enabled
597720d6a67SMika Westerberg * in the Slot Control register (PCIe r4.0, sec 6.7.3.4).
5987b4ce26bSLukas Wunner */
599720d6a67SMika Westerberg if (pdev->current_state == PCI_D3cold ||
600720d6a67SMika Westerberg (!(ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE) && !pciehp_poll_mode))
601ed91de7eSLukas Wunner return IRQ_NONE;
602ed91de7eSLukas Wunner
6036b08c385SLukas Wunner /*
6046b08c385SLukas Wunner * Keep the port accessible by holding a runtime PM ref on its parent.
6056b08c385SLukas Wunner * Defer resume of the parent to the IRQ thread if it's suspended.
6066b08c385SLukas Wunner * Mask the interrupt until then.
6076b08c385SLukas Wunner */
6086b08c385SLukas Wunner if (parent) {
6096b08c385SLukas Wunner pm_runtime_get_noresume(parent);
6106b08c385SLukas Wunner if (!pm_runtime_active(parent)) {
6116b08c385SLukas Wunner pm_runtime_put(parent);
6126b08c385SLukas Wunner disable_irq_nosync(irq);
6136b08c385SLukas Wunner atomic_or(RERUN_ISR, &ctrl->pending_events);
6146b08c385SLukas Wunner return IRQ_WAKE_THREAD;
6156b08c385SLukas Wunner }
6166b08c385SLukas Wunner }
6176b08c385SLukas Wunner
6188edf5332SStuart Hayes read_status:
619a8499f20SBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status);
620a3b0f10dSNaveen Naidu if (PCI_POSSIBLE_ERROR(status)) {
621fad214b0SMayurkumar Patel ctrl_info(ctrl, "%s: no response from device\n", __func__);
6226b08c385SLukas Wunner if (parent)
6236b08c385SLukas Wunner pm_runtime_put(parent);
62470e8b401SBjorn Helgaas return IRQ_NONE;
6251469d17dSJarod Wilson }
6261da177e4SLinus Torvalds
627a8499f20SBjorn Helgaas /*
628a8499f20SBjorn Helgaas * Slot Status contains plain status bits as well as event
629a8499f20SBjorn Helgaas * notification bits; right now we only want the event bits.
630a8499f20SBjorn Helgaas */
6318edf5332SStuart Hayes status &= PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
632a8499f20SBjorn Helgaas PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC |
6338edf5332SStuart Hayes PCI_EXP_SLTSTA_DLLSC;
6347612b3b2SKeith Busch
6357612b3b2SKeith Busch /*
6367612b3b2SKeith Busch * If we've already reported a power fault, don't report it again
6377612b3b2SKeith Busch * until we've done something to handle it.
6387612b3b2SKeith Busch */
6397612b3b2SKeith Busch if (ctrl->power_fault_detected)
6408edf5332SStuart Hayes status &= ~PCI_EXP_SLTSTA_PFD;
64123584c1eSLukas Wunner else if (status & PCI_EXP_SLTSTA_PFD)
64223584c1eSLukas Wunner ctrl->power_fault_detected = true;
6437612b3b2SKeith Busch
6448edf5332SStuart Hayes events |= status;
6456b08c385SLukas Wunner if (!events) {
6466b08c385SLukas Wunner if (parent)
6476b08c385SLukas Wunner pm_runtime_put(parent);
6481da177e4SLinus Torvalds return IRQ_NONE;
6496b08c385SLukas Wunner }
6501da177e4SLinus Torvalds
6518edf5332SStuart Hayes if (status) {
65223584c1eSLukas Wunner pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, status);
6538edf5332SStuart Hayes
6548edf5332SStuart Hayes /*
6558edf5332SStuart Hayes * In MSI mode, all event bits must be zero before the port
6568edf5332SStuart Hayes * will send a new interrupt (PCIe Base Spec r5.0 sec 6.7.3.4).
6578edf5332SStuart Hayes * So re-read the Slot Status register in case a bit was set
6588edf5332SStuart Hayes * between read and write.
6598edf5332SStuart Hayes */
6608edf5332SStuart Hayes if (pci_dev_msi_enabled(pdev) && !pciehp_poll_mode)
6618edf5332SStuart Hayes goto read_status;
6628edf5332SStuart Hayes }
6638edf5332SStuart Hayes
664a8499f20SBjorn Helgaas ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events);
6656b08c385SLukas Wunner if (parent)
6666b08c385SLukas Wunner pm_runtime_put(parent);
6671da177e4SLinus Torvalds
6687b4ce26bSLukas Wunner /*
6697b4ce26bSLukas Wunner * Command Completed notifications are not deferred to the
6707b4ce26bSLukas Wunner * IRQ thread because it may be waiting for their arrival.
6717b4ce26bSLukas Wunner */
672a8499f20SBjorn Helgaas if (events & PCI_EXP_SLTSTA_CC) {
673262303feSKenji Kaneshige ctrl->cmd_busy = 0;
6742d32a9aeSKenji Kaneshige smp_mb();
675d737bdc1SKenji Kaneshige wake_up(&ctrl->queue);
6767b4ce26bSLukas Wunner
6777b4ce26bSLukas Wunner if (events == PCI_EXP_SLTSTA_CC)
6787b4ce26bSLukas Wunner return IRQ_HANDLED;
6797b4ce26bSLukas Wunner
6807b4ce26bSLukas Wunner events &= ~PCI_EXP_SLTSTA_CC;
6811da177e4SLinus Torvalds }
6821da177e4SLinus Torvalds
6831204e35bSLukas Wunner if (pdev->ignore_hotplug) {
6841204e35bSLukas Wunner ctrl_dbg(ctrl, "ignoring hotplug event %#06x\n", events);
685b440bde7SBjorn Helgaas return IRQ_HANDLED;
686b440bde7SBjorn Helgaas }
687b440bde7SBjorn Helgaas
6887b4ce26bSLukas Wunner /* Save pending events for consumption by IRQ thread. */
6897b4ce26bSLukas Wunner atomic_or(events, &ctrl->pending_events);
6907b4ce26bSLukas Wunner return IRQ_WAKE_THREAD;
6917b4ce26bSLukas Wunner }
6927b4ce26bSLukas Wunner
pciehp_ist(int irq,void * dev_id)6937b4ce26bSLukas Wunner static irqreturn_t pciehp_ist(int irq, void *dev_id)
6947b4ce26bSLukas Wunner {
6957b4ce26bSLukas Wunner struct controller *ctrl = (struct controller *)dev_id;
6966b08c385SLukas Wunner struct pci_dev *pdev = ctrl_dev(ctrl);
6976b08c385SLukas Wunner irqreturn_t ret;
6987b4ce26bSLukas Wunner u32 events;
6997b4ce26bSLukas Wunner
700157c1062SLukas Wunner ctrl->ist_running = true;
7016b08c385SLukas Wunner pci_config_pm_runtime_get(pdev);
7026b08c385SLukas Wunner
7036b08c385SLukas Wunner /* rerun pciehp_isr() if the port was inaccessible on interrupt */
7046b08c385SLukas Wunner if (atomic_fetch_and(~RERUN_ISR, &ctrl->pending_events) & RERUN_ISR) {
7056b08c385SLukas Wunner ret = pciehp_isr(irq, dev_id);
7066b08c385SLukas Wunner enable_irq(irq);
7073e487d2eSLukas Wunner if (ret != IRQ_WAKE_THREAD)
7083e487d2eSLukas Wunner goto out;
7096b08c385SLukas Wunner }
7106b08c385SLukas Wunner
7117b4ce26bSLukas Wunner synchronize_hardirq(irq);
7127b4ce26bSLukas Wunner events = atomic_xchg(&ctrl->pending_events, 0);
7136b08c385SLukas Wunner if (!events) {
7143e487d2eSLukas Wunner ret = IRQ_NONE;
7153e487d2eSLukas Wunner goto out;
7166b08c385SLukas Wunner }
7177b4ce26bSLukas Wunner
718c6b069e9SKenji Kaneshige /* Check Attention Button Pressed */
7195054133aSBjorn Helgaas if (events & PCI_EXP_SLTSTA_ABP)
7205790a9c7SLukas Wunner pciehp_handle_button_press(ctrl);
72148fe3915SKenji Kaneshige
72234fb6bf9SKeith Busch /* Check Power Fault Detected */
72323584c1eSLukas Wunner if (events & PCI_EXP_SLTSTA_PFD) {
7245790a9c7SLukas Wunner ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
72594719ba0SDenis Efremov pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
72694719ba0SDenis Efremov PCI_EXP_SLTCTL_ATTN_IND_ON);
72734fb6bf9SKeith Busch }
72834fb6bf9SKeith Busch
729385895feSAshok Raj /*
730a97396c6SLukas Wunner * Ignore Link Down/Up events caused by Downstream Port Containment
731a97396c6SLukas Wunner * if recovery from the error succeeded.
732a97396c6SLukas Wunner */
733a97396c6SLukas Wunner if ((events & PCI_EXP_SLTSTA_DLLSC) && pci_dpc_recovered(pdev) &&
734a97396c6SLukas Wunner ctrl->state == ON_STATE) {
735a97396c6SLukas Wunner events &= ~PCI_EXP_SLTSTA_DLLSC;
736a97396c6SLukas Wunner pciehp_ignore_dpc_link_change(ctrl, pdev, irq);
737a97396c6SLukas Wunner }
738a97396c6SLukas Wunner
739a97396c6SLukas Wunner /*
74032a8cef2SLukas Wunner * Disable requests have higher priority than Presence Detect Changed
74132a8cef2SLukas Wunner * or Data Link Layer State Changed events.
742385895feSAshok Raj */
743085a9f43SHans de Goede down_read_nested(&ctrl->reset_lock, ctrl->depth);
74432a8cef2SLukas Wunner if (events & DISABLE_SLOT)
7455790a9c7SLukas Wunner pciehp_handle_disable_request(ctrl);
746d331710eSLukas Wunner else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
7475790a9c7SLukas Wunner pciehp_handle_presence_or_link_change(ctrl, events);
7485b3f7b7dSLukas Wunner up_read(&ctrl->reset_lock);
74948fe3915SKenji Kaneshige
7503e487d2eSLukas Wunner ret = IRQ_HANDLED;
7513e487d2eSLukas Wunner out:
7526b08c385SLukas Wunner pci_config_pm_runtime_put(pdev);
753157c1062SLukas Wunner ctrl->ist_running = false;
75432a8cef2SLukas Wunner wake_up(&ctrl->requester);
7553e487d2eSLukas Wunner return ret;
7561da177e4SLinus Torvalds }
7571da177e4SLinus Torvalds
pciehp_poll(void * data)758ec07a447SLukas Wunner static int pciehp_poll(void *data)
759ec07a447SLukas Wunner {
760ec07a447SLukas Wunner struct controller *ctrl = data;
761ec07a447SLukas Wunner
762ec07a447SLukas Wunner schedule_timeout_idle(10 * HZ); /* start with 10 sec delay */
763ec07a447SLukas Wunner
764ec07a447SLukas Wunner while (!kthread_should_stop()) {
76532a8cef2SLukas Wunner /* poll for interrupt events or user requests */
76632a8cef2SLukas Wunner while (pciehp_isr(IRQ_NOTCONNECTED, ctrl) == IRQ_WAKE_THREAD ||
76732a8cef2SLukas Wunner atomic_read(&ctrl->pending_events))
768ec07a447SLukas Wunner pciehp_ist(IRQ_NOTCONNECTED, ctrl);
769ec07a447SLukas Wunner
770ec07a447SLukas Wunner if (pciehp_poll_time <= 0 || pciehp_poll_time > 60)
771ec07a447SLukas Wunner pciehp_poll_time = 2; /* clamp to sane value */
772ec07a447SLukas Wunner
773ec07a447SLukas Wunner schedule_timeout_idle(pciehp_poll_time * HZ);
774ec07a447SLukas Wunner }
775ec07a447SLukas Wunner
776ec07a447SLukas Wunner return 0;
777ec07a447SLukas Wunner }
778ec07a447SLukas Wunner
pcie_enable_notification(struct controller * ctrl)77913c65840SMika Westerberg static void pcie_enable_notification(struct controller *ctrl)
780ecdde939SMark Lord {
781c27fb883SKenji Kaneshige u16 cmd, mask;
7821da177e4SLinus Torvalds
7835651c48cSKenji Kaneshige /*
7845651c48cSKenji Kaneshige * TBD: Power fault detected software notification support.
7855651c48cSKenji Kaneshige *
7865651c48cSKenji Kaneshige * Power fault detected software notification is not enabled
7875651c48cSKenji Kaneshige * now, because it caused power fault detected interrupt storm
7885651c48cSKenji Kaneshige * on some machines. On those machines, power fault detected
7895651c48cSKenji Kaneshige * bit in the slot status register was set again immediately
7905651c48cSKenji Kaneshige * when it is cleared in the interrupt service routine, and
7915651c48cSKenji Kaneshige * next power fault detected interrupt was notified again.
7925651c48cSKenji Kaneshige */
7934f854f2aSRajat Jain
7944f854f2aSRajat Jain /*
7954f854f2aSRajat Jain * Always enable link events: thus link-up and link-down shall
7964f854f2aSRajat Jain * always be treated as hotplug and unplug respectively. Enable
7974f854f2aSRajat Jain * presence detect only if Attention Button is not present.
7984f854f2aSRajat Jain */
7994f854f2aSRajat Jain cmd = PCI_EXP_SLTCTL_DLLSCE;
800ae416e6bSKenji Kaneshige if (ATTN_BUTTN(ctrl))
801322162a7SKenji Kaneshige cmd |= PCI_EXP_SLTCTL_ABPE;
8024f854f2aSRajat Jain else
8034f854f2aSRajat Jain cmd |= PCI_EXP_SLTCTL_PDCE;
804c27fb883SKenji Kaneshige if (!pciehp_poll_mode)
8056d4671b5SPali Rohár cmd |= PCI_EXP_SLTCTL_HPIE;
8066d4671b5SPali Rohár if (!pciehp_poll_mode && !NO_CMD_CMPL(ctrl))
8076d4671b5SPali Rohár cmd |= PCI_EXP_SLTCTL_CCIE;
808c27fb883SKenji Kaneshige
809322162a7SKenji Kaneshige mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
8102db0f71fSBjorn Helgaas PCI_EXP_SLTCTL_PFDE |
8114f854f2aSRajat Jain PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
8124f854f2aSRajat Jain PCI_EXP_SLTCTL_DLLSCE);
813c27fb883SKenji Kaneshige
814a5dd4b4bSAlex Williamson pcie_write_cmd_nowait(ctrl, cmd, mask);
815cf8d7b58SYinghai Lu ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
816cf8d7b58SYinghai Lu pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
8171da177e4SLinus Torvalds }
81808e7a7d2SMark Lord
pcie_disable_notification(struct controller * ctrl)819c4635eb0SKenji Kaneshige static void pcie_disable_notification(struct controller *ctrl)
820c4635eb0SKenji Kaneshige {
821c4635eb0SKenji Kaneshige u16 mask;
8226dae6202SBjorn Helgaas
823322162a7SKenji Kaneshige mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
824322162a7SKenji Kaneshige PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |
825f22daf1fSKenji Kaneshige PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
826f22daf1fSKenji Kaneshige PCI_EXP_SLTCTL_DLLSCE);
8276dae6202SBjorn Helgaas pcie_write_cmd(ctrl, 0, mask);
828cf8d7b58SYinghai Lu ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
829cf8d7b58SYinghai Lu pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
830c4635eb0SKenji Kaneshige }
831c4635eb0SKenji Kaneshige
pcie_clear_hotplug_events(struct controller * ctrl)83279037824SLukas Wunner void pcie_clear_hotplug_events(struct controller *ctrl)
83379037824SLukas Wunner {
83479037824SLukas Wunner pcie_capability_write_word(ctrl_dev(ctrl), PCI_EXP_SLTSTA,
83579037824SLukas Wunner PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
83679037824SLukas Wunner }
83779037824SLukas Wunner
pcie_enable_interrupt(struct controller * ctrl)838eb34da60SMika Westerberg void pcie_enable_interrupt(struct controller *ctrl)
839eb34da60SMika Westerberg {
840bbe54ea5SMika Westerberg u16 mask;
841bbe54ea5SMika Westerberg
842bbe54ea5SMika Westerberg mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
843bbe54ea5SMika Westerberg pcie_write_cmd(ctrl, mask, mask);
844eb34da60SMika Westerberg }
845eb34da60SMika Westerberg
pcie_disable_interrupt(struct controller * ctrl)846eb34da60SMika Westerberg void pcie_disable_interrupt(struct controller *ctrl)
847eb34da60SMika Westerberg {
848bbe54ea5SMika Westerberg u16 mask;
849bbe54ea5SMika Westerberg
850bbe54ea5SMika Westerberg /*
851bbe54ea5SMika Westerberg * Mask hot-plug interrupt to prevent it triggering immediately
852bbe54ea5SMika Westerberg * when the link goes inactive (we still get PME when any of the
853bbe54ea5SMika Westerberg * enabled events is detected). Same goes with Link Layer State
854bbe54ea5SMika Westerberg * changed event which generates PME immediately when the link goes
855bbe54ea5SMika Westerberg * inactive so mask it as well.
856bbe54ea5SMika Westerberg */
857bbe54ea5SMika Westerberg mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
858bbe54ea5SMika Westerberg pcie_write_cmd(ctrl, 0, mask);
859eb34da60SMika Westerberg }
860eb34da60SMika Westerberg
861ea401499SLukas Wunner /**
862ea401499SLukas Wunner * pciehp_slot_reset() - ignore link event caused by error-induced hot reset
863ea401499SLukas Wunner * @dev: PCI Express port service device
864ea401499SLukas Wunner *
865ea401499SLukas Wunner * Called from pcie_portdrv_slot_reset() after AER or DPC initiated a reset
866ea401499SLukas Wunner * further up in the hierarchy to recover from an error. The reset was
867ea401499SLukas Wunner * propagated down to this hotplug port. Ignore the resulting link flap.
868ea401499SLukas Wunner * If the link failed to retrain successfully, synthesize the ignored event.
869ea401499SLukas Wunner * Surprise removal during reset is detected through Presence Detect Changed.
870ea401499SLukas Wunner */
pciehp_slot_reset(struct pcie_device * dev)871ea401499SLukas Wunner int pciehp_slot_reset(struct pcie_device *dev)
872ea401499SLukas Wunner {
873ea401499SLukas Wunner struct controller *ctrl = get_service_data(dev);
874ea401499SLukas Wunner
875ea401499SLukas Wunner if (ctrl->state != ON_STATE)
876ea401499SLukas Wunner return 0;
877ea401499SLukas Wunner
878ea401499SLukas Wunner pcie_capability_write_word(dev->port, PCI_EXP_SLTSTA,
879ea401499SLukas Wunner PCI_EXP_SLTSTA_DLLSC);
880ea401499SLukas Wunner
881ea401499SLukas Wunner if (!pciehp_check_link_active(ctrl))
882ea401499SLukas Wunner pciehp_request(ctrl, PCI_EXP_SLTSTA_DLLSC);
883ea401499SLukas Wunner
884ea401499SLukas Wunner return 0;
885ea401499SLukas Wunner }
886ea401499SLukas Wunner
8872e35afaeSAlex Williamson /*
8882e35afaeSAlex Williamson * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
8892b3940b6SRajat Jain * bus reset of the bridge, but at the same time we want to ensure that it is
8902b3940b6SRajat Jain * not seen as a hot-unplug, followed by the hot-plug of the device. Thus,
8912b3940b6SRajat Jain * disable link state notification and presence detection change notification
8922b3940b6SRajat Jain * momentarily, if we see that they could interfere. Also, clear any spurious
8932e35afaeSAlex Williamson * events after.
8942e35afaeSAlex Williamson */
pciehp_reset_slot(struct hotplug_slot * hotplug_slot,bool probe)8959bdc81ceSAmey Narkhede int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe)
8962e35afaeSAlex Williamson {
897125450f8SLukas Wunner struct controller *ctrl = to_ctrl(hotplug_slot);
898cd84d340SBjorn Helgaas struct pci_dev *pdev = ctrl_dev(ctrl);
89906a8d89aSRajat Jain u16 stat_mask = 0, ctrl_mask = 0;
90018426238SSinan Kaya int rc;
9012e35afaeSAlex Williamson
9022e35afaeSAlex Williamson if (probe)
9032e35afaeSAlex Williamson return 0;
9042e35afaeSAlex Williamson
905085a9f43SHans de Goede down_write_nested(&ctrl->reset_lock, ctrl->depth);
9065b3f7b7dSLukas Wunner
9072b3940b6SRajat Jain if (!ATTN_BUTTN(ctrl)) {
90806a8d89aSRajat Jain ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
90906a8d89aSRajat Jain stat_mask |= PCI_EXP_SLTSTA_PDC;
91006a8d89aSRajat Jain }
91106a8d89aSRajat Jain ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE;
91206a8d89aSRajat Jain stat_mask |= PCI_EXP_SLTSTA_DLLSC;
91306a8d89aSRajat Jain
91406a8d89aSRajat Jain pcie_write_cmd(ctrl, 0, ctrl_mask);
915cf8d7b58SYinghai Lu ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
916cf8d7b58SYinghai Lu pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
9172e35afaeSAlex Williamson
918381634caSSinan Kaya rc = pci_bridge_secondary_bus_reset(ctrl->pcie->port);
9192e35afaeSAlex Williamson
92006a8d89aSRajat Jain pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
921a5dd4b4bSAlex Williamson pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask);
922cf8d7b58SYinghai Lu ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
923cf8d7b58SYinghai Lu pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
9245b3f7b7dSLukas Wunner
9255b3f7b7dSLukas Wunner up_write(&ctrl->reset_lock);
92618426238SSinan Kaya return rc;
9272e35afaeSAlex Williamson }
9282e35afaeSAlex Williamson
pcie_init_notification(struct controller * ctrl)929dbc7e1e5SEric W. Biederman int pcie_init_notification(struct controller *ctrl)
930c4635eb0SKenji Kaneshige {
931c4635eb0SKenji Kaneshige if (pciehp_request_irq(ctrl))
932c4635eb0SKenji Kaneshige return -1;
9336dae6202SBjorn Helgaas pcie_enable_notification(ctrl);
934dbc7e1e5SEric W. Biederman ctrl->notification_enabled = 1;
935c4635eb0SKenji Kaneshige return 0;
936c4635eb0SKenji Kaneshige }
937c4635eb0SKenji Kaneshige
pcie_shutdown_notification(struct controller * ctrl)938281e878eSLukas Wunner void pcie_shutdown_notification(struct controller *ctrl)
939c4635eb0SKenji Kaneshige {
940dbc7e1e5SEric W. Biederman if (ctrl->notification_enabled) {
941c4635eb0SKenji Kaneshige pcie_disable_notification(ctrl);
942c4635eb0SKenji Kaneshige pciehp_free_irq(ctrl);
943dbc7e1e5SEric W. Biederman ctrl->notification_enabled = 0;
944dbc7e1e5SEric W. Biederman }
945c4635eb0SKenji Kaneshige }
946c4635eb0SKenji Kaneshige
dbg_ctrl(struct controller * ctrl)9472aeeef11SKenji Kaneshige static inline void dbg_ctrl(struct controller *ctrl)
9482aeeef11SKenji Kaneshige {
949385e2491SKenji Kaneshige struct pci_dev *pdev = ctrl->pcie->port;
9503784e0c6SBjorn Helgaas u16 reg16;
9512aeeef11SKenji Kaneshige
9527e696b8aSBjorn Helgaas ctrl_dbg(ctrl, "Slot Capabilities : 0x%08x\n", ctrl->slot_cap);
953cd84d340SBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, ®16);
9547e696b8aSBjorn Helgaas ctrl_dbg(ctrl, "Slot Status : 0x%04x\n", reg16);
955cd84d340SBjorn Helgaas pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, ®16);
9567e696b8aSBjorn Helgaas ctrl_dbg(ctrl, "Slot Control : 0x%04x\n", reg16);
9572aeeef11SKenji Kaneshige }
9582aeeef11SKenji Kaneshige
959afe2478fSBjorn Helgaas #define FLAG(x, y) (((x) & (y)) ? '+' : '-')
960afe2478fSBjorn Helgaas
pcie_hotplug_depth(struct pci_dev * dev)961085a9f43SHans de Goede static inline int pcie_hotplug_depth(struct pci_dev *dev)
962085a9f43SHans de Goede {
963085a9f43SHans de Goede struct pci_bus *bus = dev->bus;
964085a9f43SHans de Goede int depth = 0;
965085a9f43SHans de Goede
966085a9f43SHans de Goede while (bus->parent) {
967085a9f43SHans de Goede bus = bus->parent;
968085a9f43SHans de Goede if (bus->self && bus->self->is_hotplug_bridge)
969085a9f43SHans de Goede depth++;
970085a9f43SHans de Goede }
971085a9f43SHans de Goede
972085a9f43SHans de Goede return depth;
973085a9f43SHans de Goede }
974085a9f43SHans de Goede
pcie_init(struct pcie_device * dev)975c4635eb0SKenji Kaneshige struct controller *pcie_init(struct pcie_device *dev)
97608e7a7d2SMark Lord {
977c4635eb0SKenji Kaneshige struct controller *ctrl;
9781f087398SMaciej W. Rozycki u32 slot_cap, slot_cap2;
97980696f99SLukas Wunner u8 poweron;
9802aeeef11SKenji Kaneshige struct pci_dev *pdev = dev->port;
9815790a9c7SLukas Wunner struct pci_bus *subordinate = pdev->subordinate;
98208e7a7d2SMark Lord
983c4635eb0SKenji Kaneshige ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
984c7abb235SMarkus Elfring if (!ctrl)
9855790a9c7SLukas Wunner return NULL;
986c7abb235SMarkus Elfring
987f7a10e32SKenji Kaneshige ctrl->pcie = dev;
988085a9f43SHans de Goede ctrl->depth = pcie_hotplug_depth(dev->port);
9891a84b99cSBjorn Helgaas pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
990576243b3SKeith Busch
991576243b3SKeith Busch if (pdev->hotplug_user_indicators)
992576243b3SKeith Busch slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP);
993576243b3SKeith Busch
994493fb50eSLukas Wunner /*
995493fb50eSLukas Wunner * We assume no Thunderbolt controllers support Command Complete events,
996493fb50eSLukas Wunner * but some controllers falsely claim they do.
997493fb50eSLukas Wunner */
998493fb50eSLukas Wunner if (pdev->is_thunderbolt)
999493fb50eSLukas Wunner slot_cap |= PCI_EXP_SLTCAP_NCCS;
1000493fb50eSLukas Wunner
10012aeeef11SKenji Kaneshige ctrl->slot_cap = slot_cap;
10022aeeef11SKenji Kaneshige mutex_init(&ctrl->ctrl_lock);
10034ff3126eSLukas Wunner mutex_init(&ctrl->state_lock);
10045b3f7b7dSLukas Wunner init_rwsem(&ctrl->reset_lock);
100532a8cef2SLukas Wunner init_waitqueue_head(&ctrl->requester);
10062aeeef11SKenji Kaneshige init_waitqueue_head(&ctrl->queue);
10074ff3126eSLukas Wunner INIT_DELAYED_WORK(&ctrl->button_work, pciehp_queue_pushbutton_work);
10082aeeef11SKenji Kaneshige dbg_ctrl(ctrl);
10092cc56f30SBjorn Helgaas
10105790a9c7SLukas Wunner down_read(&pci_bus_sem);
10115790a9c7SLukas Wunner ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
10125790a9c7SLukas Wunner up_read(&pci_bus_sem);
10135790a9c7SLukas Wunner
101420285359SAlexandru Gagniuc pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP2, &slot_cap2);
101520285359SAlexandru Gagniuc if (slot_cap2 & PCI_EXP_SLTCAP2_IBPD) {
101620285359SAlexandru Gagniuc pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_IBPD_DISABLE,
101720285359SAlexandru Gagniuc PCI_EXP_SLTCTL_IBPD_DISABLE);
101820285359SAlexandru Gagniuc ctrl->inband_presence_disabled = 1;
101920285359SAlexandru Gagniuc }
102020285359SAlexandru Gagniuc
10210b382546SStuart Hayes if (dmi_first_match(inband_presence_disabled_dmi_table))
10220b382546SStuart Hayes ctrl->inband_presence_disabled = 1;
10230b382546SStuart Hayes
1024cdf6b736SLukas Wunner /* Clear all remaining event bits in Slot Status register. */
1025df72648cSBjorn Helgaas pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
1026df72648cSBjorn Helgaas PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
1027db63d400SMika Westerberg PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC |
1028cdf6b736SLukas Wunner PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC);
102908e7a7d2SMark Lord
103020285359SAlexandru Gagniuc ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c IbPresDis%c LLActRep%c%s\n",
1031afe2478fSBjorn Helgaas (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
1032afe2478fSBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
1033afe2478fSBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
1034afe2478fSBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP),
10353784e0c6SBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_AIP),
10363784e0c6SBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_PIP),
10373784e0c6SBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_HPC),
10383784e0c6SBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_HPS),
1039afe2478fSBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
1040afe2478fSBjorn Helgaas FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
104120285359SAlexandru Gagniuc FLAG(slot_cap2, PCI_EXP_SLTCAP2_IBPD),
10421f087398SMaciej W. Rozycki FLAG(pdev->link_active_reporting, true),
1043d22b3621SBjorn Helgaas pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : "");
10442aeeef11SKenji Kaneshige
10454e6a1335SLukas Wunner /*
10464e6a1335SLukas Wunner * If empty slot's power status is on, turn power off. The IRQ isn't
10474e6a1335SLukas Wunner * requested yet, so avoid triggering a notification with this command.
10484e6a1335SLukas Wunner */
10494e6a1335SLukas Wunner if (POWER_CTRL(ctrl)) {
10505790a9c7SLukas Wunner pciehp_get_power_status(ctrl, &poweron);
105180696f99SLukas Wunner if (!pciehp_card_present_or_link_active(ctrl) && poweron) {
10524e6a1335SLukas Wunner pcie_disable_notification(ctrl);
10535790a9c7SLukas Wunner pciehp_power_off_slot(ctrl);
10544e6a1335SLukas Wunner }
10554e6a1335SLukas Wunner }
10564e6a1335SLukas Wunner
1057c4635eb0SKenji Kaneshige return ctrl;
1058c4635eb0SKenji Kaneshige }
1059c4635eb0SKenji Kaneshige
pciehp_release_ctrl(struct controller * ctrl)106082a9e79eSKenji Kaneshige void pciehp_release_ctrl(struct controller *ctrl)
1061c4635eb0SKenji Kaneshige {
10624ff3126eSLukas Wunner cancel_delayed_work_sync(&ctrl->button_work);
1063c4635eb0SKenji Kaneshige kfree(ctrl);
106408e7a7d2SMark Lord }
1065d22b3621SBjorn Helgaas
quirk_cmd_compl(struct pci_dev * pdev)1066d22b3621SBjorn Helgaas static void quirk_cmd_compl(struct pci_dev *pdev)
1067d22b3621SBjorn Helgaas {
1068d22b3621SBjorn Helgaas u32 slot_cap;
1069d22b3621SBjorn Helgaas
1070d22b3621SBjorn Helgaas if (pci_is_pcie(pdev)) {
1071d22b3621SBjorn Helgaas pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
1072d22b3621SBjorn Helgaas if (slot_cap & PCI_EXP_SLTCAP_HPC &&
1073d22b3621SBjorn Helgaas !(slot_cap & PCI_EXP_SLTCAP_NCCS))
1074d22b3621SBjorn Helgaas pdev->broken_cmd_compl = 1;
1075d22b3621SBjorn Helgaas }
1076d22b3621SBjorn Helgaas }
1077d22b3621SBjorn Helgaas DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
1078d22b3621SBjorn Helgaas PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
107982b34b08SManivannan Sadhasivam DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x010e,
108082b34b08SManivannan Sadhasivam PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
10819f72d475SManivannan Sadhasivam DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0110,
10829f72d475SManivannan Sadhasivam PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
1083d22b3621SBjorn Helgaas DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0400,
1084d22b3621SBjorn Helgaas PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
1085d22b3621SBjorn Helgaas DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0401,
1086d22b3621SBjorn Helgaas PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
108722e4d639SShunyong Yang DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_HXT, 0x0401,
108822e4d639SShunyong Yang PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
1089