Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

misc: xilinx_sdfec: Add ability to configure LDPC

Add the capability to configure LDPC mode via the ioctl
XSDFEC_ADD_LDPC_CODE_PARAMS.

Tested-by: Dragan Cvetic <dragan.cvetic@xilinx.com>
Signed-off-by: Derek Kiernan <derek.kiernan@xilinx.com>
Signed-off-by: Dragan Cvetic <dragan.cvetic@xilinx.com>
Link: https://lore.kernel.org/r/1564216438-322406-4-git-send-email-dragan.cvetic@xilinx.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Dragan Cvetic and committed by
Greg Kroah-Hartman
20ec628e 6f86ed82

+422
+324
drivers/misc/xilinx_sdfec.c
··· 20 20 #include <linux/slab.h> 21 21 #include <linux/clk.h> 22 22 #include <linux/compat.h> 23 + #include <linux/highmem.h> 23 24 24 25 #include <uapi/misc/xilinx_sdfec.h> 25 26 ··· 113 112 #define XSDFEC_TURBO_SCALE_MASK (0xFFF) 114 113 #define XSDFEC_TURBO_SCALE_BIT_POS (8) 115 114 #define XSDFEC_TURBO_SCALE_MAX (15) 115 + 116 + /* REG0 Register */ 117 + #define XSDFEC_LDPC_CODE_REG0_ADDR_BASE (0x2000) 118 + #define XSDFEC_LDPC_CODE_REG0_ADDR_HIGH (0x27F0) 119 + #define XSDFEC_REG0_N_MIN (4) 120 + #define XSDFEC_REG0_N_MAX (32768) 121 + #define XSDFEC_REG0_N_MUL_P (256) 122 + #define XSDFEC_REG0_N_LSB (0) 123 + #define XSDFEC_REG0_K_MIN (2) 124 + #define XSDFEC_REG0_K_MAX (32766) 125 + #define XSDFEC_REG0_K_MUL_P (256) 126 + #define XSDFEC_REG0_K_LSB (16) 127 + 128 + /* REG1 Register */ 129 + #define XSDFEC_LDPC_CODE_REG1_ADDR_BASE (0x2004) 130 + #define XSDFEC_LDPC_CODE_REG1_ADDR_HIGH (0x27f4) 131 + #define XSDFEC_REG1_PSIZE_MIN (2) 132 + #define XSDFEC_REG1_PSIZE_MAX (512) 133 + #define XSDFEC_REG1_NO_PACKING_MASK (0x400) 134 + #define XSDFEC_REG1_NO_PACKING_LSB (10) 135 + #define XSDFEC_REG1_NM_MASK (0xFF800) 136 + #define XSDFEC_REG1_NM_LSB (11) 137 + #define XSDFEC_REG1_BYPASS_MASK (0x100000) 138 + 139 + /* REG2 Register */ 140 + #define XSDFEC_LDPC_CODE_REG2_ADDR_BASE (0x2008) 141 + #define XSDFEC_LDPC_CODE_REG2_ADDR_HIGH (0x27f8) 142 + #define XSDFEC_REG2_NLAYERS_MIN (1) 143 + #define XSDFEC_REG2_NLAYERS_MAX (256) 144 + #define XSDFEC_REG2_NNMQC_MASK (0xFFE00) 145 + #define XSDFEC_REG2_NMQC_LSB (9) 146 + #define XSDFEC_REG2_NORM_TYPE_MASK (0x100000) 147 + #define XSDFEC_REG2_NORM_TYPE_LSB (20) 148 + #define XSDFEC_REG2_SPECIAL_QC_MASK (0x200000) 149 + #define XSDFEC_REG2_SPEICAL_QC_LSB (21) 150 + #define XSDFEC_REG2_NO_FINAL_PARITY_MASK (0x400000) 151 + #define XSDFEC_REG2_NO_FINAL_PARITY_LSB (22) 152 + #define XSDFEC_REG2_MAX_SCHEDULE_MASK (0x1800000) 153 + #define XSDFEC_REG2_MAX_SCHEDULE_LSB (23) 154 + 155 + /* REG3 Register */ 156 + #define XSDFEC_LDPC_CODE_REG3_ADDR_BASE (0x200C) 157 + #define XSDFEC_LDPC_CODE_REG3_ADDR_HIGH (0x27FC) 158 + #define XSDFEC_REG3_LA_OFF_LSB (8) 159 + #define XSDFEC_REG3_QC_OFF_LSB (16) 160 + 161 + #define XSDFEC_LDPC_REG_JUMP (0x10) 162 + #define XSDFEC_REG_WIDTH_JUMP (4) 163 + 164 + /* The maximum number of pinned pages */ 165 + #define MAX_NUM_PAGES ((XSDFEC_QC_TABLE_DEPTH / PAGE_SIZE) + 1) 116 166 117 167 /** 118 168 * struct xsdfec_clks - For managing SD-FEC clocks ··· 322 270 return err; 323 271 } 324 272 273 + static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, 274 + u32 offset) 275 + { 276 + u32 wdata; 277 + 278 + if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || 279 + (n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) { 280 + dev_dbg(xsdfec->dev, "N value is not in range"); 281 + return -EINVAL; 282 + } 283 + n <<= XSDFEC_REG0_N_LSB; 284 + 285 + if (k < XSDFEC_REG0_K_MIN || k > XSDFEC_REG0_K_MAX || 286 + (k > XSDFEC_REG0_K_MUL_P * psize) || ((k % psize) != 0)) { 287 + dev_dbg(xsdfec->dev, "K value is not in range"); 288 + return -EINVAL; 289 + } 290 + k = k << XSDFEC_REG0_K_LSB; 291 + wdata = k | n; 292 + 293 + if (XSDFEC_LDPC_CODE_REG0_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > 294 + XSDFEC_LDPC_CODE_REG0_ADDR_HIGH) { 295 + dev_dbg(xsdfec->dev, "Writing outside of LDPC reg0 space 0x%x", 296 + XSDFEC_LDPC_CODE_REG0_ADDR_BASE + 297 + (offset * XSDFEC_LDPC_REG_JUMP)); 298 + return -EINVAL; 299 + } 300 + xsdfec_regwrite(xsdfec, 301 + XSDFEC_LDPC_CODE_REG0_ADDR_BASE + 302 + (offset * XSDFEC_LDPC_REG_JUMP), 303 + wdata); 304 + return 0; 305 + } 306 + 307 + static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize, 308 + u32 no_packing, u32 nm, u32 offset) 309 + { 310 + u32 wdata; 311 + 312 + if (psize < XSDFEC_REG1_PSIZE_MIN || psize > XSDFEC_REG1_PSIZE_MAX) { 313 + dev_dbg(xsdfec->dev, "Psize is not in range"); 314 + return -EINVAL; 315 + } 316 + 317 + if (no_packing != 0 && no_packing != 1) 318 + dev_dbg(xsdfec->dev, "No-packing bit register invalid"); 319 + no_packing = ((no_packing << XSDFEC_REG1_NO_PACKING_LSB) & 320 + XSDFEC_REG1_NO_PACKING_MASK); 321 + 322 + if (nm & ~(XSDFEC_REG1_NM_MASK >> XSDFEC_REG1_NM_LSB)) 323 + dev_dbg(xsdfec->dev, "NM is beyond 10 bits"); 324 + nm = (nm << XSDFEC_REG1_NM_LSB) & XSDFEC_REG1_NM_MASK; 325 + 326 + wdata = nm | no_packing | psize; 327 + if (XSDFEC_LDPC_CODE_REG1_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > 328 + XSDFEC_LDPC_CODE_REG1_ADDR_HIGH) { 329 + dev_dbg(xsdfec->dev, "Writing outside of LDPC reg1 space 0x%x", 330 + XSDFEC_LDPC_CODE_REG1_ADDR_BASE + 331 + (offset * XSDFEC_LDPC_REG_JUMP)); 332 + return -EINVAL; 333 + } 334 + xsdfec_regwrite(xsdfec, 335 + XSDFEC_LDPC_CODE_REG1_ADDR_BASE + 336 + (offset * XSDFEC_LDPC_REG_JUMP), 337 + wdata); 338 + return 0; 339 + } 340 + 341 + static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc, 342 + u32 norm_type, u32 special_qc, u32 no_final_parity, 343 + u32 max_schedule, u32 offset) 344 + { 345 + u32 wdata; 346 + 347 + if (nlayers < XSDFEC_REG2_NLAYERS_MIN || 348 + nlayers > XSDFEC_REG2_NLAYERS_MAX) { 349 + dev_dbg(xsdfec->dev, "Nlayers is not in range"); 350 + return -EINVAL; 351 + } 352 + 353 + if (nmqc & ~(XSDFEC_REG2_NNMQC_MASK >> XSDFEC_REG2_NMQC_LSB)) 354 + dev_dbg(xsdfec->dev, "NMQC exceeds 11 bits"); 355 + nmqc = (nmqc << XSDFEC_REG2_NMQC_LSB) & XSDFEC_REG2_NNMQC_MASK; 356 + 357 + if (norm_type > 1) 358 + dev_dbg(xsdfec->dev, "Norm type is invalid"); 359 + norm_type = ((norm_type << XSDFEC_REG2_NORM_TYPE_LSB) & 360 + XSDFEC_REG2_NORM_TYPE_MASK); 361 + if (special_qc > 1) 362 + dev_dbg(xsdfec->dev, "Special QC in invalid"); 363 + special_qc = ((special_qc << XSDFEC_REG2_SPEICAL_QC_LSB) & 364 + XSDFEC_REG2_SPECIAL_QC_MASK); 365 + 366 + if (no_final_parity > 1) 367 + dev_dbg(xsdfec->dev, "No final parity check invalid"); 368 + no_final_parity = 369 + ((no_final_parity << XSDFEC_REG2_NO_FINAL_PARITY_LSB) & 370 + XSDFEC_REG2_NO_FINAL_PARITY_MASK); 371 + if (max_schedule & 372 + ~(XSDFEC_REG2_MAX_SCHEDULE_MASK >> XSDFEC_REG2_MAX_SCHEDULE_LSB)) 373 + dev_dbg(xsdfec->dev, "Max Schdule exceeds 2 bits"); 374 + max_schedule = ((max_schedule << XSDFEC_REG2_MAX_SCHEDULE_LSB) & 375 + XSDFEC_REG2_MAX_SCHEDULE_MASK); 376 + 377 + wdata = (max_schedule | no_final_parity | special_qc | norm_type | 378 + nmqc | nlayers); 379 + 380 + if (XSDFEC_LDPC_CODE_REG2_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > 381 + XSDFEC_LDPC_CODE_REG2_ADDR_HIGH) { 382 + dev_dbg(xsdfec->dev, "Writing outside of LDPC reg2 space 0x%x", 383 + XSDFEC_LDPC_CODE_REG2_ADDR_BASE + 384 + (offset * XSDFEC_LDPC_REG_JUMP)); 385 + return -EINVAL; 386 + } 387 + xsdfec_regwrite(xsdfec, 388 + XSDFEC_LDPC_CODE_REG2_ADDR_BASE + 389 + (offset * XSDFEC_LDPC_REG_JUMP), 390 + wdata); 391 + return 0; 392 + } 393 + 394 + static int xsdfec_reg3_write(struct xsdfec_dev *xsdfec, u8 sc_off, u8 la_off, 395 + u16 qc_off, u32 offset) 396 + { 397 + u32 wdata; 398 + 399 + wdata = ((qc_off << XSDFEC_REG3_QC_OFF_LSB) | 400 + (la_off << XSDFEC_REG3_LA_OFF_LSB) | sc_off); 401 + if (XSDFEC_LDPC_CODE_REG3_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > 402 + XSDFEC_LDPC_CODE_REG3_ADDR_HIGH) { 403 + dev_dbg(xsdfec->dev, "Writing outside of LDPC reg3 space 0x%x", 404 + XSDFEC_LDPC_CODE_REG3_ADDR_BASE + 405 + (offset * XSDFEC_LDPC_REG_JUMP)); 406 + return -EINVAL; 407 + } 408 + xsdfec_regwrite(xsdfec, 409 + XSDFEC_LDPC_CODE_REG3_ADDR_BASE + 410 + (offset * XSDFEC_LDPC_REG_JUMP), 411 + wdata); 412 + return 0; 413 + } 414 + 415 + static int xsdfec_table_write(struct xsdfec_dev *xsdfec, u32 offset, 416 + u32 *src_ptr, u32 len, const u32 base_addr, 417 + const u32 depth) 418 + { 419 + u32 reg = 0; 420 + u32 res; 421 + u32 n, i; 422 + u32 *addr = NULL; 423 + struct page *page[MAX_NUM_PAGES]; 424 + 425 + /* 426 + * Writes that go beyond the length of 427 + * Shared Scale(SC) table should fail 428 + */ 429 + if ((XSDFEC_REG_WIDTH_JUMP * (offset + len)) > depth) { 430 + dev_dbg(xsdfec->dev, "Write exceeds SC table length"); 431 + return -EINVAL; 432 + } 433 + 434 + n = (len * XSDFEC_REG_WIDTH_JUMP) / PAGE_SIZE; 435 + if ((len * XSDFEC_REG_WIDTH_JUMP) % PAGE_SIZE) 436 + n += 1; 437 + 438 + res = get_user_pages_fast((unsigned long)src_ptr, n, 0, page); 439 + if (res < n) { 440 + for (i = 0; i < res; i++) 441 + put_page(page[i]); 442 + return -EINVAL; 443 + } 444 + 445 + for (i = 0; i < n; i++) { 446 + addr = kmap(page[i]); 447 + do { 448 + xsdfec_regwrite(xsdfec, 449 + base_addr + ((offset + reg) * 450 + XSDFEC_REG_WIDTH_JUMP), 451 + addr[reg]); 452 + reg++; 453 + } while ((reg < len) && 454 + ((reg * XSDFEC_REG_WIDTH_JUMP) % PAGE_SIZE)); 455 + put_page(page[i]); 456 + } 457 + return reg; 458 + } 459 + 460 + static int xsdfec_add_ldpc(struct xsdfec_dev *xsdfec, void __user *arg) 461 + { 462 + struct xsdfec_ldpc_params *ldpc; 463 + int ret, n; 464 + 465 + ldpc = kzalloc(sizeof(*ldpc), GFP_KERNEL); 466 + if (!ldpc) 467 + return -ENOMEM; 468 + 469 + ret = copy_from_user(ldpc, arg, sizeof(*ldpc)); 470 + if (ret) 471 + goto err_out; 472 + 473 + if (xsdfec->config.code == XSDFEC_TURBO_CODE) { 474 + ret = -EIO; 475 + goto err_out; 476 + } 477 + 478 + /* Verify Device has not started */ 479 + if (xsdfec->state == XSDFEC_STARTED) { 480 + ret = -EIO; 481 + goto err_out; 482 + } 483 + 484 + if (xsdfec->config.code_wr_protect) { 485 + ret = -EIO; 486 + goto err_out; 487 + } 488 + 489 + /* Write Reg 0 */ 490 + ret = xsdfec_reg0_write(xsdfec, ldpc->n, ldpc->k, ldpc->psize, 491 + ldpc->code_id); 492 + if (ret) 493 + goto err_out; 494 + 495 + /* Write Reg 1 */ 496 + ret = xsdfec_reg1_write(xsdfec, ldpc->psize, ldpc->no_packing, ldpc->nm, 497 + ldpc->code_id); 498 + if (ret) 499 + goto err_out; 500 + 501 + /* Write Reg 2 */ 502 + ret = xsdfec_reg2_write(xsdfec, ldpc->nlayers, ldpc->nmqc, 503 + ldpc->norm_type, ldpc->special_qc, 504 + ldpc->no_final_parity, ldpc->max_schedule, 505 + ldpc->code_id); 506 + if (ret) 507 + goto err_out; 508 + 509 + /* Write Reg 3 */ 510 + ret = xsdfec_reg3_write(xsdfec, ldpc->sc_off, ldpc->la_off, 511 + ldpc->qc_off, ldpc->code_id); 512 + if (ret) 513 + goto err_out; 514 + 515 + /* Write Shared Codes */ 516 + n = ldpc->nlayers / 4; 517 + if (ldpc->nlayers % 4) 518 + n++; 519 + 520 + ret = xsdfec_table_write(xsdfec, ldpc->sc_off, ldpc->sc_table, n, 521 + XSDFEC_LDPC_SC_TABLE_ADDR_BASE, 522 + XSDFEC_SC_TABLE_DEPTH); 523 + if (ret < 0) 524 + goto err_out; 525 + 526 + ret = xsdfec_table_write(xsdfec, 4 * ldpc->la_off, ldpc->la_table, 527 + ldpc->nlayers, XSDFEC_LDPC_LA_TABLE_ADDR_BASE, 528 + XSDFEC_LA_TABLE_DEPTH); 529 + if (ret < 0) 530 + goto err_out; 531 + 532 + ret = xsdfec_table_write(xsdfec, 4 * ldpc->qc_off, ldpc->qc_table, 533 + ldpc->nqc, XSDFEC_LDPC_QC_TABLE_ADDR_BASE, 534 + XSDFEC_QC_TABLE_DEPTH); 535 + if (ret > 0) 536 + ret = 0; 537 + err_out: 538 + kfree(ldpc); 539 + return ret; 540 + } 541 + 325 542 static u32 326 543 xsdfec_translate_axis_width_cfg_val(enum xsdfec_axis_width axis_width_cfg) 327 544 { ··· 686 365 break; 687 366 case XSDFEC_GET_TURBO: 688 367 rval = xsdfec_get_turbo(xsdfec, arg); 368 + break; 369 + case XSDFEC_ADD_LDPC_CODE_PARAMS: 370 + rval = xsdfec_add_ldpc(xsdfec, arg); 689 371 break; 690 372 default: 691 373 /* Should not get here */
+98
include/uapi/misc/xilinx_sdfec.h
··· 13 13 14 14 #include <linux/types.h> 15 15 16 + /* Shared LDPC Tables */ 17 + #define XSDFEC_LDPC_SC_TABLE_ADDR_BASE (0x10000) 18 + #define XSDFEC_LDPC_SC_TABLE_ADDR_HIGH (0x10400) 19 + #define XSDFEC_LDPC_LA_TABLE_ADDR_BASE (0x18000) 20 + #define XSDFEC_LDPC_LA_TABLE_ADDR_HIGH (0x19000) 21 + #define XSDFEC_LDPC_QC_TABLE_ADDR_BASE (0x20000) 22 + #define XSDFEC_LDPC_QC_TABLE_ADDR_HIGH (0x28000) 23 + 24 + /* LDPC tables depth */ 25 + #define XSDFEC_SC_TABLE_DEPTH \ 26 + (XSDFEC_LDPC_SC_TABLE_ADDR_HIGH - XSDFEC_LDPC_SC_TABLE_ADDR_BASE) 27 + #define XSDFEC_LA_TABLE_DEPTH \ 28 + (XSDFEC_LDPC_LA_TABLE_ADDR_HIGH - XSDFEC_LDPC_LA_TABLE_ADDR_BASE) 29 + #define XSDFEC_QC_TABLE_DEPTH \ 30 + (XSDFEC_LDPC_QC_TABLE_ADDR_HIGH - XSDFEC_LDPC_QC_TABLE_ADDR_BASE) 31 + 16 32 /** 17 33 * enum xsdfec_code - Code Type. 18 34 * @XSDFEC_TURBO_CODE: Driver is configured for Turbo mode. ··· 143 127 }; 144 128 145 129 /** 130 + * struct xsdfec_ldpc_params - User data for LDPC codes. 131 + * @n: Number of code word bits 132 + * @k: Number of information bits 133 + * @psize: Size of sub-matrix 134 + * @nlayers: Number of layers in code 135 + * @nqc: Quasi Cyclic Number 136 + * @nmqc: Number of M-sized QC operations in parity check matrix 137 + * @nm: Number of M-size vectors in N 138 + * @norm_type: Normalization required or not 139 + * @no_packing: Determines if multiple QC ops should be performed 140 + * @special_qc: Sub-Matrix property for Circulant weight > 0 141 + * @no_final_parity: Decide if final parity check needs to be performed 142 + * @max_schedule: Experimental code word scheduling limit 143 + * @sc_off: SC offset 144 + * @la_off: LA offset 145 + * @qc_off: QC offset 146 + * @sc_table: Pointer to SC Table which must be page aligned 147 + * @la_table: Pointer to LA Table which must be page aligned 148 + * @qc_table: Pointer to QC Table which must be page aligned 149 + * @code_id: LDPC Code 150 + * 151 + * This structure describes the LDPC code that is passed to the driver by the 152 + * application. 153 + */ 154 + struct xsdfec_ldpc_params { 155 + __u32 n; 156 + __u32 k; 157 + __u32 psize; 158 + __u32 nlayers; 159 + __u32 nqc; 160 + __u32 nmqc; 161 + __u32 nm; 162 + __u32 norm_type; 163 + __u32 no_packing; 164 + __u32 special_qc; 165 + __u32 no_final_parity; 166 + __u32 max_schedule; 167 + __u32 sc_off; 168 + __u32 la_off; 169 + __u32 qc_off; 170 + __u32 *sc_table; 171 + __u32 *la_table; 172 + __u32 *qc_table; 173 + __u16 code_id; 174 + }; 175 + 176 + /** 146 177 * struct xsdfec_status - Status of SD-FEC core. 147 178 * @state: State of the SD-FEC core 148 179 * @activity: Describes if the SD-FEC instance is Active ··· 233 170 __s8 code_wr_protect; 234 171 }; 235 172 173 + /** 174 + * struct xsdfec_ldpc_param_table_sizes - Used to store sizes of SD-FEC table 175 + * entries for an individual LPDC code 176 + * parameter. 177 + * @sc_size: Size of SC table used 178 + * @la_size: Size of LA table used 179 + * @qc_size: Size of QC table used 180 + */ 181 + struct xsdfec_ldpc_param_table_sizes { 182 + __u32 sc_size; 183 + __u32 la_size; 184 + __u32 qc_size; 185 + }; 186 + 236 187 /* 237 188 * XSDFEC IOCTL List 238 189 */ ··· 266 189 * This can only be used when the driver is in the XSDFEC_STOPPED state 267 190 */ 268 191 #define XSDFEC_SET_TURBO _IOW(XSDFEC_MAGIC, 4, struct xsdfec_turbo) 192 + /** 193 + * DOC: XSDFEC_ADD_LDPC_CODE_PARAMS 194 + * @Parameters 195 + * 196 + * @struct xsdfec_ldpc_params * 197 + * Pointer to the &struct xsdfec_ldpc_params that contains the LDPC code 198 + * parameters to be added to the SD-FEC Block 199 + * 200 + * @Description 201 + * ioctl to add an LDPC code to the SD-FEC LDPC codes 202 + * 203 + * This can only be used when: 204 + * 205 + * - Driver is in the XSDFEC_STOPPED state 206 + * 207 + * - SD-FEC core is configured as LPDC 208 + * 209 + * - SD-FEC Code Write Protection is disabled 210 + */ 211 + #define XSDFEC_ADD_LDPC_CODE_PARAMS \ 212 + _IOW(XSDFEC_MAGIC, 5, struct xsdfec_ldpc_params) 269 213 /** 270 214 * DOC: XSDFEC_GET_TURBO 271 215 * @Parameters