aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-10-03 08:52:26 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-10-03 08:52:26 -0700
commitccaa36f73544163ef6e15eb29a620130755f6001 (patch)
treeb5cf50592c45e25edbd66fea451e6941e455fa83 /arch/powerpc/sysdev
parentb4a9071af62f95dc6d22040a0b37ac7225ce4d54 (diff)
parent5e980823581682d1566e7b5089cf827ddd5f3c94 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc: (29 commits) [POWERPC] Fix rheap alignment problem [POWERPC] Use check_legacy_ioport() for ISAPnP [POWERPC] Avoid NULL pointer in gpio1_interrupt [POWERPC] Enable generic rtc hook for the MPC8349 mITX [POWERPC] Add powerpc get/set_rtc_time interface to new generic rtc class [POWERPC] Create a "wrapper" script and use it in arch/powerpc/boot [POWERPC] fix spin lock nesting in hvc_iseries [POWERPC] EEH failure to mark pci slot as frozen. [POWERPC] update powerpc defconfig files after libata kconfig breakage [POWERPC] enable sysrq in pmac32_defconfig [POWERPC] UPIO_TSI cleanup [POWERPC] rewrite mkprep and mkbugboot in sane C [POWERPC] maple/pci iomem annotations [POWERPC] powerpc oprofile __user annotations [POWERPC] cell spufs iomem annotations [POWERPC] NULL noise removal: spufs [POWERPC] ppc math-emu needs -fno-builtin-fabs for math.c and fabs.c [POWERPC] update mpc8349_itx_defconfig and remove some debug settings [POWERPC] Always call cede in pseries dedicated idle loop [POWERPC] Fix loop logic in irq_alloc_virt() ...
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/Makefile5
-rw-r--r--arch/powerpc/sysdev/cpm2_common.c309
-rw-r--r--arch/powerpc/sysdev/cpm2_pic.c256
-rw-r--r--arch/powerpc/sysdev/cpm2_pic.h10
-rw-r--r--arch/powerpc/sysdev/fsl_soc.c281
-rw-r--r--arch/powerpc/sysdev/fsl_soc.h2
6 files changed, 858 insertions, 5 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index e5e999ea891..f15f4d78aee 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -17,3 +17,8 @@ ifeq ($(CONFIG_PPC_MERGE),y)
obj-$(CONFIG_PPC_I8259) += i8259.o
obj-$(CONFIG_PPC_83xx) += ipic.o
endif
+
+# Temporary hack until we have migrated to asm-powerpc
+ifeq ($(ARCH),powerpc)
+obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o
+endif
diff --git a/arch/powerpc/sysdev/cpm2_common.c b/arch/powerpc/sysdev/cpm2_common.c
new file mode 100644
index 00000000000..ec265995d5d
--- /dev/null
+++ b/arch/powerpc/sysdev/cpm2_common.c
@@ -0,0 +1,309 @@
+/*
+ * General Purpose functions for the global management of the
+ * 8260 Communication Processor Module.
+ * Copyright (c) 1999-2001 Dan Malek <dan@embeddedalley.com>
+ * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com)
+ * 2.3.99 Updates
+ *
+ * 2006 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ * Merged to arch/powerpc from arch/ppc/syslib/cpm2_common.c
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/*
+ *
+ * In addition to the individual control of the communication
+ * channels, there are a few functions that globally affect the
+ * communication processor.
+ *
+ * Buffer descriptors must be allocated from the dual ported memory
+ * space. The allocator for that is here. When the communication
+ * process is reset, we reclaim the memory available. There is
+ * currently no deallocator for this memory.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mpc8260.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/cpm2.h>
+#include <asm/rheap.h>
+#include <asm/fs_pd.h>
+
+#include <sysdev/fsl_soc.h>
+
+static void cpm2_dpinit(void);
+cpm_cpm2_t *cpmp; /* Pointer to comm processor space */
+
+/* We allocate this here because it is used almost exclusively for
+ * the communication processor devices.
+ */
+cpm2_map_t *cpm2_immr;
+intctl_cpm2_t *cpm2_intctl;
+
+#define CPM_MAP_SIZE (0x40000) /* 256k - the PQ3 reserve this amount
+ of space for CPM as it is larger
+ than on PQ2 */
+
+void
+cpm2_reset(void)
+{
+ cpm2_immr = (cpm2_map_t *)ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE);
+ cpm2_intctl = cpm2_map(im_intctl);
+
+ /* Reclaim the DP memory for our use.
+ */
+ cpm2_dpinit();
+
+ /* Tell everyone where the comm processor resides.
+ */
+ cpmp = &cpm2_immr->im_cpm;
+}
+
+/* Set a baud rate generator. This needs lots of work. There are
+ * eight BRGs, which can be connected to the CPM channels or output
+ * as clocks. The BRGs are in two different block of internal
+ * memory mapped space.
+ * The baud rate clock is the system clock divided by something.
+ * It was set up long ago during the initial boot phase and is
+ * is given to us.
+ * Baud rate clocks are zero-based in the driver code (as that maps
+ * to port numbers). Documentation uses 1-based numbering.
+ */
+#define BRG_INT_CLK (get_brgfreq())
+#define BRG_UART_CLK (BRG_INT_CLK/16)
+
+/* This function is used by UARTS, or anything else that uses a 16x
+ * oversampled clock.
+ */
+void
+cpm_setbrg(uint brg, uint rate)
+{
+ volatile uint *bp;
+
+ /* This is good enough to get SMCs running.....
+ */
+ if (brg < 4) {
+ bp = cpm2_map_size(im_brgc1, 16);
+ } else {
+ bp = cpm2_map_size(im_brgc5, 16);
+ brg -= 4;
+ }
+ bp += brg;
+ *bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN;
+
+ cpm2_unmap(bp);
+}
+
+/* This function is used to set high speed synchronous baud rate
+ * clocks.
+ */
+void
+cpm2_fastbrg(uint brg, uint rate, int div16)
+{
+ volatile uint *bp;
+
+ if (brg < 4) {
+ bp = cpm2_map_size(im_brgc1, 16);
+ }
+ else {
+ bp = cpm2_map_size(im_brgc5, 16);
+ brg -= 4;
+ }
+ bp += brg;
+ *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN;
+ if (div16)
+ *bp |= CPM_BRG_DIV16;
+
+ cpm2_unmap(bp);
+}
+
+int cpm2_clk_setup(enum cpm_clk_target target, int clock, int mode)
+{
+ int ret = 0;
+ int shift;
+ int i, bits = 0;
+ cpmux_t *im_cpmux;
+ u32 *reg;
+ u32 mask = 7;
+ u8 clk_map [24][3] = {
+ {CPM_CLK_FCC1, CPM_BRG5, 0},
+ {CPM_CLK_FCC1, CPM_BRG6, 1},
+ {CPM_CLK_FCC1, CPM_BRG7, 2},
+ {CPM_CLK_FCC1, CPM_BRG8, 3},
+ {CPM_CLK_FCC1, CPM_CLK9, 4},
+ {CPM_CLK_FCC1, CPM_CLK10, 5},
+ {CPM_CLK_FCC1, CPM_CLK11, 6},
+ {CPM_CLK_FCC1, CPM_CLK12, 7},
+ {CPM_CLK_FCC2, CPM_BRG5, 0},
+ {CPM_CLK_FCC2, CPM_BRG6, 1},
+ {CPM_CLK_FCC2, CPM_BRG7, 2},
+ {CPM_CLK_FCC2, CPM_BRG8, 3},
+ {CPM_CLK_FCC2, CPM_CLK13, 4},
+ {CPM_CLK_FCC2, CPM_CLK14, 5},
+ {CPM_CLK_FCC2, CPM_CLK15, 6},
+ {CPM_CLK_FCC2, CPM_CLK16, 7},
+ {CPM_CLK_FCC3, CPM_BRG5, 0},
+ {CPM_CLK_FCC3, CPM_BRG6, 1},
+ {CPM_CLK_FCC3, CPM_BRG7, 2},
+ {CPM_CLK_FCC3, CPM_BRG8, 3},
+ {CPM_CLK_FCC3, CPM_CLK13, 4},
+ {CPM_CLK_FCC3, CPM_CLK14, 5},
+ {CPM_CLK_FCC3, CPM_CLK15, 6},
+ {CPM_CLK_FCC3, CPM_CLK16, 7}
+ };
+
+ im_cpmux = cpm2_map(im_cpmux);
+
+ switch (target) {
+ case CPM_CLK_SCC1:
+ reg = &im_cpmux->cmx_scr;
+ shift = 24;
+ case CPM_CLK_SCC2:
+ reg = &im_cpmux->cmx_scr;
+ shift = 16;
+ break;
+ case CPM_CLK_SCC3:
+ reg = &im_cpmux->cmx_scr;
+ shift = 8;
+ break;
+ case CPM_CLK_SCC4:
+ reg = &im_cpmux->cmx_scr;
+ shift = 0;
+ break;
+ case CPM_CLK_FCC1:
+ reg = &im_cpmux->cmx_fcr;
+ shift = 24;
+ break;
+ case CPM_CLK_FCC2:
+ reg = &im_cpmux->cmx_fcr;
+ shift = 16;
+ break;
+ case CPM_CLK_FCC3:
+ reg = &im_cpmux->cmx_fcr;
+ shift = 8;
+ break;
+ default:
+ printk(KERN_ERR "cpm2_clock_setup: invalid clock target\n");
+ return -EINVAL;
+ }
+
+ if (mode == CPM_CLK_RX)
+ shift +=3;
+
+ for (i=0; i<24; i++) {
+ if (clk_map[i][0] == target && clk_map[i][1] == clock) {
+ bits = clk_map[i][2];
+ break;
+ }
+ }
+ if (i == sizeof(clk_map)/3)
+ ret = -EINVAL;
+
+ bits <<= shift;
+ mask <<= shift;
+ out_be32(reg, (in_be32(reg) & ~mask) | bits);
+
+ cpm2_unmap(im_cpmux);
+ return ret;
+}
+
+/*
+ * dpalloc / dpfree bits.
+ */
+static spinlock_t cpm_dpmem_lock;
+/* 16 blocks should be enough to satisfy all requests
+ * until the memory subsystem goes up... */
+static rh_block_t cpm_boot_dpmem_rh_block[16];
+static rh_info_t cpm_dpmem_info;
+static u8* im_dprambase;
+
+static void cpm2_dpinit(void)
+{
+ spin_lock_init(&cpm_dpmem_lock);
+
+ im_dprambase = ioremap(CPM_MAP_ADDR, CPM_DATAONLY_BASE + CPM_DATAONLY_SIZE);
+
+ /* initialize the info header */
+ rh_init(&cpm_dpmem_info, 1,
+ sizeof(cpm_boot_dpmem_rh_block) /
+ sizeof(cpm_boot_dpmem_rh_block[0]),
+ cpm_boot_dpmem_rh_block);
+
+ /* Attach the usable dpmem area */
+ /* XXX: This is actually crap. CPM_DATAONLY_BASE and
+ * CPM_DATAONLY_SIZE is only a subset of the available dpram. It
+ * varies with the processor and the microcode patches activated.
+ * But the following should be at least safe.
+ */
+ rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE,
+ CPM_DATAONLY_SIZE);
+}
+
+/* This function returns an index into the DPRAM area.
+ */
+uint cpm_dpalloc(uint size, uint align)
+{
+ void *start;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpm_dpmem_lock, flags);
+ cpm_dpmem_info.alignment = align;
+ start = rh_alloc(&cpm_dpmem_info, size, "commproc");
+ spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
+
+ return (uint)start;
+}
+EXPORT_SYMBOL(cpm_dpalloc);
+
+int cpm_dpfree(uint offset)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpm_dpmem_lock, flags);
+ ret = rh_free(&cpm_dpmem_info, (void *)offset);
+ spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(cpm_dpfree);
+
+/* not sure if this is ever needed */
+uint cpm_dpalloc_fixed(uint offset, uint size, uint align)
+{
+ void *start;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpm_dpmem_lock, flags);
+ cpm_dpmem_info.alignment = align;
+ start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc");
+ spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
+
+ return (uint)start;
+}
+EXPORT_SYMBOL(cpm_dpalloc_fixed);
+
+void cpm_dpdump(void)
+{
+ rh_dump(&cpm_dpmem_info);
+}
+EXPORT_SYMBOL(cpm_dpdump);
+
+void *cpm_dpram_addr(uint offset)
+{
+ return (void *)(im_dprambase + offset);
+}
+EXPORT_SYMBOL(cpm_dpram_addr);
diff --git a/arch/powerpc/sysdev/cpm2_pic.c b/arch/powerpc/sysdev/cpm2_pic.c
new file mode 100644
index 00000000000..51752990f7b
--- /dev/null
+++ b/arch/powerpc/sysdev/cpm2_pic.c
@@ -0,0 +1,256 @@
+/*
+ * Platform information definitions.
+ *
+ * Copied from arch/ppc/syslib/cpm2_pic.c with minor subsequent updates
+ * to make in work in arch/powerpc/. Original (c) belongs to Dan Malek.
+ *
+ * Author: Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * 1999-2001 (c) Dan Malek <dan@embeddedalley.com>
+ * 2006 (c) MontaVista Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/* The CPM2 internal interrupt controller. It is usually
+ * the only interrupt controller.
+ * There are two 32-bit registers (high/low) for up to 64
+ * possible interrupts.
+ *
+ * Now, the fun starts.....Interrupt Numbers DO NOT MAP
+ * in a simple arithmetic fashion to mask or pending registers.
+ * That is, interrupt 4 does not map to bit position 4.
+ * We create two tables, indexed by vector number, to indicate
+ * which register to use and which bit in the register to use.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/irq.h>
+
+#include <asm/immap_cpm2.h>
+#include <asm/mpc8260.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+#include "cpm2_pic.h"
+
+static struct device_node *cpm2_pic_node;
+static struct irq_host *cpm2_pic_host;
+#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
+static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
+
+static const u_char irq_to_siureg[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* bit numbers do not match the docs, these are precomputed so the bit for
+ * a given irq is (1 << irq_to_siubit[irq]) */
+static const u_char irq_to_siubit[] = {
+ 0, 15, 14, 13, 12, 11, 10, 9,
+ 8, 7, 6, 5, 4, 3, 2, 1,
+ 2, 1, 0, 14, 13, 12, 11, 10,
+ 9, 8, 7, 6, 5, 4, 3, 0,
+ 31, 30, 29, 28, 27, 26, 25, 24,
+ 23, 22, 21, 20, 19, 18, 17, 16,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+};
+
+static void cpm2_mask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ irq_nr -= CPM_IRQ_OFFSET;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_intctl->ic_simrh);
+ ppc_cached_irq_mask[word] &= ~(1 << bit);
+ simr[word] = ppc_cached_irq_mask[word];
+}
+
+static void cpm2_unmask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ irq_nr -= CPM_IRQ_OFFSET;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_intctl->ic_simrh);
+ ppc_cached_irq_mask[word] |= 1 << bit;
+ simr[word] = ppc_cached_irq_mask[word];
+}
+
+static void cpm2_mask_and_ack(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr, *sipnr;
+
+ irq_nr -= CPM_IRQ_OFFSET;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_intctl->ic_simrh);
+ sipnr = &(cpm2_intctl->ic_sipnrh);
+ ppc_cached_irq_mask[word] &= ~(1 << bit);
+ simr[word] = ppc_cached_irq_mask[word];
+ sipnr[word] = 1 << bit;
+}
+
+static void cpm2_end_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
+ && irq_desc[irq_nr].action) {
+
+ irq_nr -= CPM_IRQ_OFFSET;
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_intctl->ic_simrh);
+ ppc_cached_irq_mask[word] |= 1 << bit;
+ simr[word] = ppc_cached_irq_mask[word];
+ /*
+ * Work around large numbers of spurious IRQs on PowerPC 82xx
+ * systems.
+ */
+ mb();
+ }
+}
+
+static struct irq_chip cpm2_pic = {
+ .typename = " CPM2 SIU ",
+ .enable = cpm2_unmask_irq,
+ .disable = cpm2_mask_irq,
+ .unmask = cpm2_unmask_irq,
+ .mask_ack = cpm2_mask_and_ack,
+ .end = cpm2_end_irq,
+};
+
+int cpm2_get_irq(struct pt_regs *regs)
+{
+ int irq;
+ unsigned long bits;
+
+ /* For CPM2, read the SIVEC register and shift the bits down
+ * to get the irq number. */
+ bits = cpm2_intctl->ic_sivec;
+ irq = bits >> 26;
+
+ if (irq == 0)
+ return(-1);
+ return irq+CPM_IRQ_OFFSET;
+}
+
+static int cpm2_pic_host_match(struct irq_host *h, struct device_node *node)
+{
+ return cpm2_pic_node == NULL || cpm2_pic_node == node;
+}
+
+static int cpm2_pic_host_map(struct irq_host *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw);
+
+ get_irq_desc(virq)->status |= IRQ_LEVEL;
+ set_irq_chip_and_handler(virq, &cpm2_pic, handle_level_irq);
+ return 0;
+}
+
+static void cpm2_host_unmap(struct irq_host *h, unsigned int virq)
+{
+ /* Make sure irq is masked in hardware */
+ cpm2_mask_irq(virq);
+
+ /* remove chip and handler */
+ set_irq_chip_and_handler(virq, NULL, NULL);
+}
+
+static int cpm2_pic_host_xlate(struct irq_host *h, struct device_node *ct,
+ u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq, unsigned int *out_flags)
+{
+ static const unsigned char map_cpm2_senses[4] = {
+ IRQ_TYPE_LEVEL_LOW,
+ IRQ_TYPE_LEVEL_HIGH,
+ IRQ_TYPE_EDGE_FALLING,
+ IRQ_TYPE_EDGE_RISING,
+ };
+
+ *out_hwirq = intspec[0];
+ if (intsize > 1 && intspec[1] < 4)
+ *out_flags = map_cpm2_senses[intspec[1]];
+ else
+ *out_flags = IRQ_TYPE_NONE;
+
+ return 0;
+}
+
+static struct irq_host_ops cpm2_pic_host_ops = {
+ .match = cpm2_pic_host_match,
+ .map = cpm2_pic_host_map,
+ .unmap = cpm2_host_unmap,
+ .xlate = cpm2_pic_host_xlate,
+};
+
+void cpm2_pic_init(struct device_node *node)
+{
+ int i;
+
+ /* Clear the CPM IRQ controller, in case it has any bits set
+ * from the bootloader
+ */
+
+ /* Mask out everything */
+
+ cpm2_intctl->ic_simrh = 0x00000000;
+ cpm2_intctl->ic_simrl = 0x00000000;
+
+ wmb();
+
+ /* Ack everything */
+ cpm2_intctl->ic_sipnrh = 0xffffffff;
+ cpm2_intctl->ic_sipnrl = 0xffffffff;
+ wmb();
+
+ /* Dummy read of the vector */
+ i = cpm2_intctl->ic_sivec;
+ rmb();
+
+ /* Initialize the default interrupt mapping priorities,
+ * in case the boot rom changed something on us.
+ */
+ cpm2_intctl->ic_sicr = 0;
+ cpm2_intctl->ic_scprrh = 0x05309770;
+ cpm2_intctl->ic_scprrl = 0x05309770;
+
+ /* create a legacy host */
+ if (node)
+ cpm2_pic_node = of_node_get(node);
+
+ cpm2_pic_host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, 64, &cpm2_pic_host_ops, 64);
+ if (cpm2_pic_host == NULL) {
+ printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
+ return;
+ }
+}
diff --git a/arch/powerpc/sysdev/cpm2_pic.h b/arch/powerpc/sysdev/cpm2_pic.h
new file mode 100644
index 00000000000..d63e45d4df5
--- /dev/null
+++ b/arch/powerpc/sysdev/cpm2_pic.h
@@ -0,0 +1,10 @@
+#ifndef _PPC_KERNEL_CPM2_H
+#define _PPC_KERNEL_CPM2_H
+
+extern intctl_cpm2_t *cpm2_intctl;
+
+extern int cpm2_get_irq(struct pt_regs *regs);
+
+extern void cpm2_pic_init(struct device_node*);
+
+#endif /* _PPC_KERNEL_CPM2_H */
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 92ba378b799..022ed275ea6 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -3,6 +3,9 @@
*
* Maintained by Kumar Gala (see MAINTAINERS for contact information)
*
+ * 2006 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -20,15 +23,20 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
+#include <linux/fs_enet_pd.h>
+#include <linux/fs_uart_pd.h>
#include <asm/system.h>
#include <asm/atomic.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/time.h>
#include <asm/prom.h>
#include <sysdev/fsl_soc.h>
#include <mm/mmu_decl.h>
+#include <asm/cpm2.h>
+extern void init_fcc_ioports(struct fs_platform_info*);
static phys_addr_t immrbase = -1;
phys_addr_t get_immrbase(void)
@@ -42,7 +50,9 @@ phys_addr_t get_immrbase(void)
if (soc) {
unsigned int size;
const void *prop = get_property(soc, "reg", &size);
- immrbase = of_translate_address(soc, prop);
+
+ if (prop)
+ immrbase = of_translate_address(soc, prop);
of_node_put(soc);
};
@@ -51,6 +61,59 @@ phys_addr_t get_immrbase(void)
EXPORT_SYMBOL(get_immrbase);
+#ifdef CONFIG_CPM2
+
+static u32 brgfreq = -1;
+
+u32 get_brgfreq(void)
+{
+ struct device_node *node;
+
+ if (brgfreq != -1)
+ return brgfreq;
+
+ node = of_find_node_by_type(NULL, "cpm");
+ if (node) {
+ unsigned int size;
+ const unsigned int *prop = get_property(node, "brg-frequency",
+ &size);
+
+ if (prop)
+ brgfreq = *prop;
+ of_node_put(node);
+ };
+
+ return brgfreq;
+}
+
+EXPORT_SYMBOL(get_brgfreq);
+
+static u32 fs_baudrate = -1;
+
+u32 get_baudrate(void)
+{
+ struct device_node *node;
+
+ if (fs_baudrate != -1)
+ return fs_baudrate;
+
+ node = of_find_node_by_type(NULL, "serial");
+ if (node) {
+ unsigned int size;
+ const unsigned int *prop = get_property(node, "current-speed",
+ &size);
+
+ if (prop)
+ fs_baudrate = *prop;
+ of_node_put(node);
+ };
+
+ return fs_baudrate;
+}
+
+EXPORT_SYMBOL(get_baudrate);
+#endif /* CONFIG_CPM2 */
+
static int __init gfar_mdio_of_init(void)
{
struct device_node *np;
@@ -85,8 +148,11 @@ static int __init gfar_mdio_of_init(void)
mdio_data.irq[k] = -1;
while ((child = of_get_next_child(np, child)) != NULL) {
- const u32 *id = get_property(child, "reg", NULL);
- mdio_data.irq[*id] = irq_of_parse_and_map(child, 0);
+ int irq = irq_of_parse_and_map(child, 0);
+ if (irq != NO_IRQ) {
+ const u32 *id = get_property(child, "reg", NULL);
+ mdio_data.irq[*id] = irq;
+ }
}
ret =
@@ -128,7 +194,7 @@ static int __init gfar_of_init(void)
const char *model;
const void *mac_addr;
const phandle *ph;
- int n_res = 1;
+ int n_res = 2;
memset(r, 0, sizeof(r));
memset(&gfar_data, 0, sizeof(gfar_data));
@@ -159,7 +225,7 @@ static int __init gfar_of_init(void)
gfar_dev =
platform_device_register_simple("fsl-gianfar", i, &r[0],
- n_res + 1);
+ n_res);
if (IS_ERR(gfar_dev)) {
ret = PTR_ERR(gfar_dev);
@@ -478,3 +544,208 @@ err:
}
arch_initcall(fsl_usb_of_init);
+
+#ifdef CONFIG_CPM2
+
+static const char fcc_regs[] = "fcc_regs";
+static const char fcc_regs_c[] = "fcc_regs_c";
+static const char fcc_pram[] = "fcc_pram";
+static char bus_id[9][BUS_ID_SIZE];
+
+static int __init fs_enet_of_init(void)
+{
+ struct device_node *np;
+ unsigned int i;
+ struct platform_device *fs_enet_dev;
+ struct resource res;
+ int ret;
+
+ for (np = NULL, i = 0;
+ (np = of_find_compatible_node(np, "network", "fs_enet")) != NULL;
+ i++) {
+ struct resource r[4];
+ struct device_node *phy, *mdio;
+ struct fs_platform_info fs_enet_data;
+ const unsigned int *id, *phy_addr;
+ const void *mac_addr;
+ const phandle *ph;
+ const char *model;
+
+ memset(r, 0, sizeof(r));
+ memset(&fs_enet_data, 0, sizeof(fs_enet_data));
+
+ ret = of_address_to_resource(np, 0, &r[0]);
+ if (ret)
+ goto err;
+ r[0].name = fcc_regs;
+
+ ret = of_address_to_resource(np, 1, &r[1]);
+ if (ret)
+ goto err;
+ r[1].name = fcc_pram;
+
+ ret = of_address_to_resource(np, 2, &r[2]);
+ if (ret)
+ goto err;
+ r[2].name = fcc_regs_c;
+
+ r[3].start = r[3].end = irq_of_parse_and_map(np, 0);
+ r[3].flags = IORESOURCE_IRQ;
+
+ fs_enet_dev =
+ platform_device_register_simple("fsl-cpm-fcc", i, &r[0], 4);
+
+ if (IS_ERR(fs_enet_dev)) {
+ ret = PTR_ERR(fs_enet_dev);
+ goto err;
+ }
+
+ model = get_property(np, "model", NULL);
+ if (model == NULL) {
+ ret = -ENODEV;
+ goto unreg;
+ }
+
+ mac_addr = get_property(np, "mac-address", NULL);
+ memcpy(fs_enet_data.macaddr, mac_addr, 6);
+
+ ph = get_property(np, "phy-handle", NULL);
+ phy = of_find_node_by_phandle(*ph);
+
+ if (phy == NULL) {
+ ret = -ENODEV;
+ goto unreg;
+ }
+
+ phy_addr = get_property(phy, "reg", NULL);
+ fs_enet_data.phy_addr = *phy_addr;
+
+ id = get_property(np, "device-id", NULL);
+ fs_enet_data.fs_no = *id;
+ strcpy(fs_enet_data.fs_type, model);
+
+ mdio = of_get_parent(phy);
+ ret = of_address_to_resource(mdio, 0, &res);
+ if (ret) {
+ of_node_put(phy);
+ of_node_put(mdio);
+ goto unreg;
+ }
+
+ fs_enet_data.clk_rx = *((u32 *) get_property(np, "rx-clock", NULL));
+ fs_enet_data.clk_tx = *((u32 *) get_property(np, "tx-clock", NULL));
+
+ if (strstr(model, "FCC")) {
+ int fcc_index = *id - 1;
+
+ fs_enet_data.dpram_offset = (u32)cpm_dpram_addr(0);
+ fs_enet_data.rx_ring = 32;
+ fs_enet_data.tx_ring = 32;
+ fs_enet_data.rx_copybreak = 240;
+ fs_enet_data.use_napi = 0;
+ fs_enet_data.napi_weight = 17;
+ fs_enet_data.mem_offset = FCC_MEM_OFFSET(fcc_index);
+ fs_enet_data.cp_page = CPM_CR_FCC_PAGE(fcc_index);
+ fs_enet_data.cp_block = CPM_CR_FCC_SBLOCK(fcc_index);
+
+ snprintf((char*)&bus_id[(*id)], BUS_ID_SIZE, "%x:%02x",
+ (u32)res.start, fs_enet_data.phy_addr);
+ fs_enet_data.bus_id = (char*)&bus_id[(*id)];
+ fs_enet_data.init_ioports = init_fcc_ioports;
+ }
+
+ of_node_put(phy);
+ of_node_put(mdio);
+
+ ret = platform_device_add_data(fs_enet_dev, &fs_enet_data,
+ sizeof(struct
+ fs_platform_info));
+ if (ret)
+ goto unreg;
+ }
+ return 0;
+
+unreg:
+ platform_device_unregister(fs_enet_dev);
+err:
+ return ret;
+}
+
+arch_initcall(fs_enet_of_init);
+
+static const char scc_regs[] = "regs";
+static const char scc_pram[] = "pram";
+
+static int __init cpm_uart_of_init(void)
+{
+ struct device_node *np;
+ unsigned int i;
+ struct platform_device *cpm_uart_dev;
+ int ret;
+
+ for (np = NULL, i = 0;
+ (np = of_find_compatible_node(np, "serial", "cpm_uart")) != NULL;
+ i++) {
+ struct resource r[3];
+ struct fs_uart_platform_info cpm_uart_data;
+ const int *id;
+ const char *model;
+
+ memset(r, 0, sizeof(r));
+ memset(&cpm_uart_data, 0, sizeof(cpm_uart_data));
+
+ ret = of_address_to_resource(np, 0, &r[0]);
+ if (ret)
+ goto err;
+
+ r[0].name = scc_regs;
+
+ ret = of_address_to_resource(np, 1, &r[1]);
+ if (ret)
+ goto err;
+ r[1].name = scc_pram;
+
+ r[2].start = r[2].end = irq_of_parse_and_map(np, 0);
+ r[2].flags = IORESOURCE_IRQ;
+
+ cpm_uart_dev =
+ platform_device_register_simple("fsl-cpm-scc:uart", i, &r[0], 3);
+
+ if (IS_ERR(cpm_uart_dev)) {
+ ret = PTR_ERR(cpm_uart_dev);
+ goto err;
+ }
+
+ id = get_property(np, "device-id", NULL);
+ cpm_uart_data.fs_no = *id;
+
+ model = (char*)get_property(np, "model", NULL);
+ strcpy(cpm_uart_data.fs_type, model);
+
+ cpm_uart_data.uart_clk = ppc_proc_freq;
+
+ cpm_uart_data.tx_num_fifo = 4;
+ cpm_uart_data.tx_buf_size = 32;
+ cpm_uart_data.rx_num_fifo = 4;
+ cpm_uart_data.rx_buf_size = 32;
+ cpm_uart_data.clk_rx = *((u32 *) get_property(np, "rx-clock", NULL));
+ cpm_uart_data.clk_tx = *((u32 *) get_property(np, "tx-clock", NULL));
+
+ ret =
+ platform_device_add_data(cpm_uart_dev, &cpm_uart_data,
+ sizeof(struct
+ fs_uart_platform_info));
+ if (ret)
+ goto unreg;
+ }
+
+ return 0;
+
+unreg:
+ platform_device_unregister(cpm_uart_dev);
+err:
+ return ret;
+}
+
+arch_initcall(cpm_uart_of_init);
+#endif /* CONFIG_CPM2 */
diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h
index 5a3dd480d2f..04e145b5fc3 100644
--- a/arch/powerpc/sysdev/fsl_soc.h
+++ b/arch/powerpc/sysdev/fsl_soc.h
@@ -5,6 +5,8 @@
#include <asm/mmu.h>
extern phys_addr_t get_immrbase(void);
+extern u32 get_brgfreq(void);
+extern u32 get_baudrate(void);
#endif
#endif