1 /*- 2 * Copyright (c) 2013-2017, Mellanox Technologies, Ltd. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include "opt_rss.h" 27 #include "opt_ratelimit.h" 28 29 #include <dev/mlx5/driver.h> 30 #include <dev/mlx5/device.h> 31 #include <dev/mlx5/mlx5_core/mlx5_core.h> 32 33 int mlx5_vsc_lock(struct mlx5_core_dev *mdev) 34 { 35 device_t dev = mdev->pdev->dev.bsddev; 36 int vsc_addr = mdev->vsc_addr; 37 int retries = 0; 38 u32 lock_val; 39 u32 counter; 40 41 if (!vsc_addr) { 42 mlx5_core_warn(mdev, "Unable to acquire vsc lock, vsc_addr not initialized\n"); 43 return EINVAL; 44 } 45 46 while (true) { 47 if (retries > MLX5_VSC_MAX_RETRIES) 48 return EBUSY; 49 50 if (pci_read_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, 4)) { 51 retries++; 52 /* 53 * The PRM suggests random 0 - 10ms to prevent multiple 54 * waiters on the same interval in order to avoid starvation 55 */ 56 DELAY((random() % 9000) + 1000); 57 continue; 58 } 59 60 counter = pci_read_config(dev, vsc_addr + MLX5_VSC_COUNTER_OFFSET, 4); 61 pci_write_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, counter, 4); 62 lock_val = pci_read_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, 4); 63 64 if (lock_val == counter) 65 break; 66 67 retries++; 68 } 69 70 return 0; 71 } 72 73 void mlx5_vsc_unlock(struct mlx5_core_dev *mdev) 74 { 75 device_t dev = mdev->pdev->dev.bsddev; 76 int vsc_addr = mdev->vsc_addr; 77 78 if (!vsc_addr) { 79 mlx5_core_warn(mdev, "Unable to release vsc lock, vsc_addr not initialized\n"); 80 return; 81 } 82 83 pci_write_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, 0, 4); 84 } 85 86 int 87 mlx5_vsc_wait_on_flag(struct mlx5_core_dev *mdev, u32 expected) 88 { 89 device_t dev = mdev->pdev->dev.bsddev; 90 int vsc_addr = mdev->vsc_addr; 91 int retries = 0; 92 u32 flag; 93 94 while (true) { 95 if (retries > MLX5_VSC_MAX_RETRIES) 96 return EBUSY; 97 98 flag = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4); 99 if (expected == MLX5_VSC_GET(vsc_addr, &flag, flag)) 100 break; 101 102 retries++; 103 DELAY((random() % 90) + 10); 104 } 105 106 return 0; 107 } 108 109 int mlx5_vsc_set_space(struct mlx5_core_dev *mdev, u16 space) 110 { 111 device_t dev = mdev->pdev->dev.bsddev; 112 int vsc_addr = mdev->vsc_addr; 113 u32 vsc_space = 0; 114 115 if (!vsc_addr) { 116 mlx5_core_warn(mdev, "Unable to set vsc space, vsc_addr not initialized\n"); 117 return EINVAL; 118 } 119 120 MLX5_VSC_SET(vsc_space, &vsc_space, space, space); 121 pci_write_config(dev, vsc_addr + MLX5_VSC_SPACE_OFFSET, vsc_space, 4); 122 vsc_space = pci_read_config(dev, vsc_addr + MLX5_VSC_SPACE_OFFSET, 4); 123 124 if (MLX5_VSC_GET(vsc_space, &vsc_space, status) != MLX5_VSC_SPACE_SUPPORTED) { 125 mlx5_core_warn(mdev, "Space 0x%x is not supported.\n", space); 126 return ENOTSUP; 127 } 128 129 return 0; 130 } 131 132 int mlx5_vsc_write(struct mlx5_core_dev *mdev, u32 addr, const u32 *data) 133 { 134 device_t dev = mdev->pdev->dev.bsddev; 135 int vsc_addr = mdev->vsc_addr; 136 u32 in = 0; 137 int err; 138 139 if (!vsc_addr) { 140 mlx5_core_warn(mdev, "Unable to call vsc write, vsc_addr not initialized\n"); 141 return EINVAL; 142 } 143 144 MLX5_VSC_SET(vsc_addr, &in, address, addr); 145 MLX5_VSC_SET(vsc_addr, &in, flag, 1); 146 pci_write_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, *data, 4); 147 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4); 148 149 err = mlx5_vsc_wait_on_flag(mdev, 0); 150 if (err) 151 mlx5_core_warn(mdev, "Failed waiting for write flag!\n"); 152 153 return err; 154 } 155 156 int mlx5_vsc_read(struct mlx5_core_dev *mdev, u32 addr, u32 *data) 157 { 158 device_t dev = mdev->pdev->dev.bsddev; 159 int vsc_addr = mdev->vsc_addr; 160 int err; 161 u32 in; 162 163 if (!vsc_addr) { 164 mlx5_core_warn(mdev, "Unable to call vsc read, vsc_addr not initialized\n"); 165 return EINVAL; 166 } 167 168 MLX5_VSC_SET(vsc_addr, &in, address, addr); 169 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4); 170 171 err = mlx5_vsc_wait_on_flag(mdev, 1); 172 if (err) { 173 mlx5_core_warn(mdev, "Failed waiting for read complete flag!\n"); 174 return err; 175 } 176 177 *data = pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4); 178 179 return 0; 180 } 181 182 int mlx5_vsc_lock_addr_space(struct mlx5_core_dev *mdev, u32 addr) 183 { 184 device_t dev = mdev->pdev->dev.bsddev; 185 int vsc_addr = mdev->vsc_addr; 186 u32 data; 187 int ret; 188 u32 id; 189 190 ret = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SEMAPHORES); 191 if (ret) 192 return ret; 193 194 /* Get a unique ID based on the counter */ 195 id = pci_read_config(dev, vsc_addr + MLX5_VSC_COUNTER_OFFSET, 4); 196 197 /* Try to modify lock */ 198 ret = mlx5_vsc_write(mdev, addr, &id); 199 if (ret) 200 return ret; 201 202 /* Verify */ 203 ret = mlx5_vsc_read(mdev, addr, &data); 204 if (ret) 205 return ret; 206 if (data != id) 207 return EBUSY; 208 209 return 0; 210 } 211 212 int mlx5_vsc_unlock_addr_space(struct mlx5_core_dev *mdev, u32 addr) 213 { 214 u32 data = 0; 215 int ret; 216 217 ret = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SEMAPHORES); 218 if (ret) 219 return ret; 220 221 /* Try to modify lock */ 222 ret = mlx5_vsc_write(mdev, addr, &data); 223 if (ret) 224 return ret; 225 226 /* Verify */ 227 ret = mlx5_vsc_read(mdev, addr, &data); 228 if (ret) 229 return ret; 230 if (data != 0) 231 return EBUSY; 232 233 return 0; 234 } 235 236 int mlx5_vsc_find_cap(struct mlx5_core_dev *mdev) 237 { 238 int *capreg = &mdev->vsc_addr; 239 int err; 240 241 err = pci_find_cap(mdev->pdev->dev.bsddev, PCIY_VENDOR, capreg); 242 243 if (err) 244 *capreg = 0; 245 246 return err; 247 } 248 249