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 * $FreeBSD$ 26 */ 27 28 #include "opt_rss.h" 29 #include "opt_ratelimit.h" 30 31 #include <dev/mlx5/driver.h> 32 #include <dev/mlx5/device.h> 33 #include <dev/mlx5/mlx5_core/mlx5_core.h> 34 35 int mlx5_vsc_lock(struct mlx5_core_dev *mdev) 36 { 37 device_t dev = mdev->pdev->dev.bsddev; 38 int vsc_addr = mdev->vsc_addr; 39 int retries = 0; 40 u32 lock_val; 41 u32 counter; 42 43 if (!vsc_addr) { 44 mlx5_core_warn(mdev, "Unable to acquire vsc lock, vsc_addr not initialized\n"); 45 return EINVAL; 46 } 47 48 while (true) { 49 if (retries > MLX5_VSC_MAX_RETRIES) 50 return EBUSY; 51 52 if (pci_read_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, 4)) { 53 retries++; 54 /* 55 * The PRM suggests random 0 - 10ms to prevent multiple 56 * waiters on the same interval in order to avoid starvation 57 */ 58 DELAY((random() % 9000) + 1000); 59 continue; 60 } 61 62 counter = pci_read_config(dev, vsc_addr + MLX5_VSC_COUNTER_OFFSET, 4); 63 pci_write_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, counter, 4); 64 lock_val = pci_read_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, 4); 65 66 if (lock_val == counter) 67 break; 68 69 retries++; 70 } 71 72 return 0; 73 } 74 75 void mlx5_vsc_unlock(struct mlx5_core_dev *mdev) 76 { 77 device_t dev = mdev->pdev->dev.bsddev; 78 int vsc_addr = mdev->vsc_addr; 79 80 if (!vsc_addr) { 81 mlx5_core_warn(mdev, "Unable to release vsc lock, vsc_addr not initialized\n"); 82 return; 83 } 84 85 pci_write_config(dev, vsc_addr + MLX5_VSC_SEMA_OFFSET, 0, 4); 86 } 87 88 int 89 mlx5_vsc_wait_on_flag(struct mlx5_core_dev *mdev, u32 expected) 90 { 91 device_t dev = mdev->pdev->dev.bsddev; 92 int vsc_addr = mdev->vsc_addr; 93 int retries = 0; 94 u32 flag; 95 96 while (true) { 97 if (retries > MLX5_VSC_MAX_RETRIES) 98 return EBUSY; 99 100 flag = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4); 101 if (expected == MLX5_VSC_GET(vsc_addr, &flag, flag)) 102 break; 103 104 retries++; 105 DELAY((random() % 90) + 10); 106 } 107 108 return 0; 109 } 110 111 int mlx5_vsc_set_space(struct mlx5_core_dev *mdev, u16 space) 112 { 113 device_t dev = mdev->pdev->dev.bsddev; 114 int vsc_addr = mdev->vsc_addr; 115 u32 vsc_space = 0; 116 117 if (!vsc_addr) { 118 mlx5_core_warn(mdev, "Unable to set vsc space, vsc_addr not initialized\n"); 119 return EINVAL; 120 } 121 122 MLX5_VSC_SET(vsc_space, &vsc_space, space, space); 123 pci_write_config(dev, vsc_addr + MLX5_VSC_SPACE_OFFSET, vsc_space, 4); 124 vsc_space = pci_read_config(dev, vsc_addr + MLX5_VSC_SPACE_OFFSET, 4); 125 126 if (MLX5_VSC_GET(vsc_space, &vsc_space, status) != MLX5_VSC_SPACE_SUPPORTED) { 127 mlx5_core_warn(mdev, "Space 0x%x is not supported.\n", space); 128 return ENOTSUP; 129 } 130 131 return 0; 132 } 133 134 int mlx5_vsc_write(struct mlx5_core_dev *mdev, u32 addr, const u32 *data) 135 { 136 device_t dev = mdev->pdev->dev.bsddev; 137 int vsc_addr = mdev->vsc_addr; 138 u32 in = 0; 139 int err; 140 141 if (!vsc_addr) { 142 mlx5_core_warn(mdev, "Unable to call vsc write, vsc_addr not initialized\n"); 143 return EINVAL; 144 } 145 146 MLX5_VSC_SET(vsc_addr, &in, address, addr); 147 MLX5_VSC_SET(vsc_addr, &in, flag, 1); 148 pci_write_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, *data, 4); 149 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4); 150 151 err = mlx5_vsc_wait_on_flag(mdev, 0); 152 if (err) 153 mlx5_core_warn(mdev, "Failed waiting for write flag!\n"); 154 155 return err; 156 } 157 158 int mlx5_vsc_read(struct mlx5_core_dev *mdev, u32 addr, u32 *data) 159 { 160 device_t dev = mdev->pdev->dev.bsddev; 161 int vsc_addr = mdev->vsc_addr; 162 int err; 163 u32 in; 164 165 if (!vsc_addr) { 166 mlx5_core_warn(mdev, "Unable to call vsc read, vsc_addr not initialized\n"); 167 return EINVAL; 168 } 169 170 MLX5_VSC_SET(vsc_addr, &in, address, addr); 171 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4); 172 173 err = mlx5_vsc_wait_on_flag(mdev, 1); 174 if (err) { 175 mlx5_core_warn(mdev, "Failed waiting for read complete flag!\n"); 176 return err; 177 } 178 179 *data = pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4); 180 181 return 0; 182 } 183 184 int mlx5_vsc_lock_addr_space(struct mlx5_core_dev *mdev, u32 addr) 185 { 186 device_t dev = mdev->pdev->dev.bsddev; 187 int vsc_addr = mdev->vsc_addr; 188 u32 data; 189 int ret; 190 u32 id; 191 192 ret = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SEMAPHORES); 193 if (ret) 194 return ret; 195 196 /* Get a unique ID based on the counter */ 197 id = pci_read_config(dev, vsc_addr + MLX5_VSC_COUNTER_OFFSET, 4); 198 199 /* Try to modify lock */ 200 ret = mlx5_vsc_write(mdev, addr, &id); 201 if (ret) 202 return ret; 203 204 /* Verify */ 205 ret = mlx5_vsc_read(mdev, addr, &data); 206 if (ret) 207 return ret; 208 if (data != id) 209 return EBUSY; 210 211 return 0; 212 } 213 214 int mlx5_vsc_unlock_addr_space(struct mlx5_core_dev *mdev, u32 addr) 215 { 216 u32 data = 0; 217 int ret; 218 219 ret = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SEMAPHORES); 220 if (ret) 221 return ret; 222 223 /* Try to modify lock */ 224 ret = mlx5_vsc_write(mdev, addr, &data); 225 if (ret) 226 return ret; 227 228 /* Verify */ 229 ret = mlx5_vsc_read(mdev, addr, &data); 230 if (ret) 231 return ret; 232 if (data != 0) 233 return EBUSY; 234 235 return 0; 236 } 237 238 int mlx5_vsc_find_cap(struct mlx5_core_dev *mdev) 239 { 240 int *capreg = &mdev->vsc_addr; 241 int err; 242 243 err = pci_find_cap(mdev->pdev->dev.bsddev, PCIY_VENDOR, capreg); 244 245 if (err) 246 *capreg = 0; 247 248 return err; 249 } 250 251