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