Message ID | 20090914202017.GA14428@oksana.dev.rtsoft.ru (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Anton Vorontsov wrote: > This patch adds suspend/resume support for MPC8540, MPC8569 and > MPC8641D-compatible CPUs. > > MPC8540 and MPC8641D-compatible PMCs are trivial: we just write > the SLP bit into the PM control and status register. > > MPC8569 is a bit trickier, QE turns off during suspend, thus on > resume we must reload QE microcode and reset QE. > > So far we don't support Deep Sleep mode as found in newer MPC85xx > CPUs (i.e. MPC8536). It can be relatively easy implemented though, > and for it we reserve 'mem' suspend type. > > Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> Acked-by: Scott Wood <scottwood@freescale.com> >> I'm not sure that we're guaranteed that the sleep has taken effect >> immediately -- we may need to do a loop waiting for it to clear (on >> 85xx), probably with some delays to give the bus a chance to become idle. > > Brilliant idea, thanks! > > But it works vice versa: upon write, the code flow stops immediately > (on 85xx and 86xx), That's what I've observed as well; the question is whether it's always guaranteed to be immediate. >> The 8610 manual says that that bit should only be set as part of a >> sequence also involving the use of MSR[POW] (section 23.5.1.4). > > Well, 23.4.1.12 says: > > | Sleep status > | 0 The device is not attempting to reach sleep mode. > | 1 The device is attempting to enter sleep mode because > | POWMGTCSR[SLP] is set, or HID0[SLEEP] and MSR[POW] in > ^^ > | the e600 core are set. The core has halted fetching, snooping > | to the L1 and L2 caches is disabled, .... > > The same in 85xx specs. > > I can confirm this on real hw, after setting the SLP bit, the 8610 > actually goes into sleep mode, no code flow happens until a wakeup > event. So playing with MSR[POW] doesn't seem to be necessary. The > same for 85xx. OK, looks like section 23.5.1.4 is just bogus. > This exact snippet could be moved to the "qe" device driver, yes. > But I didn't bother because fsl_pmc have to request the QE firmware. > > You can't request the firmware in the qe driver's ->suspend() > routine necause the firmware may be on e.g. NFS filesystem or USB > stick (implies having QE Ethernet or QE USB fully functional). Is there any way for software to read out the current firmware from the device, or is it write-only? > We can solve that by implementing ppc_md.suspend_prepare() (must > be called from userspace context), there we could request the > firmware. Then QE device driver would reload it in its resume() > callback. Needless to say that it makes things a bit more > complicated to follow. > > The current code vanishes without QE anyway. But if you insist, > I can do the suspend_prepare() thing, although I'd prefer it as > a cleanup patch for 2.6.33, since it would touch more code, > specifically I'm concerned about ppc generic files. I don't insist, it just struck me as odd. -Scott
On Mon, Sep 14, 2009 at 03:45:10PM -0500, Scott Wood wrote: [...] > >You can't request the firmware in the qe driver's ->suspend() > >routine necause the firmware may be on e.g. NFS filesystem or USB > >stick (implies having QE Ethernet or QE USB fully functional). > > Is there any way for software to read out the current firmware from > the device, or is it write-only? Hm, I didn't look into iram stuff that much, but seemingly I can read it back and save. In the end, it's just a ram that we access in a weird way... Let me try it. Thanks,
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index d00131c..a0743a7 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -212,7 +212,8 @@ config ARCH_HIBERNATION_POSSIBLE config ARCH_SUSPEND_POSSIBLE def_bool y - depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx + depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx || \ + PPC_85xx || PPC_86xx config PPC_DCR_NATIVE bool @@ -642,6 +643,14 @@ config FSL_PCI select PPC_INDIRECT_PCI select PCI_QUIRKS +config FSL_PMC + bool + default y + depends on SUSPEND && (PPC_85xx || PPC_86xx) + help + Freescale MPC85xx/MPC86xx power management controller support + (suspend/resume). For MPC83xx see platforms/83xx/suspend.c + config 4xx_SOC bool diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 9d4b174..5642924 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_FSL_SOC) += fsl_soc.o obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) +obj-$(CONFIG_FSL_PMC) += fsl_pmc.o obj-$(CONFIG_FSL_LBC) += fsl_lbc.o obj-$(CONFIG_FSL_GTM) += fsl_gtm.o obj-$(CONFIG_MPC8xxx_GPIO) += mpc8xxx_gpio.o diff --git a/arch/powerpc/sysdev/fsl_pmc.c b/arch/powerpc/sysdev/fsl_pmc.c new file mode 100644 index 0000000..916a21a --- /dev/null +++ b/arch/powerpc/sysdev/fsl_pmc.c @@ -0,0 +1,131 @@ +/* + * Suspend/resume support + * + * Copyright © 2009 MontaVista Software, Inc. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/suspend.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/of_platform.h> +#include <linux/firmware.h> +#include <asm/qe.h> + +struct pmc_regs { + __be32 devdisr; + __be32 devdisr2; + __be32 :32; + __be32 :32; + __be32 pmcsr; +#define PMCSR_SLP (1 << 17) +}; + +struct pmc_data { + unsigned int flags; +#define PMC_NEED_QE_RELOAD (1 << 0) + + const char *fw_name; +}; + +static struct device *pmc_dev; +static struct pmc_regs __iomem *pmc_regs; +static const struct pmc_data *pmc_data; +static struct qe_firmware *pmc_qefw; + +static int pmc_suspend_enter(suspend_state_t state) +{ + int ret; + + setbits32(&pmc_regs->pmcsr, PMCSR_SLP); + /* At this point, the CPU is asleep. */ + + /* Upon resume, wait for SLP bit to be clear. */ + ret = spin_event_timeout((in_be32(&pmc_regs->pmcsr) & PMCSR_SLP) == 0, + 10000, 10) ? 0 : -ETIMEDOUT; + if (ret) + dev_err(pmc_dev, "tired waiting for SLP bit to clear\n"); + + if (pmc_qefw) { + int ret; + + ret = qe_upload_firmware(pmc_qefw); + if (ret) + dev_err(pmc_dev, "could not upload firmware\n"); + + qe_reset(); + } + return ret; +} + +static int pmc_suspend_valid(suspend_state_t state) +{ + if (state != PM_SUSPEND_STANDBY) + return 0; + + if (pmc_data && pmc_data->flags & PMC_NEED_QE_RELOAD && !pmc_qefw) { + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, pmc_data->fw_name, pmc_dev); + if (ret) { + dev_err(pmc_dev, "could not request firmware %s\n", + pmc_data->fw_name); + return 0; + } + + pmc_qefw = (struct qe_firmware *)fw->data; + } + + return 1; +} + +static struct platform_suspend_ops pmc_suspend_ops = { + .valid = pmc_suspend_valid, + .enter = pmc_suspend_enter, +}; + +static int pmc_probe(struct of_device *ofdev, const struct of_device_id *id) +{ + pmc_regs = of_iomap(ofdev->node, 0); + if (!pmc_regs) + return -ENOMEM; + + pmc_dev = &ofdev->dev; + pmc_data = id->data; + suspend_set_ops(&pmc_suspend_ops); + return 0; +} + +static struct pmc_data mpc8569_pmc_data = { + .flags = PMC_NEED_QE_RELOAD, + .fw_name = "fsl_qe_ucode_8569.bin", +}; + +static const struct of_device_id pmc_ids[] = { + { .compatible = "fsl,mpc8569-pmc", .data = &mpc8569_pmc_data, }, + { .compatible = "fsl,mpc8548-pmc", }, + { .compatible = "fsl,mpc8641d-pmc", }, + { }, +}; + +static struct of_platform_driver pmc_driver = { + .driver.name = "fsl-pmc", + .match_table = pmc_ids, + .probe = pmc_probe, +}; + +static int __init pmc_init(void) +{ + return of_register_platform_driver(&pmc_driver); +} +device_initcall(pmc_init);