Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (c) 2004 Topspin Communications. All rights reserved. | |
cd4e8fb4 | 3 | * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. |
1da177e4 LT |
4 | * |
5 | * This software is available to you under a choice of one of two | |
6 | * licenses. You may choose to be licensed under the terms of the GNU | |
7 | * General Public License (GPL) Version 2, available from the file | |
8 | * COPYING in the main directory of this source tree, or the | |
9 | * OpenIB.org BSD license below: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * - Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * - Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | * | |
33 | * $Id: mthca_av.c 1349 2004-12-16 21:09:43Z roland $ | |
34 | */ | |
35 | ||
4e57b681 TS |
36 | #include <linux/string.h> |
37 | #include <linux/slab.h> | |
1da177e4 | 38 | |
a4d61e84 RD |
39 | #include <rdma/ib_verbs.h> |
40 | #include <rdma/ib_cache.h> | |
1da177e4 LT |
41 | |
42 | #include "mthca_dev.h" | |
43 | ||
bf6a9e31 JM |
44 | enum { |
45 | MTHCA_RATE_TAVOR_FULL = 0, | |
46 | MTHCA_RATE_TAVOR_1X = 1, | |
47 | MTHCA_RATE_TAVOR_4X = 2, | |
48 | MTHCA_RATE_TAVOR_1X_DDR = 3 | |
49 | }; | |
50 | ||
51 | enum { | |
52 | MTHCA_RATE_MEMFREE_FULL = 0, | |
53 | MTHCA_RATE_MEMFREE_QUARTER = 1, | |
54 | MTHCA_RATE_MEMFREE_EIGHTH = 2, | |
55 | MTHCA_RATE_MEMFREE_HALF = 3 | |
56 | }; | |
57 | ||
1da177e4 | 58 | struct mthca_av { |
97f52eb4 SH |
59 | __be32 port_pd; |
60 | u8 reserved1; | |
61 | u8 g_slid; | |
62 | __be16 dlid; | |
63 | u8 reserved2; | |
64 | u8 gid_index; | |
65 | u8 msg_sr; | |
66 | u8 hop_limit; | |
67 | __be32 sl_tclass_flowlabel; | |
68 | __be32 dgid[4]; | |
1da177e4 LT |
69 | }; |
70 | ||
bf6a9e31 JM |
71 | static enum ib_rate memfree_rate_to_ib(u8 mthca_rate, u8 port_rate) |
72 | { | |
73 | switch (mthca_rate) { | |
74 | case MTHCA_RATE_MEMFREE_EIGHTH: | |
75 | return mult_to_ib_rate(port_rate >> 3); | |
76 | case MTHCA_RATE_MEMFREE_QUARTER: | |
77 | return mult_to_ib_rate(port_rate >> 2); | |
78 | case MTHCA_RATE_MEMFREE_HALF: | |
79 | return mult_to_ib_rate(port_rate >> 1); | |
80 | case MTHCA_RATE_MEMFREE_FULL: | |
81 | default: | |
82 | return mult_to_ib_rate(port_rate); | |
83 | } | |
84 | } | |
85 | ||
86 | static enum ib_rate tavor_rate_to_ib(u8 mthca_rate, u8 port_rate) | |
87 | { | |
88 | switch (mthca_rate) { | |
89 | case MTHCA_RATE_TAVOR_1X: return IB_RATE_2_5_GBPS; | |
90 | case MTHCA_RATE_TAVOR_1X_DDR: return IB_RATE_5_GBPS; | |
91 | case MTHCA_RATE_TAVOR_4X: return IB_RATE_10_GBPS; | |
b046a04e | 92 | default: return mult_to_ib_rate(port_rate); |
bf6a9e31 JM |
93 | } |
94 | } | |
95 | ||
96 | enum ib_rate mthca_rate_to_ib(struct mthca_dev *dev, u8 mthca_rate, u8 port) | |
97 | { | |
98 | if (mthca_is_memfree(dev)) { | |
99 | /* Handle old Arbel FW */ | |
100 | if (dev->limits.stat_rate_support == 0x3 && mthca_rate) | |
101 | return IB_RATE_2_5_GBPS; | |
102 | ||
103 | return memfree_rate_to_ib(mthca_rate, dev->rate[port - 1]); | |
104 | } else | |
105 | return tavor_rate_to_ib(mthca_rate, dev->rate[port - 1]); | |
106 | } | |
107 | ||
108 | static u8 ib_rate_to_memfree(u8 req_rate, u8 cur_rate) | |
109 | { | |
110 | if (cur_rate <= req_rate) | |
111 | return 0; | |
112 | ||
113 | /* | |
114 | * Inter-packet delay (IPD) to get from rate X down to a rate | |
115 | * no more than Y is (X - 1) / Y. | |
116 | */ | |
117 | switch ((cur_rate - 1) / req_rate) { | |
118 | case 0: return MTHCA_RATE_MEMFREE_FULL; | |
119 | case 1: return MTHCA_RATE_MEMFREE_HALF; | |
120 | case 2: /* fall through */ | |
121 | case 3: return MTHCA_RATE_MEMFREE_QUARTER; | |
122 | default: return MTHCA_RATE_MEMFREE_EIGHTH; | |
123 | } | |
124 | } | |
125 | ||
126 | static u8 ib_rate_to_tavor(u8 static_rate) | |
127 | { | |
128 | switch (static_rate) { | |
129 | case IB_RATE_2_5_GBPS: return MTHCA_RATE_TAVOR_1X; | |
130 | case IB_RATE_5_GBPS: return MTHCA_RATE_TAVOR_1X_DDR; | |
131 | case IB_RATE_10_GBPS: return MTHCA_RATE_TAVOR_4X; | |
132 | default: return MTHCA_RATE_TAVOR_FULL; | |
133 | } | |
134 | } | |
135 | ||
136 | u8 mthca_get_rate(struct mthca_dev *dev, int static_rate, u8 port) | |
137 | { | |
138 | u8 rate; | |
139 | ||
140 | if (!static_rate || ib_rate_to_mult(static_rate) >= dev->rate[port - 1]) | |
141 | return 0; | |
142 | ||
143 | if (mthca_is_memfree(dev)) | |
144 | rate = ib_rate_to_memfree(ib_rate_to_mult(static_rate), | |
145 | dev->rate[port - 1]); | |
146 | else | |
147 | rate = ib_rate_to_tavor(static_rate); | |
148 | ||
149 | if (!(dev->limits.stat_rate_support & (1 << rate))) | |
150 | rate = 1; | |
151 | ||
152 | return rate; | |
153 | } | |
154 | ||
1da177e4 LT |
155 | int mthca_create_ah(struct mthca_dev *dev, |
156 | struct mthca_pd *pd, | |
157 | struct ib_ah_attr *ah_attr, | |
158 | struct mthca_ah *ah) | |
159 | { | |
160 | u32 index = -1; | |
161 | struct mthca_av *av = NULL; | |
162 | ||
163 | ah->type = MTHCA_AH_PCI_POOL; | |
164 | ||
d10ddbf6 | 165 | if (mthca_is_memfree(dev)) { |
8df8a34d | 166 | ah->av = kmalloc(sizeof *ah->av, GFP_ATOMIC); |
1da177e4 LT |
167 | if (!ah->av) |
168 | return -ENOMEM; | |
169 | ||
170 | ah->type = MTHCA_AH_KMALLOC; | |
171 | av = ah->av; | |
172 | } else if (!atomic_read(&pd->sqp_count) && | |
173 | !(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) { | |
174 | index = mthca_alloc(&dev->av_table.alloc); | |
175 | ||
176 | /* fall back to allocate in host memory */ | |
177 | if (index == -1) | |
178 | goto on_hca_fail; | |
179 | ||
8df8a34d | 180 | av = kmalloc(sizeof *av, GFP_ATOMIC); |
1da177e4 LT |
181 | if (!av) |
182 | goto on_hca_fail; | |
183 | ||
184 | ah->type = MTHCA_AH_ON_HCA; | |
185 | ah->avdma = dev->av_table.ddr_av_base + | |
186 | index * MTHCA_AV_SIZE; | |
187 | } | |
188 | ||
189 | on_hca_fail: | |
190 | if (ah->type == MTHCA_AH_PCI_POOL) { | |
191 | ah->av = pci_pool_alloc(dev->av_table.pool, | |
8df8a34d | 192 | SLAB_ATOMIC, &ah->avdma); |
1da177e4 LT |
193 | if (!ah->av) |
194 | return -ENOMEM; | |
195 | ||
196 | av = ah->av; | |
197 | } | |
198 | ||
199 | ah->key = pd->ntmr.ibmr.lkey; | |
200 | ||
201 | memset(av, 0, MTHCA_AV_SIZE); | |
202 | ||
203 | av->port_pd = cpu_to_be32(pd->pd_num | (ah_attr->port_num << 24)); | |
204 | av->g_slid = ah_attr->src_path_bits; | |
205 | av->dlid = cpu_to_be16(ah_attr->dlid); | |
206 | av->msg_sr = (3 << 4) | /* 2K message */ | |
bf6a9e31 | 207 | mthca_get_rate(dev, ah_attr->static_rate, ah_attr->port_num); |
1da177e4 LT |
208 | av->sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28); |
209 | if (ah_attr->ah_flags & IB_AH_GRH) { | |
210 | av->g_slid |= 0x80; | |
211 | av->gid_index = (ah_attr->port_num - 1) * dev->limits.gid_table_len + | |
212 | ah_attr->grh.sgid_index; | |
213 | av->hop_limit = ah_attr->grh.hop_limit; | |
214 | av->sl_tclass_flowlabel |= | |
215 | cpu_to_be32((ah_attr->grh.traffic_class << 20) | | |
216 | ah_attr->grh.flow_label); | |
217 | memcpy(av->dgid, ah_attr->grh.dgid.raw, 16); | |
218 | } else { | |
219 | /* Arbel workaround -- low byte of GID must be 2 */ | |
220 | av->dgid[3] = cpu_to_be32(2); | |
221 | } | |
222 | ||
223 | if (0) { | |
224 | int j; | |
225 | ||
226 | mthca_dbg(dev, "Created UDAV at %p/%08lx:\n", | |
227 | av, (unsigned long) ah->avdma); | |
228 | for (j = 0; j < 8; ++j) | |
229 | printk(KERN_DEBUG " [%2x] %08x\n", | |
97f52eb4 | 230 | j * 4, be32_to_cpu(((__be32 *) av)[j])); |
1da177e4 LT |
231 | } |
232 | ||
233 | if (ah->type == MTHCA_AH_ON_HCA) { | |
234 | memcpy_toio(dev->av_table.av_map + index * MTHCA_AV_SIZE, | |
235 | av, MTHCA_AV_SIZE); | |
236 | kfree(av); | |
237 | } | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
242 | int mthca_destroy_ah(struct mthca_dev *dev, struct mthca_ah *ah) | |
243 | { | |
244 | switch (ah->type) { | |
245 | case MTHCA_AH_ON_HCA: | |
246 | mthca_free(&dev->av_table.alloc, | |
2fa5e2eb | 247 | (ah->avdma - dev->av_table.ddr_av_base) / |
1da177e4 LT |
248 | MTHCA_AV_SIZE); |
249 | break; | |
250 | ||
251 | case MTHCA_AH_PCI_POOL: | |
252 | pci_pool_free(dev->av_table.pool, ah->av, ah->avdma); | |
253 | break; | |
254 | ||
255 | case MTHCA_AH_KMALLOC: | |
256 | kfree(ah->av); | |
257 | break; | |
258 | } | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
9eacee2a MT |
263 | int mthca_ah_grh_present(struct mthca_ah *ah) |
264 | { | |
265 | return !!(ah->av->g_slid & 0x80); | |
266 | } | |
267 | ||
1da177e4 LT |
268 | int mthca_read_ah(struct mthca_dev *dev, struct mthca_ah *ah, |
269 | struct ib_ud_header *header) | |
270 | { | |
271 | if (ah->type == MTHCA_AH_ON_HCA) | |
272 | return -EINVAL; | |
273 | ||
274 | header->lrh.service_level = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28; | |
275 | header->lrh.destination_lid = ah->av->dlid; | |
97f52eb4 | 276 | header->lrh.source_lid = cpu_to_be16(ah->av->g_slid & 0x7f); |
9eacee2a | 277 | if (mthca_ah_grh_present(ah)) { |
1da177e4 LT |
278 | header->grh.traffic_class = |
279 | (be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 20) & 0xff; | |
280 | header->grh.flow_label = | |
281 | ah->av->sl_tclass_flowlabel & cpu_to_be32(0xfffff); | |
282 | ib_get_cached_gid(&dev->ib_dev, | |
283 | be32_to_cpu(ah->av->port_pd) >> 24, | |
f9e61929 | 284 | ah->av->gid_index % dev->limits.gid_table_len, |
1da177e4 LT |
285 | &header->grh.source_gid); |
286 | memcpy(header->grh.destination_gid.raw, | |
287 | ah->av->dgid, 16); | |
1da177e4 LT |
288 | } |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
1d89b1ae JM |
293 | int mthca_ah_query(struct ib_ah *ibah, struct ib_ah_attr *attr) |
294 | { | |
295 | struct mthca_ah *ah = to_mah(ibah); | |
296 | struct mthca_dev *dev = to_mdev(ibah->device); | |
297 | ||
298 | /* Only implement for MAD and memfree ah for now. */ | |
299 | if (ah->type == MTHCA_AH_ON_HCA) | |
300 | return -ENOSYS; | |
301 | ||
302 | memset(attr, 0, sizeof *attr); | |
303 | attr->dlid = be16_to_cpu(ah->av->dlid); | |
304 | attr->sl = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28; | |
1d89b1ae | 305 | attr->port_num = be32_to_cpu(ah->av->port_pd) >> 24; |
2290d2c9 JM |
306 | attr->static_rate = mthca_rate_to_ib(dev, ah->av->msg_sr & 0x7, |
307 | attr->port_num); | |
308 | attr->src_path_bits = ah->av->g_slid & 0x7F; | |
1d89b1ae JM |
309 | attr->ah_flags = mthca_ah_grh_present(ah) ? IB_AH_GRH : 0; |
310 | ||
311 | if (attr->ah_flags) { | |
312 | attr->grh.traffic_class = | |
313 | be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 20; | |
314 | attr->grh.flow_label = | |
315 | be32_to_cpu(ah->av->sl_tclass_flowlabel) & 0xfffff; | |
316 | attr->grh.hop_limit = ah->av->hop_limit; | |
317 | attr->grh.sgid_index = ah->av->gid_index & | |
318 | (dev->limits.gid_table_len - 1); | |
319 | memcpy(attr->grh.dgid.raw, ah->av->dgid, 16); | |
320 | } | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
f4f3d0f0 | 325 | int mthca_init_av_table(struct mthca_dev *dev) |
1da177e4 LT |
326 | { |
327 | int err; | |
328 | ||
d10ddbf6 | 329 | if (mthca_is_memfree(dev)) |
1da177e4 LT |
330 | return 0; |
331 | ||
332 | err = mthca_alloc_init(&dev->av_table.alloc, | |
333 | dev->av_table.num_ddr_avs, | |
334 | dev->av_table.num_ddr_avs - 1, | |
335 | 0); | |
336 | if (err) | |
337 | return err; | |
338 | ||
339 | dev->av_table.pool = pci_pool_create("mthca_av", dev->pdev, | |
340 | MTHCA_AV_SIZE, | |
341 | MTHCA_AV_SIZE, 0); | |
342 | if (!dev->av_table.pool) | |
343 | goto out_free_alloc; | |
344 | ||
345 | if (!(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) { | |
346 | dev->av_table.av_map = ioremap(pci_resource_start(dev->pdev, 4) + | |
347 | dev->av_table.ddr_av_base - | |
348 | dev->ddr_start, | |
349 | dev->av_table.num_ddr_avs * | |
350 | MTHCA_AV_SIZE); | |
351 | if (!dev->av_table.av_map) | |
352 | goto out_free_pool; | |
353 | } else | |
354 | dev->av_table.av_map = NULL; | |
355 | ||
356 | return 0; | |
357 | ||
358 | out_free_pool: | |
359 | pci_pool_destroy(dev->av_table.pool); | |
360 | ||
361 | out_free_alloc: | |
362 | mthca_alloc_cleanup(&dev->av_table.alloc); | |
363 | return -ENOMEM; | |
364 | } | |
365 | ||
e1f7868c | 366 | void mthca_cleanup_av_table(struct mthca_dev *dev) |
1da177e4 | 367 | { |
d10ddbf6 | 368 | if (mthca_is_memfree(dev)) |
1da177e4 LT |
369 | return; |
370 | ||
371 | if (dev->av_table.av_map) | |
372 | iounmap(dev->av_table.av_map); | |
373 | pci_pool_destroy(dev->av_table.pool); | |
374 | mthca_alloc_cleanup(&dev->av_table.alloc); | |
375 | } |