|
6 | 6 | #include <linux/dev_printk.h> |
7 | 7 | #include <linux/device.h> |
8 | 8 | #include <linux/export.h> |
| 9 | +#include <linux/math64.h> |
9 | 10 | #include <linux/module.h> |
10 | 11 | #include <linux/netlink.h> |
11 | 12 | #include <linux/regmap.h> |
12 | 13 | #include <linux/sprintf.h> |
| 14 | +#include <linux/string_choices.h> |
13 | 15 | #include <linux/unaligned.h> |
14 | 16 | #include <net/devlink.h> |
15 | 17 |
|
@@ -383,6 +385,248 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask) |
383 | 385 | ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US); |
384 | 386 | } |
385 | 387 |
|
| 388 | +int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val, |
| 389 | + unsigned int mask_reg, u16 mask_val) |
| 390 | +{ |
| 391 | + int rc; |
| 392 | + |
| 393 | + /* Set mask for the operation */ |
| 394 | + rc = zl3073x_write_u16(zldev, mask_reg, mask_val); |
| 395 | + if (rc) |
| 396 | + return rc; |
| 397 | + |
| 398 | + /* Trigger the operation */ |
| 399 | + rc = zl3073x_write_u8(zldev, op_reg, op_val); |
| 400 | + if (rc) |
| 401 | + return rc; |
| 402 | + |
| 403 | + /* Wait for the operation to actually finish */ |
| 404 | + return zl3073x_poll_zero_u8(zldev, op_reg, op_val); |
| 405 | +} |
| 406 | + |
| 407 | +/** |
| 408 | + * zl3073x_ref_state_fetch - get input reference state |
| 409 | + * @zldev: pointer to zl3073x_dev structure |
| 410 | + * @index: input reference index to fetch state for |
| 411 | + * |
| 412 | + * Function fetches information for the given input reference that are |
| 413 | + * invariant and stores them for later use. |
| 414 | + * |
| 415 | + * Return: 0 on success, <0 on error |
| 416 | + */ |
| 417 | +static int |
| 418 | +zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index) |
| 419 | +{ |
| 420 | + struct zl3073x_ref *input = &zldev->ref[index]; |
| 421 | + u8 ref_config; |
| 422 | + int rc; |
| 423 | + |
| 424 | + /* If the input is differential then the configuration for N-pin |
| 425 | + * reference is ignored and P-pin config is used for both. |
| 426 | + */ |
| 427 | + if (zl3073x_is_n_pin(index) && |
| 428 | + zl3073x_ref_is_diff(zldev, index - 1)) { |
| 429 | + input->enabled = zl3073x_ref_is_enabled(zldev, index - 1); |
| 430 | + input->diff = true; |
| 431 | + |
| 432 | + return 0; |
| 433 | + } |
| 434 | + |
| 435 | + guard(mutex)(&zldev->multiop_lock); |
| 436 | + |
| 437 | + /* Read reference configuration */ |
| 438 | + rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, |
| 439 | + ZL_REG_REF_MB_MASK, BIT(index)); |
| 440 | + if (rc) |
| 441 | + return rc; |
| 442 | + |
| 443 | + /* Read ref_config register */ |
| 444 | + rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config); |
| 445 | + if (rc) |
| 446 | + return rc; |
| 447 | + |
| 448 | + input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config); |
| 449 | + input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config); |
| 450 | + |
| 451 | + dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index, |
| 452 | + str_enabled_disabled(input->enabled), |
| 453 | + input->diff ? "differential" : "single-ended"); |
| 454 | + |
| 455 | + return rc; |
| 456 | +} |
| 457 | + |
| 458 | +/** |
| 459 | + * zl3073x_out_state_fetch - get output state |
| 460 | + * @zldev: pointer to zl3073x_dev structure |
| 461 | + * @index: output index to fetch state for |
| 462 | + * |
| 463 | + * Function fetches information for the given output (not output pin) |
| 464 | + * that are invariant and stores them for later use. |
| 465 | + * |
| 466 | + * Return: 0 on success, <0 on error |
| 467 | + */ |
| 468 | +static int |
| 469 | +zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index) |
| 470 | +{ |
| 471 | + struct zl3073x_out *out = &zldev->out[index]; |
| 472 | + u8 output_ctrl, output_mode; |
| 473 | + int rc; |
| 474 | + |
| 475 | + /* Read output configuration */ |
| 476 | + rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl); |
| 477 | + if (rc) |
| 478 | + return rc; |
| 479 | + |
| 480 | + /* Store info about output enablement and synthesizer the output |
| 481 | + * is connected to. |
| 482 | + */ |
| 483 | + out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl); |
| 484 | + out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl); |
| 485 | + |
| 486 | + dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index, |
| 487 | + str_enabled_disabled(out->enabled), out->synth); |
| 488 | + |
| 489 | + guard(mutex)(&zldev->multiop_lock); |
| 490 | + |
| 491 | + /* Read output configuration */ |
| 492 | + rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, |
| 493 | + ZL_REG_OUTPUT_MB_MASK, BIT(index)); |
| 494 | + if (rc) |
| 495 | + return rc; |
| 496 | + |
| 497 | + /* Read output_mode */ |
| 498 | + rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode); |
| 499 | + if (rc) |
| 500 | + return rc; |
| 501 | + |
| 502 | + /* Extract and store output signal format */ |
| 503 | + out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT, |
| 504 | + output_mode); |
| 505 | + |
| 506 | + dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index, |
| 507 | + out->signal_format); |
| 508 | + |
| 509 | + return rc; |
| 510 | +} |
| 511 | + |
| 512 | +/** |
| 513 | + * zl3073x_synth_state_fetch - get synth state |
| 514 | + * @zldev: pointer to zl3073x_dev structure |
| 515 | + * @index: synth index to fetch state for |
| 516 | + * |
| 517 | + * Function fetches information for the given synthesizer that are |
| 518 | + * invariant and stores them for later use. |
| 519 | + * |
| 520 | + * Return: 0 on success, <0 on error |
| 521 | + */ |
| 522 | +static int |
| 523 | +zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index) |
| 524 | +{ |
| 525 | + struct zl3073x_synth *synth = &zldev->synth[index]; |
| 526 | + u16 base, m, n; |
| 527 | + u8 synth_ctrl; |
| 528 | + u32 mult; |
| 529 | + int rc; |
| 530 | + |
| 531 | + /* Read synth control register */ |
| 532 | + rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl); |
| 533 | + if (rc) |
| 534 | + return rc; |
| 535 | + |
| 536 | + /* Store info about synth enablement and DPLL channel the synth is |
| 537 | + * driven by. |
| 538 | + */ |
| 539 | + synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl); |
| 540 | + synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl); |
| 541 | + |
| 542 | + dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index, |
| 543 | + str_enabled_disabled(synth->enabled), synth->dpll); |
| 544 | + |
| 545 | + guard(mutex)(&zldev->multiop_lock); |
| 546 | + |
| 547 | + /* Read synth configuration */ |
| 548 | + rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD, |
| 549 | + ZL_REG_SYNTH_MB_MASK, BIT(index)); |
| 550 | + if (rc) |
| 551 | + return rc; |
| 552 | + |
| 553 | + /* The output frequency is determined by the following formula: |
| 554 | + * base * multiplier * numerator / denominator |
| 555 | + * |
| 556 | + * Read registers with these values |
| 557 | + */ |
| 558 | + rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base); |
| 559 | + if (rc) |
| 560 | + return rc; |
| 561 | + |
| 562 | + rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult); |
| 563 | + if (rc) |
| 564 | + return rc; |
| 565 | + |
| 566 | + rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m); |
| 567 | + if (rc) |
| 568 | + return rc; |
| 569 | + |
| 570 | + rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n); |
| 571 | + if (rc) |
| 572 | + return rc; |
| 573 | + |
| 574 | + /* Check denominator for zero to avoid div by 0 */ |
| 575 | + if (!n) { |
| 576 | + dev_err(zldev->dev, |
| 577 | + "Zero divisor for SYNTH%u retrieved from device\n", |
| 578 | + index); |
| 579 | + return -EINVAL; |
| 580 | + } |
| 581 | + |
| 582 | + /* Compute and store synth frequency */ |
| 583 | + zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n); |
| 584 | + |
| 585 | + dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index, |
| 586 | + zldev->synth[index].freq); |
| 587 | + |
| 588 | + return rc; |
| 589 | +} |
| 590 | + |
| 591 | +static int |
| 592 | +zl3073x_dev_state_fetch(struct zl3073x_dev *zldev) |
| 593 | +{ |
| 594 | + int rc; |
| 595 | + u8 i; |
| 596 | + |
| 597 | + for (i = 0; i < ZL3073X_NUM_REFS; i++) { |
| 598 | + rc = zl3073x_ref_state_fetch(zldev, i); |
| 599 | + if (rc) { |
| 600 | + dev_err(zldev->dev, |
| 601 | + "Failed to fetch input state: %pe\n", |
| 602 | + ERR_PTR(rc)); |
| 603 | + return rc; |
| 604 | + } |
| 605 | + } |
| 606 | + |
| 607 | + for (i = 0; i < ZL3073X_NUM_SYNTHS; i++) { |
| 608 | + rc = zl3073x_synth_state_fetch(zldev, i); |
| 609 | + if (rc) { |
| 610 | + dev_err(zldev->dev, |
| 611 | + "Failed to fetch synth state: %pe\n", |
| 612 | + ERR_PTR(rc)); |
| 613 | + return rc; |
| 614 | + } |
| 615 | + } |
| 616 | + |
| 617 | + for (i = 0; i < ZL3073X_NUM_OUTS; i++) { |
| 618 | + rc = zl3073x_out_state_fetch(zldev, i); |
| 619 | + if (rc) { |
| 620 | + dev_err(zldev->dev, |
| 621 | + "Failed to fetch output state: %pe\n", |
| 622 | + ERR_PTR(rc)); |
| 623 | + return rc; |
| 624 | + } |
| 625 | + } |
| 626 | + |
| 627 | + return rc; |
| 628 | +} |
| 629 | + |
386 | 630 | /** |
387 | 631 | * zl3073x_dev_probe - initialize zl3073x device |
388 | 632 | * @zldev: pointer to zl3073x device |
@@ -450,6 +694,11 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, |
450 | 694 | return dev_err_probe(zldev->dev, rc, |
451 | 695 | "Failed to initialize mutex\n"); |
452 | 696 |
|
| 697 | + /* Fetch device state */ |
| 698 | + rc = zl3073x_dev_state_fetch(zldev); |
| 699 | + if (rc) |
| 700 | + return rc; |
| 701 | + |
453 | 702 | /* Register the devlink instance and parameters */ |
454 | 703 | rc = zl3073x_devlink_register(zldev); |
455 | 704 | if (rc) |
|
0 commit comments