162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Suspend/resume support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2009 MontaVista Software, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Anton Vorontsov <avorontsov@ru.mvista.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/suspend.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1762306a36Sopenharmony_ci#include <linux/of_address.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct pmc_regs { 2162306a36Sopenharmony_ci __be32 devdisr; 2262306a36Sopenharmony_ci __be32 devdisr2; 2362306a36Sopenharmony_ci __be32 :32; 2462306a36Sopenharmony_ci __be32 :32; 2562306a36Sopenharmony_ci __be32 pmcsr; 2662306a36Sopenharmony_ci#define PMCSR_SLP (1 << 17) 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct device *pmc_dev; 3062306a36Sopenharmony_cistatic struct pmc_regs __iomem *pmc_regs; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int pmc_suspend_enter(suspend_state_t state) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int ret; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci setbits32(&pmc_regs->pmcsr, PMCSR_SLP); 3762306a36Sopenharmony_ci /* At this point, the CPU is asleep. */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Upon resume, wait for SLP bit to be clear. */ 4062306a36Sopenharmony_ci ret = spin_event_timeout((in_be32(&pmc_regs->pmcsr) & PMCSR_SLP) == 0, 4162306a36Sopenharmony_ci 10000, 10) ? 0 : -ETIMEDOUT; 4262306a36Sopenharmony_ci if (ret) 4362306a36Sopenharmony_ci dev_err(pmc_dev, "tired waiting for SLP bit to clear\n"); 4462306a36Sopenharmony_ci return ret; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int pmc_suspend_valid(suspend_state_t state) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci if (state != PM_SUSPEND_STANDBY) 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci return 1; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic const struct platform_suspend_ops pmc_suspend_ops = { 5562306a36Sopenharmony_ci .valid = pmc_suspend_valid, 5662306a36Sopenharmony_ci .enter = pmc_suspend_enter, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int pmc_probe(struct platform_device *ofdev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci pmc_regs = of_iomap(ofdev->dev.of_node, 0); 6262306a36Sopenharmony_ci if (!pmc_regs) 6362306a36Sopenharmony_ci return -ENOMEM; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci pmc_dev = &ofdev->dev; 6662306a36Sopenharmony_ci suspend_set_ops(&pmc_suspend_ops); 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const struct of_device_id pmc_ids[] = { 7162306a36Sopenharmony_ci { .compatible = "fsl,mpc8548-pmc", }, 7262306a36Sopenharmony_ci { .compatible = "fsl,mpc8641d-pmc", }, 7362306a36Sopenharmony_ci { }, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct platform_driver pmc_driver = { 7762306a36Sopenharmony_ci .driver = { 7862306a36Sopenharmony_ci .name = "fsl-pmc", 7962306a36Sopenharmony_ci .of_match_table = pmc_ids, 8062306a36Sopenharmony_ci }, 8162306a36Sopenharmony_ci .probe = pmc_probe, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cibuiltin_platform_driver(pmc_driver); 85