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
mlx5_vsc_lock(struct mlx5_core_dev * mdev)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() % 9000) + 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
mlx5_vsc_unlock(struct mlx5_core_dev * mdev)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
mlx5_vsc_wait_on_flag(struct mlx5_core_dev * mdev,u32 expected)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((random() % 90) + 10);
103 }
104
105 return 0;
106 }
107
mlx5_vsc_set_space(struct mlx5_core_dev * mdev,u16 space)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
mlx5_vsc_write(struct mlx5_core_dev * mdev,u32 addr,const u32 * data)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
mlx5_vsc_read(struct mlx5_core_dev * mdev,u32 addr,u32 * data)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
mlx5_vsc_lock_addr_space(struct mlx5_core_dev * mdev,u32 addr)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
mlx5_vsc_unlock_addr_space(struct mlx5_core_dev * mdev,u32 addr)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
mlx5_vsc_find_cap(struct mlx5_core_dev * mdev)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