diff options
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r-- | arch/powerpc/sysdev/Kconfig | 11 | ||||
-rw-r--r-- | arch/powerpc/sysdev/Makefile | 5 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_cpm.c | 636 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_cpm_asm.S | 800 | ||||
-rwxr-xr-x | arch/powerpc/sysdev/ppc4xx_msi.c | 408 | ||||
-rwxr-xr-x | arch/powerpc/sysdev/ppc4xx_msi.h | 65 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_ocm.c | 448 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_pci.c | 250 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_pci.h | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_soc.c | 15 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_suspend.c | 133 |
11 files changed, 2763 insertions, 10 deletions
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig index 396582835cb..d96a97ee7a4 100644 --- a/arch/powerpc/sysdev/Kconfig +++ b/arch/powerpc/sysdev/Kconfig @@ -12,3 +12,14 @@ config PPC_MSI_BITMAP depends on PCI_MSI default y if MPIC default y if FSL_PCI + default y if 4xx + +config DCU_ENABLE + bool "Enable L2 Data Cache" + depends on 460EX || 460SX || 440GX || APM82181 + help + Enable L-2 D-Cache + default n if 460EX + default y if 460SX + default y if 440GX + default y if APM82181 diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 9d4b17462f1..33b88b57d33 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -34,12 +34,14 @@ obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_I8259) += i8259.o obj-$(CONFIG_IPIC) += ipic.o obj-$(CONFIG_4xx) += uic.o +obj-$(CONFIG_PPC4xx_OCM) += ppc4xx_ocm.o obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o obj-$(CONFIG_OF_RTC) += of_rtc.o ifeq ($(CONFIG_PCI),y) obj-$(CONFIG_4xx) += ppc4xx_pci.o +obj-$(CONFIG_PCI_MSI) += ppc4xx_msi.o endif obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o @@ -53,6 +55,9 @@ obj-$(CONFIG_UCODE_PATCH) += micropatch.o obj-$(CONFIG_PPC_MPC512x) += mpc5xxx_clocks.o obj-$(CONFIG_PPC_MPC52xx) += mpc5xxx_clocks.o +obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o ppc4xx_cpm_asm.o + ifeq ($(CONFIG_SUSPEND),y) obj-$(CONFIG_6xx) += 6xx-suspend.o +obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_suspend.o endif diff --git a/arch/powerpc/sysdev/ppc4xx_cpm.c b/arch/powerpc/sysdev/ppc4xx_cpm.c new file mode 100644 index 00000000000..d0804f0d316 --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_cpm.c @@ -0,0 +1,636 @@ +/* + * arch/powerpc/sysdev/ppc4xx_cpm.c + * + * PowerPC 4xx Clock and Power Management + * + * (C) Copyright 2009, Applied Micro Circuits Corporation + * Victor Gallardo (vgallardo@amcc.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/proc_fs.h> +#include <linux/dma-mapping.h> +#include <asm/uaccess.h> +#include <asm/prom.h> +#include <asm/io.h> +#include <asm/dcr.h> +#include <asm/dcr-regs.h> +#include <asm/mmio-regs.h> +#include <asm/ppc4xx_cpm.h> +#include <asm/ppc4xx_ocm.h> + +#define CPM_PM_MEM_DATA_SIZE (256*sizeof(unsigned long)) +#define CPM_PM_MEM_ALIGN 16 + +#define CPM_ER 0x0 +#define CPM_FR 0x1 +#define CPM_SR 0x2 + +#define CPM_PCIE_SLEEP 0x00040000 +#define CPM_SATA0_SLEEP 0x00001000 + +extern void cpm_suspend_mem(int pm_mode, unsigned long *data, void *resume); +extern const long cpm_suspend_mem_size; + +extern void cpm_resume_mem(void); +extern const long cpm_resume_mem_size; + +struct cpm_pm_mem { + /* functions in OCM */ + void (*suspend)(int pm_mode, unsigned long *data, void *resume); + void (*resume )(void); + /* data OCM */ + unsigned long *data; +}; + +struct cpm_pm_iic { + phys_addr_t phys; + size_t size; +}; + +struct cpm_pm_emac { + phys_addr_t phys; + size_t size; +}; + +struct cpm { + unsigned int index; + unsigned int dcrbase; + unsigned int ready; + unsigned int save_er; + unsigned int save_fr; + unsigned int pm_cpu; + unsigned int pm_off; + unsigned int pm_doze; + unsigned int pm_nap; + unsigned int pm_deepsleep; + struct cpm_pm_iic pm_iic; + struct cpm_pm_emac pm_emac; +}; + +extern int pcie_used; + +//static struct proc_dir_entry * cpm_proc_entry; + +static struct cpm *cpm_nodes = NULL; +static int cpm_count = 0; + +static struct cpm_pm_mem cpm_pm_mem; + +static u32 dcrbase_l2c = 0; + +const char *cpm_mode_name(int mode) +{ + switch (mode) { + case CPM_PM_DOZE: + return "doze"; + case CPM_PM_NAP: + return "nap"; + case CPM_PM_DEEPSLEEP: + return "deepsleep"; + default: + BUG(); + } +} + +static struct cpm * get_cpm_node(unsigned int index) +{ + BUG_ON(index >= cpm_count); + + return &cpm_nodes[index]; +} + +static int cpm_enable(unsigned int index, unsigned int val) +{ + struct cpm *cpm = get_cpm_node(index); + + mtdcr(cpm->dcrbase + CPM_ER, + mfdcr(cpm->dcrbase + CPM_ER) | val); + + return 0; +} + +static int cpm_force_enable(unsigned int index, unsigned int val) +{ + struct cpm *cpm = get_cpm_node(index); + + mtdcr(cpm->dcrbase + CPM_FR, + mfdcr(cpm->dcrbase + CPM_FR) | val); + + while ((mfdcr(cpm->dcrbase + CPM_SR) & val) != val); + + return 0; +} + +static int cpm_save(unsigned int index) +{ + struct cpm *cpm = get_cpm_node(index); + + cpm->save_er = mfdcr(cpm->dcrbase + CPM_ER); + cpm->save_fr = mfdcr(cpm->dcrbase + CPM_FR); + + return 0; +} + +static int cpm_restore(unsigned int index) +{ + struct cpm *cpm = get_cpm_node(index); + + mtdcr(cpm->dcrbase + CPM_ER, cpm->save_er); + mtdcr(cpm->dcrbase + CPM_FR, cpm->save_fr); + + while ((mfdcr(cpm->dcrbase + CPM_SR) & cpm->save_fr) != cpm->save_fr); + + return 0; +} + +static int cpm_pm_enable(int pm_mode) +{ + unsigned int i; + unsigned int pm_val; + + for (i=0; i < cpm_count; i++) { + struct cpm *cpm = get_cpm_node(i); + + switch(pm_mode) { + case CPM_PM_DOZE: + pm_val = cpm->pm_doze; + break; + case CPM_PM_NAP: + pm_val = cpm->pm_nap; + break; + case CPM_PM_DEEPSLEEP: + pm_val = cpm->pm_deepsleep; + break; + default: + return -EINVAL; + } + + cpm_save(i); + cpm_enable(i, pm_val); + cpm_force_enable(i, pm_val); + } + + return 0; +} + +static int cpm_pm_disable(int pm_mode) +{ + unsigned int i; + + for (i=0; i < cpm_count; i++) { + switch(pm_mode) { + case CPM_PM_DOZE: + case CPM_PM_NAP: + case CPM_PM_DEEPSLEEP: + cpm_restore(i); + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static void cpm_flush_caches(void) +{ + struct page *page; + size_t size = PAGE_ALIGN(0x8000); + unsigned long order = get_order(size); + + /* Flush and invalidate L1 cache */ + page = alloc_pages(GFP_NOWAIT, order); + if (page) { + unsigned long kaddr = (unsigned long)page_address(page); + unsigned long endaddr = kaddr + size; + unsigned long addr; + + for (addr = kaddr; addr < endaddr; addr += L1_CACHE_BYTES) + asm ("dcbt 0,%0": :"r" (addr)); + asm ("sync"); + + for (addr = kaddr; addr < endaddr; addr += L1_CACHE_BYTES) + asm ("dcbf 0,%0": :"r" (addr)); + asm ("sync"); + asm ("isync"); + + __free_pages(page, order); + } + + if (dcrbase_l2c != 0) { + /* Invalidate L2 cache using the Hardware Clear Command */ + mtdcr(dcrbase_l2c + DCRN_L2C0_ADDR, 0); + mtdcr(dcrbase_l2c + DCRN_L2C0_CMD, L2C_CMD_HCC); + + /* Wait for Command Complete to set */ + while (!(mfdcr(dcrbase_l2c + DCRN_L2C0_SR) & L2C_SR_CC)) + ; + //printk(KERN_INFO "%s\n", __func__); + } +} + +static int cpm_pm_mem_idle(int pm_mode) +{ + void __iomem *iicp = NULL; + + if (pm_mode < CPM_PM_NAP) + return -EINVAL; + + if ( !cpm_pm_mem.suspend || !cpm_pm_mem.resume ||!cpm_pm_mem.data) + return -ENOMEM; + + printk(KERN_INFO "%s\n", __func__); + + if (pm_mode == CPM_PM_DEEPSLEEP) { + /* FIXME: Disable I2C interrupbecause it causes interrupt that wake up the system */ + struct cpm *cpm = get_cpm_node(0); + iicp = ioremap(cpm->pm_iic.phys, cpm->pm_iic.size); + cpm_pm_mem.data[CPM_PM_DATA_IIC_PTR] = (unsigned long)iicp; + } + + if (!iicp) { + printk(KERN_INFO "No iic\n"); + pm_mode = CPM_PM_NAP; + } else { + printk(KERN_INFO "%s: iic address = 0x%p\n", + __func__, iicp); + printk(KERN_INFO "%s: iic status = 0x%02x\n", + __func__, in_8(iicp+IIC_STS)); + } + /* call OCM code */ + cpm_pm_mem.suspend(pm_mode, cpm_pm_mem.data, cpm_pm_mem.resume); + printk(KERN_INFO "%s: wakeup\n", __func__); + + if (iicp) + iounmap(iicp); + + return 0; +} + +int cpm_pm_idle(int pm_mode) +{ + unsigned long tcr_save = 0; + unsigned long ccr0_save = 0; + int emac0_mr0; + void __iomem *emacp = NULL; + struct cpm *cpm; + + printk(KERN_INFO "%s\n", __func__); + + + cpm = get_cpm_node(0); + emacp = ioremap(cpm->pm_emac.phys, cpm->pm_emac.size); + if (emacp) { + emac0_mr0 = in_be32(emacp + 0); + /*printk(KERN_INFO "EMAC0_MR0 value before set WOL:0x%x\n", emac0_mr0);*/ + emac0_mr0 &= CPM_PM_DISABLE_EMAC0_MR0_RXE; + out_be32(emacp + 0, emac0_mr0); + emac0_mr0 = in_be32(emacp + 0); + while (!(emac0_mr0 & CPM_PM_EMAC0_MR0_RXI + && !(emac0_mr0 & CPM_PM_ENABLE_EMAC0_MR0_RXE))) { + emac0_mr0 = in_be32(emacp + 0); + } + emac0_mr0 |= CPM_PM_ENABLE_EMAC0_MR0_WKE; + out_be32(emacp + 0, emac0_mr0); + emac0_mr0 = in_be32(emacp + 0); + emac0_mr0 |= CPM_PM_ENABLE_EMAC0_MR0_RXE; + out_be32(emacp + 0, emac0_mr0); + /*printk(KERN_INFO "EMAC0_MR0 value after set WOL:0x%x\n", emac0_mr0);*/ + } + + ccr0_save = mfspr(SPRN_CCR0); + tcr_save = mfspr(SPRN_TCR); + mtspr(SPRN_TCR, tcr_save & ~TCR_DIE); + mtspr(SPRN_CCR0, ccr0_save | CCR0_DTB | CCR0_GDCBT); + + cpm_flush_caches(); + cpm_pm_enable(pm_mode); + + + if (cpm_pm_mem_idle(pm_mode) < 0) { + unsigned long msr_save; + + /* set wait state MSR */ + msr_save = mfmsr(); + mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE); + isync(); + /* return to initial state */ + mtmsr(msr_save); + isync(); + } + + cpm_pm_disable(pm_mode); + mtspr(SPRN_CCR0, ccr0_save); + mtspr(SPRN_TCR, tcr_save); + + if (emacp) { + emac0_mr0 = in_be32(emacp + 0); + emac0_mr0 &= CPM_PM_DISABLE_EMAC0_MR0_RXE; + out_be32(emacp + 0, emac0_mr0); + emac0_mr0 = in_be32(emacp + 0); + while (!(emac0_mr0 & CPM_PM_EMAC0_MR0_RXI + && !(emac0_mr0 & CPM_PM_ENABLE_EMAC0_MR0_RXE))) { + emac0_mr0 = in_be32(emacp + 0); + } + emac0_mr0 &= CPM_PM_DISABLE_EMAC0_MR0_WKE; + out_be32(emacp + 0, emac0_mr0); + emac0_mr0 = in_be32(emacp + 0); + emac0_mr0 |= CPM_PM_ENABLE_EMAC0_MR0_RXE; + out_be32(emacp + 0, emac0_mr0); + /*printk(KERN_INFO "EMAC0_MR0 value after disable WOL:0x%x\n", emac0_mr0);*/ + } + + printk(KERN_INFO "%s: wakeup\n", __func__); + + return 0; +} + + +int cpm_pm_suspend(suspend_state_t state, int suspend_mode) +{ + int pm_mode; + + switch (state) { + case PM_SUSPEND_STANDBY: + /* standby only support DOZE */ + pm_mode = CPM_PM_DOZE; + break; + case PM_SUSPEND_MEM: + if (suspend_mode < CPM_PM_NAP) + suspend_mode = CPM_PM_DOZE; + pm_mode = suspend_mode; + break; + default: + printk(KERN_INFO "%s: -EINVAL\n", __func__); + return -EINVAL; + } + + cpm_pm_idle(pm_mode); + + return 0; +} + +static void __init cpm_get_pm_emac(struct cpm *cpm, struct device_node *node) +{ + const phandle *phandle; + struct device_node *dev_node; + struct resource rsrc; + + phandle = of_get_property(node, "pm-emac-device", NULL); + if (!phandle) { + printk(KERN_INFO "CPM%d: pm-memory property not defined\n", + cpm->index); + return; + } + + dev_node = of_find_node_by_phandle(*phandle); + if (!dev_node) { + printk(KERN_ERR "CPM%d: Can't find pm-emac-device node\n", + cpm->index); + return; + } + + printk(KERN_INFO "CPM%d: pm-emac-device resource %s\n", + cpm->index, dev_node->full_name); + + if (of_address_to_resource(dev_node, 0, &rsrc)) { + printk(KERN_ERR "CPM%d: Can't get address to %s resource\n", + cpm->index, dev_node->full_name); + return; + } + + cpm->pm_emac.phys = rsrc.start; + cpm->pm_emac.size = rsrc.end - rsrc.start + 1; +} + +static void __init cpm_get_pm_iic(struct cpm *cpm, struct device_node *node) +{ + const phandle *phandle; + struct device_node *dev_node; + struct resource rsrc; + + phandle = of_get_property(node, "pm-iic-device", NULL); + if (!phandle) { + printk(KERN_INFO "CPM%d: pm-memory property not defined\n", + cpm->index); + return; + } + + dev_node = of_find_node_by_phandle(*phandle); + if (!dev_node) { + printk(KERN_ERR "CPM%d: Can't find pm-iic-device node\n", + cpm->index); + return; + } + + printk(KERN_INFO "CPM%d: pm-iic-device resource %s\n", + cpm->index, dev_node->full_name); + + if (of_address_to_resource(dev_node, 0, &rsrc)) { + printk(KERN_ERR "CPM%d: Can't get address to %s resource\n", + cpm->index, dev_node->full_name); + return; + } + + cpm->pm_iic.phys = rsrc.start; + cpm->pm_iic.size = rsrc.end - rsrc.start + 1; + + //printk(KERN_INFO "CPM%d: pm-iic-device address 0x%llx\n", + //cpm->index, cpm->pm_iic.phys); +} + +static void __init cpm_init_node(struct device_node *node) +{ + struct cpm *cpm; + const unsigned int *cell_index; + const unsigned int *dcr_reg; + const unsigned int *pm_cpu; + const unsigned int *pm_off; + const unsigned int *pm_doze; + const unsigned int *pm_nap; + const unsigned int *pm_deepsleep; + int len; + + cell_index = of_get_property(node, "cell-index", &len); + if (!cell_index) + BUG_ON("cpm: missing cell-index property"); + else if ((len != sizeof(unsigned int)) || *cell_index >= cpm_count) + BUG_ON("cpm: invalid cell-index property"); + else if (cpm_nodes[*cell_index].ready) + BUG_ON("cpm: duplicate cell-index property"); + + cpm = &cpm_nodes[*cell_index]; + cpm->index = *cell_index; + + dcr_reg = of_get_property(node, "dcr-reg", &len); + if (!dcr_reg || (len != 2*sizeof(unsigned int))) + BUG_ON("cpm: missing or invalid dcr-reg property"); + + cpm->dcrbase = *dcr_reg; + + pm_cpu = of_get_property(node, "pm-cpu", &len); + if (pm_cpu && (len == sizeof(unsigned int))) + cpm->pm_cpu = *pm_cpu; + + pm_off = of_get_property(node, "pm-off", &len); + if (pm_off && (len == sizeof(unsigned int))) + cpm->pm_off = *pm_off; + + pm_doze = of_get_property(node, "pm-doze", &len); + if (pm_doze && (len == sizeof(unsigned int))) + cpm->pm_doze = *pm_doze; + + pm_nap = of_get_property(node, "pm-nap", &len); + if (pm_nap && (len == sizeof(unsigned int))) + cpm->pm_nap = *pm_nap; + + pm_deepsleep = of_get_property(node, "pm-deepsleep", &len); + if (pm_deepsleep && (len == sizeof(unsigned int))) + cpm->pm_deepsleep = *pm_deepsleep; + + cpm_get_pm_iic(cpm, node); + cpm_get_pm_emac(cpm, node); + + printk (KERN_INFO "CPM%d: DCR at 0x%x\n", cpm->index, cpm->dcrbase); + + cpm->ready = 1; + + mtdcr(cpm->dcrbase + CPM_ER, + mfdcr(cpm->dcrbase + CPM_ER) | cpm->pm_off | cpm->pm_cpu); + mtdcr(cpm->dcrbase + CPM_FR, + mfdcr(cpm->dcrbase + CPM_FR) | cpm->pm_off); + + /* put unused IP into sleep */ + if (pcie_used == 0){ + mtdcr(cpm->dcrbase + CPM_ER, + mfdcr(cpm->dcrbase + CPM_ER) | CPM_PCIE_SLEEP); + mtdcr(cpm->dcrbase + CPM_FR, + mfdcr(cpm->dcrbase + CPM_FR) | CPM_PCIE_SLEEP); + } + else if (pcie_used == 1){ + mtdcr(cpm->dcrbase + CPM_ER, + mfdcr(cpm->dcrbase + CPM_ER) | CPM_SATA0_SLEEP); + mtdcr(cpm->dcrbase + CPM_FR, + mfdcr(cpm->dcrbase + CPM_FR) | CPM_SATA0_SLEEP); + } +} + + +static int __init cpm_pm_mem_init(void) +{ + struct cpm_pm_mem *mem; + phys_addr_t p; + + mem = &cpm_pm_mem; + + memset(mem,0,sizeof(struct cpm_pm_mem)); + + mem->suspend = ocm_alloc(&p,cpm_suspend_mem_size, CPM_PM_MEM_ALIGN, + + OCM_NON_CACHED, "cpm_suspend_mem"); + if (!mem->suspend) { + printk(KERN_ERR "CPM: failed to allocate suspend memory!\n"); + return -ENOMEM; + } + + mem->resume = ocm_alloc(&p,cpm_resume_mem_size, CPM_PM_MEM_ALIGN, + OCM_NON_CACHED, "cpm_resume_mem"); + if (!mem->resume) { + printk(KERN_ERR "CPM: failed to allocate resume memory!\n"); + ocm_free(mem->suspend); + mem->suspend = NULL; + return -ENOMEM; + } + + mem->data = ocm_alloc(&p,CPM_PM_MEM_DATA_SIZE, CPM_PM_MEM_ALIGN, + OCM_NON_CACHED, "cpm_data_mem"); + if (!mem->data) { + printk(KERN_ERR "CPM: failed to allocate data memory!\n"); + ocm_free(mem->suspend); + ocm_free(mem->resume); + mem->suspend = NULL; + mem->resume = NULL; + return -ENOMEM; + } + + printk(KERN_INFO "CPM: ocm suspend address 0x%p\n",mem->suspend); + printk(KERN_INFO "CPM: ocm resume address 0x%p\n",mem->resume); + printk(KERN_INFO "CPM: ocm data address 0x%p\n",mem->data); + + memcpy(mem->suspend, cpm_suspend_mem, cpm_suspend_mem_size); + memcpy(mem->resume, cpm_resume_mem, cpm_resume_mem_size); + memset(mem->data,0,CPM_PM_MEM_DATA_SIZE); + + return 0; +} + +static int __init cpm_init(void) +{ + struct device_node *np; + int count; + const u32 *dcrreg; + + count = 0; + for_each_compatible_node(np, NULL, "ibm,cpm") { + count++; + } +// printk(KERN_ERR "CPM: cpm_init %d!\n", count); + if (!count) { + return 0; + } + + cpm_nodes = kzalloc((count*sizeof(struct cpm)), GFP_KERNEL); + if (!cpm_nodes) { + printk(KERN_ERR "CPM: out of memory allocating CPM!\n"); + return -ENOMEM; + } + + cpm_count = count; + + for_each_compatible_node(np, NULL, "ibm,cpm") { + cpm_init_node(np); + } + + cpm_pm_mem_init(); + +// cpm_proc_init(); + + np = of_find_compatible_node(NULL, NULL, "ibm,l2-cache"); + if (!np) { + printk(KERN_ERR "Can't get l2-cache node\n"); + return 0; + } + + dcrreg = of_get_property(np, "dcr-reg", &count); + if (!dcrreg || (count != 4 * sizeof(u32))) { + printk(KERN_ERR "%s: Can't get DCR register base !", + np->full_name); + of_node_put(np); + return -ENODEV; + } + dcrbase_l2c = dcrreg[2]; + + return 0; +} + +subsys_initcall(cpm_init); diff --git a/arch/powerpc/sysdev/ppc4xx_cpm_asm.S b/arch/powerpc/sysdev/ppc4xx_cpm_asm.S new file mode 100644 index 00000000000..76e880d42b6 --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_cpm_asm.S @@ -0,0 +1,800 @@ +/* + * arch/powerpc/sysdev/ppc4xx_cpm_asm.S + * + * PowerPC 4xx Clock and Power Management + * + * (C) Copyright 2009, Applied Micro Circuits Corporation + * Victor Gallardo (vgallardo@amcc.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <asm/reg.h> +#include <asm/dcr-regs.h> +#include <asm/mmio-regs.h> +#include <asm/ppc_asm.h> +#include <asm/ppc4xx_cpm.h> + +/* + * NOTE: for the following macros + * r6 overwritten, r4 is data pointer + */ +#define SAVE_SPR(addr,reg) \ + mfspr r6, reg; \ + stw r6, ((addr)*4)(r4); + +#define RESTORE_SPR(addr, reg) \ + lwz r6, ((addr)*4)(r4); \ + mtspr reg, r6; + +#define SAVE_DCR(addr, reg) \ + mfdcr r6, reg; \ + stw r6, ((addr)*4)(r4); + +#define RESTORE_DCR(addr, reg) \ + lwz r6, ((addr)*4)(r4); \ + mtdcr reg, r6; + +#define SAVE_IDCR(addr, base, reg) \ + li r6, reg; \ + mtdcr DCRN_ ## base ## _CONFIG_ADDR, r6; \ + mfdcr r6, DCRN_ ## base ## _CONFIG_DATA; \ + stw r6, ((addr)*4)(r4); + +#define RESTORE_IDCR(addr, base, reg) \ + li r6, reg; \ + mtdcr DCRN_ ## base ## _CONFIG_ADDR, r6; \ + lwz r6, ((addr)*4)(r4); \ + mtdcr DCRN_ ## base ## _CONFIG_DATA, r6; + +/* + * NOTE: for the following macros + * r6 overwritten, r7 has value + */ +#define GET_IDCR(base, reg) \ + li r6, reg; \ + mtdcr DCRN_ ## base ## _CONFIG_ADDR, r6; \ + mfdcr r7, DCRN_ ## base ## _CONFIG_DATA; + +#define SET_IDCR(base, reg) \ + li r6, reg; \ + mtdcr DCRN_ ## base ## _CONFIG_ADDR, r6; \ + mtdcr DCRN_ ## base ## _CONFIG_DATA, r7; + + +/* + * NOTE: helper function to get saved data offset + */ +#define GET_DATA(addr) ((addr)*4)(r4); + +.text + +/* + * cpm_suspend_mem(int pm_mode, unsigned long *data, void *resume) + * + * NOTE: r3 => pm_mode + * r4 => data pointer + * r5 => resume + */ +_GLOBAL(cpm_suspend_mem) + + /* save off current interrupt vector base and extint offset */ + SAVE_SPR(0, SPRN_IVPR) + SAVE_SPR(1, SPRN_IVOR4) + + /* establish new interrupt vector base and extint offset */ + mtspr SPRN_IVPR,r5 + mtspr SPRN_IVOR4,r5 + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) \ + || defined(CONFIG_APM82181) || defined(CONFIG_APM82161) + +#if defined(CONFIG_APM82181) || defined(CONFIG_APM82161) + + SAVE_IDCR(2, SDRAM0, SDRAM0_BESR) + SAVE_IDCR(3, SDRAM0, SDRAM0_BEARL) + SAVE_IDCR(4, SDRAM0, SDRAM0_BEARH) + SAVE_IDCR(5, SDRAM0, SDRAM0_WMIRQ) + SAVE_IDCR(6, SDRAM0, SDRAM0_PLBOPT) + SAVE_IDCR(7, SDRAM0, SDRAM0_PUABA) + + + SAVE_IDCR(11, SDRAM0, SDRAM0_MCOPT2) + SAVE_IDCR(12, SDRAM0, SDRAM0_MCOPT1) + SAVE_IDCR(13, SDRAM0, SDRAM0_MODT0) + SAVE_IDCR(14, SDRAM0, SDRAM0_MODT1) + SAVE_IDCR(17, SDRAM0, SDRAM0_CODT) + SAVE_IDCR(18, SDRAM0, SDRAM0_RTR) + SAVE_IDCR(19, SDRAM0, SDRAM0_MB0CF) + SAVE_IDCR(20, SDRAM0, SDRAM0_MB1CF) + + SAVE_IDCR(23, SDRAM0, SDRAM0_INITPLR0) + SAVE_IDCR(24, SDRAM0, SDRAM0_INITPLR1) + SAVE_IDCR(25, SDRAM0, SDRAM0_INITPLR2) + SAVE_IDCR(26, SDRAM0, SDRAM0_INITPLR3) + SAVE_IDCR(27, SDRAM0, SDRAM0_INITPLR4) + SAVE_IDCR(28, SDRAM0, SDRAM0_INITPLR5) + SAVE_IDCR(29, SDRAM0, SDRAM0_INITPLR6) + SAVE_IDCR(30, SDRAM0, SDRAM0_INITPLR7) + SAVE_IDCR(31, SDRAM0, SDRAM0_INITPLR8) + SAVE_IDCR(32, SDRAM0, SDRAM0_INITPLR9) + SAVE_IDCR(33, SDRAM0, SDRAM0_INITPLR10) + SAVE_IDCR(34, SDRAM0, SDRAM0_INITPLR11) + SAVE_IDCR(35, SDRAM0, SDRAM0_INITPLR12) + SAVE_IDCR(36, SDRAM0, SDRAM0_INITPLR13) + SAVE_IDCR(37, SDRAM0, SDRAM0_INITPLR14) + SAVE_IDCR(38, SDRAM0, SDRAM0_INITPLR15) + + SAVE_IDCR(39, SDRAM0, SDRAM0_RQDC) + SAVE_IDCR(40, SDRAM0, SDRAM0_RFDC) + SAVE_IDCR(41, SDRAM0, SDRAM0_RDCC) + SAVE_IDCR(42, SDRAM0, SDRAM0_DLCR) + SAVE_IDCR(43, SDRAM0, SDRAM0_CLKTR) + SAVE_IDCR(44, SDRAM0, SDRAM0_WRDTR) + SAVE_IDCR(45, SDRAM0, SDRAM0_SDTR1) + SAVE_IDCR(46, SDRAM0, SDRAM0_SDTR2) + SAVE_IDCR(47, SDRAM0, SDRAM0_SDTR3) + SAVE_IDCR(48, SDRAM0, SDRAM0_MMODE) + SAVE_IDCR(49, SDRAM0, SDRAM0_MEMODE) + SAVE_IDCR(50, SDRAM0, SDRAM0_ECCES) + + /* save off some CPR registers */ + SAVE_IDCR(51, CPR0, CPR0_PLB2D) + SAVE_IDCR(52, CPR0, CPR0_OPBD) + SAVE_IDCR(53, CPR0, CPR0_DDR2D) + +#else + /* save off Memory Queue registers */ + SAVE_DCR(2, MQ0_B0BASE) + SAVE_DCR(3, MQ0_B1BASE) + SAVE_DCR(4, MQ0_B2BASE) + SAVE_DCR(5, MQ0_B3BASE) + SAVE_DCR(6, MQ0_CF1H) + SAVE_DCR(7, MQ0_BAUL) + SAVE_DCR(8, MQ0_CF1L) + SAVE_DCR(9, MQ0_CFBHL) + SAVE_DCR(10, MQ0_BAUH) + + /* save off DDR Controller registers */ + SAVE_IDCR(11, MCIF0, MCIF0_MCOPT2) + SAVE_IDCR(12, MCIF0, MCIF0_MCOPT1) + SAVE_IDCR(13, MCIF0, MCIF0_MODT0) + SAVE_IDCR(14, MCIF0, MCIF0_MODT1) + SAVE_IDCR(15, MCIF0, MCIF0_MODT2) + SAVE_IDCR(16, MCIF0, MCIF0_MODT3) + SAVE_IDCR(17, MCIF0, MCIF0_CODT) + SAVE_IDCR(18, MCIF0, MCIF0_RTR) + SAVE_IDCR(19, MCIF0, MCIF0_MB0CF) + SAVE_IDCR(20, MCIF0, MCIF0_MB1CF) + SAVE_IDCR(21, MCIF0, MCIF0_MB2CF) + SAVE_IDCR(22, MCIF0, MCIF0_MB3CF) + + SAVE_IDCR(23, MCIF0, MCIF0_INITPLR0) + SAVE_IDCR(24, MCIF0, MCIF0_INITPLR1) + SAVE_IDCR(25, MCIF0, MCIF0_INITPLR2) + SAVE_IDCR(26, MCIF0, MCIF0_INITPLR3) + SAVE_IDCR(27, MCIF0, MCIF0_INITPLR4) + SAVE_IDCR(28, MCIF0, MCIF0_INITPLR5) + SAVE_IDCR(29, MCIF0, MCIF0_INITPLR6) + SAVE_IDCR(30, MCIF0, MCIF0_INITPLR7) + SAVE_IDCR(31, MCIF0, MCIF0_INITPLR8) + SAVE_IDCR(32, MCIF0, MCIF0_INITPLR9) + SAVE_IDCR(33, MCIF0, MCIF0_INITPLR10) + SAVE_IDCR(34, MCIF0, MCIF0_INITPLR11) + SAVE_IDCR(35, MCIF0, MCIF0_INITPLR12) + SAVE_IDCR(36, MCIF0, MCIF0_INITPLR13) + SAVE_IDCR(37, MCIF0, MCIF0_INITPLR14) + SAVE_IDCR(38, MCIF0, MCIF0_INITPLR15) + + SAVE_IDCR(39, MCIF0, MCIF0_RQDC) + SAVE_IDCR(40, MCIF0, MCIF0_RFDC) + SAVE_IDCR(41, MCIF0, MCIF0_RCDC) + SAVE_IDCR(42, MCIF0, MCIF0_DLCR) + SAVE_IDCR(43, MCIF0, MCIF0_CLKTR) + SAVE_IDCR(44, MCIF0, MCIF0_WRDTR) + SAVE_IDCR(45, MCIF0, MCIF0_SDTR1) + SAVE_IDCR(46, MCIF0, MCIF0_SDTR2) + SAVE_IDCR(47, MCIF0, MCIF0_SDTR3) + SAVE_IDCR(48, MCIF0, MCIF0_MMODE) + SAVE_IDCR(49, MCIF0, MCIF0_MEMODE) + SAVE_IDCR(50, MCIF0, MCIF0_ECCES) + + /* save off some CPR registers */ + SAVE_IDCR(51, CPR0, CPR0_PLBED) + SAVE_IDCR(52, CPR0, CPR0_OPBD) + SAVE_IDCR(53, CPR0, CPR0_AHBD) +#endif /*defined(CONFIG_APM82181) || defined(CONFIG_APM82161)*/ + + + +#if defined(CONFIG_APM82181) || defined(CONFIG_APM82161) + /* Put DDR in self refresh */ + GET_IDCR(SDRAM0, SDRAM0_MCOPT2) + lis r6, SDRAM0_MCOPT2_SREN@h + ori r6, r6, SDRAM0_MCOPT2_SREN@l + or r7, r7, r6 + SET_IDCR(SDRAM0, SDRAM0_MCOPT2) +chk_self_refresh: + GET_IDCR(SDRAM0, SDRAM0_MCSTAT) + lis r6, SDRAM0_MCSTAT_SRMS@h + ori r6, r6, SDRAM0_MCSTAT_SRMS@l + and r7, r7, r6 + cmpwi r7, 0 + beq chk_self_refresh +#else + /* Put DDR in self refresh */ + GET_IDCR(MCIF0, MCIF0_MCOPT2) + lis r6, MCIF0_MCOPT2_SREN@h + ori r6, r6, MCIF0_MCOPT2_SREN@l + or r7, r7, r6 + SET_IDCR(MCIF0, MCIF0_MCOPT2) +chk_self_refresh: + GET_IDCR(MCIF0, MCIF0_MCSTAT) + lis r6, MCIF0_MCSTAT_SRMS@h + ori r6, r6, MCIF0_MCSTAT_SRMS@l + and r7, r7, r6 + cmpwi r7, 0 + beq chk_self_refresh +#endif /*defined(CONFIG_APM82181) || defined(CONFIG_APM82161)*/ + + /* Put DDR and L2C in soft reset */ + GET_IDCR(SDR0, SDR0_SRST0) + lis r6, (SDR0_SRST0_DMC | SDR0_SRST0_L2C)@h + ori r6, r6, (SDR0_SRST0_DMC | SDR0_SRST0_L2C)@l + or r7, r7, r6 + SET_IDCR(SDR0, SDR0_SRST0) + + /* Put PLL in bypass mode */ + GET_IDCR(CPR0, CPR0_PLLC) + lis r6, ~CPR0_PLLC_ENG@h + ori r6, r6, ~CPR0_PLLC_ENG@l + and r7, r7, r6 + SET_IDCR(CPR0, CPR0_PLLC) + + /* Configure OPB and EBC clock to same freq as PLB clock - 33MHz */ +#if defined(CONFIG_APM82181) || defined(CONFIG_APM82161) + lis r7, CPR0_PLB2D_DIV1@h + ori r7, r7, CPR0_PLB2D_DIV1@l + SET_IDCR(CPR0, CPR0_PLB2D) + lis r7, CPR0_DDR2D_DIV1@h + ori r7, r7, CPR0_DDR2D_DIV1@l + SET_IDCR(CPR0, CPR0_DDR2D) + lis r7, CPR0_OPBD_DIV1@h + ori r7, r7, CPR0_OPBD_DIV1@l + SET_IDCR(CPR0, CPR0_OPBD) +#else + lis r7, CPR0_PLBED_DIV2@h + ori r7, r7, CPR0_PLBED_DIV2@l + SET_IDCR(CPR0, CPR0_PLBED) + lis r7, CPR0_OPBD_DIV1@h + ori r7, r7, CPR0_OPBD_DIV1@l + SET_IDCR(CPR0, CPR0_OPBD) + lis r7, CPR0_AHBD_DIV1@h + ori r7, r7, CPR0_AHBD_DIV1@l + SET_IDCR(CPR0, CPR0_AHBD) +#endif + /* Update PLL now */ + lis r7, CPR0_CLKUPD_CUD@h + ori r7, r7, CPR0_CLKUPD_CUD@l + SET_IDCR(CPR0, CPR0_CLKUPD) + +#endif /* defined(CONFIG_460EX) || defined(CONFIG_460GT) || defined(CONFIG_APM82181)\ + defined(CONFIG_APM82161)*/ + + /* Reduce voltage from 1.2v to 1.0v (in steps of 50mv) */ + cmpwi r3, CPM_PM_DEEPSLEEP + bne cpm_suspend_mem_wait + +#if defined(CONFIG_CPM_PM_AD5243) + /* IIC voltage adjust*/ + lwz r7, GET_DATA(CPM_PM_DATA_IIC_PTR) + li r19,6 +voltage_config: + cmpwi r19,6 + bne voltage_config_1 + li r18,CPM_PM_AD5243_1_250V + b voltage_init +voltage_config_1: + cmpwi r19,5 + bne voltage_config_2 + li r18,CPM_PM_AD5243_1_200V + b voltage_init +voltage_config_2: + cmpwi r19,4 + bne voltage_config_3 + li r18,CPM_PM_AD5243_1_150V + b voltage_init +voltage_config_3: + cmpwi r19,3 + bne voltage_config_4 + li r18,CPM_PM_AD5243_1_100V + b voltage_init +voltage_config_4: + cmpwi r19,2 + bne voltage_config_5 + li r18,CPM_PM_AD5243_1_050V + b voltage_init +voltage_config_5: + cmpwi r19,1 + bne voltage_exit + li r18,CPM_PM_AD5243_1_000V + +voltage_init: + li r14,IIC_STS_SCMP + stb r14,IIC_STS(r7) + eieio + + li r15,12 +voltage_init_1: + sync + lbz r14,IIC_STS(r7) + isync + andi. r14,r14,IIC_STS_PT + cmpwi r14,IIC_STS_PT + bne voltage_init_2 + addi r15,r15,-1 + cmpwi r15,0 + beq voltage_end + b voltage_init_1 +voltage_init_2: + sync + lbz r14,IIC_MDCNTL(r7) + isync + ori r14,r14,(IIC_MDCNTL_FMDB|IIC_MDCNTL_FSDB) + stb r14,IIC_MDCNTL(r7) + eieio + + li r14,0 + stb r14,IIC_HMADR(r7) + eieio + li r14,CPM_PM_AD5243_ADDR + stb r14,IIC_LMADR(r7) + eieio + + li r14,1 + stb r14,IIC_MDBUF(r7) + eieio + li r14,(IIC_CNTL_PT|IIC_CNTL_CHT) + stb r14,IIC_CNTL(r7) + eieio + + li r15,40 +voltage_init_3: + li r17,256 +voltage_init_3_delay: + addi r17,r17,-1 + cmpwi r17,0 + bne voltage_init_3_delay + sync + lbz r14,IIC_STS(r7) + isync + andi. r16,r14,IIC_STS_PT + cmpwi r16,IIC_STS_PT + bne voltage_init_4 + andi. r16,r14,IIC_STS_ERR + cmpwi r16,IIC_STS_ERR + beq voltage_end + addi r15,r15,-1 + cmpwi r15,0 + bne voltage_init_3 +voltage_init_4: + stb r18,IIC_MDBUF(r7) + eieio + li r14,IIC_CNTL_PT + stb r14,IIC_CNTL(r7) + eieio + + li r15,40 +voltage_init_5: + li r17,256 +voltage_init_5_delay: + addi r17,r17,-1 + cmpwi r17,0 + bne voltage_init_5_delay + sync + lbz r14,IIC_STS(r7) + isync + andi. r16,r14,IIC_STS_PT + cmpwi r16,IIC_STS_PT + bne voltage_end + andi. r16,r14,IIC_STS_ERR + cmpwi r16,IIC_STS_ERR + beq voltage_end + addi r15,r15,-1 + cmpwi r15,0 + bne voltage_init_5 +voltage_end: + addi r19,r19,-1 + b vol |