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        const struct of_device_id *match;
  49        struct device *dev = &pdev->dev;
  50        struct pci_host_bridge *bridge;
  51        struct cdns_pcie_ep *ep;
  52        struct cdns_pcie_rc *rc;
  53        int phy_count;
  54        bool is_rc;
  55        int ret;
  56
  57        match = of_match_device(cdns_plat_pcie_of_match, dev);
  58        if (!match)
  59                return -EINVAL;
  60
  61        data = (struct cdns_plat_pcie_of_data *)match->data;
  62        is_rc = data->is_rc;
  63
  64        pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc);
  65        cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL);
  66        if (!cdns_plat_pcie)
  67                return -ENOMEM;
  68
  69        platform_set_drvdata(pdev, cdns_plat_pcie);
  70        if (is_rc) {
  71                if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST))
  72                        return -ENODEV;
  73
  74                bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc));
  75                if (!bridge)
  76                        return -ENOMEM;
  77
  78                rc = pci_host_bridge_priv(bridge);
  79                rc->pcie.dev = dev;
  80                rc->pcie.ops = &cdns_plat_ops;
  81                cdns_plat_pcie->pcie = &rc->pcie;
  82                cdns_plat_pcie->is_rc = is_rc;
  83
  84                ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
  85                if (ret) {
  86                        dev_err(dev, "failed to init phy\n");
  87                        return ret;
  88                }
  89                pm_runtime_enable(dev);
  90                ret = pm_runtime_get_sync(dev);
  91                if (ret < 0) {
  92                        dev_err(dev, "pm_runtime_get_sync() failed\n");
  93                        goto err_get_sync;
  94                }
  95
  96                ret = cdns_pcie_host_setup(rc);
  97                if (ret)
  98                        goto err_init;
  99        } else {
 100                if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_EP))
 101                        return -ENODEV;
 102
 103                ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
 104                if (!ep)
 105                        return -ENOMEM;
 106
 107                ep->pcie.dev = dev;
 108                ep->pcie.ops = &cdns_plat_ops;
 109                cdns_plat_pcie->pcie = &ep->pcie;
 110                cdns_plat_pcie->is_rc = is_rc;
 111
 112                ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
 113                if (ret) {
 114                        dev_err(dev, "failed to init phy\n");
 115                        return ret;
 116                }
 117
 118                pm_runtime_enable(dev);
 119                ret = pm_runtime_get_sync(dev);
 120                if (ret < 0) {
 121                        dev_err(dev, "pm_runtime_get_sync() failed\n");
 122                        goto err_get_sync;
 123                }
 124
 125                ret = cdns_pcie_ep_setup(ep);
 126                if (ret)
 127                        goto err_init;
 128        }
 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