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