LCOV - code coverage report
Current view: top level - drivers/pci/hotplug - pciehp_core.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 6 154 3.9 %
Date: 2022-03-28 16:04:14 Functions: 1 15 6.7 %
Branches: 0 54 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0+
       2                 :            : /*
       3                 :            :  * PCI Express Hot Plug Controller Driver
       4                 :            :  *
       5                 :            :  * Copyright (C) 1995,2001 Compaq Computer Corporation
       6                 :            :  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
       7                 :            :  * Copyright (C) 2001 IBM Corp.
       8                 :            :  * Copyright (C) 2003-2004 Intel Corporation
       9                 :            :  *
      10                 :            :  * All rights reserved.
      11                 :            :  *
      12                 :            :  * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
      13                 :            :  *
      14                 :            :  * Authors:
      15                 :            :  *   Dan Zink <dan.zink@compaq.com>
      16                 :            :  *   Greg Kroah-Hartman <greg@kroah.com>
      17                 :            :  *   Dely Sy <dely.l.sy@intel.com>"
      18                 :            :  */
      19                 :            : 
      20                 :            : #define pr_fmt(fmt) "pciehp: " fmt
      21                 :            : #define dev_fmt pr_fmt
      22                 :            : 
      23                 :            : #include <linux/moduleparam.h>
      24                 :            : #include <linux/kernel.h>
      25                 :            : #include <linux/slab.h>
      26                 :            : #include <linux/types.h>
      27                 :            : #include <linux/pci.h>
      28                 :            : #include "pciehp.h"
      29                 :            : 
      30                 :            : #include "../pci.h"
      31                 :            : 
      32                 :            : /* Global variables */
      33                 :            : bool pciehp_poll_mode;
      34                 :            : int pciehp_poll_time;
      35                 :            : 
      36                 :            : /*
      37                 :            :  * not really modular, but the easiest way to keep compat with existing
      38                 :            :  * bootargs behaviour is to continue using module_param here.
      39                 :            :  */
      40                 :            : module_param(pciehp_poll_mode, bool, 0644);
      41                 :            : module_param(pciehp_poll_time, int, 0644);
      42                 :            : MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
      43                 :            : MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
      44                 :            : 
      45                 :            : static int set_attention_status(struct hotplug_slot *slot, u8 value);
      46                 :            : static int get_power_status(struct hotplug_slot *slot, u8 *value);
      47                 :            : static int get_latch_status(struct hotplug_slot *slot, u8 *value);
      48                 :            : static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
      49                 :            : 
      50                 :          0 : static int init_slot(struct controller *ctrl)
      51                 :            : {
      52                 :          0 :         struct hotplug_slot_ops *ops;
      53                 :          0 :         char name[SLOT_NAME_SIZE];
      54                 :          0 :         int retval;
      55                 :            : 
      56                 :            :         /* Setup hotplug slot ops */
      57                 :          0 :         ops = kzalloc(sizeof(*ops), GFP_KERNEL);
      58         [ #  # ]:          0 :         if (!ops)
      59                 :            :                 return -ENOMEM;
      60                 :            : 
      61                 :          0 :         ops->enable_slot = pciehp_sysfs_enable_slot;
      62                 :          0 :         ops->disable_slot = pciehp_sysfs_disable_slot;
      63                 :          0 :         ops->get_power_status = get_power_status;
      64                 :          0 :         ops->get_adapter_status = get_adapter_status;
      65                 :          0 :         ops->reset_slot = pciehp_reset_slot;
      66         [ #  # ]:          0 :         if (MRL_SENS(ctrl))
      67                 :          0 :                 ops->get_latch_status = get_latch_status;
      68         [ #  # ]:          0 :         if (ATTN_LED(ctrl)) {
      69                 :          0 :                 ops->get_attention_status = pciehp_get_attention_status;
      70                 :          0 :                 ops->set_attention_status = set_attention_status;
      71         [ #  # ]:          0 :         } else if (ctrl->pcie->port->hotplug_user_indicators) {
      72                 :          0 :                 ops->get_attention_status = pciehp_get_raw_indicator_status;
      73                 :          0 :                 ops->set_attention_status = pciehp_set_raw_indicator_status;
      74                 :            :         }
      75                 :            : 
      76                 :            :         /* register this slot with the hotplug pci core */
      77                 :          0 :         ctrl->hotplug_slot.ops = ops;
      78                 :          0 :         snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
      79                 :            : 
      80                 :          0 :         retval = pci_hp_initialize(&ctrl->hotplug_slot,
      81                 :            :                                    ctrl->pcie->port->subordinate, 0, name);
      82         [ #  # ]:          0 :         if (retval) {
      83                 :          0 :                 ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
      84                 :          0 :                 kfree(ops);
      85                 :            :         }
      86                 :            :         return retval;
      87                 :            : }
      88                 :            : 
      89                 :          0 : static void cleanup_slot(struct controller *ctrl)
      90                 :            : {
      91                 :          0 :         struct hotplug_slot *hotplug_slot = &ctrl->hotplug_slot;
      92                 :            : 
      93                 :          0 :         pci_hp_destroy(hotplug_slot);
      94                 :          0 :         kfree(hotplug_slot->ops);
      95                 :          0 : }
      96                 :            : 
      97                 :            : /*
      98                 :            :  * set_attention_status - Turns the Attention Indicator on, off or blinking
      99                 :            :  */
     100                 :          0 : static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
     101                 :            : {
     102         [ #  # ]:          0 :         struct controller *ctrl = to_ctrl(hotplug_slot);
     103                 :          0 :         struct pci_dev *pdev = ctrl->pcie->port;
     104                 :            : 
     105         [ #  # ]:          0 :         if (status)
     106                 :          0 :                 status <<= PCI_EXP_SLTCTL_ATTN_IND_SHIFT;
     107                 :            :         else
     108                 :            :                 status = PCI_EXP_SLTCTL_ATTN_IND_OFF;
     109                 :            : 
     110                 :          0 :         pci_config_pm_runtime_get(pdev);
     111                 :          0 :         pciehp_set_indicators(ctrl, INDICATOR_NOOP, status);
     112                 :          0 :         pci_config_pm_runtime_put(pdev);
     113                 :          0 :         return 0;
     114                 :            : }
     115                 :            : 
     116                 :          0 : static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
     117                 :            : {
     118                 :          0 :         struct controller *ctrl = to_ctrl(hotplug_slot);
     119                 :          0 :         struct pci_dev *pdev = ctrl->pcie->port;
     120                 :            : 
     121                 :          0 :         pci_config_pm_runtime_get(pdev);
     122                 :          0 :         pciehp_get_power_status(ctrl, value);
     123                 :          0 :         pci_config_pm_runtime_put(pdev);
     124                 :          0 :         return 0;
     125                 :            : }
     126                 :            : 
     127                 :          0 : static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
     128                 :            : {
     129                 :          0 :         struct controller *ctrl = to_ctrl(hotplug_slot);
     130                 :          0 :         struct pci_dev *pdev = ctrl->pcie->port;
     131                 :            : 
     132                 :          0 :         pci_config_pm_runtime_get(pdev);
     133                 :          0 :         pciehp_get_latch_status(ctrl, value);
     134                 :          0 :         pci_config_pm_runtime_put(pdev);
     135                 :          0 :         return 0;
     136                 :            : }
     137                 :            : 
     138                 :          0 : static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
     139                 :            : {
     140                 :          0 :         struct controller *ctrl = to_ctrl(hotplug_slot);
     141                 :          0 :         struct pci_dev *pdev = ctrl->pcie->port;
     142                 :          0 :         int ret;
     143                 :            : 
     144                 :          0 :         pci_config_pm_runtime_get(pdev);
     145                 :          0 :         ret = pciehp_card_present_or_link_active(ctrl);
     146                 :          0 :         pci_config_pm_runtime_put(pdev);
     147         [ #  # ]:          0 :         if (ret < 0)
     148                 :            :                 return ret;
     149                 :            : 
     150                 :          0 :         *value = ret;
     151                 :          0 :         return 0;
     152                 :            : }
     153                 :            : 
     154                 :            : /**
     155                 :            :  * pciehp_check_presence() - synthesize event if presence has changed
     156                 :            :  *
     157                 :            :  * On probe and resume, an explicit presence check is necessary to bring up an
     158                 :            :  * occupied slot or bring down an unoccupied slot.  This can't be triggered by
     159                 :            :  * events in the Slot Status register, they may be stale and are therefore
     160                 :            :  * cleared.  Secondly, sending an interrupt for "events that occur while
     161                 :            :  * interrupt generation is disabled [when] interrupt generation is subsequently
     162                 :            :  * enabled" is optional per PCIe r4.0, sec 6.7.3.4.
     163                 :            :  */
     164                 :          0 : static void pciehp_check_presence(struct controller *ctrl)
     165                 :            : {
     166                 :          0 :         int occupied;
     167                 :            : 
     168                 :          0 :         down_read(&ctrl->reset_lock);
     169                 :          0 :         mutex_lock(&ctrl->state_lock);
     170                 :            : 
     171                 :          0 :         occupied = pciehp_card_present_or_link_active(ctrl);
     172   [ #  #  #  # ]:          0 :         if ((occupied > 0 && (ctrl->state == OFF_STATE ||
     173         [ #  # ]:          0 :                           ctrl->state == BLINKINGON_STATE)) ||
     174         [ #  # ]:          0 :             (!occupied && (ctrl->state == ON_STATE ||
     175                 :            :                            ctrl->state == BLINKINGOFF_STATE)))
     176                 :          0 :                 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
     177                 :            : 
     178                 :          0 :         mutex_unlock(&ctrl->state_lock);
     179                 :          0 :         up_read(&ctrl->reset_lock);
     180                 :          0 : }
     181                 :            : 
     182                 :          0 : static int pciehp_probe(struct pcie_device *dev)
     183                 :            : {
     184                 :          0 :         int rc;
     185                 :          0 :         struct controller *ctrl;
     186                 :            : 
     187                 :            :         /* If this is not a "hotplug" service, we have no business here. */
     188         [ #  # ]:          0 :         if (dev->service != PCIE_PORT_SERVICE_HP)
     189                 :            :                 return -ENODEV;
     190                 :            : 
     191         [ #  # ]:          0 :         if (!dev->port->subordinate) {
     192                 :            :                 /* Can happen if we run out of bus numbers during probe */
     193                 :          0 :                 pci_err(dev->port,
     194                 :            :                         "Hotplug bridge without secondary bus, ignoring\n");
     195                 :          0 :                 return -ENODEV;
     196                 :            :         }
     197                 :            : 
     198                 :          0 :         ctrl = pcie_init(dev);
     199         [ #  # ]:          0 :         if (!ctrl) {
     200                 :          0 :                 pci_err(dev->port, "Controller initialization failed\n");
     201                 :          0 :                 return -ENODEV;
     202                 :            :         }
     203                 :          0 :         set_service_data(dev, ctrl);
     204                 :            : 
     205                 :            :         /* Setup the slot information structures */
     206                 :          0 :         rc = init_slot(ctrl);
     207         [ #  # ]:          0 :         if (rc) {
     208         [ #  # ]:          0 :                 if (rc == -EBUSY)
     209                 :          0 :                         ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
     210                 :            :                 else
     211                 :          0 :                         ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc);
     212                 :          0 :                 goto err_out_release_ctlr;
     213                 :            :         }
     214                 :            : 
     215                 :            :         /* Enable events after we have setup the data structures */
     216                 :          0 :         rc = pcie_init_notification(ctrl);
     217         [ #  # ]:          0 :         if (rc) {
     218                 :          0 :                 ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc);
     219                 :          0 :                 goto err_out_free_ctrl_slot;
     220                 :            :         }
     221                 :            : 
     222                 :            :         /* Publish to user space */
     223                 :          0 :         rc = pci_hp_add(&ctrl->hotplug_slot);
     224         [ #  # ]:          0 :         if (rc) {
     225                 :          0 :                 ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);
     226                 :          0 :                 goto err_out_shutdown_notification;
     227                 :            :         }
     228                 :            : 
     229                 :          0 :         pciehp_check_presence(ctrl);
     230                 :            : 
     231                 :          0 :         return 0;
     232                 :            : 
     233                 :            : err_out_shutdown_notification:
     234                 :          0 :         pcie_shutdown_notification(ctrl);
     235                 :          0 : err_out_free_ctrl_slot:
     236                 :          0 :         cleanup_slot(ctrl);
     237                 :          0 : err_out_release_ctlr:
     238                 :          0 :         pciehp_release_ctrl(ctrl);
     239                 :          0 :         return -ENODEV;
     240                 :            : }
     241                 :            : 
     242                 :          0 : static void pciehp_remove(struct pcie_device *dev)
     243                 :            : {
     244                 :          0 :         struct controller *ctrl = get_service_data(dev);
     245                 :            : 
     246                 :          0 :         pci_hp_del(&ctrl->hotplug_slot);
     247                 :          0 :         pcie_shutdown_notification(ctrl);
     248                 :          0 :         cleanup_slot(ctrl);
     249                 :          0 :         pciehp_release_ctrl(ctrl);
     250                 :          0 : }
     251                 :            : 
     252                 :            : #ifdef CONFIG_PM
     253                 :            : static bool pme_is_native(struct pcie_device *dev)
     254                 :            : {
     255                 :            :         const struct pci_host_bridge *host;
     256                 :            : 
     257                 :            :         host = pci_find_host_bridge(dev->port->bus);
     258                 :            :         return pcie_ports_native || host->native_pme;
     259                 :            : }
     260                 :            : 
     261                 :          0 : static void pciehp_disable_interrupt(struct pcie_device *dev)
     262                 :            : {
     263                 :            :         /*
     264                 :            :          * Disable hotplug interrupt so that it does not trigger
     265                 :            :          * immediately when the downstream link goes down.
     266                 :            :          */
     267         [ #  # ]:          0 :         if (pme_is_native(dev))
     268                 :          0 :                 pcie_disable_interrupt(get_service_data(dev));
     269                 :          0 : }
     270                 :            : 
     271                 :            : #ifdef CONFIG_PM_SLEEP
     272                 :          0 : static int pciehp_suspend(struct pcie_device *dev)
     273                 :            : {
     274                 :            :         /*
     275                 :            :          * If the port is already runtime suspended we can keep it that
     276                 :            :          * way.
     277                 :            :          */
     278         [ #  # ]:          0 :         if (dev_pm_smart_suspend_and_suspended(&dev->port->dev))
     279                 :            :                 return 0;
     280                 :            : 
     281                 :          0 :         pciehp_disable_interrupt(dev);
     282                 :          0 :         return 0;
     283                 :            : }
     284                 :            : 
     285                 :          0 : static int pciehp_resume_noirq(struct pcie_device *dev)
     286                 :            : {
     287         [ #  # ]:          0 :         struct controller *ctrl = get_service_data(dev);
     288                 :            : 
     289                 :            :         /* pci_restore_state() just wrote to the Slot Control register */
     290                 :          0 :         ctrl->cmd_started = jiffies;
     291                 :          0 :         ctrl->cmd_busy = true;
     292                 :            : 
     293                 :            :         /* clear spurious events from rediscovery of inserted card */
     294         [ #  # ]:          0 :         if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE)
     295                 :          0 :                 pcie_clear_hotplug_events(ctrl);
     296                 :            : 
     297                 :          0 :         return 0;
     298                 :            : }
     299                 :            : #endif
     300                 :            : 
     301                 :          0 : static int pciehp_resume(struct pcie_device *dev)
     302                 :            : {
     303                 :          0 :         struct controller *ctrl = get_service_data(dev);
     304                 :            : 
     305         [ #  # ]:          0 :         if (pme_is_native(dev))
     306                 :          0 :                 pcie_enable_interrupt(ctrl);
     307                 :            : 
     308                 :          0 :         pciehp_check_presence(ctrl);
     309                 :            : 
     310                 :          0 :         return 0;
     311                 :            : }
     312                 :            : 
     313                 :          0 : static int pciehp_runtime_suspend(struct pcie_device *dev)
     314                 :            : {
     315                 :          0 :         pciehp_disable_interrupt(dev);
     316                 :          0 :         return 0;
     317                 :            : }
     318                 :            : 
     319                 :          0 : static int pciehp_runtime_resume(struct pcie_device *dev)
     320                 :            : {
     321         [ #  # ]:          0 :         struct controller *ctrl = get_service_data(dev);
     322                 :            : 
     323                 :            :         /* pci_restore_state() just wrote to the Slot Control register */
     324                 :          0 :         ctrl->cmd_started = jiffies;
     325                 :          0 :         ctrl->cmd_busy = true;
     326                 :            : 
     327                 :            :         /* clear spurious events from rediscovery of inserted card */
     328   [ #  #  #  # ]:          0 :         if ((ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) &&
     329                 :          0 :              pme_is_native(dev))
     330                 :          0 :                 pcie_clear_hotplug_events(ctrl);
     331                 :            : 
     332                 :          0 :         return pciehp_resume(dev);
     333                 :            : }
     334                 :            : #endif /* PM */
     335                 :            : 
     336                 :            : static struct pcie_port_service_driver hpdriver_portdrv = {
     337                 :            :         .name           = "pciehp",
     338                 :            :         .port_type      = PCIE_ANY_PORT,
     339                 :            :         .service        = PCIE_PORT_SERVICE_HP,
     340                 :            : 
     341                 :            :         .probe          = pciehp_probe,
     342                 :            :         .remove         = pciehp_remove,
     343                 :            : 
     344                 :            : #ifdef  CONFIG_PM
     345                 :            : #ifdef  CONFIG_PM_SLEEP
     346                 :            :         .suspend        = pciehp_suspend,
     347                 :            :         .resume_noirq   = pciehp_resume_noirq,
     348                 :            :         .resume         = pciehp_resume,
     349                 :            : #endif
     350                 :            :         .runtime_suspend = pciehp_runtime_suspend,
     351                 :            :         .runtime_resume = pciehp_runtime_resume,
     352                 :            : #endif  /* PM */
     353                 :            : };
     354                 :            : 
     355                 :         13 : int __init pcie_hp_init(void)
     356                 :            : {
     357                 :         13 :         int retval = 0;
     358                 :            : 
     359                 :         13 :         retval = pcie_port_service_register(&hpdriver_portdrv);
     360                 :         13 :         pr_debug("pcie_port_service_register = %d\n", retval);
     361                 :         13 :         if (retval)
     362                 :            :                 pr_debug("Failure to register service\n");
     363                 :            : 
     364                 :         13 :         return retval;
     365                 :            : }

Generated by: LCOV version 1.14