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