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. | |
1da177e4 LT |
32 | */ |
33 | ||
4e57b681 TS |
34 | #include <linux/string.h> |
35 | #include <linux/slab.h> | |
1da177e4 | 36 | |
a4d61e84 RD |
37 | #include <rdma/ib_verbs.h> |
38 | #include <rdma/ib_cache.h> | |
1da177e4 LT |
39 | |
40 | #include "mthca_dev.h" | |
41 | ||
bf6a9e31 JM |
42 | enum { |
43 | MTHCA_RATE_TAVOR_FULL = 0, | |
44 | MTHCA_RATE_TAVOR_1X = 1, | |
45 | MTHCA_RATE_TAVOR_4X = 2, | |
46 | MTHCA_RATE_TAVOR_1X_DDR = 3 | |
47 | }; | |
48 | ||
49 | enum { | |
50 | MTHCA_RATE_MEMFREE_FULL = 0, | |
51 | MTHCA_RATE_MEMFREE_QUARTER = 1, | |
52 | MTHCA_RATE_MEMFREE_EIGHTH = 2, | |
53 | MTHCA_RATE_MEMFREE_HALF = 3 | |
54 | }; | |
55 | ||
1da177e4 | 56 | struct mthca_av { |
97f52eb4 SH |
57 | __be32 port_pd; |
58 | u8 reserved1; | |
59 | u8 g_slid; | |
60 | __be16 dlid; | |
61 | u8 reserved2; | |
62 | u8 gid_index; | |
63 | u8 msg_sr; | |
64 | u8 hop_limit; | |
65 | __be32 sl_tclass_flowlabel; | |
66 | __be32 dgid[4]; | |
1da177e4 LT |
67 | }; |
68 | ||
bf6a9e31 JM |
69 | static enum ib_rate memfree_rate_to_ib(u8 mthca_rate, u8 port_rate) |
70 | { | |
71 | switch (mthca_rate) { | |
72 | case MTHCA_RATE_MEMFREE_EIGHTH: | |
73 | return mult_to_ib_rate(port_rate >> 3); | |
74 | case MTHCA_RATE_MEMFREE_QUARTER: | |
75 | return mult_to_ib_rate(port_rate >> 2); | |
76 | case MTHCA_RATE_MEMFREE_HALF: | |
77 | return mult_to_ib_rate(port_rate >> 1); | |
78 | case MTHCA_RATE_MEMFREE_FULL: | |
79 | default: | |
80 | return mult_to_ib_rate(port_rate); | |
81 | } | |
82 | } | |
83 | ||
84 | static enum ib_rate tavor_rate_to_ib(u8 mthca_rate, u8 port_rate) | |
85 | { | |
86 | switch (mthca_rate) { | |
87 | case MTHCA_RATE_TAVOR_1X: return IB_RATE_2_5_GBPS; | |
88 | case MTHCA_RATE_TAVOR_1X_DDR: return IB_RATE_5_GBPS; | |
89 | case MTHCA_RATE_TAVOR_4X: return IB_RATE_10_GBPS; | |
b046a04e | 90 | default: return mult_to_ib_rate(port_rate); |
bf6a9e31 JM |
91 | } |
92 | } | |
93 | ||
94 | enum ib_rate mthca_rate_to_ib(struct mthca_dev *dev, u8 mthca_rate, u8 port) | |
95 | { | |
96 | if (mthca_is_memfree(dev)) { | |
97 | /* Handle old Arbel FW */ | |
98 | if (dev->limits.stat_rate_support == 0x3 && mthca_rate) | |
99 | return IB_RATE_2_5_GBPS; | |
100 | ||
101 | return memfree_rate_to_ib(mthca_rate, dev->rate[port - 1]); | |
102 | } else | |
103 | return tavor_rate_to_ib(mthca_rate, dev->rate[port - 1]); | |
104 | } | |
105 | ||
106 | static u8 ib_rate_to_memfree(u8 req_rate, u8 cur_rate) | |
107 | { | |
108 | if (cur_rate <= req_rate) | |
109 | return 0; | |
110 | ||
111 | /* | |
112 | * Inter-packet delay (IPD) to get from rate X down to a rate | |
113 | * no more than Y is (X - 1) / Y. | |
114 | */ | |
115 | switch ((cur_rate - 1) / req_rate) { | |
116 | case 0: return MTHCA_RATE_MEMFREE_FULL; | |
117 | case 1: return MTHCA_RATE_MEMFREE_HALF; | |
118 | case 2: /* fall through */ | |
119 | case 3: return MTHCA_RATE_MEMFREE_QUARTER; | |
120 | default: return MTHCA_RATE_MEMFREE_EIGHTH; | |
121 | } | |
122 | } | |
123 | ||
124 | static u8 ib_rate_to_tavor(u8 static_rate) | |
125 | { | |
126 | switch (static_rate) { | |
127 | case IB_RATE_2_5_GBPS: return MTHCA_RATE_TAVOR_1X; | |
128 | case IB_RATE_5_GBPS: return MTHCA_RATE_TAVOR_1X_DDR; | |
129 | case IB_RATE_10_GBPS: return MTHCA_RATE_TAVOR_4X; | |
130 | default: return MTHCA_RATE_TAVOR_FULL; | |
131 | } | |
132 | } | |
133 | ||
134 | u8 mthca_get_rate(struct mthca_dev *dev, int static_rate, u8 port) | |
135 | { | |
136 | u8 rate; | |
137 | ||
138 | if (!static_rate || ib_rate_to_mult(static_rate) >= dev->rate[port - 1]) | |
139 | return 0; | |
140 | ||
141 | if (mthca_is_memfree(dev)) | |
142 | rate = ib_rate_to_memfree(ib_rate_to_mult(static_rate), | |
143 | dev->rate[port - 1]); | |
144 | else | |
145 | rate = ib_rate_to_tavor(static_rate); | |
146 | ||
147 | if (!(dev->limits.stat_rate_support & (1 << rate))) | |
148 | rate = 1; | |
149 | ||
150 | return rate; | |
151 | } | |
152 | ||
1da177e4 LT |
153 | int mthca_create_ah(struct mthca_dev *dev, |
154 | struct mthca_pd *pd, | |
155 | struct ib_ah_attr *ah_attr, | |
156 | struct mthca_ah *ah) | |
157 | { | |
158 | u32 index = -1; | |
159 | struct mthca_av *av = NULL; | |
160 | ||
161 | ah->type = MTHCA_AH_PCI_POOL; | |
162 | ||
d10ddbf6 | 163 | if (mthca_is_memfree(dev)) { |
8df8a34d | 164 | ah->av = kmalloc(sizeof *ah->av, GFP_ATOMIC); |
1da177e4 LT |
165 | if (!ah->av) |
166 | return -ENOMEM; | |
167 | ||
168 | ah->type = MTHCA_AH_KMALLOC; | |
169 | av = ah->av; | |
170 | } else if (!atomic_read(&pd->sqp_count) && | |
171 | !(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) { | |
172 | index = mthca_alloc(&dev->av_table.alloc); | |
173 | ||
174 | /* fall back to allocate in host memory */ | |
175 | if (index == -1) | |
176 | goto on_hca_fail; | |
177 | ||
8df8a34d | 178 | av = kmalloc(sizeof *av, GFP_ATOMIC); |
1da177e4 LT |
179 | if (!av) |
180 | goto on_hca_fail; | |
181 | ||
182 | ah->type = MTHCA_AH_ON_HCA; | |
183 | ah->avdma = dev->av_table.ddr_av_base + | |
184 | index * MTHCA_AV_SIZE; | |
185 | } | |
186 | ||
187 | on_hca_fail: | |
188 | if (ah->type == MTHCA_AH_PCI_POOL) { | |
7ceb740c SJ |
189 | ah->av = pci_pool_zalloc(dev->av_table.pool, |
190 | GFP_ATOMIC, &ah->avdma); | |
1da177e4 LT |
191 | if (!ah->av) |
192 | return -ENOMEM; | |
193 | ||
194 | av = ah->av; | |
195 | } | |
196 | ||
197 | ah->key = pd->ntmr.ibmr.lkey; | |
198 | ||
1da177e4 LT |
199 | av->port_pd = cpu_to_be32(pd->pd_num | (ah_attr->port_num << 24)); |
200 | av->g_slid = ah_attr->src_path_bits; | |
201 | av->dlid = cpu_to_be16(ah_attr->dlid); | |
202 | av->msg_sr = (3 << 4) | /* 2K message */ | |
bf6a9e31 | 203 | mthca_get_rate(dev, ah_attr->static_rate, ah_attr->port_num); |
1da177e4 LT |
204 | av->sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28); |
205 | if (ah_attr->ah_flags & IB_AH_GRH) { | |
206 | av->g_slid |= 0x80; | |
207 | av->gid_index = (ah_attr->port_num - 1) * dev->limits.gid_table_len + | |
208 | ah_attr->grh.sgid_index; | |
209 | av->hop_limit = ah_attr->grh.hop_limit; | |
210 | av->sl_tclass_flowlabel |= | |
211 | cpu_to_be32((ah_attr->grh.traffic_class << 20) | | |
212 | ah_attr->grh.flow_label); | |
213 | memcpy(av->dgid, ah_attr->grh.dgid.raw, 16); | |
214 | } else { | |
215 | /* Arbel workaround -- low byte of GID must be 2 */ | |
216 | av->dgid[3] = cpu_to_be32(2); | |
217 | } | |
218 | ||
219 | if (0) { | |
220 | int j; | |
221 | ||
222 | mthca_dbg(dev, "Created UDAV at %p/%08lx:\n", | |
223 | av, (unsigned long) ah->avdma); | |
224 | for (j = 0; j < 8; ++j) | |
225 | printk(KERN_DEBUG " [%2x] %08x\n", | |
97f52eb4 | 226 | j * 4, be32_to_cpu(((__be32 *) av)[j])); |
1da177e4 LT |
227 | } |
228 | ||
229 | if (ah->type == MTHCA_AH_ON_HCA) { | |
230 | memcpy_toio(dev->av_table.av_map + index * MTHCA_AV_SIZE, | |
231 | av, MTHCA_AV_SIZE); | |
232 | kfree(av); | |
233 | } | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | int mthca_destroy_ah(struct mthca_dev *dev, struct mthca_ah *ah) | |
239 | { | |
240 | switch (ah->type) { | |
241 | case MTHCA_AH_ON_HCA: | |
242 | mthca_free(&dev->av_table.alloc, | |
2fa5e2eb | 243 | (ah->avdma - dev->av_table.ddr_av_base) / |
1da177e4 LT |
244 | MTHCA_AV_SIZE); |
245 | break; | |
246 | ||
247 | case MTHCA_AH_PCI_POOL: | |
248 | pci_pool_free(dev->av_table.pool, ah->av, ah->avdma); | |
249 | break; | |
250 | ||
251 | case MTHCA_AH_KMALLOC: | |
252 | kfree(ah->av); | |
253 | break; | |
254 | } | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
9eacee2a MT |
259 | int mthca_ah_grh_present(struct mthca_ah *ah) |
260 | { | |
261 | return !!(ah->av->g_slid & 0x80); | |
262 | } | |
263 | ||
1da177e4 LT |
264 | int mthca_read_ah(struct mthca_dev *dev, struct mthca_ah *ah, |
265 | struct ib_ud_header *header) | |
266 | { | |
267 | if (ah->type == MTHCA_AH_ON_HCA) | |
268 | return -EINVAL; | |
269 | ||
270 | header->lrh.service_level = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28; | |
271 | header->lrh.destination_lid = ah->av->dlid; | |
97f52eb4 | 272 | header->lrh.source_lid = cpu_to_be16(ah->av->g_slid & 0x7f); |
9eacee2a | 273 | if (mthca_ah_grh_present(ah)) { |
1da177e4 LT |
274 | header->grh.traffic_class = |
275 | (be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 20) & 0xff; | |
276 | header->grh.flow_label = | |
277 | ah->av->sl_tclass_flowlabel & cpu_to_be32(0xfffff); | |
3f37cae6 | 278 | header->grh.hop_limit = ah->av->hop_limit; |
1da177e4 LT |
279 | ib_get_cached_gid(&dev->ib_dev, |
280 | be32_to_cpu(ah->av->port_pd) >> 24, | |
f9e61929 | 281 | ah->av->gid_index % dev->limits.gid_table_len, |
55ee3ab2 | 282 | &header->grh.source_gid, NULL); |
1da177e4 LT |
283 | memcpy(header->grh.destination_gid.raw, |
284 | ah->av->dgid, 16); | |
1da177e4 LT |
285 | } |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
1d89b1ae JM |
290 | int mthca_ah_query(struct ib_ah *ibah, struct ib_ah_attr *attr) |
291 | { | |
292 | struct mthca_ah *ah = to_mah(ibah); | |
293 | struct mthca_dev *dev = to_mdev(ibah->device); | |
294 | ||
295 | /* Only implement for MAD and memfree ah for now. */ | |
296 | if (ah->type == MTHCA_AH_ON_HCA) | |
297 | return -ENOSYS; | |
298 | ||
299 | memset(attr, 0, sizeof *attr); | |
300 | attr->dlid = be16_to_cpu(ah->av->dlid); | |
301 | attr->sl = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28; | |
1d89b1ae | 302 | attr->port_num = be32_to_cpu(ah->av->port_pd) >> 24; |
2290d2c9 JM |
303 | attr->static_rate = mthca_rate_to_ib(dev, ah->av->msg_sr & 0x7, |
304 | attr->port_num); | |
305 | attr->src_path_bits = ah->av->g_slid & 0x7F; | |
1d89b1ae JM |
306 | attr->ah_flags = mthca_ah_grh_present(ah) ? IB_AH_GRH : 0; |
307 | ||
308 | if (attr->ah_flags) { | |
309 | attr->grh.traffic_class = | |
310 | be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 20; | |
311 | attr->grh.flow_label = | |
312 | be32_to_cpu(ah->av->sl_tclass_flowlabel) & 0xfffff; | |
313 | attr->grh.hop_limit = ah->av->hop_limit; | |
314 | attr->grh.sgid_index = ah->av->gid_index & | |
315 | (dev->limits.gid_table_len - 1); | |
316 | memcpy(attr->grh.dgid.raw, ah->av->dgid, 16); | |
317 | } | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
f4f3d0f0 | 322 | int mthca_init_av_table(struct mthca_dev *dev) |
1da177e4 LT |
323 | { |
324 | int err; | |
325 | ||
d10ddbf6 | 326 | if (mthca_is_memfree(dev)) |
1da177e4 LT |
327 | return 0; |
328 | ||
329 | err = mthca_alloc_init(&dev->av_table.alloc, | |
330 | dev->av_table.num_ddr_avs, | |
331 | dev->av_table.num_ddr_avs - 1, | |
332 | 0); | |
333 | if (err) | |
334 | return err; | |
335 | ||
336 | dev->av_table.pool = pci_pool_create("mthca_av", dev->pdev, | |
337 | MTHCA_AV_SIZE, | |
338 | MTHCA_AV_SIZE, 0); | |
339 | if (!dev->av_table.pool) | |
340 | goto out_free_alloc; | |
341 | ||
342 | if (!(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) { | |
343 | dev->av_table.av_map = ioremap(pci_resource_start(dev->pdev, 4) + | |
344 | dev->av_table.ddr_av_base - | |
345 | dev->ddr_start, | |
346 | dev->av_table.num_ddr_avs * | |
347 | MTHCA_AV_SIZE); | |
348 | if (!dev->av_table.av_map) | |
349 | goto out_free_pool; | |
350 | } else | |
351 | dev->av_table.av_map = NULL; | |
352 | ||
353 | return 0; | |
354 | ||
355 | out_free_pool: | |
356 | pci_pool_destroy(dev->av_table.pool); | |
357 | ||
358 | out_free_alloc: | |
359 | mthca_alloc_cleanup(&dev->av_table.alloc); | |
360 | return -ENOMEM; | |
361 | } | |
362 | ||
e1f7868c | 363 | void mthca_cleanup_av_table(struct mthca_dev *dev) |
1da177e4 | 364 | { |
d10ddbf6 | 365 | if (mthca_is_memfree(dev)) |
1da177e4 LT |
366 | return; |
367 | ||
368 | if (dev->av_table.av_map) | |
369 | iounmap(dev->av_table.av_map); | |
370 | pci_pool_destroy(dev->av_table.pool); | |
371 | mthca_alloc_cleanup(&dev->av_table.alloc); | |
372 | } |