uboot/drivers/pci/pcie_layerscape_fixup.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2017-2020 NXP
   4 * Copyright 2014-2015 Freescale Semiconductor, Inc.
   5 * Layerscape PCIe driver
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <init.h>
  11#include <log.h>
  12#include <pci.h>
  13#include <asm/arch/fsl_serdes.h>
  14#include <asm/io.h>
  15#include <errno.h>
  16#ifdef CONFIG_OF_BOARD_SETUP
  17#include <linux/libfdt.h>
  18#include <fdt_support.h>
  19#ifdef CONFIG_ARM
  20#include <asm/arch/clock.h>
  21#endif
  22#include "pcie_layerscape.h"
  23#include "pcie_layerscape_fixup_common.h"
  24
  25#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2)
  26/*
  27 * Return next available LUT index.
  28 */
  29static int ls_pcie_next_lut_index(struct ls_pcie_rc *pcie_rc)
  30{
  31        if (pcie_rc->next_lut_index < PCIE_LUT_ENTRY_COUNT)
  32                return pcie_rc->next_lut_index++;
  33        else
  34                return -ENOSPC;  /* LUT is full */
  35}
  36
  37static void lut_writel(struct ls_pcie_rc *pcie_rc, unsigned int value,
  38                       unsigned int offset)
  39{
  40        struct ls_pcie *pcie = pcie_rc->pcie;
  41
  42        if (pcie->big_endian)
  43                out_be32(pcie->lut + offset, value);
  44        else
  45                out_le32(pcie->lut + offset, value);
  46}
  47
  48/*
  49 * Program a single LUT entry
  50 */
  51static void ls_pcie_lut_set_mapping(struct ls_pcie_rc *pcie_rc, int index,
  52                                    u32 devid, u32 streamid)
  53{
  54        /* leave mask as all zeroes, want to match all bits */
  55        lut_writel(pcie_rc, devid << 16, PCIE_LUT_UDR(index));
  56        lut_writel(pcie_rc, streamid | PCIE_LUT_ENABLE, PCIE_LUT_LDR(index));
  57}
  58
  59/*
  60 * An msi-map is a property to be added to the pci controller
  61 * node.  It is a table, where each entry consists of 4 fields
  62 * e.g.:
  63 *
  64 *      msi-map = <[devid] [phandle-to-msi-ctrl] [stream-id] [count]
  65 *                 [devid] [phandle-to-msi-ctrl] [stream-id] [count]>;
  66 */
  67static void fdt_pcie_set_msi_map_entry_ls(void *blob,
  68                                          struct ls_pcie_rc *pcie_rc,
  69                                          u32 devid, u32 streamid)
  70{
  71        u32 *prop;
  72        u32 phandle;
  73        int nodeoffset;
  74        uint svr;
  75        char *compat = NULL;
  76        struct ls_pcie *pcie = pcie_rc->pcie;
  77
  78        /* find pci controller node */
  79        nodeoffset = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie",
  80                                                   pcie_rc->dbi_res.start);
  81        if (nodeoffset < 0) {
  82#ifdef CONFIG_FSL_PCIE_COMPAT /* Compatible with older version of dts node */
  83                svr = (get_svr() >> SVR_VAR_PER_SHIFT) & 0xFFFFFE;
  84                if (svr == SVR_LS2088A || svr == SVR_LS2084A ||
  85                    svr == SVR_LS2048A || svr == SVR_LS2044A ||
  86                    svr == SVR_LS2081A || svr == SVR_LS2041A)
  87                        compat = "fsl,ls2088a-pcie";
  88                else
  89                        compat = CONFIG_FSL_PCIE_COMPAT;
  90                if (compat)
  91                        nodeoffset = fdt_node_offset_by_compat_reg(blob,
  92                                        compat, pcie_rc->dbi_res.start);
  93#endif
  94                if (nodeoffset < 0)
  95                        return;
  96        }
  97
  98        /* get phandle to MSI controller */
  99        prop = (u32 *)fdt_getprop(blob, nodeoffset, "msi-parent", 0);
 100        if (prop == NULL) {
 101                debug("\n%s: ERROR: missing msi-parent: PCIe%d\n",
 102                      __func__, pcie->idx);
 103                return;
 104        }
 105        phandle = fdt32_to_cpu(*prop);
 106
 107        /* set one msi-map row */
 108        fdt_appendprop_u32(blob, nodeoffset, "msi-map", devid);
 109        fdt_appendprop_u32(blob, nodeoffset, "msi-map", phandle);
 110        fdt_appendprop_u32(blob, nodeoffset, "msi-map", streamid);
 111        fdt_appendprop_u32(blob, nodeoffset, "msi-map", 1);
 112}
 113
 114/*
 115 * An iommu-map is a property to be added to the pci controller
 116 * node.  It is a table, where each entry consists of 4 fields
 117 * e.g.:
 118 *
 119 *      iommu-map = <[devid] [phandle-to-iommu-ctrl] [stream-id] [count]
 120 *                 [devid] [phandle-to-iommu-ctrl] [stream-id] [count]>;
 121 */
 122static void fdt_pcie_set_iommu_map_entry_ls(void *blob,
 123                                            struct ls_pcie_rc *pcie_rc,
 124                                            u32 devid, u32 streamid)
 125{
 126        u32 *prop;
 127        u32 iommu_map[4];
 128        int nodeoffset;
 129        int lenp;
 130        uint svr;
 131        char *compat = NULL;
 132        struct ls_pcie *pcie = pcie_rc->pcie;
 133
 134        /* find pci controller node */
 135        nodeoffset = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie",
 136                                                   pcie_rc->dbi_res.start);
 137        if (nodeoffset < 0) {
 138#ifdef CONFIG_FSL_PCIE_COMPAT /* Compatible with older version of dts node */
 139                svr = (get_svr() >> SVR_VAR_PER_SHIFT) & 0xFFFFFE;
 140                if (svr == SVR_LS2088A || svr == SVR_LS2084A ||
 141                    svr == SVR_LS2048A || svr == SVR_LS2044A ||
 142                    svr == SVR_LS2081A || svr == SVR_LS2041A)
 143                        compat = "fsl,ls2088a-pcie";
 144                else
 145                        compat = CONFIG_FSL_PCIE_COMPAT;
 146
 147                if (compat)
 148                        nodeoffset = fdt_node_offset_by_compat_reg(blob,
 149                                                compat, pcie_rc->dbi_res.start);
 150#endif
 151                if (nodeoffset < 0)
 152                        return;
 153        }
 154
 155        /* get phandle to iommu controller */
 156        prop = fdt_getprop_w(blob, nodeoffset, "iommu-map", &lenp);
 157        if (prop == NULL) {
 158                debug("\n%s: ERROR: missing iommu-map: PCIe%d\n",
 159                      __func__, pcie->idx);
 160                return;
 161        }
 162
 163        /* set iommu-map row */
 164        iommu_map[0] = cpu_to_fdt32(devid);
 165        iommu_map[1] = *++prop;
 166        iommu_map[2] = cpu_to_fdt32(streamid);
 167        iommu_map[3] = cpu_to_fdt32(1);
 168
 169        if (devid == 0) {
 170                fdt_setprop_inplace(blob, nodeoffset, "iommu-map",
 171                                    iommu_map, 16);
 172        } else {
 173                fdt_appendprop(blob, nodeoffset, "iommu-map", iommu_map, 16);
 174        }
 175}
 176
 177static void fdt_fixup_pcie_ls(void *blob)
 178{
 179        struct udevice *dev, *bus;
 180        struct ls_pcie_rc *pcie_rc;
 181        int streamid;
 182        int index;
 183        pci_dev_t bdf;
 184
 185        /* Scan all known buses */
 186        for (pci_find_first_device(&dev);
 187             dev;
 188             pci_find_next_device(&dev)) {
 189                for (bus = dev; device_is_on_pci_bus(bus);)
 190                        bus = bus->parent;
 191
 192                /* Only do the fixups for layerscape PCIe controllers */
 193                if (!device_is_compatible(bus, "fsl,ls-pcie") &&
 194                    !device_is_compatible(bus, CONFIG_FSL_PCIE_COMPAT))
 195                        continue;
 196
 197                pcie_rc = dev_get_priv(bus);
 198
 199                streamid = pcie_next_streamid(pcie_rc->stream_id_cur,
 200                                              pcie_rc->pcie->idx);
 201                if (streamid < 0) {
 202                        debug("ERROR: no stream ids free\n");
 203                        continue;
 204                } else {
 205                        pcie_rc->stream_id_cur++;
 206                }
 207
 208                index = ls_pcie_next_lut_index(pcie_rc);
 209                if (index < 0) {
 210                        debug("ERROR: no LUT indexes free\n");
 211                        continue;
 212                }
 213
 214                /* the DT fixup must be relative to the hose first_busno */
 215                bdf = dm_pci_get_bdf(dev) - PCI_BDF(bus->seq, 0, 0);
 216                /* map PCI b.d.f to streamID in LUT */
 217                ls_pcie_lut_set_mapping(pcie_rc, index, bdf >> 8,
 218                                        streamid);
 219                /* update msi-map in device tree */
 220                fdt_pcie_set_msi_map_entry_ls(blob, pcie_rc, bdf >> 8,
 221                                              streamid);
 222                /* update iommu-map in device tree */
 223                fdt_pcie_set_iommu_map_entry_ls(blob, pcie_rc, bdf >> 8,
 224                                                streamid);
 225        }
 226        pcie_board_fix_fdt(blob);
 227}
 228#endif
 229
 230static void ft_pcie_rc_fix(void *blob, struct ls_pcie_rc *pcie_rc)
 231{
 232        int off;
 233        uint svr;
 234        char *compat = NULL;
 235        struct ls_pcie *pcie = pcie_rc->pcie;
 236
 237        off = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie",
 238                                            pcie_rc->dbi_res.start);
 239        if (off < 0) {
 240#ifdef CONFIG_FSL_PCIE_COMPAT /* Compatible with older version of dts node */
 241                svr = (get_svr() >> SVR_VAR_PER_SHIFT) & 0xFFFFFE;
 242                if (svr == SVR_LS2088A || svr == SVR_LS2084A ||
 243                    svr == SVR_LS2048A || svr == SVR_LS2044A ||
 244                    svr == SVR_LS2081A || svr == SVR_LS2041A)
 245                        compat = "fsl,ls2088a-pcie";
 246                else
 247                        compat = CONFIG_FSL_PCIE_COMPAT;
 248                if (compat)
 249                        off = fdt_node_offset_by_compat_reg(blob,
 250                                        compat, pcie_rc->dbi_res.start);
 251#endif
 252                if (off < 0)
 253                        return;
 254        }
 255
 256        if (pcie_rc->enabled && pcie->mode == PCI_HEADER_TYPE_BRIDGE)
 257                fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0);
 258        else
 259                fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
 260}
 261
 262static void ft_pcie_ep_fix(void *blob, struct ls_pcie_rc *pcie_rc)
 263{
 264        int off;
 265        struct ls_pcie *pcie = pcie_rc->pcie;
 266
 267        off = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_EP_COMPAT,
 268                                            pcie_rc->dbi_res.start);
 269        if (off < 0)
 270                return;
 271
 272        if (pcie_rc->enabled && pcie->mode == PCI_HEADER_TYPE_NORMAL)
 273                fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0);
 274        else
 275                fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
 276}
 277
 278static void ft_pcie_ls_setup(void *blob, struct ls_pcie_rc *pcie_rc)
 279{
 280        ft_pcie_ep_fix(blob, pcie_rc);
 281        ft_pcie_rc_fix(blob, pcie_rc);
 282}
 283
 284/* Fixup Kernel DT for PCIe */
 285void ft_pci_setup_ls(void *blob, struct bd_info *bd)
 286{
 287        struct ls_pcie_rc *pcie_rc;
 288
 289        list_for_each_entry(pcie_rc, &ls_pcie_list, list)
 290                ft_pcie_ls_setup(blob, pcie_rc);
 291
 292#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2)
 293        fdt_fixup_pcie_ls(blob);
 294#endif
 295}
 296
 297#else /* !CONFIG_OF_BOARD_SETUP */
 298void ft_pci_setup_ls(void *blob, struct bd_info *bd)
 299{
 300}
 301#endif
 302