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
32int 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
72void 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
85int
86mlx5_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
108int 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
131int 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
155int 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
181int 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
211int 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
235int 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