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 voltage_config +voltage_exit: + /* End of voltage adjust*/ +#endif /* defined(CONFIG_CPM_PM_AD5243) */ + +cpm_suspend_mem_wait: + + + /* Put processor into Wait State */ + mfmsr r7 + oris r6,r7,(MSR_WE|MSR_EE|MSR_CE|MSR_DE)@h + ori r6,r6,(MSR_WE|MSR_EE|MSR_CE|MSR_DE)@l + mtmsr r6 + sync + isync + mtmsr r7 + sync + isync + /* return from SRAM/OCM code */ + + blr + +_GLOBAL(cpm_suspend_mem_size) + .long $-cpm_suspend_mem + +/* + * cpm_resume_mem(void) + * + * NOTE: r3 => pm_mode + * r4 => data pointer + */ +_GLOBAL(cpm_resume_mem) + /* save off some registers */ + mtsprg 0,r5 + mtsprg 1,r6 + mtsprg 2,r7 + + + /* Restore voltage */ + cmpwi r3, CPM_PM_DEEPSLEEP + bne cpm_pm_mem_extint_restore + +#if defined(CONFIG_CPM_PM_AD5243) + /* IIC voltage adjust*/ + lwz r7, GET_DATA(CPM_PM_DATA_IIC_PTR) + li r19,6 +ervoltage_config: + cmpwi r19,6 + bne ervoltage_config_1 + li r18,CPM_PM_AD5243_1_050V + b ervoltage_init +ervoltage_config_1: + cmpwi r19,5 + bne ervoltage_config_2 + li r18,CPM_PM_AD5243_1_100V + b ervoltage_init +ervoltage_config_2: + cmpwi r19,4 + bne ervoltage_config_3 + li r18,CPM_PM_AD5243_1_150V + b ervoltage_init +ervoltage_config_3: + cmpwi r19,3 + bne ervoltage_config_4 + li r18,CPM_PM_AD5243_1_200V + b ervoltage_init +ervoltage_config_4: + cmpwi r19,2 + bne ervoltage_config_5 + li r18,CPM_PM_AD5243_1_250V + b ervoltage_init +ervoltage_config_5: + cmpwi r19,1 + bne ervoltage_exit + li r18,CPM_PM_AD5243_1_290V + +ervoltage_init: + li r14,IIC_STS_SCMP + stb r14,IIC_STS(r7) + eieio + + li r15,12 +ervoltage_init_1: + sync + lbz r14,IIC_STS(r7) + isync + andi. r14,r14,IIC_STS_PT + cmpwi r14,IIC_STS_PT + bne ervoltage_init_2 + addi r15,r15,-1 + cmpwi r15,0 + beq ervoltage_end + b ervoltage_init_1 +ervoltage_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 +ervoltage_init_3: + li r17,256 +ervoltage_init_3_delay: + addi r17,r17,-1 + cmpwi r17,0 + bne ervoltage_init_3_delay + sync + lbz r14,IIC_STS(r7) + isync + andi. r16,r14,IIC_STS_PT + cmpwi r16,IIC_STS_PT + bne ervoltage_init_4 + andi. r16,r14,IIC_STS_ERR + cmpwi r16,IIC_STS_ERR + beq ervoltage_end + addi r15,r15,-1 + cmpwi r15,0 + bne ervoltage_init_3 +ervoltage_init_4: + stb r18,IIC_MDBUF(r7) + eieio + li r14,IIC_CNTL_PT + stb r14,IIC_CNTL(r7) + eieio + + li r15,40 +ervoltage_init_5: + li r17,256 +ervoltage_init_5_delay: + addi r17,r17,-1 + cmpwi r17,0 + bne ervoltage_init_5_delay + sync + lbz r14,IIC_STS(r7) + isync + andi. r16,r14,IIC_STS_PT + cmpwi r16,IIC_STS_PT + bne ervoltage_end + andi. r16,r14,IIC_STS_ERR + cmpwi r16,IIC_STS_ERR + beq ervoltage_end + addi r15,r15,-1 + cmpwi r15,0 + bne ervoltage_init_5 +ervoltage_end: + addi r19,r19,-1 + b ervoltage_config +ervoltage_exit: + /* End of voltage adjust*/ +#endif /* defined(CONFIG_CPM_PM_AD5243) */ + +cpm_pm_mem_extint_restore: + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) || defined(CONFIG_APM82181)\ +|| defined(CONFIG_APM82161) + +#if defined(CONFIG_APM82181) || defined(CONFIG_APM82161) + /* Configure OPB and EBC clock to same freq as PLB clock - normal */ + RESTORE_IDCR(51, CPR0, CPR0_PLB2D) + RESTORE_IDCR(52, CPR0, CPR0_OPBD) + RESTORE_IDCR(53, CPR0, CPR0_DDR2D) +#else + /* Configure OPB and EBC clock to same freq as PLB clock - normal */ + RESTORE_IDCR(51, CPR0, CPR0_PLBED) + RESTORE_IDCR(52, CPR0, CPR0_OPBD) + RESTORE_IDCR(53, CPR0, CPR0_AHBD) +#endif /* defined(CONFIG_APM82181) || defined(CONFIG_APM82161) */ + /* Put PLL in normal mode */ + GET_IDCR(CPR0, CPR0_PLLC) + lis r6, CPR0_PLLC_ENG@h + ori r6, r6, CPR0_PLLC_ENG@l + or r7, r7, r6 + SET_IDCR(CPR0, CPR0_PLLC) + + /* Update PLL now */ + lis r7, CPR0_CLKUPD_CUD@h + ori r7, r7, CPR0_CLKUPD_CUD@l + SET_IDCR(CPR0, CPR0_CLKUPD) + + + /* Take DDR and L2C out of 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 + and r7, r7, r6 + SET_IDCR(SDR0, SDR0_SRST0) + +#if defined(CONFIG_APM82181) || defined(CONFIG_APM82161) + /* restore DDR Controller registers */ + RESTORE_IDCR(12, SDRAM0, SDRAM0_MCOPT1) + RESTORE_IDCR(17, SDRAM0, SDRAM0_CODT) + RESTORE_IDCR(13, SDRAM0, SDRAM0_MODT0) + RESTORE_IDCR(14, SDRAM0, SDRAM0_MODT1) + RESTORE_IDCR(18, SDRAM0, SDRAM0_RTR) + RESTORE_IDCR(48, SDRAM0, SDRAM0_MMODE) + RESTORE_IDCR(44, SDRAM0, SDRAM0_WRDTR) + RESTORE_IDCR(43, SDRAM0, SDRAM0_CLKTR) + RESTORE_IDCR(19, SDRAM0, SDRAM0_MB0CF) + RESTORE_IDCR(20, SDRAM0, SDRAM0_MB1CF) + RESTORE_IDCR(45, SDRAM0, SDRAM0_SDTR1) + RESTORE_IDCR(46, SDRAM0, SDRAM0_SDTR2) + RESTORE_IDCR(47, SDRAM0, SDRAM0_SDTR3) + RESTORE_IDCR(49, SDRAM0, SDRAM0_MEMODE) + RESTORE_IDCR(50, SDRAM0, SDRAM0_ECCES) + RESTORE_IDCR(23, SDRAM0, SDRAM0_INITPLR0) + RESTORE_IDCR(24, SDRAM0, SDRAM0_INITPLR1) + RESTORE_IDCR(25, SDRAM0, SDRAM0_INITPLR2) + RESTORE_IDCR(26, SDRAM0, SDRAM0_INITPLR3) + RESTORE_IDCR(27, SDRAM0, SDRAM0_INITPLR4) + RESTORE_IDCR(28, SDRAM0, SDRAM0_INITPLR5) + RESTORE_IDCR(29, SDRAM0, SDRAM0_INITPLR6) + RESTORE_IDCR(30, SDRAM0, SDRAM0_INITPLR7) + RESTORE_IDCR(31, SDRAM0, SDRAM0_INITPLR8) + RESTORE_IDCR(32, SDRAM0, SDRAM0_INITPLR9) + RESTORE_IDCR(33, SDRAM0, SDRAM0_INITPLR10) + RESTORE_IDCR(34, SDRAM0, SDRAM0_INITPLR11) + RESTORE_IDCR(35, SDRAM0, SDRAM0_INITPLR12) + RESTORE_IDCR(36, SDRAM0, SDRAM0_INITPLR13) + RESTORE_IDCR(37, SDRAM0, SDRAM0_INITPLR14) + RESTORE_IDCR(38, SDRAM0, SDRAM0_INITPLR15) + + RESTORE_IDCR(2, SDRAM0, SDRAM0_BESR) + RESTORE_IDCR(3, SDRAM0, SDRAM0_BEARL) + RESTORE_IDCR(4, SDRAM0, SDRAM0_BEARH) + RESTORE_IDCR(5, SDRAM0, SDRAM0_WMIRQ) + RESTORE_IDCR(6, SDRAM0, SDRAM0_PLBOPT) + RESTORE_IDCR(7, SDRAM0, SDRAM0_PUABA) + + + /* take DDR out of self refresh */ + lis r7, SDRAM0_MCOPT2_IPTR@h + ori r7, r7, SDRAM0_MCOPT2_IPTR@l + SET_IDCR(SDRAM0, SDRAM0_MCOPT2) +ewait_proload_seq: + GET_IDCR(SDRAM0, SDRAM0_MCSTAT) + lis r6, SDRAM0_MCSTAT_MIC@h + ori r6, r6, SDRAM0_MCSTAT_MIC@l + and r7, r7, r6 + cmpwi r7, 0 + beq ewait_proload_seq +echk_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 + bne echk_self_refresh + + /* Enable DDR controller */ + GET_IDCR(SDRAM0, SDRAM0_MCOPT2) + lis r6, SDRAM0_MCOPT2_DCEN@h + ori r6, r6, SDRAM0_MCOPT2_DCEN@l + or r7, r7, r6 + SET_IDCR(SDRAM0, SDRAM0_MCOPT2) + + /* Restore FQDC, RFDC, and RCDC */ + RESTORE_IDCR(39, SDRAM0, SDRAM0_RQDC) + RESTORE_IDCR(40, SDRAM0, SDRAM0_RFDC) + RESTORE_IDCR(41, SDRAM0, SDRAM0_RDCC) + RESTORE_IDCR(42, SDRAM0, SDRAM0_DLCR) + +#else + /* restore DDR Controller registers */ + RESTORE_IDCR(12, MCIF0, MCIF0_MCOPT1) + RESTORE_IDCR(17, MCIF0, MCIF0_CODT) + RESTORE_IDCR(13, MCIF0, MCIF0_MODT0) + RESTORE_IDCR(14, MCIF0, MCIF0_MODT1) + RESTORE_IDCR(15, MCIF0, MCIF0_MODT2) + RESTORE_IDCR(16, MCIF0, MCIF0_MODT3) + RESTORE_IDCR(18, MCIF0, MCIF0_RTR) + RESTORE_IDCR(48, MCIF0, MCIF0_MMODE) + RESTORE_IDCR(44, MCIF0, MCIF0_WRDTR) + RESTORE_IDCR(43, MCIF0, MCIF0_CLKTR) + RESTORE_IDCR(19, MCIF0, MCIF0_MB0CF) + RESTORE_IDCR(20, MCIF0, MCIF0_MB1CF) + RESTORE_IDCR(21, MCIF0, MCIF0_MB2CF) + RESTORE_IDCR(22, MCIF0, MCIF0_MB3CF) + RESTORE_IDCR(45, MCIF0, MCIF0_SDTR1) + RESTORE_IDCR(46, MCIF0, MCIF0_SDTR2) + RESTORE_IDCR(47, MCIF0, MCIF0_SDTR3) + RESTORE_IDCR(49, MCIF0, MCIF0_MEMODE) + RESTORE_IDCR(50, MCIF0, MCIF0_ECCES) + RESTORE_IDCR(23, MCIF0, MCIF0_INITPLR0) + RESTORE_IDCR(24, MCIF0, MCIF0_INITPLR1) + RESTORE_IDCR(25, MCIF0, MCIF0_INITPLR2) + RESTORE_IDCR(26, MCIF0, MCIF0_INITPLR3) + RESTORE_IDCR(27, MCIF0, MCIF0_INITPLR4) + RESTORE_IDCR(28, MCIF0, MCIF0_INITPLR5) + RESTORE_IDCR(29, MCIF0, MCIF0_INITPLR6) + RESTORE_IDCR(30, MCIF0, MCIF0_INITPLR7) + RESTORE_IDCR(31, MCIF0, MCIF0_INITPLR8) + RESTORE_IDCR(32, MCIF0, MCIF0_INITPLR9) + RESTORE_IDCR(33, MCIF0, MCIF0_INITPLR10) + RESTORE_IDCR(34, MCIF0, MCIF0_INITPLR11) + RESTORE_IDCR(35, MCIF0, MCIF0_INITPLR12) + RESTORE_IDCR(36, MCIF0, MCIF0_INITPLR13) + RESTORE_IDCR(37, MCIF0, MCIF0_INITPLR14) + RESTORE_IDCR(38, MCIF0, MCIF0_INITPLR15) + + /* restore Memory Queue registers */ + RESTORE_DCR(2, MQ0_B0BASE) + RESTORE_DCR(3, MQ0_B1BASE) + RESTORE_DCR(4, MQ0_B2BASE) + RESTORE_DCR(5, MQ0_B3BASE) + RESTORE_DCR(6, MQ0_CF1H) + RESTORE_DCR(7, MQ0_BAUL) + RESTORE_DCR(8, MQ0_CF1L) + RESTORE_DCR(9, MQ0_CFBHL) + RESTORE_DCR(10, MQ0_BAUH) + + /* take DDR out of self refresh */ + lis r7, MCIF0_MCOPT2_IPTR@h + ori r7, r7, MCIF0_MCOPT2_IPTR@l + SET_IDCR(MCIF0, MCIF0_MCOPT2) +ewait_proload_seq: + GET_IDCR(MCIF0, MCIF0_MCSTAT) + lis r6, MCIF0_MCSTAT_MIC@h + ori r6, r6, MCIF0_MCSTAT_MIC@l + and r7, r7, r6 + cmpwi r7, 0 + beq ewait_proload_seq +echk_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 + bne echk_self_refresh + + /* Enable DDR controller */ + GET_IDCR(MCIF0, MCIF0_MCOPT2) + lis r6, MCIF0_MCOPT2_DCEN@h + ori r6, r6, MCIF0_MCOPT2_DCEN@l + or r7, r7, r6 + SET_IDCR(MCIF0, MCIF0_MCOPT2) + + /* Restore FQDC, RFDC, and RCDC */ + RESTORE_IDCR(39, MCIF0, MCIF0_RQDC) + RESTORE_IDCR(40, MCIF0, MCIF0_RFDC) + RESTORE_IDCR(41, MCIF0, MCIF0_RCDC) + RESTORE_IDCR(42, MCIF0, MCIF0_DLCR) +#endif /*defined(CONFIG_APM82181) || defined(CONFIG_APM82161)*/ + +#endif /* defined(CONFIG_460EX) || defined(CONFIG_460GT) || defined(CONFIG_APM82181) \ + defined(CONFIG_APM82161)*/ + + /* restored saved off interrupt vector base and extint offset */ + RESTORE_SPR(0, SPRN_IVPR) + RESTORE_SPR(1, SPRN_IVOR4) + + /* restore saved off registers */ + mfsprg r5,0 + mfsprg r6,1 + mfsprg r7,2 + + /* return from interrupt */ + rfi + +_GLOBAL(cpm_resume_mem_size) + .long $-cpm_resume_mem + diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c new file mode 100755 index 00000000000..8bec6fff4ce --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_msi.c @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2009 Applied Micro Circuits corporation. + * + * Author: Feng Kan <fkan@amcc.com> + * Tirumala Marri <tmarri@amcc.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; version 2 of the + * License. + */ +#include <linux/irq.h> +#include <linux/bootmem.h> +#include <linux/pci.h> +#include <linux/msi.h> +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <asm/prom.h> +#include <asm/hw_irq.h> +#include <asm/ppc-pci.h> +#include <asm/dcr.h> +#include <asm/dcr-regs.h> +#include <linux/proc_fs.h> +#include <linux/uaccess.h> +#include <linux/delay.h> +#include "ppc4xx_msi.h" + +#define MSI_DEBUG 0 + +#define MSI_PROC_ROOT "driver/msi" +#define MSI_PROC_NAME "term" + + +void __iomem *msi_virt; +static struct ppc4xx_msi *ppc4xx_msi_data; + +/* Can use this register address for catching the MSI message */ +#if defined(CONFIG_460EX) || defined (CONFIG_APM82181) + +#ifdef MSI_DEBUG + +#define MSI_TERM_ADDR 0xc0ec70000ull +#else +#define MSI_TERM_ADDR 0xc10000080ull +#endif /*MSI_DEBUG*/ +#endif /*defined(CONFIG_460EX) || defined (CONFIG_APM82181) */ + +static int ppc4xx_msi_init_allocator(struct ppc4xx_msi *msi_data) +{ + int rc; + + rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, + msi_data->irqhost->of_node); + if (rc) + return rc; + rc = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap); + if (rc < 0) { + msi_bitmap_free(&msi_data->bitmap); + return rc; + } + return 0; +} + + +static void ppc4xx_msi_cascade(unsigned int irq, struct irq_desc *desc) +{ + unsigned int cascade_irq; + struct ppc4xx_msi *msi_data = ppc4xx_msi_data; + int msir_index = -1; +#ifdef MSI_DEBUG + printk("PCIE-MSI: cascade %s called irq %d %d\n", + __func__, irq, (int)virq_to_hw(irq)); +#endif + spin_lock(&desc->lock); + if (desc->chip->mask_ack) { + desc->chip->mask_ack(irq); + } else { + desc->chip->mask(irq); + desc->chip->ack(irq); + } + + if (unlikely(desc->status & IRQ_INPROGRESS)) + goto unlock; + + msir_index = (int)desc->handler_data; + + if (msir_index >= NR_MSI_IRQS) + cascade_irq = NO_IRQ; + + desc->status |= IRQ_INPROGRESS; + + cascade_irq = irq_linear_revmap(msi_data->irqhost, msir_index); + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); + else + printk("%s: no cacade handler found\n", __FUNCTION__); + desc->status &= ~IRQ_INPROGRESS; + + if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) + desc->chip->unmask(irq); +unlock: + spin_unlock(&desc->lock); +} + +static void ppc4xx_compose_msi_msg(struct pci_dev *pdev, int hwirq, + struct msi_msg *msg) +{ + struct ppc4xx_msi *msi_data = ppc4xx_msi_data; + + msg->address_lo = msi_data->msi_addr_lo; + msg->address_hi = msi_data->msi_addr_hi; + msg->data = hwirq | MSI_DATA_PATTERN; +} + + +int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + struct msi_desc *entry; + int rc, hwirq; + unsigned int virq; + struct msi_msg msg; + struct ppc4xx_msi *msi_data = ppc4xx_msi_data; + + printk("PCIE-MSI: vendor %x\n", dev->vendor); + list_for_each_entry(entry, &dev->msi_list, list) { + hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); + if (hwirq < 0) { + rc = hwirq; + pr_debug("%s: fail allocating msi interrupt\n", + __func__); + goto out_free; + } + virq = irq_create_mapping(msi_data->irqhost, hwirq); + printk("PCIE-MSI: hwirq requested %d, virt %d\n", hwirq, virq); + if (virq == NO_IRQ) { + pr_debug("%s: fail mapping irq\n", __func__); + rc = -ENOSPC; + goto out_free; + } + set_irq_msi(virq, entry); + ppc4xx_compose_msi_msg(dev, hwirq, &msg); + printk("PCIE-MSI: message: h %08x l %08x data %08x\n", + msg.address_hi, msg.address_lo, msg.data); + + write_msi_msg(virq, &msg); + } + + return 0; +out_free: + return rc; +} + +void ppc4xx_teardown_msi_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry; + struct ppc4xx_msi *msi_data = ppc4xx_msi_data; + + printk("PCIE-MSI: tearing down msi irqs for %p\n", dev); + + list_for_each_entry(entry, &dev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + set_irq_msi(entry->irq, NULL); + msi_bitmap_free_hwirqs(&msi_data->bitmap, + virq_to_hw(entry->irq), 1); + irq_dispose_mapping(entry->irq); + + } + + return; +} + +static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type) +{ + printk("PCIE-MSI:%s called. vec %x type %d\n", + __func__, nvec, type); + return 0; +} + +/* Stub, unneeded. */ +static void ppc4xx_msi_end_irq(unsigned int virq) +{ +} + + +static struct irq_chip ppc4xx_msi_chip = { + .mask = mask_msi_irq, + .unmask = unmask_msi_irq, + .ack = ppc4xx_msi_end_irq, + .name = " UIC", +}; + +static int ppc4xx_msi_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct irq_chip *chip = &ppc4xx_msi_chip; + + irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_RISING; + + set_irq_chip_and_handler(virq, chip, handle_edge_irq); + + return 0; +} + +static struct irq_host_ops ppc4xx_msi_host_ops = { + .map = ppc4xx_msi_host_map, +}; + + +static int __devinit ppc4xx_msi_probe(struct of_device *dev, + const struct of_device_id *match) +{ + struct ppc4xx_msi *msi; + struct resource res, rmsi; + int i; + int rc; + int virt_msir = 0; + const u32 *sdr_base; + dma_addr_t msi_phys = 0; + + printk("PCIE-MSI: Setting up MSI ...\n"); + msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL); + if (!msi) { + dev_err(&dev->dev, "No memory for MSI structure\n"); + rc = -ENOMEM; + goto error_out; + } + + /* TODO: parse device tree interrupts early to get dynamic IRQ count */ + msi->irqhost = irq_alloc_host(dev->node, IRQ_HOST_MAP_LINEAR, + NR_MSI_IRQS, &ppc4xx_msi_host_ops, 0); + if (msi->irqhost == NULL) { + dev_err(&dev->dev, "No memory for MSI irqhost\n"); + rc = -ENOMEM; + goto error_out; + } + + /* Get MSI ranges */ + rc = of_address_to_resource(dev->node, 0, &rmsi); + if (rc) { + printk("%s rmsi resource error!\n", + dev->node->full_name); + goto error_out; + } + + /* Get the MSI reg base */ + rc = of_address_to_resource(dev->node, 1, &res); + printk("PCIE-MSI: MSI reg base %llx - %llx\n", res.start, res.end); + if (rc) { + dev_err(&dev->dev, "%s regbase resource error!\n", + dev->node->full_name); + goto error_out; + } + /* Get the sdr-base */ + sdr_base = (u32 *)of_get_property(dev->node, "sdr-base", NULL); + printk("PCIE-MSI: MSI sdr base %08x\n", *sdr_base); + if (sdr_base == NULL) { + pr_debug("%s: %s - no sdr-base property found!\n", __func__, + dev->node->full_name); + goto error_out; + } + msi->sdr_base = *sdr_base; + mtdcri(SDR0, msi->sdr_base + PE_IHS1_OFF, RES_TO_U32_HIGH(res.start)); + mtdcri(SDR0, msi->sdr_base + PE_IHS2_OFF, RES_TO_U32_LOW(res.start)| + PE_IHS2_BREV_EN); + + printk("PCIE-MSI: PE_IHS h %08x l %08x\n", + mfdcri(SDR0, msi->sdr_base + PE_IHS1_OFF), + mfdcri(SDR0, msi->sdr_base + PE_IHS2_OFF)); + + msi->msi_regs = ioremap(res.start, res.end - res.start + 1); + if (!msi->msi_regs) { + pr_debug("%s: PIH mapping failed\n", __func__); + goto error_out; + } +#if defined(CONFIG_405EX) || defined(CONFIG_440SPe) || defined(CONFIG_460SX) + /* PIMs setup for first 2GB */ + msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL); + if (msi_virt == NULL) { + pr_debug("%s: No memory for MSI trigger\n", __func__); + rc = -ENOMEM; + goto error_out; + } + msi->msi_addr_hi = 0; + msi->msi_addr_lo = (u32)msi_phys; + +#elif defined(CONFIG_460EX) || defined (CONFIG_APM82181) + msi->msi_addr_hi = (u32)(MSI_TERM_ADDR>>32); + msi->msi_addr_lo = (u32)(MSI_TERM_ADDR & 0xffffffff); +#ifdef MSI_DEBUG + msi_virt = ioremap(MSI_TERM_ADDR, 0x20); + if(!msi_virt) { + printk("ERROR: Ioremap failed %llx\n", MSI_TERM_ADDR); + goto error_out; + } +#endif /*MSI_DEBUG*/ + +#endif + /* Progam the Interrupt handler Termination addr registers */ + out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi); + out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo); + + /* Program MSI Expected data and Mask bits */ + out_be32(msi->msi_regs + PEIH_MSIED, MSI_DATA_PATTERN); + out_be32(msi->msi_regs + PEIH_MSIMK, MSI_MASK_PATTERN); + + iounmap(msi->msi_regs); + + msi->irqhost->host_data = msi; + + if (ppc4xx_msi_init_allocator(msi)) { + pr_debug("%s: Error allocating MSI bitmap\n", __func__); + goto error_out; + } + + for (i = 0; i < NR_MSI_IRQS; i++) { + virt_msir = irq_of_parse_and_map(dev->node, i); + if (virt_msir != NO_IRQ) { + set_irq_data(virt_msir, (void *)i); + set_irq_chained_handler(virt_msir, ppc4xx_msi_cascade); + } else { + printk("No IRQ!\n"); + } + } + + ppc4xx_msi_data = msi; + + WARN_ON(ppc_md.setup_msi_irqs); + ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs; + ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs; + ppc_md.msi_check_device = ppc4xx_msi_check_device; + return 0; +error_out: + if (msi_virt) + dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys); + if (msi) + kfree(msi); + return rc; +} + +int msi_proc_read (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + printk("PCIE-MSI: Read from Term Address\n"); +#ifdef MSI_DEBUG + printk("message %08x\n", in_le32(msi_virt)); + + out_le32(msi_virt, 0x0); +#endif + return 0; +} + +int msi_proc_write (struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char tmp[2]; + unsigned long val; + + /* Verify MSIx from user space by echo "x" > /proc/4xx_msi/test */ + if (copy_from_user(tmp, buffer, count)) + return -EFAULT; + val = simple_strtoul(tmp, NULL, 16); + + printk("PCIE-MSI: Testing interrupt:%d\n", (int)val); + +#ifdef MSI_DEBUG + printk("Write to term address with data=0x%08x\n",(u32)(MSI_DATA_PATTERN | val)); + out_le32(msi_virt, MSI_DATA_PATTERN | val); + mdelay(10); +#endif + return count; + + +} + +static const struct of_device_id ppc4xx_msi_ids[] = { + { + .compatible = "amcc,ppc4xx-msi", + }, + {} +}; + +static struct of_platform_driver ppc4xx_msi_driver = { + .name = "ppc4xx-msi", + .match_table = ppc4xx_msi_ids, + .probe = ppc4xx_msi_probe, +}; + +static __init int ppc4xx_msi_init(void) +{ + struct proc_dir_entry *p ,*root=NULL; + + root = proc_mkdir(MSI_PROC_ROOT, NULL); + if (!root) { + printk("%s: failed to create proc entry\n",__func__); + goto err; + } + + p = create_proc_entry(MSI_PROC_NAME, 0, root); + if (p) { + p->read_proc = msi_proc_read; + p->write_proc = msi_proc_write; + } +err: + return of_register_platform_driver(&ppc4xx_msi_driver); + +} +subsys_initcall(ppc4xx_msi_init); diff --git a/arch/powerpc/sysdev/ppc4xx_msi.h b/arch/powerpc/sysdev/ppc4xx_msi.h new file mode 100755 index 00000000000..64b2f335068 --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_msi.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 Applied Micro Circuits Corporation. + * + * Author: Tirumala Marri <tmarri@amcc.com> + * Feng Kan <fkan@amcc.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; version 2 of the + * License. + */ +#ifndef __PPC4XX_MSI_H__ +#define __PPC4XX_MSI_H__ + +#include <asm/msi_bitmap.h> + +#define PEIH_TERMADH 0x00 +#define PEIH_TERMADL 0x08 +#define PEIH_MSIED 0x10 +#define PEIH_MSIMK 0x18 +#define PEIH_MSIASS 0x20 +#define PEIH_FLUSH0 0x30 +#define PEIH_FLUSH1 0x38 +#define PEIH_CNTRST 0x48 + +#define PE_IHS1_OFF 0x00 +#define PE_IHS2_OFF 0x01 + +/* Adjust pattern for PCIE MSI*/ +#define MSI_DATA_PATTERN 0x00004440 +#define MSI_MASK_PATTERN 0x0000ffe0 + +#define PE_IHS2_BREV_EN 0x02 + +#define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL)) +#define U64_TO_U32_HIGH(val) ((u32)((val) >> 32)) + +#ifdef CONFIG_PHYS_64BIT +#define RES_TO_U32_LOW(val) U64_TO_U32_LOW(val) +#define RES_TO_U32_HIGH(val) U64_TO_U32_HIGH(val) +#else +#define RES_TO_U32_LOW(val) (val) +#define RES_TO_U32_HIGH(val) (0) +#endif + + +struct ppc4xx_msi { + struct irq_host *irqhost; + unsigned long cascade_irq; + u32 msi_addr_lo; + u32 msi_addr_hi; + void __iomem *msi_regs; + u32 feature; + struct msi_bitmap bitmap; + u32 sdr_base; +}; + +#ifdef CONFIG_405EX +#define NR_MSI_IRQS 14 +#elif defined(CONFIG_460EX) || defined (CONFIG_460SX) +#define NR_MSI_IRQS 4 +#elif defined (CONFIG_APM82181) +#define NR_MSI_IRQS 8 +#endif + +#endif /* __PPC4XX_MSI_H__ */ diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c new file mode 100644 index 00000000000..7a8be5f3d7b --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c @@ -0,0 +1,448 @@ +/* + * PowerPC 4xx OCM memory allocation support + * + * (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/seq_file.h> +#include <linux/spinlock.h> +#include <linux/dma-mapping.h> +#include <linux/list.h> +#include <asm/uaccess.h> +#include <asm/prom.h> +#include <asm/dcr.h> +#include <asm/rheap.h> +#include <asm/mmu.h> +#include <asm/ppc4xx_ocm.h> + +#define OCM_DISABLED 0 +#define OCM_ENABLED 1 + +struct ocm_block { + struct list_head list; + void __iomem *addr; + int size; + const char *owner; +}; + +/* non-cached or cached region */ +struct ocm_region { + phys_addr_t phys; + void __iomem *virt; + + int memtotal; + int memfree; + + rh_info_t *rh; + struct list_head list; +}; + +struct ocm_info { + int index; + int status; + int ready; + + phys_addr_t phys; + + int alignment; + int memtotal; + int cache_size; + + struct ocm_region nc; /* non-cached region */ + struct ocm_region c; /* cached region */ +}; + +static struct ocm_info *ocm_nodes = NULL; +static int ocm_count = 0; +static spinlock_t ocm_lock; + +static struct ocm_info * ocm_get_node(unsigned int index) +{ + if (index >= ocm_count) { + printk(KERN_ERR "OCM: invalid index"); + return NULL; + } + + return &ocm_nodes[index]; +} + +void * ocm_alloc(phys_addr_t *phys, int size, int align, + int flags, const char *owner) +{ + void __iomem *addr = NULL; + unsigned long offset; + unsigned long sl_flags; + struct ocm_info *ocm; + struct ocm_region *ocm_reg; + struct ocm_block *ocm_blk; + int i; + + spin_lock_irqsave(&ocm_lock, sl_flags); + + for (i=0; i < ocm_count; i++) { + ocm = ocm_get_node(i); + + if (!ocm || !ocm->ready) + continue; + + if (flags == OCM_NON_CACHED) + ocm_reg = &ocm->nc; + else + ocm_reg = &ocm->c; + + if (!ocm_reg->virt) + continue; + + if (align < ocm->alignment) + align = ocm->alignment; + + offset = rh_alloc_align(ocm_reg->rh, size, align, NULL); + + if (IS_ERR_VALUE(offset)) + continue; + + ocm_blk = kzalloc(sizeof(struct ocm_block*), GFP_KERNEL); + if (!ocm_blk) { + printk(KERN_ERR "OCM: could not allocate ocm block"); + rh_free(ocm_reg->rh, offset); + break; + } + + *phys = ocm_reg->phys + offset; + addr = ocm_reg->virt + offset; + size = ALIGN(size, align); + + ocm_blk->addr = addr; + ocm_blk->size = size; + ocm_blk->owner = owner; + list_add_tail(&ocm_blk->list, &ocm_reg->list); + + ocm_reg->memfree -= size; + + break; + } + + spin_unlock_irqrestore(&ocm_lock, sl_flags); + return addr; +} + +static int ocm_free_region(struct ocm_region *ocm_reg, const void * addr) +{ + struct ocm_block *blk, *tmp; + unsigned long offset; + + if (!ocm_reg->virt) + return 0; + + list_for_each_entry_safe(blk,tmp,&ocm_reg->list,list) { + if (blk->addr == addr) { + offset = addr - ocm_reg->virt; + ocm_reg->memfree += blk->size; + rh_free(ocm_reg->rh, offset); + list_del(&blk->list); + kfree(blk); + return 1; + } + } + + return 0; +} + +void ocm_free(const void * addr) +{ + unsigned long sl_flags; + int i; + + if (!addr) + return; + + spin_lock_irqsave(&ocm_lock, sl_flags); + + for (i=0; i < ocm_count; i++) { + struct ocm_info *ocm = ocm_get_node(i); + + if (!ocm || !ocm->ready) + continue; + + if (ocm_free_region(&ocm->nc, addr) || + ocm_free_region(&ocm->c, addr)) + goto done; + } + +done: + spin_unlock_irqrestore(&ocm_lock, sl_flags); +} + +static void __init ocm_init_node(int count, struct device_node *node) +{ + struct ocm_info *ocm; + void __iomem *virt; + + const unsigned int *cell_index; + const char *status; + const unsigned int *cache_size; + int len; + + struct resource rsrc; + int ioflags; + + ocm = ocm_get_node(count); + + cell_index = of_get_property(node, "cell-index", &len); + if (!cell_index) { + printk(KERN_ERR "OCM: missing cell-index property"); + return; + } + ocm->index = *cell_index; + + status = of_get_property(node, "status", &len); + if (status && strcmp(status,"disabled") == 0) + ocm->status = OCM_DISABLED; + else + ocm->status = OCM_ENABLED; + + cache_size = of_get_property(node, "cached-region-size", &len); + if (cache_size) + ocm->cache_size = *cache_size; + + if (of_address_to_resource(node, 0, &rsrc)) { + printk(KERN_ERR "OCM%d: could not get resource address\n", + ocm->index); + return; + } + + ocm->phys = rsrc.start; + ocm->memtotal = (rsrc.end - rsrc.start + 1); + + printk(KERN_INFO "OCM%d: %d Bytes (%s)\n", + ocm->index, ocm->memtotal, + (ocm->status == OCM_DISABLED)? "disabled" : "enabled"); + + if (ocm->status == OCM_DISABLED) + return; + + /* request region */ + + if (!request_mem_region(ocm->phys, ocm->memtotal, "ppc4xx_ocm")) { + printk(KERN_ERR "OCM%d: could not request region\n", + ocm->index); + return; + } + + /* clear the complete region */ + + ioflags = _PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_EXEC; + virt = __ioremap(ocm->phys, ocm->memtotal, ioflags); + + if (!virt) { + printk(KERN_ERR "OCM%d: failed to ioremap memory\n", + ocm->index); + return; + } + + memset((void*)virt,0,ocm->memtotal); + + iounmap(virt); + + /* Configure non-cached and cached regions */ + + ocm->nc.phys = ocm->phys; + ocm->nc.memtotal = ocm->memtotal - ocm->cache_size; + ocm->nc.memfree = ocm->nc.memtotal; + + ocm->c.phys = ocm->phys + ocm->nc.memtotal; + ocm->c.memtotal = ocm->cache_size; + ocm->c.memfree = ocm->c.memtotal; + + if (ocm->nc.memtotal == 0) + ocm->nc.phys = 0; + + if (ocm->c.memtotal == 0) + ocm->c.phys = 0; + + printk(KERN_INFO "OCM%d: %d Bytes (non-cached)\n", + ocm->index, ocm->nc.memtotal); + + printk(KERN_INFO "OCM%d: %d Bytes (cached)\n", + ocm->index, ocm->c.memtotal); + + /* ioremap the non-cached region */ + if (ocm->nc.memtotal) { + ioflags = _PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_EXEC; + ocm->nc.virt = __ioremap(ocm->nc.phys, ocm->nc.memtotal, + ioflags); + + if (!ocm->nc.virt) { + printk(KERN_ERR + "OCM%d: failed to ioremap non-cached memory\n", + ocm->index); + ocm->nc.memfree = 0; + return; + } + } + + /* ioremap the cached region */ + + if (ocm->c.memtotal) { + ioflags = _PAGE_EXEC; + ocm->c.virt = __ioremap(ocm->c.phys, ocm->c.memtotal, + ioflags); + + if (!ocm->c.virt) { + printk(KERN_ERR + "OCM%d: failed to ioremap cached memory\n", + ocm->index); + ocm->c.memfree = 0; + return; + } + } + + /* Create Remote Heaps */ + + ocm->alignment = 4; /* default 4 byte alignment */ + + if ( ocm->nc.virt) { + ocm->nc.rh = rh_create(ocm->alignment); + rh_attach_region(ocm->nc.rh, 0, ocm->nc.memtotal); + } + + if ( ocm->c.virt) { + ocm->c.rh = rh_create(ocm->alignment); + rh_attach_region(ocm->c.rh, 0, ocm->c.memtotal); + } + + INIT_LIST_HEAD(&ocm->nc.list); + INIT_LIST_HEAD(&ocm->c.list); + + ocm->ready = 1; + + return; +} + +static int ocm_proc_show(struct seq_file *m, void *v) +{ + struct ocm_block *blk, *tmp; + unsigned long sl_flags; + unsigned int i; + + spin_lock_irqsave(&ocm_lock, sl_flags); + + for (i=0; i < ocm_count; i++) { + struct ocm_info *ocm = ocm_get_node(i); + + if (!ocm || !ocm->ready) + continue; + + seq_printf(m, "OCM : %d\n", ocm->index); + seq_printf(m, "PhysAddr : 0x%llx\n", ocm->phys); + seq_printf(m, "MemTotal : %d Bytes\n", ocm->memtotal); + seq_printf(m, "MemTotal(NC) : %d Bytes\n", ocm->nc.memtotal); + seq_printf(m, "MemTotal(C) : %d Bytes\n", ocm->c.memtotal); + + seq_printf(m, "\n"); + + seq_printf(m, "NC.PhysAddr : 0x%llx\n", ocm->nc.phys); + seq_printf(m, "NC.VirtAddr : 0x%p\n", ocm->nc.virt); + seq_printf(m, "NC.MemTotal : %d Bytes\n", ocm->nc.memtotal); + seq_printf(m, "NC.MemFree : %d Bytes\n", ocm->nc.memfree); + + list_for_each_entry_safe(blk,tmp,&ocm->nc.list,list) { + seq_printf(m, "NC.MemUsed : %d Bytes (%s)\n", + blk->size, blk->owner); + } + + seq_printf(m, "\n"); + + seq_printf(m, "C.PhysAddr : 0x%llx\n", ocm->c.phys); + seq_printf(m, "C.VirtAddr : 0x%p\n", ocm->c.virt); + seq_printf(m, "C.MemTotal : %d Bytes\n", ocm->c.memtotal); + seq_printf(m, "C.MemFree : %d Bytes\n", ocm->c.memfree); + + list_for_each_entry_safe(blk,tmp,&ocm->c.list,list) { + seq_printf(m, "C.MemUsed : %d Bytes (%s)\n", + blk->size, blk->owner); + } + + + seq_printf(m,"\n"); + } + + spin_unlock_irqrestore(&ocm_lock, sl_flags); + + return 0; +} + +static int ocm_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ocm_proc_show, NULL); +} + +static const struct file_operations ocm_proc_fops = { + .open = ocm_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void __init ocm_proc_init(void) +{ + proc_create("ocminfo", 0, NULL, &ocm_proc_fops); +} + +static int __init ocm_init(void) +{ + struct device_node *np; + int count; + + spin_lock_init(&ocm_lock); + + count = 0; + for_each_compatible_node(np, NULL, "ibm,ocm") { + count++; + } + + if (!count) + return 0; + + ocm_nodes = kzalloc((count*sizeof(struct ocm_info)), GFP_KERNEL); + if (!ocm_nodes) { + printk(KERN_ERR "OCM: failed to allocate OCM nodes!\n"); + return -ENOMEM; + } + + ocm_count = count; + count = 0; + + for_each_compatible_node(np, NULL, "ibm,ocm") { + ocm_init_node(count, np); + count++; + } + + ocm_proc_init(); + + return 0; +} + +arch_initcall(ocm_init); diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index 6ff9d71b4c0..716b57f1e03 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -34,6 +34,8 @@ #include "ppc4xx_pci.h" +int pcie_used; + static int dma_offset_set; #define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL)) @@ -44,6 +46,169 @@ static int dma_offset_set; #define RES_TO_U32_HIGH(val) \ ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_HIGH(val) : (0)) +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) +static int __init get_pci_exp_ptr(struct pci_controller *hose, int bus, u8 *ptr, int *devfn) +{ + u8 cap_id; + u8 next; + int j; + + u8 devfn_trace[256]; + memset(devfn_trace, 0, 256); + + for (j = 0; j<=0xff; j++){ + if (early_read_config_byte(hose, bus, j, PCI_CAPABILITY_LIST, ptr) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Capabliity" + " List Register\n"); + return -1; + } + + if (*ptr != 0xff) + break; + } + + if (*ptr == 0xff) + return 0; + else + *devfn = j; + + while (*ptr){ + if (early_read_config_byte(hose, bus, j, *ptr + PCI_CAP_LIST_ID, &cap_id) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read" + " Capability ID\n"); + return -1; + } + + if (early_read_config_byte(hose, bus, j, *ptr + PCI_CAP_LIST_NEXT, &next) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read" + " Capability List Next\n"); + return -1; + } + + if (devfn_trace[*ptr]++){ + printk("<chain looped>\n"); + return -1; + } + if (cap_id == 0xff){ + printk("<chain broken>\n"); + return -1; + } + /* + * We only interested in PCI Express capability + * which have payload size etc + */ + if (cap_id == PCI_CAP_ID_EXP) + return 0; + else{ + *ptr = next; + } + } + + return -1; +} + +void ppc440_config_pcie_mps(struct pci_controller *hose) +{ + int i; + int devfn; + u32 mps; + u32 ecdevcppa; + u32 ecdevctl; + u32 mps_supported; + u8 ptr; + int adjust_mps=1; + + /* + * Check supported max payload size for all devices + * If all devices on this hose supported mps > 256, then we will set the max payload + * size of the root complex and each device's mps to 256bytes + */ + for (i = hose->first_busno +1; i<=hose->last_busno; i++){ + if (get_pci_exp_ptr(hose, i, &ptr, &devfn) < 0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Capabliity" + " List Register on bus:%d\n", i); + continue; + } + + if (ptr == 0xff){ + continue; + } + + if (early_read_config_dword(hose, hose->first_busno, 0, 0x00, &ecdevctl)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Control Register\n"); + return; + } + + if (early_read_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCAP, &mps_supported) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + + if (early_read_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCTL, &mps) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + printk("Max pay load supported =0x%x max pay load prog 0x%x \n",mps_supported, mps); + if (!(mps_supported & PCI_EXP_DEVCAP_PAYLOAD)){ + adjust_mps = 0; + break; + } + } + + if (adjust_mps){ + /* we already know all devices on this hose have supported mps > 128bytes + * we will set mps for each the devices at 256byte + */ + + printk("hose->first_busno = %x, hose= %x\n",hose->first_busno, (u32)hose); + + if (early_read_config_dword(hose, hose->first_busno, 0, 0x00, &ecdevctl)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Control Register\n"); + return; + } + + /* Set supported MPS and MPS to 256byte for the root complex */ + if (early_read_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCTL, &ecdevctl)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Control Register\n"); + return; + } + + early_write_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCTL, (ecdevctl & 0xffffff1f) | 0x00000020); + if (early_read_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCAPPA, &ecdevcppa)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + + early_write_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCAPPA, (ecdevcppa & 0xfffffff8) | 0x00000001); + + for (i = hose->first_busno +1; i<=hose->last_busno; i++){ + if (get_pci_exp_ptr(hose, i, &ptr, &devfn) < 0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Capabliity" + " List Register on bus:%d \n", i); + return; + } + + if (ptr != 0xff) { + + if (early_read_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCTL, &mps) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + + early_write_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCTL, (mps & 0xffffff1f) | 0x00000020); + } + } +} +} +#endif + static inline int ppc440spe_revA(void) { /* Catch both 440SPe variants, with and without RAID6 support */ @@ -866,6 +1031,9 @@ static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = static int __init ppc460ex_pciex_core_init(struct device_node *np) { /* Nothing to do, return 2 ports */ +#if defined(CONFIG_APM82181) + return 1; +#endif return 2; } @@ -874,6 +1042,27 @@ static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) u32 val; u32 utlset1; + /* + * Do a software reset on PCIe ports. + * This code is to fix the issue that pci drivers doesn't re-assign + * bus number for PCIE devices after Uboot + * scanned and configured all the busses (eg. PCIE NIC IntelPro/1000 + * PT quad port, SAS LSI 1064E) + */ + switch (port->index) + { + case 0: + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); + mdelay(10); + break; + case 1: + mtdcri(SDR0, PESDR1_460EX_PHY_CTL_RST, 0x0); + mdelay(10); + break; + default: + break; + } + if (port->endpoint) val = PTYPE_LEGACY_ENDPOINT << 20; else @@ -881,7 +1070,15 @@ static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) if (port->index == 0) { val |= LNKW_X1 << 12; +#if defined(CONFIG_APM82181) +#if defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + utlset1 = 0x10000000; +#else + utlset1 = 0x00000000; +#endif +#else utlset1 = 0x20000000; +#endif } else { val |= LNKW_X4 << 12; utlset1 = 0x20101101; @@ -889,15 +1086,28 @@ static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val); mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, utlset1); +#if defined(CONFIG_APM82181) +#if defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01110000); +#else + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000); +#endif +#else mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01210000); +#endif switch (port->index) { case 0: mtdcri(SDR0, PESDR0_460EX_L0CDRCTL, 0x00003230); mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); - +#if defined(CONFIG_APM82181) mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x10000000); + mdelay(50); + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x30000000); +#else + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x10000000); +#endif break; case 1: @@ -953,13 +1163,23 @@ static int ppc460ex_pciex_init_utl(struct ppc4xx_pciex_port *port) * Set buffer allocations and then assert VRB and TXE. */ out_be32(port->utl_base + PEUTL_PBCTL, 0x0800000c); +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + out_be32(port->utl_base + PEUTL_OUTTR, 0x02000000); + out_be32(port->utl_base + PEUTL_INTR, 0x02000000); + +#else out_be32(port->utl_base + PEUTL_OUTTR, 0x08000000); out_be32(port->utl_base + PEUTL_INTR, 0x02000000); +#endif out_be32(port->utl_base + PEUTL_OPDBSZ, 0x04000000); out_be32(port->utl_base + PEUTL_PBBSZ, 0x00000000); out_be32(port->utl_base + PEUTL_IPHBSZ, 0x02000000); out_be32(port->utl_base + PEUTL_IPDBSZ, 0x04000000); out_be32(port->utl_base + PEUTL_RCIRQEN,0x00f00000); +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + out_be32(port->utl_base + PEUTL_PCTL, 0x80841066); + udelay(100); +#endif out_be32(port->utl_base + PEUTL_PCTL, 0x80800066); return 0; @@ -974,7 +1194,7 @@ static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata = #endif /* CONFIG_44x */ -#ifdef CONFIG_40x +#if defined(CONFIG_40x) static int __init ppc405ex_pciex_core_init(struct device_node *np) { @@ -1088,7 +1308,7 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np) if (of_device_is_compatible(np, "ibm,plb-pciex-460ex")) ppc4xx_pciex_hwops = &ppc460ex_pcie_hwops; #endif /* CONFIG_44x */ -#ifdef CONFIG_40x +#if defined(CONFIG_40x) if (of_device_is_compatible(np, "ibm,plb-pciex-405ex")) ppc4xx_pciex_hwops = &ppc405ex_pcie_hwops; #endif @@ -1456,7 +1676,6 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, sa | 3); break; } - return 0; } @@ -1561,9 +1780,9 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, * if it works */ out_le32(mbase + PECFG_PIM0LAL, 0x00000000); - out_le32(mbase + PECFG_PIM0LAH, 0x00000000); + out_le32(mbase + PECFG_PIM0LAH, 0x00000008); /* Moving on HB*/ out_le32(mbase + PECFG_PIM1LAL, 0x00000000); - out_le32(mbase + PECFG_PIM1LAH, 0x00000000); + out_le32(mbase + PECFG_PIM1LAH, 0x0000000c); /* Moving on HB */ out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); out_le32(mbase + PECFG_PIM01SAL, 0x00000000); @@ -1718,17 +1937,31 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) if (!port->endpoint) { /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ out_le32(mbase + 0x208, 0x06040001); +#ifdef CONFIG_PCI_MSI + printk("Disabling INTX for MSI testing.\n"); + out_be32((mbase + 0x23C), 0x00000000); + out_be32((mbase + 0x4), 0x07040000); + + printk("Setting for 64-bit MSI and 4 MSI messages.\n"); + out_be32((mbase + 0x048),in_be32((mbase + 0x048)) | 0x00a50000); +#endif printk(KERN_INFO "PCIE%d: successfully set as root-complex\n", port->index); } else { /* Set Class Code to Processor/PPC */ out_le32(mbase + 0x208, 0x0b200001); +#ifdef CONFIG_PCI_MSI + printk("Setting for 64-bit MSI and 4 MSI messages.\n"); + out_be32((mbase + 0x048),in_be32((mbase + 0x048)) | 0x00a50000); +#endif printk(KERN_INFO "PCIE%d: successfully set as endpoint\n", port->index); } - +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + ppc440_config_pcie_mps(hose); +#endif return; fail: if (hose) @@ -1772,8 +2005,11 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np) /* * Check if device is enabled */ + + pcie_used = 1; if (!of_device_is_available(np)) { printk(KERN_INFO "PCIE%d: Port disabled via device-tree\n", port->index); + pcie_used = 0; return; } diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h index d04e40b306f..74a303d067b 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.h +++ b/arch/powerpc/sysdev/ppc4xx_pci.h @@ -383,6 +383,7 @@ /* * Config space register offsets */ +#define PECFG_ECDEVCTL 0x60 #define PECFG_ECRTCTL 0x074 #define PECFG_BAR0LMPA 0x210 @@ -390,6 +391,7 @@ #define PECFG_BAR1MPA 0x218 #define PECFG_BAR2LMPA 0x220 #define PECFG_BAR2HMPA 0x224 +#define PECFG_ECDEVCAPPA 0x25C #define PECFG_PIMEN 0x33c #define PECFG_PIM0LAL 0x340 diff --git a/arch/powerpc/sysdev/ppc4xx_soc.c b/arch/powerpc/sysdev/ppc4xx_soc.c index 5b32adc9a9b..d2398b9dc52 100644 --- a/arch/powerpc/sysdev/ppc4xx_soc.c +++ b/arch/powerpc/sysdev/ppc4xx_soc.c @@ -153,6 +153,7 @@ static int __init ppc4xx_l2c_probe(void) /* Clear Cache Parity and Tag Errors */ mtdcr(dcrbase_l2c + DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE); +#if defined(CONFIG_DCU_ENABLE) /* Enable 64G snoop region starting at 0 */ r = mfdcr(dcrbase_l2c + DCRN_L2C0_SNP0) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); @@ -163,19 +164,27 @@ static int __init ppc4xx_l2c_probe(void) ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR; mtdcr(dcrbase_l2c + DCRN_L2C0_SNP1, r); - +#endif asm volatile ("sync" ::: "memory"); /* Enable ICU/DCU ports */ r = mfdcr(dcrbase_l2c + DCRN_L2C0_CFG); r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM | L2C_CFG_TPEI | L2C_CFG_CPEI | L2C_CFG_NAM | L2C_CFG_NBRM); - r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN + r |= L2C_CFG_ICU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN | L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM; +#if defined(CONFIG_DCU_ENABLE) + r |= L2C_CFG_DCU; +#endif + /* Check for 460EX/GT special handling */ - if (of_device_is_compatible(np, "ibm,l2-cache-460ex")) + if (of_device_is_compatible(np, "ibm,l2-cache-460ex")) { r |= L2C_CFG_RDBW; +#if defined(CONFIG_DCU_ENABLE) + r |= L2C_CFG_SNP440; +#endif + } mtdcr(dcrbase_l2c + DCRN_L2C0_CFG, r); diff --git a/arch/powerpc/sysdev/ppc4xx_suspend.c b/arch/powerpc/sysdev/ppc4xx_suspend.c new file mode 100644 index 00000000000..9aab914fdaf --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_suspend.c @@ -0,0 +1,133 @@ +/* + * PowerPC 4xx suspend and standby support + * + * (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/init.h> +#include <linux/suspend.h> +#include <linux/kobject.h> +#include <linux/string.h> +#include <asm/ppc4xx_cpm.h> + +#define CONFIG_PM_DEBUG + +#if defined(CONFIG_PM_DEBUG) +# define PM_DEBUG(fmt, arg...) printk(KERN_DEBUG fmt, ## arg) +#else +# define PM_DEBUG(fmt, arg...) ((void)0) +#endif + + +static int suspend_mode = CPM_PM_DOZE; + +static ssize_t ppc_4xx_suspend_available_modes_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *s = buf; + int i; + + for (i = 0; i < CPM_PM_MODES_MAX; i++) { + s += sprintf(s, "%s ", cpm_mode_name(i)); + } + + *(s-1) = '\n'; /* convert the last space to a newline */ + + return (s - buf); +} + +static ssize_t ppc_4xx_suspend_mode_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *s = buf; + s += sprintf(s, "%s\n", cpm_mode_name(suspend_mode)); + return (s - buf); +} + +static ssize_t ppc_4xx_suspend_mode_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t n) +{ + int i; + char *p; + int len; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + + for (i = 0; i < CPM_PM_MODES_MAX; i++) { + if (strncmp(buf, cpm_mode_name(i), len) == 0) { + suspend_mode = i; + return n; + } + } + + return -EINVAL; +} + +static struct kobj_attribute ppc_4xx_suspend_available_modes_attr = + __ATTR(suspend_available_modes, 0444, + ppc_4xx_suspend_available_modes_show, NULL); + +static struct kobj_attribute ppc_4xx_suspend_mode_attr = + __ATTR(suspend_mode, 0644, + ppc_4xx_suspend_mode_show, ppc_4xx_suspend_mode_store); + + +static int ppc4xx_suspend_valid(suspend_state_t state) +{ + return ((state == PM_SUSPEND_STANDBY) || (state == PM_SUSPEND_MEM)); +} + +static int ppc4xx_suspend_enter(suspend_state_t state) +{ + PM_DEBUG("%s\n", __func__); + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + cpm_pm_suspend(state, suspend_mode); + return 0; + default: + return -EINVAL; + } +} + +static struct platform_suspend_ops ppc4xx_suspend_ops = { + .valid = ppc4xx_suspend_valid, + .enter = ppc4xx_suspend_enter, +}; + +int __init ppc4xx_suspend_init(void) +{ + int err; + suspend_set_ops(&ppc4xx_suspend_ops); + PM_DEBUG("Create sysfs power"); + err = sysfs_create_file(power_kobj, + &ppc_4xx_suspend_mode_attr.attr); + + err = sysfs_create_file(power_kobj, + &ppc_4xx_suspend_available_modes_attr.attr); + return 0; +} + +arch_initcall(ppc4xx_suspend_init); + |