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