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 27 enum { 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 53 void 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 64 int 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 109 pci_unlock: 110 pci_cfg_access_unlock(dev->pdev); 111 return ret; 112 } 113 114 int 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 123 int 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 167 out: 168 return ret; 169 } 170 171 static 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 195 static 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 217 out: 218 return ret; 219 } 220 221 static 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); 239 out: 240 return ret; 241 } 242 243 static 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; 263 out: 264 return ret; 265 } 266 267 int 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 283 int 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