linux/drivers/pci/dwc/pci-layerscape.c
<<
>>
Prefs
   1/*
   2 * PCIe host controller driver for Freescale Layerscape SoCs
   3 *
   4 * Copyright (C) 2014 Freescale Semiconductor.
   5 *
   6 * Author: Minghuan Lian <Minghuan.Lian@freescale.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/interrupt.h>
  15#include <linux/init.h>
  16#include <linux/of_pci.h>
  17#include <linux/of_platform.h>
  18#include <linux/of_irq.h>
  19#include <linux/of_address.h>
  20#include <linux/pci.h>
  21#include <linux/platform_device.h>
  22#include <linux/resource.h>
  23#include <linux/mfd/syscon.h>
  24#include <linux/regmap.h>
  25
  26#include "pcie-designware.h"
  27
  28/* PEX1/2 Misc Ports Status Register */
  29#define SCFG_PEXMSCPORTSR(pex_idx)      (0x94 + (pex_idx) * 4)
  30#define LTSSM_STATE_SHIFT       20
  31#define LTSSM_STATE_MASK        0x3f
  32#define LTSSM_PCIE_L0           0x11 /* L0 state */
  33
  34/* PEX Internal Configuration Registers */
  35#define PCIE_STRFMR1            0x71c /* Symbol Timer & Filter Mask Register1 */
  36
  37#define PCIE_IATU_NUM           6
  38
  39struct ls_pcie_drvdata {
  40        u32 lut_offset;
  41        u32 ltssm_shift;
  42        u32 lut_dbg;
  43        const struct dw_pcie_host_ops *ops;
  44        const struct dw_pcie_ops *dw_pcie_ops;
  45};
  46
  47struct ls_pcie {
  48        struct dw_pcie *pci;
  49        void __iomem *lut;
  50        struct regmap *scfg;
  51        const struct ls_pcie_drvdata *drvdata;
  52        int index;
  53};
  54
  55#define to_ls_pcie(x)   dev_get_drvdata((x)->dev)
  56
  57static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
  58{
  59        struct dw_pcie *pci = pcie->pci;
  60        u32 header_type;
  61
  62        header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE);
  63        header_type &= 0x7f;
  64
  65        return header_type == PCI_HEADER_TYPE_BRIDGE;
  66}
  67
  68/* Clear multi-function bit */
  69static void ls_pcie_clear_multifunction(struct ls_pcie *pcie)
  70{
  71        struct dw_pcie *pci = pcie->pci;
  72
  73        iowrite8(PCI_HEADER_TYPE_BRIDGE, pci->dbi_base + PCI_HEADER_TYPE);
  74}
  75
  76/* Drop MSG TLP except for Vendor MSG */
  77static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie)
  78{
  79        u32 val;
  80        struct dw_pcie *pci = pcie->pci;
  81
  82        val = ioread32(pci->dbi_base + PCIE_STRFMR1);
  83        val &= 0xDFFFFFFF;
  84        iowrite32(val, pci->dbi_base + PCIE_STRFMR1);
  85}
  86
  87static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie)
  88{
  89        int i;
  90
  91        for (i = 0; i < PCIE_IATU_NUM; i++)
  92                dw_pcie_disable_atu(pcie->pci, DW_PCIE_REGION_OUTBOUND, i);
  93}
  94
  95static int ls1021_pcie_link_up(struct dw_pcie *pci)
  96{
  97        u32 state;
  98        struct ls_pcie *pcie = to_ls_pcie(pci);
  99
 100        if (!pcie->scfg)
 101                return 0;
 102
 103        regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
 104        state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
 105
 106        if (state < LTSSM_PCIE_L0)
 107                return 0;
 108
 109        return 1;
 110}
 111
 112static int ls_pcie_link_up(struct dw_pcie *pci)
 113{
 114        struct ls_pcie *pcie = to_ls_pcie(pci);
 115        u32 state;
 116
 117        state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >>
 118                 pcie->drvdata->ltssm_shift) &
 119                 LTSSM_STATE_MASK;
 120
 121        if (state < LTSSM_PCIE_L0)
 122                return 0;
 123
 124        return 1;
 125}
 126
 127static int ls_pcie_host_init(struct pcie_port *pp)
 128{
 129        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 130        struct ls_pcie *pcie = to_ls_pcie(pci);
 131
 132        /*
 133         * Disable outbound windows configured by the bootloader to avoid
 134         * one transaction hitting multiple outbound windows.
 135         * dw_pcie_setup_rc() will reconfigure the outbound windows.
 136         */
 137        ls_pcie_disable_outbound_atus(pcie);
 138
 139        dw_pcie_dbi_ro_wr_en(pci);
 140        ls_pcie_clear_multifunction(pcie);
 141        dw_pcie_dbi_ro_wr_dis(pci);
 142
 143        ls_pcie_drop_msg_tlp(pcie);
 144
 145        dw_pcie_setup_rc(pp);
 146
 147        return 0;
 148}
 149
 150static int ls1021_pcie_host_init(struct pcie_port *pp)
 151{
 152        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 153        struct ls_pcie *pcie = to_ls_pcie(pci);
 154        struct device *dev = pci->dev;
 155        u32 index[2];
 156        int ret;
 157
 158        pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node,
 159                                                     "fsl,pcie-scfg");
 160        if (IS_ERR(pcie->scfg)) {
 161                ret = PTR_ERR(pcie->scfg);
 162                dev_err(dev, "No syscfg phandle specified\n");
 163                pcie->scfg = NULL;
 164                return ret;
 165        }
 166
 167        if (of_property_read_u32_array(dev->of_node,
 168                                       "fsl,pcie-scfg", index, 2)) {
 169                pcie->scfg = NULL;
 170                return -EINVAL;
 171        }
 172        pcie->index = index[1];
 173
 174        return ls_pcie_host_init(pp);
 175}
 176
 177static int ls_pcie_msi_host_init(struct pcie_port *pp,
 178                                 struct msi_controller *chip)
 179{
 180        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 181        struct device *dev = pci->dev;
 182        struct device_node *np = dev->of_node;
 183        struct device_node *msi_node;
 184
 185        /*
 186         * The MSI domain is set by the generic of_msi_configure().  This
 187         * .msi_host_init() function keeps us from doing the default MSI
 188         * domain setup in dw_pcie_host_init() and also enforces the
 189         * requirement that "msi-parent" exists.
 190         */
 191        msi_node = of_parse_phandle(np, "msi-parent", 0);
 192        if (!msi_node) {
 193                dev_err(dev, "failed to find msi-parent\n");
 194                return -EINVAL;
 195        }
 196
 197        return 0;
 198}
 199
 200static const struct dw_pcie_host_ops ls1021_pcie_host_ops = {
 201        .host_init = ls1021_pcie_host_init,
 202        .msi_host_init = ls_pcie_msi_host_init,
 203};
 204
 205static const struct dw_pcie_host_ops ls_pcie_host_ops = {
 206        .host_init = ls_pcie_host_init,
 207        .msi_host_init = ls_pcie_msi_host_init,
 208};
 209
 210static const struct dw_pcie_ops dw_ls1021_pcie_ops = {
 211        .link_up = ls1021_pcie_link_up,
 212};
 213
 214static const struct dw_pcie_ops dw_ls_pcie_ops = {
 215        .link_up = ls_pcie_link_up,
 216};
 217
 218static struct ls_pcie_drvdata ls1021_drvdata = {
 219        .ops = &ls1021_pcie_host_ops,
 220        .dw_pcie_ops = &dw_ls1021_pcie_ops,
 221};
 222
 223static struct ls_pcie_drvdata ls1043_drvdata = {
 224        .lut_offset = 0x10000,
 225        .ltssm_shift = 24,
 226        .lut_dbg = 0x7fc,
 227        .ops = &ls_pcie_host_ops,
 228        .dw_pcie_ops = &dw_ls_pcie_ops,
 229};
 230
 231static struct ls_pcie_drvdata ls1046_drvdata = {
 232        .lut_offset = 0x80000,
 233        .ltssm_shift = 24,
 234        .lut_dbg = 0x407fc,
 235        .ops = &ls_pcie_host_ops,
 236        .dw_pcie_ops = &dw_ls_pcie_ops,
 237};
 238
 239static struct ls_pcie_drvdata ls2080_drvdata = {
 240        .lut_offset = 0x80000,
 241        .ltssm_shift = 0,
 242        .lut_dbg = 0x7fc,
 243        .ops = &ls_pcie_host_ops,
 244        .dw_pcie_ops = &dw_ls_pcie_ops,
 245};
 246
 247static struct ls_pcie_drvdata ls2088_drvdata = {
 248        .lut_offset = 0x80000,
 249        .ltssm_shift = 0,
 250        .lut_dbg = 0x407fc,
 251        .ops = &ls_pcie_host_ops,
 252        .dw_pcie_ops = &dw_ls_pcie_ops,
 253};
 254
 255static const struct of_device_id ls_pcie_of_match[] = {
 256        { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
 257        { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
 258        { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
 259        { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata },
 260        { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata },
 261        { .compatible = "fsl,ls2088a-pcie", .data = &ls2088_drvdata },
 262        { .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata },
 263        { },
 264};
 265
 266static int __init ls_add_pcie_port(struct ls_pcie *pcie)
 267{
 268        struct dw_pcie *pci = pcie->pci;
 269        struct pcie_port *pp = &pci->pp;
 270        struct device *dev = pci->dev;
 271        int ret;
 272
 273        pp->ops = pcie->drvdata->ops;
 274
 275        ret = dw_pcie_host_init(pp);
 276        if (ret) {
 277                dev_err(dev, "failed to initialize host\n");
 278                return ret;
 279        }
 280
 281        return 0;
 282}
 283
 284static int __init ls_pcie_probe(struct platform_device *pdev)
 285{
 286        struct device *dev = &pdev->dev;
 287        struct dw_pcie *pci;
 288        struct ls_pcie *pcie;
 289        struct resource *dbi_base;
 290        int ret;
 291
 292        pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
 293        if (!pcie)
 294                return -ENOMEM;
 295
 296        pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
 297        if (!pci)
 298                return -ENOMEM;
 299
 300        pcie->drvdata = of_device_get_match_data(dev);
 301
 302        pci->dev = dev;
 303        pci->ops = pcie->drvdata->dw_pcie_ops;
 304
 305        pcie->pci = pci;
 306
 307        dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
 308        pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
 309        if (IS_ERR(pci->dbi_base))
 310                return PTR_ERR(pci->dbi_base);
 311
 312        pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset;
 313
 314        if (!ls_pcie_is_bridge(pcie))
 315                return -ENODEV;
 316
 317        platform_set_drvdata(pdev, pcie);
 318
 319        ret = ls_add_pcie_port(pcie);
 320        if (ret < 0)
 321                return ret;
 322
 323        return 0;
 324}
 325
 326static struct platform_driver ls_pcie_driver = {
 327        .driver = {
 328                .name = "layerscape-pcie",
 329                .of_match_table = ls_pcie_of_match,
 330                .suppress_bind_attrs = true,
 331        },
 332};
 333builtin_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
 334