linux/drivers/pci/controller/cadence/pcie-cadence-plat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Cadence PCIe platform  driver.
   4 *
   5 * Copyright (c) 2019, Cadence Design Systems
   6 * Author: Tom Joseph <tjoseph@cadence.com>
   7 */
   8#include <linux/kernel.h>
   9#include <linux/of_address.h>
  10#include <linux/of_pci.h>
  11#include <linux/platform_device.h>
  12#include <linux/pm_runtime.h>
  13#include <linux/of_device.h>
  14#include "pcie-cadence.h"
  15
  16#define CDNS_PLAT_CPU_TO_BUS_ADDR       0x0FFFFFFF
  17
  18/**
  19 * struct cdns_plat_pcie - private data for this PCIe platform driver
  20 * @pcie: Cadence PCIe controller
  21 * @is_rc: Set to 1 indicates the PCIe controller mode is Root Complex,
  22 *         if 0 it is in Endpoint mode.
  23 */
  24struct cdns_plat_pcie {
  25        struct cdns_pcie        *pcie;
  26        bool is_rc;
  27};
  28
  29struct cdns_plat_pcie_of_data {
  30        bool is_rc;
  31};
  32
  33static const struct of_device_id cdns_plat_pcie_of_match[];
  34
  35static u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr)
  36{
  37        return cpu_addr & CDNS_PLAT_CPU_TO_BUS_ADDR;
  38}
  39
  40static const struct cdns_pcie_ops cdns_plat_ops = {
  41        .cpu_addr_fixup = cdns_plat_cpu_addr_fixup,
  42};
  43
  44static int cdns_plat_pcie_probe(struct platform_device *pdev)
  45{
  46        const struct cdns_plat_pcie_of_data *data;
  47        struct cdns_plat_pcie *cdns_plat_pcie;
  48        struct device *dev = &pdev->dev;
  49        struct pci_host_bridge *bridge;
  50        struct cdns_pcie_ep *ep;
  51        struct cdns_pcie_rc *rc;
  52        int phy_count;
  53        bool is_rc;
  54        int ret;
  55
  56        data = of_device_get_match_data(dev);
  57        if (!data)
  58                return -EINVAL;
  59
  60        is_rc = data->is_rc;
  61
  62        pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc);
  63        cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL);
  64        if (!cdns_plat_pcie)
  65                return -ENOMEM;
  66
  67        platform_set_drvdata(pdev, cdns_plat_pcie);
  68        if (is_rc) {
  69                if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST))
  70                        return -ENODEV;
  71
  72                bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc));
  73                if (!bridge)
  74                        return -ENOMEM;
  75
  76                rc = pci_host_bridge_priv(bridge);
  77                rc->pcie.dev = dev;
  78                rc->pcie.ops = &cdns_plat_ops;
  79                cdns_plat_pcie->pcie = &rc->pcie;
  80                cdns_plat_pcie->is_rc = is_rc;
  81
  82                ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
  83                if (ret) {
  84                        dev_err(dev, "failed to init phy\n");
  85                        return ret;
  86                }
  87                pm_runtime_enable(dev);
  88                ret = pm_runtime_get_sync(dev);
  89                if (ret < 0) {
  90                        dev_err(dev, "pm_runtime_get_sync() failed\n");
  91                        goto err_get_sync;
  92                }
  93
  94                ret = cdns_pcie_host_setup(rc);
  95                if (ret)
  96                        goto err_init;
  97        } else {
  98                if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_EP))
  99                        return -ENODEV;
 100
 101                ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
 102                if (!ep)
 103                        return -ENOMEM;
 104
 105                ep->pcie.dev = dev;
 106                ep->pcie.ops = &cdns_plat_ops;
 107                cdns_plat_pcie->pcie = &ep->pcie;
 108                cdns_plat_pcie->is_rc = is_rc;
 109
 110                ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
 111                if (ret) {
 112                        dev_err(dev, "failed to init phy\n");
 113                        return ret;
 114                }
 115
 116                pm_runtime_enable(dev);
 117                ret = pm_runtime_get_sync(dev);
 118                if (ret < 0) {
 119                        dev_err(dev, "pm_runtime_get_sync() failed\n");
 120                        goto err_get_sync;
 121                }
 122
 123                ret = cdns_pcie_ep_setup(ep);
 124                if (ret)
 125                        goto err_init;
 126        }
 127
 128        return 0;
 129
 130 err_init:
 131 err_get_sync:
 132        pm_runtime_put_sync(dev);
 133        pm_runtime_disable(dev);
 134        cdns_pcie_disable_phy(cdns_plat_pcie->pcie);
 135        phy_count = cdns_plat_pcie->pcie->phy_count;
 136        while (phy_count--)
 137                device_link_del(cdns_plat_pcie->pcie->link[phy_count]);
 138
 139        return 0;
 140}
 141
 142static void cdns_plat_pcie_shutdown(struct platform_device *pdev)
 143{
 144        struct device *dev = &pdev->dev;
 145        struct cdns_pcie *pcie = dev_get_drvdata(dev);
 146        int ret;
 147
 148        ret = pm_runtime_put_sync(dev);
 149        if (ret < 0)
 150                dev_dbg(dev, "pm_runtime_put_sync failed\n");
 151
 152        pm_runtime_disable(dev);
 153
 154        cdns_pcie_disable_phy(pcie);
 155}
 156
 157static const struct cdns_plat_pcie_of_data cdns_plat_pcie_host_of_data = {
 158        .is_rc = true,
 159};
 160
 161static const struct cdns_plat_pcie_of_data cdns_plat_pcie_ep_of_data = {
 162        .is_rc = false,
 163};
 164
 165static const struct of_device_id cdns_plat_pcie_of_match[] = {
 166        {
 167                .compatible = "cdns,cdns-pcie-host",
 168                .data = &cdns_plat_pcie_host_of_data,
 169        },
 170        {
 171                .compatible = "cdns,cdns-pcie-ep",
 172                .data = &cdns_plat_pcie_ep_of_data,
 173        },
 174        {},
 175};
 176
 177static struct platform_driver cdns_plat_pcie_driver = {
 178        .driver = {
 179                .name = "cdns-pcie",
 180                .of_match_table = cdns_plat_pcie_of_match,
 181                .pm     = &cdns_pcie_pm_ops,
 182        },
 183        .probe = cdns_plat_pcie_probe,
 184        .shutdown = cdns_plat_pcie_shutdown,
 185};
 186builtin_platform_driver(cdns_plat_pcie_driver);
 187