/* * Copyright (c) 2006-2022, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2022-10-24 GuEe-GUI first version */ #include #include #define DBG_TAG "pci.pme" #define DBG_LVL DBG_INFO #include /* * Power Management Capability Register: * * 31 27 26 25 24 22 21 20 19 18 16 15 8 7 0 * +---------+---+---+--------+---+---+---+------+-----------+----------------+ * | | | | | | | | | | Capabilitiy ID | * +---------+---+---+--------+---+---+---+------+-----------+----------------+ * ^ ^ ^ ^ ^ ^ ^ ^ ^ * | | | | | | | | | * | | | | | | | | +---- Next Capabilitiy Pointer * | | | | | | | +------------- Version * | | | | | | +------------------- PME Clock * | | | | | +----------------------- Immediate Readiness on Return to D0 * | | | | +--------------------------- Device Specifiic Initializtion * | | | +--------------------------------- Aux Current * | | +---------------------------------------- D1 Support * | +-------------------------------------------- D2 Support * +--------------------------------------------------- PME Support */ void rt_pci_pme_init(struct rt_pci_device *pdev) { rt_uint16_t pmc; if (!pdev || !(pdev->pme_cap = rt_pci_find_capability(pdev, PCIY_PMG))) { return; } rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_CAP, &pmc); if ((pmc & PCIM_PCAP_SPEC) > 3) { LOG_E("%s: Unsupported PME CAP regs spec %u", rt_dm_dev_get_name(&pdev->parent), pmc & PCIM_PCAP_SPEC); return; } pmc &= PCIM_PCAP_PMEMASK; if (pmc) { pdev->pme_support = RT_FIELD_GET(PCIM_PCAP_PMEMASK, pmc); rt_pci_pme_active(pdev, RT_FALSE); } } rt_err_t rt_pci_enable_wake(struct rt_pci_device *pdev, enum rt_pci_power state, rt_bool_t enable) { if (!pdev || state >= RT_PCI_PME_MAX) { return -RT_EINVAL; } if (enable) { if (rt_pci_pme_capable(pdev, state) || rt_pci_pme_capable(pdev, RT_PCI_D3COLD)) { rt_pci_pme_active(pdev, RT_EOK); } } else { rt_pci_pme_active(pdev, RT_FALSE); } return RT_EOK; } static void pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable) { rt_uint16_t pmcsr; if (!pdev->pme_support) { return; } rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, &pmcsr); /* Clear PME_Status by writing 1 to it and enable PME# */ pmcsr |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; if (!enable) { pmcsr &= ~PCIM_PSTAT_PMEENABLE; } rt_pci_write_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, pmcsr); } void rt_pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable) { if (!pdev) { return; } pci_pme_active(pdev, enable); rt_dm_power_domain_attach(&pdev->parent, enable); }