1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2022-10-24     GuEe-GUI     first version
9  */
10 
11 #include <drivers/pci.h>
12 #include <drivers/core/power_domain.h>
13 
14 #define DBG_TAG "pci.pme"
15 #define DBG_LVL DBG_INFO
16 #include <rtdbg.h>
17 
18 /*
19  * Power Management Capability Register:
20  *
21  *   31      27 26  25 24    22  21  20  19 18  16 15        8 7              0
22  *  +---------+---+---+--------+---+---+---+------+-----------+----------------+
23  *  |         |   |   |        |   |   |   |      |           | Capabilitiy ID |
24  *  +---------+---+---+--------+---+---+---+------+-----------+----------------+
25  *       ^      ^   ^      ^     ^   ^   ^     ^        ^
26  *       |      |   |      |     |   |   |     |        |
27  *       |      |   |      |     |   |   |     |        +---- Next Capabilitiy Pointer
28  *       |      |   |      |     |   |   |     +------------- Version
29  *       |      |   |      |     |   |   +------------------- PME Clock
30  *       |      |   |      |     |   +----------------------- Immediate Readiness on Return to D0
31  *       |      |   |      |     +--------------------------- Device Specifiic Initializtion
32  *       |      |   |      +--------------------------------- Aux Current
33  *       |      |   +---------------------------------------- D1 Support
34  *       |      +-------------------------------------------- D2 Support
35  *       +--------------------------------------------------- PME Support
36  */
37 
rt_pci_pme_init(struct rt_pci_device * pdev)38 void rt_pci_pme_init(struct rt_pci_device *pdev)
39 {
40     rt_uint16_t pmc;
41 
42     if (!pdev || !(pdev->pme_cap = rt_pci_find_capability(pdev, PCIY_PMG)))
43     {
44         return;
45     }
46 
47     rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_CAP, &pmc);
48 
49     if ((pmc & PCIM_PCAP_SPEC) > 3)
50     {
51         LOG_E("%s: Unsupported PME CAP regs spec %u",
52                 rt_dm_dev_get_name(&pdev->parent), pmc & PCIM_PCAP_SPEC);
53 
54         return;
55     }
56 
57     pmc &= PCIM_PCAP_PMEMASK;
58 
59     if (pmc)
60     {
61         pdev->pme_support = RT_FIELD_GET(PCIM_PCAP_PMEMASK, pmc);
62 
63         rt_pci_pme_active(pdev, RT_FALSE);
64     }
65 }
66 
rt_pci_enable_wake(struct rt_pci_device * pdev,enum rt_pci_power state,rt_bool_t enable)67 rt_err_t rt_pci_enable_wake(struct rt_pci_device *pdev,
68         enum rt_pci_power state, rt_bool_t enable)
69 {
70     if (!pdev || state >= RT_PCI_PME_MAX)
71     {
72         return -RT_EINVAL;
73     }
74 
75     if (enable)
76     {
77         if (rt_pci_pme_capable(pdev, state) ||
78             rt_pci_pme_capable(pdev, RT_PCI_D3COLD))
79         {
80             rt_pci_pme_active(pdev, RT_EOK);
81         }
82     }
83     else
84     {
85         rt_pci_pme_active(pdev, RT_FALSE);
86     }
87 
88     return RT_EOK;
89 }
90 
pci_pme_active(struct rt_pci_device * pdev,rt_bool_t enable)91 static void pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable)
92 {
93     rt_uint16_t pmcsr;
94 
95     if (!pdev->pme_support)
96     {
97         return;
98     }
99 
100     rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, &pmcsr);
101     /* Clear PME_Status by writing 1 to it and enable PME# */
102     pmcsr |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
103 
104     if (!enable)
105     {
106         pmcsr &= ~PCIM_PSTAT_PMEENABLE;
107     }
108 
109     rt_pci_write_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, pmcsr);
110     pdev->pm_enabled = enable;
111 }
112 
rt_pci_pme_active(struct rt_pci_device * pdev,rt_bool_t enable)113 void rt_pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable)
114 {
115     if (!pdev)
116     {
117         return;
118     }
119 
120     pci_pme_active(pdev, enable);
121     rt_dm_power_domain_attach(&pdev->parent, enable);
122 }
123