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