aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/Kconfig11
-rw-r--r--arch/powerpc/sysdev/Makefile5
-rw-r--r--arch/powerpc/sysdev/ppc4xx_cpm.c636
-rw-r--r--arch/powerpc/sysdev/ppc4xx_cpm_asm.S800
-rwxr-xr-xarch/powerpc/sysdev/ppc4xx_msi.c408
-rwxr-xr-xarch/powerpc/sysdev/ppc4xx_msi.h65
-rw-r--r--arch/powerpc/sysdev/ppc4xx_ocm.c448
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.c250
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.h2
-rw-r--r--arch/powerpc/sysdev/ppc4xx_soc.c15
-rw-r--r--arch/powerpc/sysdev/ppc4xx_suspend.c133
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);
+