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