Path: blob/master/arch/powerpc/platforms/83xx/suspend.c
26481 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* MPC83xx suspend support3*4* Author: Scott Wood <[email protected]>5*6* Copyright (c) 2006-2007 Freescale Semiconductor, Inc.7*/89#include <linux/pm.h>10#include <linux/types.h>11#include <linux/ioport.h>12#include <linux/interrupt.h>13#include <linux/wait.h>14#include <linux/sched/signal.h>15#include <linux/kthread.h>16#include <linux/freezer.h>17#include <linux/suspend.h>18#include <linux/fsl_devices.h>19#include <linux/of_address.h>20#include <linux/of_irq.h>21#include <linux/platform_device.h>22#include <linux/export.h>2324#include <asm/reg.h>25#include <asm/io.h>26#include <asm/time.h>27#include <asm/mpc6xx.h>28#include <asm/switch_to.h>2930#include <sysdev/fsl_soc.h>3132#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */33#define PMCCR1_NEXT_STATE_SHIFT 234#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/35#define IMMR_SYSCR_OFFSET 0x10036#define IMMR_RCW_OFFSET 0x90037#define RCW_PCI_HOST 0x800000003839void mpc83xx_enter_deep_sleep(phys_addr_t immrbase);4041struct mpc83xx_pmc {42u32 config;43#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */44#define PMCCR_SLPEN 1 /* System low power enable */4546u32 event;47u32 mask;48/* All but PMCI are deep-sleep only */49#define PMCER_GPIO 0x10050#define PMCER_PCI 0x08051#define PMCER_USB 0x04052#define PMCER_ETSEC1 0x02053#define PMCER_ETSEC2 0x01054#define PMCER_TIMER 0x00855#define PMCER_INT1 0x00456#define PMCER_INT2 0x00257#define PMCER_PMCI 0x00158#define PMCER_ALL 0x1FF5960/* deep-sleep only */61u32 config1;62#define PMCCR1_USE_STATE 0x8000000063#define PMCCR1_PME_EN 0x0000008064#define PMCCR1_ASSERT_PME 0x0000004065#define PMCCR1_POWER_OFF 0x000000206667/* deep-sleep only */68u32 config2;69};7071struct mpc83xx_rcw {72u32 rcwlr;73u32 rcwhr;74};7576struct mpc83xx_clock {77u32 spmr;78u32 occr;79u32 sccr;80};8182struct mpc83xx_syscr {83__be32 sgprl;84__be32 sgprh;85__be32 spridr;86__be32 :32;87__be32 spcr;88__be32 sicrl;89__be32 sicrh;90};9192struct mpc83xx_saved {93u32 sicrl;94u32 sicrh;95u32 sccr;96};9798struct pmc_type {99int has_deep_sleep;100};101102static int has_deep_sleep, deep_sleeping;103static int pmc_irq;104static struct mpc83xx_pmc __iomem *pmc_regs;105static struct mpc83xx_clock __iomem *clock_regs;106static struct mpc83xx_syscr __iomem *syscr_regs;107static struct mpc83xx_saved saved_regs;108static int is_pci_agent, wake_from_pci;109static phys_addr_t immrbase;110static int pci_pm_state;111static DECLARE_WAIT_QUEUE_HEAD(agent_wq);112113int fsl_deep_sleep(void)114{115return deep_sleeping;116}117EXPORT_SYMBOL(fsl_deep_sleep);118119static int mpc83xx_change_state(void)120{121u32 curr_state;122u32 reg_cfg1 = in_be32(&pmc_regs->config1);123124if (is_pci_agent) {125pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >>126PMCCR1_NEXT_STATE_SHIFT;127curr_state = reg_cfg1 & PMCCR1_CURR_STATE;128129if (curr_state != pci_pm_state) {130reg_cfg1 &= ~PMCCR1_CURR_STATE;131reg_cfg1 |= pci_pm_state;132out_be32(&pmc_regs->config1, reg_cfg1);133134wake_up(&agent_wq);135return 1;136}137}138139return 0;140}141142static irqreturn_t pmc_irq_handler(int irq, void *dev_id)143{144u32 event = in_be32(&pmc_regs->event);145int ret = IRQ_NONE;146147if (mpc83xx_change_state())148ret = IRQ_HANDLED;149150if (event) {151out_be32(&pmc_regs->event, event);152ret = IRQ_HANDLED;153}154155return ret;156}157158static void mpc83xx_suspend_restore_regs(void)159{160out_be32(&syscr_regs->sicrl, saved_regs.sicrl);161out_be32(&syscr_regs->sicrh, saved_regs.sicrh);162out_be32(&clock_regs->sccr, saved_regs.sccr);163}164165static void mpc83xx_suspend_save_regs(void)166{167saved_regs.sicrl = in_be32(&syscr_regs->sicrl);168saved_regs.sicrh = in_be32(&syscr_regs->sicrh);169saved_regs.sccr = in_be32(&clock_regs->sccr);170}171172static int mpc83xx_suspend_enter(suspend_state_t state)173{174int ret = -EAGAIN;175176/* Don't go to sleep if there's a race where pci_pm_state changes177* between the agent thread checking it and the PM code disabling178* interrupts.179*/180if (wake_from_pci) {181if (pci_pm_state != (deep_sleeping ? 3 : 2))182goto out;183184out_be32(&pmc_regs->config1,185in_be32(&pmc_regs->config1) | PMCCR1_PME_EN);186}187188/* Put the system into low-power mode and the RAM189* into self-refresh mode once the core goes to190* sleep.191*/192193out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN);194195/* If it has deep sleep (i.e. it's an 831x or compatible),196* disable power to the core upon entering sleep mode. This will197* require going through the boot firmware upon a wakeup event.198*/199200if (deep_sleeping) {201mpc83xx_suspend_save_regs();202203out_be32(&pmc_regs->mask, PMCER_ALL);204205out_be32(&pmc_regs->config1,206in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF);207208if (IS_ENABLED(CONFIG_PPC_FPU))209enable_kernel_fp();210211mpc83xx_enter_deep_sleep(immrbase);212213out_be32(&pmc_regs->config1,214in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF);215216out_be32(&pmc_regs->mask, PMCER_PMCI);217218mpc83xx_suspend_restore_regs();219} else {220out_be32(&pmc_regs->mask, PMCER_PMCI);221222mpc6xx_enter_standby();223}224225ret = 0;226227out:228out_be32(&pmc_regs->config1,229in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN);230231return ret;232}233234static void mpc83xx_suspend_end(void)235{236deep_sleeping = 0;237}238239static int mpc83xx_suspend_valid(suspend_state_t state)240{241return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM;242}243244static int mpc83xx_suspend_begin(suspend_state_t state)245{246switch (state) {247case PM_SUSPEND_STANDBY:248deep_sleeping = 0;249return 0;250251case PM_SUSPEND_MEM:252if (has_deep_sleep)253deep_sleeping = 1;254255return 0;256257default:258return -EINVAL;259}260}261262static int agent_thread_fn(void *data)263{264set_freezable();265266while (1) {267wait_event_freezable(agent_wq, pci_pm_state >= 2);268269if (signal_pending(current) || pci_pm_state < 2)270continue;271272/* With a preemptible kernel (or SMP), this could race with273* a userspace-driven suspend request. It's probably best274* to avoid mixing the two with such a configuration (or275* else fix it by adding a mutex to state_store that we can276* synchronize with).277*/278279wake_from_pci = 1;280281pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM :282PM_SUSPEND_STANDBY);283284wake_from_pci = 0;285}286287return 0;288}289290static void mpc83xx_set_agent(void)291{292out_be32(&pmc_regs->config1, PMCCR1_USE_STATE);293out_be32(&pmc_regs->mask, PMCER_PMCI);294295kthread_run(agent_thread_fn, NULL, "PCI power mgt");296}297298static int mpc83xx_is_pci_agent(void)299{300struct mpc83xx_rcw __iomem *rcw_regs;301int ret;302303rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET,304sizeof(struct mpc83xx_rcw));305306if (!rcw_regs)307return -ENOMEM;308309ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST);310311iounmap(rcw_regs);312return ret;313}314315static const struct platform_suspend_ops mpc83xx_suspend_ops = {316.valid = mpc83xx_suspend_valid,317.begin = mpc83xx_suspend_begin,318.enter = mpc83xx_suspend_enter,319.end = mpc83xx_suspend_end,320};321322static struct pmc_type pmc_types[] = {323{324.has_deep_sleep = 1,325},326{327.has_deep_sleep = 0,328}329};330331static const struct of_device_id pmc_match[] = {332{333.compatible = "fsl,mpc8313-pmc",334.data = &pmc_types[0],335},336{337.compatible = "fsl,mpc8349-pmc",338.data = &pmc_types[1],339},340{}341};342343static int pmc_probe(struct platform_device *ofdev)344{345struct device_node *np = ofdev->dev.of_node;346struct resource res;347const struct pmc_type *type;348int ret = 0;349350type = of_device_get_match_data(&ofdev->dev);351if (!type)352return -EINVAL;353354if (!of_device_is_available(np))355return -ENODEV;356357has_deep_sleep = type->has_deep_sleep;358immrbase = get_immrbase();359360is_pci_agent = mpc83xx_is_pci_agent();361if (is_pci_agent < 0)362return is_pci_agent;363364ret = of_address_to_resource(np, 0, &res);365if (ret)366return -ENODEV;367368pmc_irq = irq_of_parse_and_map(np, 0);369if (pmc_irq) {370ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED,371"pmc", ofdev);372373if (ret)374return -EBUSY;375}376377pmc_regs = ioremap(res.start, sizeof(*pmc_regs));378379if (!pmc_regs) {380ret = -ENOMEM;381goto out;382}383384ret = of_address_to_resource(np, 1, &res);385if (ret) {386ret = -ENODEV;387goto out_pmc;388}389390clock_regs = ioremap(res.start, sizeof(*clock_regs));391392if (!clock_regs) {393ret = -ENOMEM;394goto out_pmc;395}396397if (has_deep_sleep) {398syscr_regs = ioremap(immrbase + IMMR_SYSCR_OFFSET,399sizeof(*syscr_regs));400if (!syscr_regs) {401ret = -ENOMEM;402goto out_syscr;403}404}405406if (is_pci_agent)407mpc83xx_set_agent();408409suspend_set_ops(&mpc83xx_suspend_ops);410return 0;411412out_syscr:413iounmap(clock_regs);414out_pmc:415iounmap(pmc_regs);416out:417if (pmc_irq)418free_irq(pmc_irq, ofdev);419420return ret;421}422423static struct platform_driver pmc_driver = {424.driver = {425.name = "mpc83xx-pmc",426.of_match_table = pmc_match,427.suppress_bind_attrs = true,428},429.probe = pmc_probe,430};431432builtin_platform_driver(pmc_driver);433434435