|
12 | 12 | #include <zephyr/logging/log.h> |
13 | 13 | #include <zephyr/sys/byteorder.h> |
14 | 14 | #include <zephyr/sys/util.h> |
| 15 | +#include <zephyr/pm/device.h> |
| 16 | +#include <zephyr/pm/device_runtime.h> |
15 | 17 |
|
16 | 18 | LOG_MODULE_REGISTER(can_tcan4x5x, CONFIG_CAN_LOG_LEVEL); |
17 | 19 |
|
@@ -100,6 +102,10 @@ LOG_MODULE_REGISTER(can_tcan4x5x, CONFIG_CAN_LOG_LEVEL); |
100 | 102 | #define CAN_TCAN4X5X_MODE_CONFIG_SWE_DIS BIT(1) |
101 | 103 | #define CAN_TCAN4X5X_MODE_CONFIG_TEST_MODE_CONFIG BIT(0) |
102 | 104 |
|
| 105 | +#define CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_SLEEP 0 |
| 106 | +#define CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_STANDBY 1 |
| 107 | +#define CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_NORMAL 2 |
| 108 | + |
103 | 109 | /* Timestamp Prescaler register */ |
104 | 110 | #define CAN_TCAN4X5X_TIMESTAMP_PRESCALER 0x0804 |
105 | 111 | #define CAN_TCAN4X5X_TIMESTAMP_PRESCALER_MASK GENMASK(7, 0) |
@@ -201,6 +207,9 @@ LOG_MODULE_REGISTER(can_tcan4x5x, CONFIG_CAN_LOG_LEVEL); |
201 | 207 |
|
202 | 208 | /* TCAN4x5x timing requirements */ |
203 | 209 | #define CAN_TCAN4X5X_T_MODE_STBY_NOM_US 70 |
| 210 | +#define CAN_TCAN4X5X_T_MODE_NOM_SLP_US 200 |
| 211 | +#define CAN_TCAN4X5X_T_MODE_NOM_STBY_US 200 |
| 212 | +#define CAN_TCAN4X5X_T_MODE_SLP_STBY_US 200 |
204 | 213 | #define CAN_TCAN4X5X_T_WAKE_US 50 |
205 | 214 | #define CAN_TCAN4X5X_T_PULSE_WIDTH_US 30 |
206 | 215 | #define CAN_TCAN4X5X_T_RESET_US 1000 |
@@ -551,14 +560,161 @@ static int tcan4x5x_reset(const struct device *dev) |
551 | 560 | return 0; |
552 | 561 | } |
553 | 562 |
|
| 563 | +static int tcan4x5x_set_config_mode_sel(const struct device *dev, uint8_t mode, uint32_t *reg) |
| 564 | +{ |
| 565 | + int err; |
| 566 | + uint8_t current_mode; |
| 567 | + |
| 568 | + switch (mode) { |
| 569 | + case CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_SLEEP: |
| 570 | + case CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_STANDBY: |
| 571 | + case CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_NORMAL: |
| 572 | + break; |
| 573 | + default: |
| 574 | + LOG_ERR("invalid mode %u", mode); |
| 575 | + return -EINVAL; |
| 576 | + } |
| 577 | + |
| 578 | + err = tcan4x5x_read_tcan_reg(dev, CAN_TCAN4X5X_MODE_CONFIG, reg); |
| 579 | + if (err != 0) { |
| 580 | + LOG_ERR("failed to read configuration register (err %d)", err); |
| 581 | + return -EIO; |
| 582 | + } |
| 583 | + |
| 584 | + current_mode = FIELD_GET(CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL, *reg); |
| 585 | + LOG_DBG("current mode %u, new mode %u", current_mode, mode); |
| 586 | + |
| 587 | + *reg &= ~(CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL); |
| 588 | + *reg |= FIELD_PREP(CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL, mode); |
| 589 | + |
| 590 | + err = tcan4x5x_write_tcan_reg(dev, CAN_TCAN4X5X_MODE_CONFIG, *reg); |
| 591 | + if (err != 0) { |
| 592 | + LOG_ERR("failed to write configuration register (err %d)", err); |
| 593 | + return -EIO; |
| 594 | + } |
| 595 | + |
| 596 | + if (current_mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_STANDBY && |
| 597 | + mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_NORMAL) { |
| 598 | + /* Wait for standby to normal mode switch */ |
| 599 | + k_busy_wait(CAN_TCAN4X5X_T_MODE_STBY_NOM_US); |
| 600 | + } else if (current_mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_NORMAL && |
| 601 | + mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_SLEEP) { |
| 602 | + /* Wait for normal to sleep mode switch */ |
| 603 | + k_busy_wait(CAN_TCAN4X5X_T_MODE_NOM_SLP_US); |
| 604 | + } else if (current_mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_NORMAL && |
| 605 | + mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_STANDBY) { |
| 606 | + /* Wait for normal to standby mode switch */ |
| 607 | + k_busy_wait(CAN_TCAN4X5X_T_MODE_NOM_STBY_US); |
| 608 | + } else if (current_mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_SLEEP && |
| 609 | + mode == CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_STANDBY) { |
| 610 | + /* Wait for sleep to standby mode switch */ |
| 611 | + k_busy_wait(CAN_TCAN4X5X_T_MODE_SLP_STBY_US); |
| 612 | + } |
| 613 | + |
| 614 | + return 0; |
| 615 | +} |
| 616 | + |
| 617 | +static int tcan4x5x_init_normal_mode(const struct device *dev) |
| 618 | +{ |
| 619 | + const struct can_mcan_config *mcan_config = dev->config; |
| 620 | + const struct tcan4x5x_config *tcan_config = mcan_config->custom; |
| 621 | + int err = 0; |
| 622 | + uint32_t reg; |
| 623 | + |
| 624 | + /* Set TCAN4x5x mode normal */ |
| 625 | + err = tcan4x5x_set_config_mode_sel(dev, CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_NORMAL, ®); |
| 626 | + if (err != 0) { |
| 627 | + return -ENODEV; |
| 628 | + } |
| 629 | + |
| 630 | + /* Configure the frequency reference */ |
| 631 | + if (tcan_config->clk_freq == MHZ(20)) { |
| 632 | + /* 20 MHz frequency reference */ |
| 633 | + reg &= ~(CAN_TCAN4X5X_MODE_CONFIG_CLK_REF); |
| 634 | + } else { |
| 635 | + /* 40 MHz frequency reference */ |
| 636 | + reg |= CAN_TCAN4X5X_MODE_CONFIG_CLK_REF; |
| 637 | + } |
| 638 | + |
| 639 | + /* Set nWKRQ voltage to VIO */ |
| 640 | + reg |= CAN_TCAN4X5X_MODE_CONFIG_NWKRQ_VOLTAGE; |
| 641 | + |
| 642 | + /* Write remaining configuration to the device */ |
| 643 | + err = tcan4x5x_write_tcan_reg(dev, CAN_TCAN4X5X_MODE_CONFIG, reg); |
| 644 | + if (err != 0) { |
| 645 | + LOG_ERR("failed to write configuration register (err %d)", err); |
| 646 | + return -EIO; |
| 647 | + } |
| 648 | + |
| 649 | + /* Configure Message RAM */ |
| 650 | + err = can_mcan_configure_mram(dev, CAN_TCAN4X5X_MRAM_BASE, CAN_TCAN4X5X_MRAM_BASE); |
| 651 | + if (err != 0) { |
| 652 | + return -EIO; |
| 653 | + } |
| 654 | + |
| 655 | + /* Initialize M_CAN */ |
| 656 | + err = can_mcan_init(dev); |
| 657 | + if (err != 0) { |
| 658 | + LOG_ERR("failed to initialize mcan (err %d)", err); |
| 659 | + return err; |
| 660 | + } |
| 661 | + |
| 662 | + return err; |
| 663 | +} |
| 664 | + |
| 665 | +#ifdef CONFIG_PM_DEVICE |
| 666 | +static int tcan4x5x_pm_control(const struct device *dev, enum pm_device_action action) |
| 667 | +{ |
| 668 | + int err = 0; |
| 669 | + uint32_t reg; |
| 670 | + |
| 671 | + switch (action) { |
| 672 | + case PM_DEVICE_ACTION_SUSPEND: |
| 673 | + if (pm_device_is_busy(dev)) { |
| 674 | + LOG_DBG("Cannot suspend while device is busy"); |
| 675 | + return -EBUSY; |
| 676 | + } |
| 677 | + |
| 678 | + /* |
| 679 | + * Enter sleep mode. |
| 680 | + * NOTE: All RX filters are cleared when entering sleep mode. |
| 681 | + * User must remove and re-add filters at the application layer. |
| 682 | + */ |
| 683 | + err = tcan4x5x_set_config_mode_sel(dev, CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL_SLEEP, |
| 684 | + ®); |
| 685 | + return err; |
| 686 | + case PM_DEVICE_ACTION_RESUME: |
| 687 | + /* Wake up the device */ |
| 688 | +#if TCAN4X5X_WAKE_GPIO_SUPPORT |
| 689 | + LOG_DBG("Waking up TCAN4x5x via WAKE GPIO"); |
| 690 | + err = tcan4x5x_wake(dev); |
| 691 | + if (err != 0) { |
| 692 | + return err; |
| 693 | + } |
| 694 | +#else |
| 695 | + LOG_DBG("Waking up TCAN4x5x via reset"); |
| 696 | + err = tcan4x5x_reset(dev); |
| 697 | + if (err != 0) { |
| 698 | + return err; |
| 699 | + } |
| 700 | +#endif |
| 701 | + /* Enter normal mode */ |
| 702 | + return tcan4x5x_init_normal_mode(dev); |
| 703 | + default: |
| 704 | + break; |
| 705 | + } |
| 706 | + |
| 707 | + return -ENOTSUP; |
| 708 | +} |
| 709 | +#endif /* CONFIG_PM_DEVICE */ |
| 710 | + |
554 | 711 | static int tcan4x5x_init(const struct device *dev) |
555 | 712 | { |
556 | 713 | const struct can_mcan_config *mcan_config = dev->config; |
557 | 714 | const struct tcan4x5x_config *tcan_config = mcan_config->custom; |
558 | 715 | struct can_mcan_data *mcan_data = dev->data; |
559 | 716 | struct tcan4x5x_data *tcan_data = mcan_data->custom; |
560 | 717 | k_tid_t tid; |
561 | | - uint32_t reg; |
562 | 718 | int err; |
563 | 719 |
|
564 | 720 | /* Initialize int_sem to 1 to ensure any pending IRQ is serviced */ |
@@ -671,48 +827,7 @@ static int tcan4x5x_init(const struct device *dev) |
671 | 827 | FIELD_GET(GENMASK(15, 8), info[2]), FIELD_GET(GENMASK(7, 0), info[2])); |
672 | 828 | #endif /* CONFIG_CAN_LOG_LEVEL >= LOG_LEVEL_DBG */ |
673 | 829 |
|
674 | | - /* Set TCAN4x5x mode normal */ |
675 | | - err = tcan4x5x_read_tcan_reg(dev, CAN_TCAN4X5X_MODE_CONFIG, ®); |
676 | | - if (err != 0) { |
677 | | - LOG_ERR("failed to read configuration register (err %d)", err); |
678 | | - return -ENODEV; |
679 | | - } |
680 | | - |
681 | | - reg &= ~(CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL); |
682 | | - reg |= FIELD_PREP(CAN_TCAN4X5X_MODE_CONFIG_MODE_SEL, 0x02); |
683 | | - reg |= CAN_TCAN4X5X_MODE_CONFIG_WAKE_CONFIG; |
684 | | - |
685 | | - if (tcan_config->clk_freq == MHZ(20)) { |
686 | | - /* 20 MHz frequency reference */ |
687 | | - reg &= ~(CAN_TCAN4X5X_MODE_CONFIG_CLK_REF); |
688 | | - } else { |
689 | | - /* 40 MHz frequency reference */ |
690 | | - reg |= CAN_TCAN4X5X_MODE_CONFIG_CLK_REF; |
691 | | - } |
692 | | - |
693 | | - err = tcan4x5x_write_tcan_reg(dev, CAN_TCAN4X5X_MODE_CONFIG, reg); |
694 | | - if (err != 0) { |
695 | | - LOG_ERR("failed to write configuration register (err %d)", err); |
696 | | - return -ENODEV; |
697 | | - } |
698 | | - |
699 | | - /* Wait for standby to normal mode switch */ |
700 | | - k_busy_wait(CAN_TCAN4X5X_T_MODE_STBY_NOM_US); |
701 | | - |
702 | | - /* Configure Message RAM */ |
703 | | - err = can_mcan_configure_mram(dev, CAN_TCAN4X5X_MRAM_BASE, CAN_TCAN4X5X_MRAM_BASE); |
704 | | - if (err != 0) { |
705 | | - return -EIO; |
706 | | - } |
707 | | - |
708 | | - /* Initialize M_CAN */ |
709 | | - err = can_mcan_init(dev); |
710 | | - if (err != 0) { |
711 | | - LOG_ERR("failed to initialize mcan (err %d)", err); |
712 | | - return err; |
713 | | - } |
714 | | - |
715 | | - return 0; |
| 830 | + return tcan4x5x_init_normal_mode(dev); |
716 | 831 | } |
717 | 832 |
|
718 | 833 | static DEVICE_API(can, tcan4x5x_driver_api) = { |
@@ -794,8 +909,9 @@ static const struct can_mcan_ops tcan4x5x_ops = { |
794 | 909 | static struct can_mcan_data can_mcan_data_##inst = \ |
795 | 910 | CAN_MCAN_DATA_INITIALIZER(&tcan4x5x_data_##inst); \ |
796 | 911 | \ |
797 | | - CAN_DEVICE_DT_INST_DEFINE(inst, tcan4x5x_init, NULL, &can_mcan_data_##inst, \ |
798 | | - &can_mcan_config_##inst, POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \ |
799 | | - &tcan4x5x_driver_api); |
| 912 | + PM_DEVICE_DT_INST_DEFINE(inst, tcan4x5x_pm_control); \ |
| 913 | + CAN_DEVICE_DT_INST_DEFINE(inst, tcan4x5x_init, PM_DEVICE_DT_INST_GET(inst), \ |
| 914 | + &can_mcan_data_##inst, &can_mcan_config_##inst, POST_KERNEL, \ |
| 915 | + CONFIG_CAN_INIT_PRIORITY, &tcan4x5x_driver_api); |
800 | 916 |
|
801 | 917 | DT_INST_FOREACH_STATUS_OKAY(TCAN4X5X_INIT) |
0 commit comments