Commit | Line | Data |
---|---|---|
0e16aafb SJ |
1 | /* |
2 | * Driver for IBM Power 842 compression accelerator | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 | * | |
18 | * Copyright (C) IBM Corporation, 2012 | |
19 | * | |
20 | * Authors: Robert Jennings <rcj@linux.vnet.ibm.com> | |
21 | * Seth Jennings <sjenning@linux.vnet.ibm.com> | |
22 | */ | |
23 | ||
33b58b01 | 24 | #include <asm/vio.h> |
0e16aafb | 25 | |
7011a122 | 26 | #include "nx-842.h" |
0e16aafb SJ |
27 | #include "nx_csbcpb.h" /* struct nx_csbcpb */ |
28 | ||
0e16aafb SJ |
29 | MODULE_LICENSE("GPL"); |
30 | MODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>"); | |
31 | MODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors"); | |
32 | ||
0e16aafb SJ |
33 | /* IO buffer must be 128 byte aligned */ |
34 | #define IO_BUFFER_ALIGN 128 | |
35 | ||
959e6659 DS |
36 | static struct nx842_constraints nx842_pseries_constraints = { |
37 | .alignment = IO_BUFFER_ALIGN, | |
38 | .multiple = DDE_BUFFER_LAST_MULT, | |
39 | .minimum = IO_BUFFER_ALIGN, | |
40 | .maximum = PAGE_SIZE, /* dynamic, max_sync_size */ | |
41 | }; | |
42 | ||
b8e04187 | 43 | static int check_constraints(unsigned long buf, unsigned int *len, bool in) |
0e16aafb | 44 | { |
b8e04187 DS |
45 | if (!IS_ALIGNED(buf, nx842_pseries_constraints.alignment)) { |
46 | pr_debug("%s buffer 0x%lx not aligned to 0x%x\n", | |
47 | in ? "input" : "output", buf, | |
48 | nx842_pseries_constraints.alignment); | |
49 | return -EINVAL; | |
50 | } | |
51 | if (*len % nx842_pseries_constraints.multiple) { | |
52 | pr_debug("%s buffer len 0x%x not multiple of 0x%x\n", | |
53 | in ? "input" : "output", *len, | |
54 | nx842_pseries_constraints.multiple); | |
55 | if (in) | |
56 | return -EINVAL; | |
57 | *len = round_down(*len, nx842_pseries_constraints.multiple); | |
58 | } | |
59 | if (*len < nx842_pseries_constraints.minimum) { | |
60 | pr_debug("%s buffer len 0x%x under minimum 0x%x\n", | |
61 | in ? "input" : "output", *len, | |
62 | nx842_pseries_constraints.minimum); | |
63 | return -EINVAL; | |
64 | } | |
65 | if (*len > nx842_pseries_constraints.maximum) { | |
66 | pr_debug("%s buffer len 0x%x over maximum 0x%x\n", | |
67 | in ? "input" : "output", *len, | |
68 | nx842_pseries_constraints.maximum); | |
69 | if (in) | |
70 | return -EINVAL; | |
71 | *len = nx842_pseries_constraints.maximum; | |
72 | } | |
73 | return 0; | |
0e16aafb SJ |
74 | } |
75 | ||
b8e04187 DS |
76 | /* I assume we need to align the CSB? */ |
77 | #define WORKMEM_ALIGN (256) | |
78 | ||
79 | struct nx842_workmem { | |
80 | /* scatterlist */ | |
81 | char slin[4096]; | |
82 | char slout[4096]; | |
83 | /* coprocessor status/parameter block */ | |
84 | struct nx_csbcpb csbcpb; | |
85 | ||
86 | char padding[WORKMEM_ALIGN]; | |
87 | } __aligned(WORKMEM_ALIGN); | |
88 | ||
0e16aafb SJ |
89 | /* Macros for fields within nx_csbcpb */ |
90 | /* Check the valid bit within the csbcpb valid field */ | |
91 | #define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7)) | |
92 | ||
93 | /* CE macros operate on the completion_extension field bits in the csbcpb. | |
94 | * CE0 0=full completion, 1=partial completion | |
95 | * CE1 0=CE0 indicates completion, 1=termination (output may be modified) | |
96 | * CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */ | |
97 | #define NX842_CSBCPB_CE0(x) (x & BIT_MASK(7)) | |
98 | #define NX842_CSBCPB_CE1(x) (x & BIT_MASK(6)) | |
99 | #define NX842_CSBCPB_CE2(x) (x & BIT_MASK(5)) | |
100 | ||
101 | /* The NX unit accepts data only on 4K page boundaries */ | |
b8e04187 | 102 | #define NX842_HW_PAGE_SIZE (4096) |
0e16aafb SJ |
103 | #define NX842_HW_PAGE_MASK (~(NX842_HW_PAGE_SIZE-1)) |
104 | ||
105 | enum nx842_status { | |
106 | UNAVAILABLE, | |
107 | AVAILABLE | |
108 | }; | |
109 | ||
110 | struct ibm_nx842_counters { | |
111 | atomic64_t comp_complete; | |
112 | atomic64_t comp_failed; | |
113 | atomic64_t decomp_complete; | |
114 | atomic64_t decomp_failed; | |
115 | atomic64_t swdecomp; | |
116 | atomic64_t comp_times[32]; | |
117 | atomic64_t decomp_times[32]; | |
118 | }; | |
119 | ||
120 | static struct nx842_devdata { | |
121 | struct vio_dev *vdev; | |
122 | struct device *dev; | |
123 | struct ibm_nx842_counters *counters; | |
124 | unsigned int max_sg_len; | |
125 | unsigned int max_sync_size; | |
126 | unsigned int max_sync_sg; | |
127 | enum nx842_status status; | |
128 | } __rcu *devdata; | |
129 | static DEFINE_SPINLOCK(devdata_mutex); | |
130 | ||
131 | #define NX842_COUNTER_INC(_x) \ | |
132 | static inline void nx842_inc_##_x( \ | |
133 | const struct nx842_devdata *dev) { \ | |
134 | if (dev) \ | |
135 | atomic64_inc(&dev->counters->_x); \ | |
136 | } | |
137 | NX842_COUNTER_INC(comp_complete); | |
138 | NX842_COUNTER_INC(comp_failed); | |
139 | NX842_COUNTER_INC(decomp_complete); | |
140 | NX842_COUNTER_INC(decomp_failed); | |
141 | NX842_COUNTER_INC(swdecomp); | |
142 | ||
143 | #define NX842_HIST_SLOTS 16 | |
144 | ||
145 | static void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time) | |
146 | { | |
147 | int bucket = fls(time); | |
148 | ||
149 | if (bucket) | |
150 | bucket = min((NX842_HIST_SLOTS - 1), bucket - 1); | |
151 | ||
152 | atomic64_inc(×[bucket]); | |
153 | } | |
154 | ||
155 | /* NX unit operation flags */ | |
156 | #define NX842_OP_COMPRESS 0x0 | |
157 | #define NX842_OP_CRC 0x1 | |
158 | #define NX842_OP_DECOMPRESS 0x2 | |
159 | #define NX842_OP_COMPRESS_CRC (NX842_OP_COMPRESS | NX842_OP_CRC) | |
160 | #define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC) | |
161 | #define NX842_OP_ASYNC (1<<23) | |
162 | #define NX842_OP_NOTIFY (1<<22) | |
163 | #define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8) | |
164 | ||
165 | static unsigned long nx842_get_desired_dma(struct vio_dev *viodev) | |
166 | { | |
167 | /* No use of DMA mappings within the driver. */ | |
168 | return 0; | |
169 | } | |
170 | ||
171 | struct nx842_slentry { | |
33b58b01 | 172 | unsigned long ptr; /* Real address (use __pa()) */ |
0e16aafb SJ |
173 | unsigned long len; |
174 | }; | |
175 | ||
176 | /* pHyp scatterlist entry */ | |
177 | struct nx842_scatterlist { | |
178 | int entry_nr; /* number of slentries */ | |
179 | struct nx842_slentry *entries; /* ptr to array of slentries */ | |
180 | }; | |
181 | ||
182 | /* Does not include sizeof(entry_nr) in the size */ | |
183 | static inline unsigned long nx842_get_scatterlist_size( | |
184 | struct nx842_scatterlist *sl) | |
185 | { | |
186 | return sl->entry_nr * sizeof(struct nx842_slentry); | |
187 | } | |
188 | ||
189 | static int nx842_build_scatterlist(unsigned long buf, int len, | |
190 | struct nx842_scatterlist *sl) | |
191 | { | |
192 | unsigned long nextpage; | |
193 | struct nx842_slentry *entry; | |
194 | ||
195 | sl->entry_nr = 0; | |
196 | ||
197 | entry = sl->entries; | |
198 | while (len) { | |
0ba3e101 | 199 | entry->ptr = nx842_get_pa((void *)buf); |
0e16aafb SJ |
200 | nextpage = ALIGN(buf + 1, NX842_HW_PAGE_SIZE); |
201 | if (nextpage < buf + len) { | |
202 | /* we aren't at the end yet */ | |
203 | if (IS_ALIGNED(buf, NX842_HW_PAGE_SIZE)) | |
204 | /* we are in the middle (or beginning) */ | |
205 | entry->len = NX842_HW_PAGE_SIZE; | |
206 | else | |
207 | /* we are at the beginning */ | |
208 | entry->len = nextpage - buf; | |
209 | } else { | |
210 | /* at the end */ | |
211 | entry->len = len; | |
212 | } | |
213 | ||
214 | len -= entry->len; | |
215 | buf += entry->len; | |
216 | sl->entry_nr++; | |
217 | entry++; | |
218 | } | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
0e16aafb SJ |
223 | static int nx842_validate_result(struct device *dev, |
224 | struct cop_status_block *csb) | |
225 | { | |
226 | /* The csb must be valid after returning from vio_h_cop_sync */ | |
227 | if (!NX842_CSBCBP_VALID_CHK(csb->valid)) { | |
228 | dev_err(dev, "%s: cspcbp not valid upon completion.\n", | |
229 | __func__); | |
230 | dev_dbg(dev, "valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x\n", | |
231 | csb->valid, | |
232 | csb->crb_seq_number, | |
233 | csb->completion_code, | |
234 | csb->completion_extension); | |
235 | dev_dbg(dev, "processed_bytes:%d address:0x%016lx\n", | |
236 | csb->processed_byte_count, | |
237 | (unsigned long)csb->address); | |
238 | return -EIO; | |
239 | } | |
240 | ||
241 | /* Check return values from the hardware in the CSB */ | |
242 | switch (csb->completion_code) { | |
243 | case 0: /* Completed without error */ | |
244 | break; | |
245 | case 64: /* Target bytes > Source bytes during compression */ | |
246 | case 13: /* Output buffer too small */ | |
247 | dev_dbg(dev, "%s: Compression output larger than input\n", | |
248 | __func__); | |
249 | return -ENOSPC; | |
250 | case 66: /* Input data contains an illegal template field */ | |
251 | case 67: /* Template indicates data past the end of the input stream */ | |
252 | dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n", | |
253 | __func__, csb->completion_code); | |
254 | return -EINVAL; | |
255 | default: | |
256 | dev_dbg(dev, "%s: Unspecified error (code:%d)\n", | |
257 | __func__, csb->completion_code); | |
258 | return -EIO; | |
259 | } | |
260 | ||
261 | /* Hardware sanity check */ | |
262 | if (!NX842_CSBCPB_CE2(csb->completion_extension)) { | |
263 | dev_err(dev, "%s: No error returned by hardware, but " | |
264 | "data returned is unusable, contact support.\n" | |
265 | "(Additional info: csbcbp->processed bytes " | |
266 | "does not specify processed bytes for the " | |
267 | "target buffer.)\n", __func__); | |
268 | return -EIO; | |
269 | } | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
274 | /** | |
7011a122 | 275 | * nx842_pseries_compress - Compress data using the 842 algorithm |
0e16aafb SJ |
276 | * |
277 | * Compression provide by the NX842 coprocessor on IBM Power systems. | |
278 | * The input buffer is compressed and the result is stored in the | |
279 | * provided output buffer. | |
280 | * | |
281 | * Upon return from this function @outlen contains the length of the | |
282 | * compressed data. If there is an error then @outlen will be 0 and an | |
283 | * error will be specified by the return code from this function. | |
284 | * | |
b8e04187 DS |
285 | * @in: Pointer to input buffer |
286 | * @inlen: Length of input buffer | |
0e16aafb SJ |
287 | * @out: Pointer to output buffer |
288 | * @outlen: Length of output buffer | |
289 | * @wrkmem: ptr to buffer for working memory, size determined by | |
7011a122 | 290 | * NX842_MEM_COMPRESS |
0e16aafb SJ |
291 | * |
292 | * Returns: | |
293 | * 0 Success, output of length @outlen stored in the buffer at @out | |
294 | * -ENOMEM Unable to allocate internal buffers | |
295 | * -ENOSPC Output buffer is to small | |
0e16aafb SJ |
296 | * -EIO Internal error |
297 | * -ENODEV Hardware unavailable | |
298 | */ | |
7011a122 DS |
299 | static int nx842_pseries_compress(const unsigned char *in, unsigned int inlen, |
300 | unsigned char *out, unsigned int *outlen, | |
301 | void *wmem) | |
0e16aafb | 302 | { |
0e16aafb SJ |
303 | struct nx842_devdata *local_devdata; |
304 | struct device *dev = NULL; | |
305 | struct nx842_workmem *workmem; | |
306 | struct nx842_scatterlist slin, slout; | |
307 | struct nx_csbcpb *csbcpb; | |
b8e04187 DS |
308 | int ret = 0, max_sync_size; |
309 | unsigned long inbuf, outbuf; | |
0e16aafb SJ |
310 | struct vio_pfo_op op = { |
311 | .done = NULL, | |
312 | .handle = 0, | |
313 | .timeout = 0, | |
314 | }; | |
b8e04187 | 315 | unsigned long start = get_tb(); |
0e16aafb | 316 | |
0e16aafb | 317 | inbuf = (unsigned long)in; |
b8e04187 DS |
318 | if (check_constraints(inbuf, &inlen, true)) |
319 | return -EINVAL; | |
320 | ||
321 | outbuf = (unsigned long)out; | |
322 | if (check_constraints(outbuf, outlen, false)) | |
0e16aafb SJ |
323 | return -EINVAL; |
324 | ||
325 | rcu_read_lock(); | |
326 | local_devdata = rcu_dereference(devdata); | |
327 | if (!local_devdata || !local_devdata->dev) { | |
328 | rcu_read_unlock(); | |
329 | return -ENODEV; | |
330 | } | |
331 | max_sync_size = local_devdata->max_sync_size; | |
332 | dev = local_devdata->dev; | |
333 | ||
0e16aafb | 334 | /* Init scatterlist */ |
b8e04187 | 335 | workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN); |
0e16aafb SJ |
336 | slin.entries = (struct nx842_slentry *)workmem->slin; |
337 | slout.entries = (struct nx842_slentry *)workmem->slout; | |
338 | ||
339 | /* Init operation */ | |
340 | op.flags = NX842_OP_COMPRESS; | |
341 | csbcpb = &workmem->csbcpb; | |
342 | memset(csbcpb, 0, sizeof(*csbcpb)); | |
0ba3e101 NF |
343 | op.csbcpb = nx842_get_pa(csbcpb); |
344 | op.out = nx842_get_pa(slout.entries); | |
0e16aafb | 345 | |
b8e04187 DS |
346 | if ((inbuf & NX842_HW_PAGE_MASK) == |
347 | ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) { | |
348 | /* Create direct DDE */ | |
349 | op.in = nx842_get_pa((void *)inbuf); | |
350 | op.inlen = inlen; | |
351 | } else { | |
352 | /* Create indirect DDE (scatterlist) */ | |
353 | nx842_build_scatterlist(inbuf, inlen, &slin); | |
354 | op.in = nx842_get_pa(slin.entries); | |
355 | op.inlen = -nx842_get_scatterlist_size(&slin); | |
356 | } | |
0e16aafb | 357 | |
b8e04187 DS |
358 | if ((outbuf & NX842_HW_PAGE_MASK) == |
359 | ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) { | |
360 | /* Create direct DDE */ | |
361 | op.out = nx842_get_pa((void *)outbuf); | |
362 | op.outlen = *outlen; | |
363 | } else { | |
364 | /* Create indirect DDE (scatterlist) */ | |
365 | nx842_build_scatterlist(outbuf, *outlen, &slout); | |
366 | op.out = nx842_get_pa(slout.entries); | |
0e16aafb | 367 | op.outlen = -nx842_get_scatterlist_size(&slout); |
b8e04187 | 368 | } |
0e16aafb | 369 | |
b8e04187 DS |
370 | /* Send request to pHyp */ |
371 | ret = vio_h_cop_sync(local_devdata->vdev, &op); | |
0e16aafb | 372 | |
b8e04187 DS |
373 | /* Check for pHyp error */ |
374 | if (ret) { | |
375 | dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n", | |
376 | __func__, ret, op.hcall_err); | |
377 | ret = -EIO; | |
378 | goto unlock; | |
0e16aafb SJ |
379 | } |
380 | ||
b8e04187 DS |
381 | /* Check for hardware error */ |
382 | ret = nx842_validate_result(dev, &csbcpb->csb); | |
383 | if (ret) | |
384 | goto unlock; | |
385 | ||
386 | *outlen = csbcpb->csb.processed_byte_count; | |
387 | dev_dbg(dev, "%s: processed_bytes=%d\n", __func__, *outlen); | |
0e16aafb SJ |
388 | |
389 | unlock: | |
390 | if (ret) | |
391 | nx842_inc_comp_failed(local_devdata); | |
392 | else { | |
393 | nx842_inc_comp_complete(local_devdata); | |
394 | ibm_nx842_incr_hist(local_devdata->counters->comp_times, | |
b8e04187 | 395 | (get_tb() - start) / tb_ticks_per_usec); |
0e16aafb SJ |
396 | } |
397 | rcu_read_unlock(); | |
398 | return ret; | |
399 | } | |
0e16aafb | 400 | |
0e16aafb | 401 | /** |
7011a122 | 402 | * nx842_pseries_decompress - Decompress data using the 842 algorithm |
0e16aafb SJ |
403 | * |
404 | * Decompression provide by the NX842 coprocessor on IBM Power systems. | |
405 | * The input buffer is decompressed and the result is stored in the | |
406 | * provided output buffer. The size allocated to the output buffer is | |
407 | * provided by the caller of this function in @outlen. Upon return from | |
408 | * this function @outlen contains the length of the decompressed data. | |
409 | * If there is an error then @outlen will be 0 and an error will be | |
410 | * specified by the return code from this function. | |
411 | * | |
b8e04187 | 412 | * @in: Pointer to input buffer |
0e16aafb | 413 | * @inlen: Length of input buffer |
b8e04187 DS |
414 | * @out: Pointer to output buffer |
415 | * @outlen: Length of output buffer | |
0e16aafb | 416 | * @wrkmem: ptr to buffer for working memory, size determined by |
7011a122 | 417 | * NX842_MEM_COMPRESS |
0e16aafb SJ |
418 | * |
419 | * Returns: | |
420 | * 0 Success, output of length @outlen stored in the buffer at @out | |
421 | * -ENODEV Hardware decompression device is unavailable | |
422 | * -ENOMEM Unable to allocate internal buffers | |
423 | * -ENOSPC Output buffer is to small | |
424 | * -EINVAL Bad input data encountered when attempting decompress | |
425 | * -EIO Internal error | |
426 | */ | |
7011a122 DS |
427 | static int nx842_pseries_decompress(const unsigned char *in, unsigned int inlen, |
428 | unsigned char *out, unsigned int *outlen, | |
429 | void *wmem) | |
0e16aafb | 430 | { |
0e16aafb SJ |
431 | struct nx842_devdata *local_devdata; |
432 | struct device *dev = NULL; | |
433 | struct nx842_workmem *workmem; | |
434 | struct nx842_scatterlist slin, slout; | |
435 | struct nx_csbcpb *csbcpb; | |
b8e04187 | 436 | int ret = 0, max_sync_size; |
0e16aafb SJ |
437 | unsigned long inbuf, outbuf; |
438 | struct vio_pfo_op op = { | |
439 | .done = NULL, | |
440 | .handle = 0, | |
441 | .timeout = 0, | |
442 | }; | |
b8e04187 | 443 | unsigned long start = get_tb(); |
0e16aafb SJ |
444 | |
445 | /* Ensure page alignment and size */ | |
b8e04187 DS |
446 | inbuf = (unsigned long)in; |
447 | if (check_constraints(inbuf, &inlen, true)) | |
448 | return -EINVAL; | |
449 | ||
0e16aafb | 450 | outbuf = (unsigned long)out; |
b8e04187 | 451 | if (check_constraints(outbuf, outlen, false)) |
0e16aafb SJ |
452 | return -EINVAL; |
453 | ||
454 | rcu_read_lock(); | |
455 | local_devdata = rcu_dereference(devdata); | |
b8e04187 DS |
456 | if (!local_devdata || !local_devdata->dev) { |
457 | rcu_read_unlock(); | |
458 | return -ENODEV; | |
0e16aafb | 459 | } |
b8e04187 DS |
460 | max_sync_size = local_devdata->max_sync_size; |
461 | dev = local_devdata->dev; | |
462 | ||
463 | workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN); | |
0e16aafb SJ |
464 | |
465 | /* Init scatterlist */ | |
466 | slin.entries = (struct nx842_slentry *)workmem->slin; | |
467 | slout.entries = (struct nx842_slentry *)workmem->slout; | |
468 | ||
469 | /* Init operation */ | |
470 | op.flags = NX842_OP_DECOMPRESS; | |
471 | csbcpb = &workmem->csbcpb; | |
472 | memset(csbcpb, 0, sizeof(*csbcpb)); | |
0ba3e101 | 473 | op.csbcpb = nx842_get_pa(csbcpb); |
0e16aafb | 474 | |
b8e04187 DS |
475 | if ((inbuf & NX842_HW_PAGE_MASK) == |
476 | ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) { | |
477 | /* Create direct DDE */ | |
478 | op.in = nx842_get_pa((void *)inbuf); | |
479 | op.inlen = inlen; | |
480 | } else { | |
481 | /* Create indirect DDE (scatterlist) */ | |
482 | nx842_build_scatterlist(inbuf, inlen, &slin); | |
483 | op.in = nx842_get_pa(slin.entries); | |
484 | op.inlen = -nx842_get_scatterlist_size(&slin); | |
485 | } | |
0e16aafb | 486 | |
b8e04187 DS |
487 | if ((outbuf & NX842_HW_PAGE_MASK) == |
488 | ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) { | |
489 | /* Create direct DDE */ | |
490 | op.out = nx842_get_pa((void *)outbuf); | |
491 | op.outlen = *outlen; | |
492 | } else { | |
493 | /* Create indirect DDE (scatterlist) */ | |
494 | nx842_build_scatterlist(outbuf, *outlen, &slout); | |
495 | op.out = nx842_get_pa(slout.entries); | |
496 | op.outlen = -nx842_get_scatterlist_size(&slout); | |
497 | } | |
0e16aafb | 498 | |
b8e04187 DS |
499 | /* Send request to pHyp */ |
500 | ret = vio_h_cop_sync(local_devdata->vdev, &op); | |
0e16aafb | 501 | |
b8e04187 DS |
502 | /* Check for pHyp error */ |
503 | if (ret) { | |
504 | dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n", | |
505 | __func__, ret, op.hcall_err); | |
506 | goto unlock; | |
0e16aafb SJ |
507 | } |
508 | ||
b8e04187 DS |
509 | /* Check for hardware error */ |
510 | ret = nx842_validate_result(dev, &csbcpb->csb); | |
511 | if (ret) | |
512 | goto unlock; | |
513 | ||
514 | *outlen = csbcpb->csb.processed_byte_count; | |
0e16aafb SJ |
515 | |
516 | unlock: | |
517 | if (ret) | |
518 | /* decompress fail */ | |
519 | nx842_inc_decomp_failed(local_devdata); | |
520 | else { | |
0e16aafb SJ |
521 | nx842_inc_decomp_complete(local_devdata); |
522 | ibm_nx842_incr_hist(local_devdata->counters->decomp_times, | |
b8e04187 | 523 | (get_tb() - start) / tb_ticks_per_usec); |
0e16aafb SJ |
524 | } |
525 | ||
526 | rcu_read_unlock(); | |
527 | return ret; | |
528 | } | |
0e16aafb SJ |
529 | |
530 | /** | |
531 | * nx842_OF_set_defaults -- Set default (disabled) values for devdata | |
532 | * | |
533 | * @devdata - struct nx842_devdata to update | |
534 | * | |
535 | * Returns: | |
536 | * 0 on success | |
537 | * -ENOENT if @devdata ptr is NULL | |
538 | */ | |
539 | static int nx842_OF_set_defaults(struct nx842_devdata *devdata) | |
540 | { | |
541 | if (devdata) { | |
542 | devdata->max_sync_size = 0; | |
543 | devdata->max_sync_sg = 0; | |
544 | devdata->max_sg_len = 0; | |
545 | devdata->status = UNAVAILABLE; | |
546 | return 0; | |
547 | } else | |
548 | return -ENOENT; | |
549 | } | |
550 | ||
551 | /** | |
552 | * nx842_OF_upd_status -- Update the device info from OF status prop | |
553 | * | |
554 | * The status property indicates if the accelerator is enabled. If the | |
555 | * device is in the OF tree it indicates that the hardware is present. | |
556 | * The status field indicates if the device is enabled when the status | |
557 | * is 'okay'. Otherwise the device driver will be disabled. | |
558 | * | |
559 | * @devdata - struct nx842_devdata to update | |
560 | * @prop - struct property point containing the maxsyncop for the update | |
561 | * | |
562 | * Returns: | |
563 | * 0 - Device is available | |
564 | * -EINVAL - Device is not available | |
565 | */ | |
566 | static int nx842_OF_upd_status(struct nx842_devdata *devdata, | |
567 | struct property *prop) { | |
568 | int ret = 0; | |
569 | const char *status = (const char *)prop->value; | |
570 | ||
571 | if (!strncmp(status, "okay", (size_t)prop->length)) { | |
572 | devdata->status = AVAILABLE; | |
573 | } else { | |
574 | dev_info(devdata->dev, "%s: status '%s' is not 'okay'\n", | |
575 | __func__, status); | |
576 | devdata->status = UNAVAILABLE; | |
577 | } | |
578 | ||
579 | return ret; | |
580 | } | |
581 | ||
582 | /** | |
583 | * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop | |
584 | * | |
585 | * Definition of the 'ibm,max-sg-len' OF property: | |
586 | * This field indicates the maximum byte length of a scatter list | |
587 | * for the platform facility. It is a single cell encoded as with encode-int. | |
588 | * | |
589 | * Example: | |
590 | * # od -x ibm,max-sg-len | |
591 | * 0000000 0000 0ff0 | |
592 | * | |
593 | * In this example, the maximum byte length of a scatter list is | |
594 | * 0x0ff0 (4,080). | |
595 | * | |
596 | * @devdata - struct nx842_devdata to update | |
597 | * @prop - struct property point containing the maxsyncop for the update | |
598 | * | |
599 | * Returns: | |
600 | * 0 on success | |
601 | * -EINVAL on failure | |
602 | */ | |
603 | static int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata, | |
604 | struct property *prop) { | |
605 | int ret = 0; | |
606 | const int *maxsglen = prop->value; | |
607 | ||
608 | if (prop->length != sizeof(*maxsglen)) { | |
609 | dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__); | |
610 | dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__, | |
611 | prop->length, sizeof(*maxsglen)); | |
612 | ret = -EINVAL; | |
613 | } else { | |
614 | devdata->max_sg_len = (unsigned int)min(*maxsglen, | |
615 | (int)NX842_HW_PAGE_SIZE); | |
616 | } | |
617 | ||
618 | return ret; | |
619 | } | |
620 | ||
621 | /** | |
622 | * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop | |
623 | * | |
624 | * Definition of the 'ibm,max-sync-cop' OF property: | |
625 | * Two series of cells. The first series of cells represents the maximums | |
626 | * that can be synchronously compressed. The second series of cells | |
627 | * represents the maximums that can be synchronously decompressed. | |
628 | * 1. The first cell in each series contains the count of the number of | |
629 | * data length, scatter list elements pairs that follow – each being | |
630 | * of the form | |
631 | * a. One cell data byte length | |
632 | * b. One cell total number of scatter list elements | |
633 | * | |
634 | * Example: | |
635 | * # od -x ibm,max-sync-cop | |
636 | * 0000000 0000 0001 0000 1000 0000 01fe 0000 0001 | |
637 | * 0000020 0000 1000 0000 01fe | |
638 | * | |
639 | * In this example, compression supports 0x1000 (4,096) data byte length | |
640 | * and 0x1fe (510) total scatter list elements. Decompression supports | |
641 | * 0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list | |
642 | * elements. | |
643 | * | |
644 | * @devdata - struct nx842_devdata to update | |
645 | * @prop - struct property point containing the maxsyncop for the update | |
646 | * | |
647 | * Returns: | |
648 | * 0 on success | |
649 | * -EINVAL on failure | |
650 | */ | |
651 | static int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata, | |
652 | struct property *prop) { | |
653 | int ret = 0; | |
654 | const struct maxsynccop_t { | |
655 | int comp_elements; | |
656 | int comp_data_limit; | |
657 | int comp_sg_limit; | |
658 | int decomp_elements; | |
659 | int decomp_data_limit; | |
660 | int decomp_sg_limit; | |
661 | } *maxsynccop; | |
662 | ||
663 | if (prop->length != sizeof(*maxsynccop)) { | |
664 | dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__); | |
665 | dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length, | |
666 | sizeof(*maxsynccop)); | |
667 | ret = -EINVAL; | |
668 | goto out; | |
669 | } | |
670 | ||
671 | maxsynccop = (const struct maxsynccop_t *)prop->value; | |
672 | ||
673 | /* Use one limit rather than separate limits for compression and | |
674 | * decompression. Set a maximum for this so as not to exceed the | |
675 | * size that the header can support and round the value down to | |
676 | * the hardware page size (4K) */ | |
677 | devdata->max_sync_size = | |
678 | (unsigned int)min(maxsynccop->comp_data_limit, | |
679 | maxsynccop->decomp_data_limit); | |
680 | ||
681 | devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size, | |
b8e04187 | 682 | 65536); |
0e16aafb | 683 | |
b8e04187 | 684 | if (devdata->max_sync_size < 4096) { |
0e16aafb SJ |
685 | dev_err(devdata->dev, "%s: hardware max data size (%u) is " |
686 | "less than the driver minimum, unable to use " | |
687 | "the hardware device\n", | |
688 | __func__, devdata->max_sync_size); | |
689 | ret = -EINVAL; | |
690 | goto out; | |
691 | } | |
692 | ||
959e6659 DS |
693 | nx842_pseries_constraints.maximum = devdata->max_sync_size; |
694 | ||
0e16aafb SJ |
695 | devdata->max_sync_sg = (unsigned int)min(maxsynccop->comp_sg_limit, |
696 | maxsynccop->decomp_sg_limit); | |
697 | if (devdata->max_sync_sg < 1) { | |
698 | dev_err(devdata->dev, "%s: hardware max sg size (%u) is " | |
699 | "less than the driver minimum, unable to use " | |
700 | "the hardware device\n", | |
701 | __func__, devdata->max_sync_sg); | |
702 | ret = -EINVAL; | |
703 | goto out; | |
704 | } | |
705 | ||
706 | out: | |
707 | return ret; | |
708 | } | |
709 | ||
710 | /** | |
711 | * | |
712 | * nx842_OF_upd -- Handle OF properties updates for the device. | |
713 | * | |
714 | * Set all properties from the OF tree. Optionally, a new property | |
715 | * can be provided by the @new_prop pointer to overwrite an existing value. | |
716 | * The device will remain disabled until all values are valid, this function | |
717 | * will return an error for updates unless all values are valid. | |
718 | * | |
719 | * @new_prop: If not NULL, this property is being updated. If NULL, update | |
720 | * all properties from the current values in the OF tree. | |
721 | * | |
722 | * Returns: | |
723 | * 0 - Success | |
724 | * -ENOMEM - Could not allocate memory for new devdata structure | |
725 | * -EINVAL - property value not found, new_prop is not a recognized | |
726 | * property for the device or property value is not valid. | |
727 | * -ENODEV - Device is not available | |
728 | */ | |
729 | static int nx842_OF_upd(struct property *new_prop) | |
730 | { | |
731 | struct nx842_devdata *old_devdata = NULL; | |
732 | struct nx842_devdata *new_devdata = NULL; | |
733 | struct device_node *of_node = NULL; | |
734 | struct property *status = NULL; | |
735 | struct property *maxsglen = NULL; | |
736 | struct property *maxsyncop = NULL; | |
737 | int ret = 0; | |
738 | unsigned long flags; | |
739 | ||
740 | spin_lock_irqsave(&devdata_mutex, flags); | |
741 | old_devdata = rcu_dereference_check(devdata, | |
742 | lockdep_is_held(&devdata_mutex)); | |
743 | if (old_devdata) | |
744 | of_node = old_devdata->dev->of_node; | |
745 | ||
746 | if (!old_devdata || !of_node) { | |
747 | pr_err("%s: device is not available\n", __func__); | |
748 | spin_unlock_irqrestore(&devdata_mutex, flags); | |
749 | return -ENODEV; | |
750 | } | |
751 | ||
752 | new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS); | |
753 | if (!new_devdata) { | |
754 | dev_err(old_devdata->dev, "%s: Could not allocate memory for device data\n", __func__); | |
755 | ret = -ENOMEM; | |
756 | goto error_out; | |
757 | } | |
758 | ||
759 | memcpy(new_devdata, old_devdata, sizeof(*old_devdata)); | |
760 | new_devdata->counters = old_devdata->counters; | |
761 | ||
762 | /* Set ptrs for existing properties */ | |
763 | status = of_find_property(of_node, "status", NULL); | |
764 | maxsglen = of_find_property(of_node, "ibm,max-sg-len", NULL); | |
765 | maxsyncop = of_find_property(of_node, "ibm,max-sync-cop", NULL); | |
766 | if (!status || !maxsglen || !maxsyncop) { | |
767 | dev_err(old_devdata->dev, "%s: Could not locate device properties\n", __func__); | |
768 | ret = -EINVAL; | |
769 | goto error_out; | |
770 | } | |
771 | ||
259092a3 GL |
772 | /* |
773 | * If this is a property update, there are only certain properties that | |
774 | * we care about. Bail if it isn't in the below list | |
775 | */ | |
776 | if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) || | |
777 | strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) || | |
778 | strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length))) | |
779 | goto out; | |
0e16aafb SJ |
780 | |
781 | /* Perform property updates */ | |
782 | ret = nx842_OF_upd_status(new_devdata, status); | |
783 | if (ret) | |
784 | goto error_out; | |
785 | ||
786 | ret = nx842_OF_upd_maxsglen(new_devdata, maxsglen); | |
787 | if (ret) | |
788 | goto error_out; | |
789 | ||
790 | ret = nx842_OF_upd_maxsyncop(new_devdata, maxsyncop); | |
791 | if (ret) | |
792 | goto error_out; | |
793 | ||
794 | out: | |
795 | dev_info(old_devdata->dev, "%s: max_sync_size new:%u old:%u\n", | |
796 | __func__, new_devdata->max_sync_size, | |
797 | old_devdata->max_sync_size); | |
798 | dev_info(old_devdata->dev, "%s: max_sync_sg new:%u old:%u\n", | |
799 | __func__, new_devdata->max_sync_sg, | |
800 | old_devdata->max_sync_sg); | |
801 | dev_info(old_devdata->dev, "%s: max_sg_len new:%u old:%u\n", | |
802 | __func__, new_devdata->max_sg_len, | |
803 | old_devdata->max_sg_len); | |
804 | ||
805 | rcu_assign_pointer(devdata, new_devdata); | |
806 | spin_unlock_irqrestore(&devdata_mutex, flags); | |
807 | synchronize_rcu(); | |
808 | dev_set_drvdata(new_devdata->dev, new_devdata); | |
809 | kfree(old_devdata); | |
810 | return 0; | |
811 | ||
812 | error_out: | |
813 | if (new_devdata) { | |
814 | dev_info(old_devdata->dev, "%s: device disabled\n", __func__); | |
815 | nx842_OF_set_defaults(new_devdata); | |
816 | rcu_assign_pointer(devdata, new_devdata); | |
817 | spin_unlock_irqrestore(&devdata_mutex, flags); | |
818 | synchronize_rcu(); | |
819 | dev_set_drvdata(new_devdata->dev, new_devdata); | |
820 | kfree(old_devdata); | |
821 | } else { | |
822 | dev_err(old_devdata->dev, "%s: could not update driver from hardware\n", __func__); | |
823 | spin_unlock_irqrestore(&devdata_mutex, flags); | |
824 | } | |
825 | ||
826 | if (!ret) | |
827 | ret = -EINVAL; | |
828 | return ret; | |
829 | } | |
830 | ||
831 | /** | |
832 | * nx842_OF_notifier - Process updates to OF properties for the device | |
833 | * | |
834 | * @np: notifier block | |
835 | * @action: notifier action | |
836 | * @update: struct pSeries_reconfig_prop_update pointer if action is | |
837 | * PSERIES_UPDATE_PROPERTY | |
838 | * | |
839 | * Returns: | |
840 | * NOTIFY_OK on success | |
841 | * NOTIFY_BAD encoded with error number on failure, use | |
842 | * notifier_to_errno() to decode this value | |
843 | */ | |
1cf3d8b3 | 844 | static int nx842_OF_notifier(struct notifier_block *np, unsigned long action, |
f5242e5a | 845 | void *data) |
0e16aafb | 846 | { |
f5242e5a | 847 | struct of_reconfig_data *upd = data; |
0e16aafb SJ |
848 | struct nx842_devdata *local_devdata; |
849 | struct device_node *node = NULL; | |
850 | ||
0e16aafb SJ |
851 | rcu_read_lock(); |
852 | local_devdata = rcu_dereference(devdata); | |
853 | if (local_devdata) | |
854 | node = local_devdata->dev->of_node; | |
855 | ||
856 | if (local_devdata && | |
1cf3d8b3 NF |
857 | action == OF_RECONFIG_UPDATE_PROPERTY && |
858 | !strcmp(upd->dn->name, node->name)) { | |
0e16aafb | 859 | rcu_read_unlock(); |
1cf3d8b3 | 860 | nx842_OF_upd(upd->prop); |
0e16aafb SJ |
861 | } else |
862 | rcu_read_unlock(); | |
863 | ||
864 | return NOTIFY_OK; | |
865 | } | |
866 | ||
867 | static struct notifier_block nx842_of_nb = { | |
868 | .notifier_call = nx842_OF_notifier, | |
869 | }; | |
870 | ||
871 | #define nx842_counter_read(_name) \ | |
872 | static ssize_t nx842_##_name##_show(struct device *dev, \ | |
873 | struct device_attribute *attr, \ | |
874 | char *buf) { \ | |
875 | struct nx842_devdata *local_devdata; \ | |
876 | int p = 0; \ | |
877 | rcu_read_lock(); \ | |
878 | local_devdata = rcu_dereference(devdata); \ | |
879 | if (local_devdata) \ | |
880 | p = snprintf(buf, PAGE_SIZE, "%ld\n", \ | |
881 | atomic64_read(&local_devdata->counters->_name)); \ | |
882 | rcu_read_unlock(); \ | |
883 | return p; \ | |
884 | } | |
885 | ||
886 | #define NX842DEV_COUNTER_ATTR_RO(_name) \ | |
887 | nx842_counter_read(_name); \ | |
888 | static struct device_attribute dev_attr_##_name = __ATTR(_name, \ | |
889 | 0444, \ | |
890 | nx842_##_name##_show,\ | |
891 | NULL); | |
892 | ||
893 | NX842DEV_COUNTER_ATTR_RO(comp_complete); | |
894 | NX842DEV_COUNTER_ATTR_RO(comp_failed); | |
895 | NX842DEV_COUNTER_ATTR_RO(decomp_complete); | |
896 | NX842DEV_COUNTER_ATTR_RO(decomp_failed); | |
897 | NX842DEV_COUNTER_ATTR_RO(swdecomp); | |
898 | ||
899 | static ssize_t nx842_timehist_show(struct device *, | |
900 | struct device_attribute *, char *); | |
901 | ||
902 | static struct device_attribute dev_attr_comp_times = __ATTR(comp_times, 0444, | |
903 | nx842_timehist_show, NULL); | |
904 | static struct device_attribute dev_attr_decomp_times = __ATTR(decomp_times, | |
905 | 0444, nx842_timehist_show, NULL); | |
906 | ||
907 | static ssize_t nx842_timehist_show(struct device *dev, | |
908 | struct device_attribute *attr, char *buf) { | |
909 | char *p = buf; | |
910 | struct nx842_devdata *local_devdata; | |
911 | atomic64_t *times; | |
912 | int bytes_remain = PAGE_SIZE; | |
913 | int bytes; | |
914 | int i; | |
915 | ||
916 | rcu_read_lock(); | |
917 | local_devdata = rcu_dereference(devdata); | |
918 | if (!local_devdata) { | |
919 | rcu_read_unlock(); | |
920 | return 0; | |
921 | } | |
922 | ||
923 | if (attr == &dev_attr_comp_times) | |
924 | times = local_devdata->counters->comp_times; | |
925 | else if (attr == &dev_attr_decomp_times) | |
926 | times = local_devdata->counters->decomp_times; | |
927 | else { | |
928 | rcu_read_unlock(); | |
929 | return 0; | |
930 | } | |
931 | ||
932 | for (i = 0; i < (NX842_HIST_SLOTS - 2); i++) { | |
933 | bytes = snprintf(p, bytes_remain, "%u-%uus:\t%ld\n", | |
934 | i ? (2<<(i-1)) : 0, (2<<i)-1, | |
935 | atomic64_read(×[i])); | |
936 | bytes_remain -= bytes; | |
937 | p += bytes; | |
938 | } | |
939 | /* The last bucket holds everything over | |
940 | * 2<<(NX842_HIST_SLOTS - 2) us */ | |
941 | bytes = snprintf(p, bytes_remain, "%uus - :\t%ld\n", | |
942 | 2<<(NX842_HIST_SLOTS - 2), | |
943 | atomic64_read(×[(NX842_HIST_SLOTS - 1)])); | |
944 | p += bytes; | |
945 | ||
946 | rcu_read_unlock(); | |
947 | return p - buf; | |
948 | } | |
949 | ||
950 | static struct attribute *nx842_sysfs_entries[] = { | |
951 | &dev_attr_comp_complete.attr, | |
952 | &dev_attr_comp_failed.attr, | |
953 | &dev_attr_decomp_complete.attr, | |
954 | &dev_attr_decomp_failed.attr, | |
955 | &dev_attr_swdecomp.attr, | |
956 | &dev_attr_comp_times.attr, | |
957 | &dev_attr_decomp_times.attr, | |
958 | NULL, | |
959 | }; | |
960 | ||
961 | static struct attribute_group nx842_attribute_group = { | |
962 | .name = NULL, /* put in device directory */ | |
963 | .attrs = nx842_sysfs_entries, | |
964 | }; | |
965 | ||
7011a122 | 966 | static struct nx842_driver nx842_pseries_driver = { |
3e648cbe | 967 | .name = KBUILD_MODNAME, |
7011a122 | 968 | .owner = THIS_MODULE, |
959e6659 | 969 | .constraints = &nx842_pseries_constraints, |
7011a122 DS |
970 | .compress = nx842_pseries_compress, |
971 | .decompress = nx842_pseries_decompress, | |
972 | }; | |
973 | ||
0e16aafb SJ |
974 | static int __init nx842_probe(struct vio_dev *viodev, |
975 | const struct vio_device_id *id) | |
976 | { | |
977 | struct nx842_devdata *old_devdata, *new_devdata = NULL; | |
978 | unsigned long flags; | |
979 | int ret = 0; | |
980 | ||
981 | spin_lock_irqsave(&devdata_mutex, flags); | |
982 | old_devdata = rcu_dereference_check(devdata, | |
983 | lockdep_is_held(&devdata_mutex)); | |
984 | ||
985 | if (old_devdata && old_devdata->vdev != NULL) { | |
986 | dev_err(&viodev->dev, "%s: Attempt to register more than one instance of the hardware\n", __func__); | |
987 | ret = -1; | |
988 | goto error_unlock; | |
989 | } | |
990 | ||
991 | dev_set_drvdata(&viodev->dev, NULL); | |
992 | ||
993 | new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS); | |
994 | if (!new_devdata) { | |
995 | dev_err(&viodev->dev, "%s: Could not allocate memory for device data\n", __func__); | |
996 | ret = -ENOMEM; | |
997 | goto error_unlock; | |
998 | } | |
999 | ||
1000 | new_devdata->counters = kzalloc(sizeof(*new_devdata->counters), | |
1001 | GFP_NOFS); | |
1002 | if (!new_devdata->counters) { | |
1003 | dev_err(&viodev->dev, "%s: Could not allocate memory for performance counters\n", __func__); | |
1004 | ret = -ENOMEM; | |
1005 | goto error_unlock; | |
1006 | } | |
1007 | ||
1008 | new_devdata->vdev = viodev; | |
1009 | new_devdata->dev = &viodev->dev; | |
1010 | nx842_OF_set_defaults(new_devdata); | |
1011 | ||
1012 | rcu_assign_pointer(devdata, new_devdata); | |
1013 | spin_unlock_irqrestore(&devdata_mutex, flags); | |
1014 | synchronize_rcu(); | |
1015 | kfree(old_devdata); | |
1016 | ||
1cf3d8b3 | 1017 | of_reconfig_notifier_register(&nx842_of_nb); |
0e16aafb SJ |
1018 | |
1019 | ret = nx842_OF_upd(NULL); | |
1020 | if (ret && ret != -ENODEV) { | |
1021 | dev_err(&viodev->dev, "could not parse device tree. %d\n", ret); | |
1022 | ret = -1; | |
1023 | goto error; | |
1024 | } | |
1025 | ||
1026 | rcu_read_lock(); | |
cda43576 | 1027 | dev_set_drvdata(&viodev->dev, rcu_dereference(devdata)); |
0e16aafb SJ |
1028 | rcu_read_unlock(); |
1029 | ||
1030 | if (sysfs_create_group(&viodev->dev.kobj, &nx842_attribute_group)) { | |
1031 | dev_err(&viodev->dev, "could not create sysfs device attributes\n"); | |
1032 | ret = -1; | |
1033 | goto error; | |
1034 | } | |
1035 | ||
1036 | return 0; | |
1037 | ||
1038 | error_unlock: | |
1039 | spin_unlock_irqrestore(&devdata_mutex, flags); | |
1040 | if (new_devdata) | |
1041 | kfree(new_devdata->counters); | |
1042 | kfree(new_devdata); | |
1043 | error: | |
1044 | return ret; | |
1045 | } | |
1046 | ||
1047 | static int __exit nx842_remove(struct vio_dev *viodev) | |
1048 | { | |
1049 | struct nx842_devdata *old_devdata; | |
1050 | unsigned long flags; | |
1051 | ||
1052 | pr_info("Removing IBM Power 842 compression device\n"); | |
1053 | sysfs_remove_group(&viodev->dev.kobj, &nx842_attribute_group); | |
1054 | ||
1055 | spin_lock_irqsave(&devdata_mutex, flags); | |
1056 | old_devdata = rcu_dereference_check(devdata, | |
1057 | lockdep_is_held(&devdata_mutex)); | |
1cf3d8b3 | 1058 | of_reconfig_notifier_unregister(&nx842_of_nb); |
7ded6e3d | 1059 | RCU_INIT_POINTER(devdata, NULL); |
0e16aafb SJ |
1060 | spin_unlock_irqrestore(&devdata_mutex, flags); |
1061 | synchronize_rcu(); | |
1062 | dev_set_drvdata(&viodev->dev, NULL); | |
1063 | if (old_devdata) | |
1064 | kfree(old_devdata->counters); | |
1065 | kfree(old_devdata); | |
7011a122 | 1066 | |
0e16aafb SJ |
1067 | return 0; |
1068 | } | |
1069 | ||
b8e04187 | 1070 | static struct vio_device_id nx842_vio_driver_ids[] = { |
3e648cbe | 1071 | {"ibm,compression-v1", "ibm,compression"}, |
0e16aafb SJ |
1072 | {"", ""}, |
1073 | }; | |
1074 | ||
b8e04187 | 1075 | static struct vio_driver nx842_vio_driver = { |
3e648cbe | 1076 | .name = KBUILD_MODNAME, |
0e16aafb | 1077 | .probe = nx842_probe, |
13269ec6 | 1078 | .remove = __exit_p(nx842_remove), |
0e16aafb | 1079 | .get_desired_dma = nx842_get_desired_dma, |
b8e04187 | 1080 | .id_table = nx842_vio_driver_ids, |
0e16aafb SJ |
1081 | }; |
1082 | ||
1083 | static int __init nx842_init(void) | |
1084 | { | |
1085 | struct nx842_devdata *new_devdata; | |
3e648cbe DS |
1086 | int ret; |
1087 | ||
0e16aafb SJ |
1088 | pr_info("Registering IBM Power 842 compression driver\n"); |
1089 | ||
7011a122 DS |
1090 | BUILD_BUG_ON(sizeof(struct nx842_workmem) > NX842_MEM_COMPRESS); |
1091 | ||
3e648cbe DS |
1092 | if (!of_find_compatible_node(NULL, NULL, "ibm,compression")) |
1093 | return -ENODEV; | |
1094 | ||
0e16aafb SJ |
1095 | RCU_INIT_POINTER(devdata, NULL); |
1096 | new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL); | |
1097 | if (!new_devdata) { | |
1098 | pr_err("Could not allocate memory for device data\n"); | |
1099 | return -ENOMEM; | |
1100 | } | |
1101 | new_devdata->status = UNAVAILABLE; | |
1102 | RCU_INIT_POINTER(devdata, new_devdata); | |
1103 | ||
3e648cbe DS |
1104 | ret = vio_register_driver(&nx842_vio_driver); |
1105 | if (ret) { | |
1106 | pr_err("Could not register VIO driver %d\n", ret); | |
1107 | ||
1108 | kfree(new_devdata); | |
1109 | return ret; | |
1110 | } | |
1111 | ||
1112 | if (!nx842_platform_driver_set(&nx842_pseries_driver)) { | |
1113 | vio_unregister_driver(&nx842_vio_driver); | |
1114 | kfree(new_devdata); | |
1115 | return -EEXIST; | |
1116 | } | |
1117 | ||
1118 | return 0; | |
0e16aafb SJ |
1119 | } |
1120 | ||
1121 | module_init(nx842_init); | |
1122 | ||
1123 | static void __exit nx842_exit(void) | |
1124 | { | |
1125 | struct nx842_devdata *old_devdata; | |
1126 | unsigned long flags; | |
1127 | ||
1128 | pr_info("Exiting IBM Power 842 compression driver\n"); | |
3e648cbe | 1129 | nx842_platform_driver_unset(&nx842_pseries_driver); |
0e16aafb SJ |
1130 | spin_lock_irqsave(&devdata_mutex, flags); |
1131 | old_devdata = rcu_dereference_check(devdata, | |
1132 | lockdep_is_held(&devdata_mutex)); | |
7ded6e3d | 1133 | RCU_INIT_POINTER(devdata, NULL); |
0e16aafb SJ |
1134 | spin_unlock_irqrestore(&devdata_mutex, flags); |
1135 | synchronize_rcu(); | |
b8e04187 | 1136 | if (old_devdata && old_devdata->dev) |
0e16aafb SJ |
1137 | dev_set_drvdata(old_devdata->dev, NULL); |
1138 | kfree(old_devdata); | |
b8e04187 | 1139 | vio_unregister_driver(&nx842_vio_driver); |
0e16aafb SJ |
1140 | } |
1141 | ||
1142 | module_exit(nx842_exit); | |
1143 |