linux/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
   2/* Copyright (c) 2019 Mellanox Technologies */
   3
   4#include <linux/pci.h>
   5#include "mlx5_core.h"
   6#include "pci_vsc.h"
   7
   8#define MLX5_EXTRACT_C(source, offset, size)    \
   9        ((((u32)(source)) >> (offset)) & MLX5_ONES32(size))
  10#define MLX5_EXTRACT(src, start, len)           \
  11        (((len) == 32) ? (src) : MLX5_EXTRACT_C(src, start, len))
  12#define MLX5_ONES32(size)                       \
  13        ((size) ? (0xffffffff >> (32 - (size))) : 0)
  14#define MLX5_MASK32(offset, size)               \
  15        (MLX5_ONES32(size) << (offset))
  16#define MLX5_MERGE_C(rsrc1, rsrc2, start, len)  \
  17        ((((rsrc2) << (start)) & (MLX5_MASK32((start), (len)))) | \
  18        ((rsrc1) & (~MLX5_MASK32((start), (len)))))
  19#define MLX5_MERGE(rsrc1, rsrc2, start, len)    \
  20        (((len) == 32) ? (rsrc2) : MLX5_MERGE_C(rsrc1, rsrc2, start, len))
  21#define vsc_read(dev, offset, val) \
  22        pci_read_config_dword((dev)->pdev, (dev)->vsc_addr + (offset), (val))
  23#define vsc_write(dev, offset, val) \
  24        pci_write_config_dword((dev)->pdev, (dev)->vsc_addr + (offset), (val))
  25#define VSC_MAX_RETRIES 2048
  26
  27enum {
  28        VSC_CTRL_OFFSET = 0x4,
  29        VSC_COUNTER_OFFSET = 0x8,
  30        VSC_SEMAPHORE_OFFSET = 0xc,
  31        VSC_ADDR_OFFSET = 0x10,
  32        VSC_DATA_OFFSET = 0x14,
  33
  34        VSC_FLAG_BIT_OFFS = 31,
  35        VSC_FLAG_BIT_LEN = 1,
  36
  37        VSC_SYND_BIT_OFFS = 30,
  38        VSC_SYND_BIT_LEN = 1,
  39
  40        VSC_ADDR_BIT_OFFS = 0,
  41        VSC_ADDR_BIT_LEN = 30,
  42
  43        VSC_SPACE_BIT_OFFS = 0,
  44        VSC_SPACE_BIT_LEN = 16,
  45
  46        VSC_SIZE_VLD_BIT_OFFS = 28,
  47        VSC_SIZE_VLD_BIT_LEN = 1,
  48
  49        VSC_STATUS_BIT_OFFS = 29,
  50        VSC_STATUS_BIT_LEN = 3,
  51};
  52
  53void mlx5_pci_vsc_init(struct mlx5_core_dev *dev)
  54{
  55        if (!mlx5_core_is_pf(dev))
  56                return;
  57
  58        dev->vsc_addr = pci_find_capability(dev->pdev,
  59                                            PCI_CAP_ID_VNDR);
  60        if (!dev->vsc_addr)
  61                mlx5_core_warn(dev, "Failed to get valid vendor specific ID\n");
  62}
  63
  64int mlx5_vsc_gw_lock(struct mlx5_core_dev *dev)
  65{
  66        u32 counter = 0;
  67        int retries = 0;
  68        u32 lock_val;
  69        int ret;
  70
  71        pci_cfg_access_lock(dev->pdev);
  72        do {
  73                if (retries > VSC_MAX_RETRIES) {
  74                        ret = -EBUSY;
  75                        goto pci_unlock;
  76                }
  77
  78                /* Check if semaphore is already locked */
  79                ret = vsc_read(dev, VSC_SEMAPHORE_OFFSET, &lock_val);
  80                if (ret)
  81                        goto pci_unlock;
  82
  83                if (lock_val) {
  84                        retries++;
  85                        usleep_range(1000, 2000);
  86                        continue;
  87                }
  88
  89                /* Read and write counter value, if written value is
  90                 * the same, semaphore was acquired successfully.
  91                 */
  92                ret = vsc_read(dev, VSC_COUNTER_OFFSET, &counter);
  93                if (ret)
  94                        goto pci_unlock;
  95
  96                ret = vsc_write(dev, VSC_SEMAPHORE_OFFSET, counter);
  97                if (ret)
  98                        goto pci_unlock;
  99
 100                ret = vsc_read(dev, VSC_SEMAPHORE_OFFSET, &lock_val);
 101                if (ret)
 102                        goto pci_unlock;
 103
 104                retries++;
 105        } while (counter != lock_val);
 106
 107        return 0;
 108
 109pci_unlock:
 110        pci_cfg_access_unlock(dev->pdev);
 111        return ret;
 112}
 113
 114int mlx5_vsc_gw_unlock(struct mlx5_core_dev *dev)
 115{
 116        int ret;
 117
 118        ret = vsc_write(dev, VSC_SEMAPHORE_OFFSET, MLX5_VSC_UNLOCK);
 119        pci_cfg_access_unlock(dev->pdev);
 120        return ret;
 121}
 122
 123int mlx5_vsc_gw_set_space(struct mlx5_core_dev *dev, u16 space,
 124                          u32 *ret_space_size)
 125{
 126        int ret;
 127        u32 val = 0;
 128
 129        if (!mlx5_vsc_accessible(dev))
 130                return -EINVAL;
 131
 132        if (ret_space_size)
 133                *ret_space_size = 0;
 134
 135        /* Get a unique val */
 136        ret = vsc_read(dev, VSC_CTRL_OFFSET, &val);
 137        if (ret)
 138                goto out;
 139
 140        /* Try to modify the lock */
 141        val = MLX5_MERGE(val, space, VSC_SPACE_BIT_OFFS, VSC_SPACE_BIT_LEN);
 142        ret = vsc_write(dev, VSC_CTRL_OFFSET, val);
 143        if (ret)
 144                goto out;
 145
 146        /* Verify lock was modified */
 147        ret = vsc_read(dev, VSC_CTRL_OFFSET, &val);
 148        if (ret)
 149                goto out;
 150
 151        if (MLX5_EXTRACT(val, VSC_STATUS_BIT_OFFS, VSC_STATUS_BIT_LEN) == 0)
 152                return -EINVAL;
 153
 154        /* Get space max address if indicated by size valid bit */
 155        if (ret_space_size &&
 156            MLX5_EXTRACT(val, VSC_SIZE_VLD_BIT_OFFS, VSC_SIZE_VLD_BIT_LEN)) {
 157                ret = vsc_read(dev, VSC_ADDR_OFFSET, &val);
 158                if (ret) {
 159                        mlx5_core_warn(dev, "Failed to get max space size\n");
 160                        goto out;
 161                }
 162                *ret_space_size = MLX5_EXTRACT(val, VSC_ADDR_BIT_OFFS,
 163                                               VSC_ADDR_BIT_LEN);
 164        }
 165        return 0;
 166
 167out:
 168        return ret;
 169}
 170
 171static int mlx5_vsc_wait_on_flag(struct mlx5_core_dev *dev, u8 expected_val)
 172{
 173        int retries = 0;
 174        u32 flag;
 175        int ret;
 176
 177        do {
 178                if (retries > VSC_MAX_RETRIES)
 179                        return -EBUSY;
 180
 181                ret = vsc_read(dev, VSC_ADDR_OFFSET, &flag);
 182                if (ret)
 183                        return ret;
 184                flag = MLX5_EXTRACT(flag, VSC_FLAG_BIT_OFFS, VSC_FLAG_BIT_LEN);
 185                retries++;
 186
 187                if ((retries & 0xf) == 0)
 188                        usleep_range(1000, 2000);
 189
 190        } while (flag != expected_val);
 191
 192        return 0;
 193}
 194
 195static int mlx5_vsc_gw_write(struct mlx5_core_dev *dev, unsigned int address,
 196                             u32 data)
 197{
 198        int ret;
 199
 200        if (MLX5_EXTRACT(address, VSC_SYND_BIT_OFFS,
 201                         VSC_FLAG_BIT_LEN + VSC_SYND_BIT_LEN))
 202                return -EINVAL;
 203
 204        /* Set flag to 0x1 */
 205        address = MLX5_MERGE(address, 1, VSC_FLAG_BIT_OFFS, 1);
 206        ret = vsc_write(dev, VSC_DATA_OFFSET, data);
 207        if (ret)
 208                goto out;
 209
 210        ret = vsc_write(dev, VSC_ADDR_OFFSET, address);
 211        if (ret)
 212                goto out;
 213
 214        /* Wait for the flag to be cleared */
 215        ret = mlx5_vsc_wait_on_flag(dev, 0);
 216
 217out:
 218        return ret;
 219}
 220
 221static int mlx5_vsc_gw_read(struct mlx5_core_dev *dev, unsigned int address,
 222                            u32 *data)
 223{
 224        int ret;
 225
 226        if (MLX5_EXTRACT(address, VSC_SYND_BIT_OFFS,
 227                         VSC_FLAG_BIT_LEN + VSC_SYND_BIT_LEN))
 228                return -EINVAL;
 229
 230        ret = vsc_write(dev, VSC_ADDR_OFFSET, address);
 231        if (ret)
 232                goto out;
 233
 234        ret = mlx5_vsc_wait_on_flag(dev, 1);
 235        if (ret)
 236                goto out;
 237
 238        ret = vsc_read(dev, VSC_DATA_OFFSET, data);
 239out:
 240        return ret;
 241}
 242
 243static int mlx5_vsc_gw_read_fast(struct mlx5_core_dev *dev,
 244                                 unsigned int read_addr,
 245                                 unsigned int *next_read_addr,
 246                                 u32 *data)
 247{
 248        int ret;
 249
 250        ret = mlx5_vsc_gw_read(dev, read_addr, data);
 251        if (ret)
 252                goto out;
 253
 254        ret = vsc_read(dev, VSC_ADDR_OFFSET, next_read_addr);
 255        if (ret)
 256                goto out;
 257
 258        *next_read_addr = MLX5_EXTRACT(*next_read_addr, VSC_ADDR_BIT_OFFS,
 259                                       VSC_ADDR_BIT_LEN);
 260
 261        if (*next_read_addr <= read_addr)
 262                ret = -EINVAL;
 263out:
 264        return ret;
 265}
 266
 267int mlx5_vsc_gw_read_block_fast(struct mlx5_core_dev *dev, u32 *data,
 268                                int length)
 269{
 270        unsigned int next_read_addr = 0;
 271        unsigned int read_addr = 0;
 272
 273        while (read_addr < length) {
 274                if (mlx5_vsc_gw_read_fast(dev, read_addr, &next_read_addr,
 275                                          &data[(read_addr >> 2)]))
 276                        return read_addr;
 277
 278                read_addr = next_read_addr;
 279        }
 280        return length;
 281}
 282
 283int mlx5_vsc_sem_set_space(struct mlx5_core_dev *dev, u16 space,
 284                           enum mlx5_vsc_state state)
 285{
 286        u32 data, id = 0;
 287        int ret;
 288
 289        ret = mlx5_vsc_gw_set_space(dev, MLX5_SEMAPHORE_SPACE_DOMAIN, NULL);
 290        if (ret) {
 291                mlx5_core_warn(dev, "Failed to set gw space %d\n", ret);
 292                return ret;
 293        }
 294
 295        if (state == MLX5_VSC_LOCK) {
 296                /* Get a unique ID based on the counter */
 297                ret = vsc_read(dev, VSC_COUNTER_OFFSET, &id);
 298                if (ret)
 299                        return ret;
 300        }
 301
 302        /* Try to modify lock */
 303        ret = mlx5_vsc_gw_write(dev, space, id);
 304        if (ret)
 305                return ret;
 306
 307        /* Verify lock was modified */
 308        ret = mlx5_vsc_gw_read(dev, space, &data);
 309        if (ret)
 310                return -EINVAL;
 311
 312        if (data != id)
 313                return -EBUSY;
 314
 315        return 0;
 316}
 317