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