xref: /freebsd/sys/dev/mlx5/mlx5_core/mlx5_vsc.c (revision 4d98df72)
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 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() % 11) * 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 
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
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(10);
103 	}
104 
105 	return 0;
106 }
107 
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 
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 
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 
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 
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 
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