diff options
author | Marc St-Jean <stjeanma@pmc-sierra.com> | 2007-06-14 15:54:47 -0600 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-07-10 17:33:02 +0100 |
commit | 35832e26f95ba14a6b6f0519441c5cb64cca6bf9 (patch) | |
tree | db3c05782a2140b19917344fb640070a655d75fd /arch | |
parent | a4b156d47d204904fa104c3e585b4c67b89195f3 (diff) |
[MIPS] PMC MSP71xx core platform
Patch to add core platform support for the PMC-Sierra MSP71xx devices.
Signed-off-by: Marc St-Jean <Marc_St-Jean@pmc-sierra.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/Makefile | 11 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_elb.c | 46 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_hwbutton.c | 179 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_irq.c | 124 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_irq_cic.c | 134 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c | 109 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_prom.c | 566 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_setup.c | 256 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_time.c | 94 | ||||
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_usb.c | 150 |
10 files changed, 1669 insertions, 0 deletions
diff --git a/arch/mips/pmc-sierra/msp71xx/Makefile b/arch/mips/pmc-sierra/msp71xx/Makefile new file mode 100644 index 000000000000..4bba79c1cc79 --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the PMC-Sierra MSP SOCs +# +obj-y += msp_prom.o msp_setup.o msp_irq.o \ + msp_time.o msp_serial.o msp_elb.o +obj-$(CONFIG_PMC_MSP7120_GW) += msp_hwbutton.o +obj-$(CONFIG_IRQ_MSP_SLP) += msp_irq_slp.o +obj-$(CONFIG_IRQ_MSP_CIC) += msp_irq_cic.o +obj-$(CONFIG_PCI) += msp_pci.o +obj-$(CONFIG_MSPETH) += msp_eth.o +obj-$(CONFIG_USB_MSP71XX) += msp_usb.o diff --git a/arch/mips/pmc-sierra/msp71xx/msp_elb.c b/arch/mips/pmc-sierra/msp71xx/msp_elb.c new file mode 100644 index 000000000000..3e9641007216 --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_elb.c @@ -0,0 +1,46 @@ +/* + * Sets up the proper Chip Select configuration registers. It is assumed that + * PMON sets up the ADDR and MASK registers properly. + * + * Copyright 2005-2006 PMC-Sierra, Inc. + * Author: Marc St-Jean, Marc_St-Jean@pmc-sierra.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <msp_regs.h> + +static int __init msp_elb_setup(void) +{ +#if defined(CONFIG_PMC_MSP7120_GW) \ + || defined(CONFIG_PMC_MSP7120_EVAL) + /* + * Force all CNFG to be identical and equal to CS0, + * according to OPS doc + */ + *CS1_CNFG_REG = *CS2_CNFG_REG = *CS3_CNFG_REG = *CS0_CNFG_REG; +#endif + return 0; +} + +subsys_initcall(msp_elb_setup); diff --git a/arch/mips/pmc-sierra/msp71xx/msp_hwbutton.c b/arch/mips/pmc-sierra/msp71xx/msp_hwbutton.c new file mode 100644 index 000000000000..6fa85728158b --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_hwbutton.c @@ -0,0 +1,179 @@ +/* + * Sets up interrupt handlers for various hardware switches which are + * connected to interrupt lines. + * + * Copyright 2005-2207 PMC-Sierra, Inc. + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> + +#include <msp_int.h> +#include <msp_regs.h> +#include <msp_regops.h> +#ifdef CONFIG_PMCTWILED +#include <msp_led_macros.h> +#endif + +/* For hwbutton_interrupt->initial_state */ +#define HWBUTTON_HI 0x1 +#define HWBUTTON_LO 0x2 + +/* + * This struct describes a hardware button + */ +struct hwbutton_interrupt { + char *name; /* Name of button */ + int irq; /* Actual LINUX IRQ */ + int eirq; /* Extended IRQ number (0-7) */ + int initial_state; /* The "normal" state of the switch */ + void (*handle_hi)(void *); /* Handler: switch input has gone HI */ + void (*handle_lo)(void *); /* Handler: switch input has gone LO */ + void *data; /* Optional data to pass to handler */ +}; + +#ifdef CONFIG_PMC_MSP7120_GW +extern void msp_restart(char *); + +static void softreset_push(void *data) +{ + printk(KERN_WARNING "SOFTRESET switch was pushed\n"); + + /* + * In the future you could move this to the release handler, + * timing the difference between the 'push' and 'release', and only + * doing this ungraceful restart if the button has been down for + * a certain amount of time; otherwise doing a graceful restart. + */ + + msp_restart(NULL); +} + +static void softreset_release(void *data) +{ + printk(KERN_WARNING "SOFTRESET switch was released\n"); + + /* Do nothing */ +} + +static void standby_on(void *data) +{ + printk(KERN_WARNING "STANDBY switch was set to ON (not implemented)\n"); + + /* TODO: Put board in standby mode */ +#ifdef CONFIG_PMCTWILED + msp_led_turn_off(MSP_LED_PWRSTANDBY_GREEN); + msp_led_turn_on(MSP_LED_PWRSTANDBY_RED); +#endif +} + +static void standby_off(void *data) +{ + printk(KERN_WARNING + "STANDBY switch was set to OFF (not implemented)\n"); + + /* TODO: Take out of standby mode */ +#ifdef CONFIG_PMCTWILED + msp_led_turn_on(MSP_LED_PWRSTANDBY_GREEN); + msp_led_turn_off(MSP_LED_PWRSTANDBY_RED); +#endif +} + +static struct hwbutton_interrupt softreset_sw = { + .name = "Softreset button", + .irq = MSP_INT_EXT0, + .eirq = 0, + .initial_state = HWBUTTON_HI, + .handle_hi = softreset_release, + .handle_lo = softreset_push, + .data = NULL, +}; + +static struct hwbutton_interrupt standby_sw = { + .name = "Standby switch", + .irq = MSP_INT_EXT1, + .eirq = 1, + .initial_state = HWBUTTON_HI, + .handle_hi = standby_off, + .handle_lo = standby_on, + .data = NULL, +}; +#endif /* CONFIG_PMC_MSP7120_GW */ + +static irqreturn_t hwbutton_handler(int irq, void *data) +{ + struct hwbutton_interrupt *hirq = data; + unsigned long cic_ext = *CIC_EXT_CFG_REG; + + if (irq != hirq->irq) + return IRQ_NONE; + + if (CIC_EXT_IS_ACTIVE_HI(cic_ext, hirq->eirq)) { + /* Interrupt: pin is now HI */ + CIC_EXT_SET_ACTIVE_LO(cic_ext, hirq->eirq); + hirq->handle_hi(hirq->data); + } else { + /* Interrupt: pin is now LO */ + CIC_EXT_SET_ACTIVE_HI(cic_ext, hirq->eirq); + hirq->handle_lo(hirq->data); + } + + /* + * Invert the POLARITY of this level interrupt to ack the interrupt + * Thus next state change will invoke the opposite message + */ + *CIC_EXT_CFG_REG = cic_ext; + + return IRQ_HANDLED; +} + +static int msp_hwbutton_register(struct hwbutton_interrupt *hirq) +{ + unsigned long cic_ext; + + if (hirq->handle_hi == NULL || hirq->handle_lo == NULL) + return -EINVAL; + + cic_ext = *CIC_EXT_CFG_REG; + CIC_EXT_SET_TRIGGER_LEVEL(cic_ext, hirq->eirq); + if (hirq->initial_state == HWBUTTON_HI) + CIC_EXT_SET_ACTIVE_LO(cic_ext, hirq->eirq); + else + CIC_EXT_SET_ACTIVE_HI(cic_ext, hirq->eirq); + *CIC_EXT_CFG_REG = cic_ext; + + return request_irq(hirq->irq, hwbutton_handler, SA_INTERRUPT, + hirq->name, (void *)hirq); +} + +static int __init msp_hwbutton_setup(void) +{ +#ifdef CONFIG_PMC_MSP7120_GW + msp_hwbutton_register(&softreset_sw); + msp_hwbutton_register(&standby_sw); +#endif + return 0; +} + +subsys_initcall(msp_hwbutton_setup); diff --git a/arch/mips/pmc-sierra/msp71xx/msp_irq.c b/arch/mips/pmc-sierra/msp71xx/msp_irq.c new file mode 100644 index 000000000000..734d598a2e3a --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_irq.c @@ -0,0 +1,124 @@ +/* + * IRQ vector handles + * + * Copyright (C) 1995, 1996, 1997, 2003 by Ralf Baechle + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/time.h> + +#include <asm/irq_cpu.h> + +#include <msp_int.h> + +extern void msp_int_handle(void); + +/* SLP bases systems */ +extern void msp_slp_irq_init(void); +extern void msp_slp_irq_dispatch(void); + +/* CIC based systems */ +extern void msp_cic_irq_init(void); +extern void msp_cic_irq_dispatch(void); + +/* + * The PMC-Sierra MSP interrupts are arranged in a 3 level cascaded + * hierarchical system. The first level are the direct MIPS interrupts + * and are assigned the interrupt range 0-7. The second level is the SLM + * interrupt controller and is assigned the range 8-39. The third level + * comprises the Peripherial block, the PCI block, the PCI MSI block and + * the SLP. The PCI interrupts and the SLP errors are handled by the + * relevant subsystems so the core interrupt code needs only concern + * itself with the Peripheral block. These are assigned interrupts in + * the range 40-71. + */ + +asmlinkage void plat_irq_dispatch(struct pt_regs *regs) +{ + u32 pending; + + pending = read_c0_status() & read_c0_cause(); + + /* + * jump to the correct interrupt routine + * These are arranged in priority order and the timer + * comes first! + */ + +#ifdef CONFIG_IRQ_MSP_CIC /* break out the CIC stuff for now */ + if (pending & C_IRQ4) /* do the peripherals first, that's the timer */ + msp_cic_irq_dispatch(); + + else if (pending & C_IRQ0) + do_IRQ(MSP_INT_MAC0); + + else if (pending & C_IRQ1) + do_IRQ(MSP_INT_MAC1); + + else if (pending & C_IRQ2) + do_IRQ(MSP_INT_USB); + + else if (pending & C_IRQ3) + do_IRQ(MSP_INT_SAR); + + else if (pending & C_IRQ5) + do_IRQ(MSP_INT_SEC); + +#else + if (pending & C_IRQ5) + do_IRQ(MSP_INT_TIMER); + + else if (pending & C_IRQ0) + do_IRQ(MSP_INT_MAC0); + + else if (pending & C_IRQ1) + do_IRQ(MSP_INT_MAC1); + + else if (pending & C_IRQ3) + do_IRQ(MSP_INT_VE); + + else if (pending & C_IRQ4) + msp_slp_irq_dispatch(); +#endif + + else if (pending & C_SW0) /* do software after hardware */ + do_IRQ(MSP_INT_SW0); + + else if (pending & C_SW1) + do_IRQ(MSP_INT_SW1); +} + +static struct irqaction cascade_msp = { + .handler = no_action, + .name = "MSP cascade" +}; + + +void __init arch_init_irq(void) +{ + /* initialize the 1st-level CPU based interrupt controller */ + mips_cpu_irq_init(); + +#ifdef CONFIG_IRQ_MSP_CIC + msp_cic_irq_init(); + + /* setup the cascaded interrupts */ + setup_irq(MSP_INT_CIC, &cascade_msp); + setup_irq(MSP_INT_PER, &cascade_msp); +#else + /* setup the 2nd-level SLP register based interrupt controller */ + msp_slp_irq_init(); + + /* setup the cascaded SLP/PER interrupts */ + setup_irq(MSP_INT_SLP, &cascade_msp); + setup_irq(MSP_INT_PER, &cascade_msp); +#endif +} diff --git a/arch/mips/pmc-sierra/msp71xx/msp_irq_cic.c b/arch/mips/pmc-sierra/msp71xx/msp_irq_cic.c new file mode 100644 index 000000000000..5175357d0a25 --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_irq_cic.c @@ -0,0 +1,134 @@ +/* + * This file define the irq handler for MSP SLM subsystem interrupts. + * + * Copyright 2005-2007 PMC-Sierra, Inc, derived from irq_cpu.c + * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/bitops.h> + +#include <asm/system.h> + +#include <msp_cic_int.h> +#include <msp_regs.h> + +/* + * NOTE: We are only enabling support for VPE0 right now. + */ + +static inline void unmask_msp_cic_irq(unsigned int irq) +{ + + /* check for PER interrupt range */ + if (irq < MSP_PER_INTBASE) + *CIC_VPE0_MSK_REG |= (1 << (irq - MSP_CIC_INTBASE)); + else + *PER_INT_MSK_REG |= (1 << (irq - MSP_PER_INTBASE)); +} + +static inline void mask_msp_cic_irq(unsigned int irq) +{ + /* check for PER interrupt range */ + if (irq < MSP_PER_INTBASE) + *CIC_VPE0_MSK_REG &= ~(1 << (irq - MSP_CIC_INTBASE)); + else + *PER_INT_MSK_REG &= ~(1 << (irq - MSP_PER_INTBASE)); +} + +/* + * While we ack the interrupt interrupts are disabled and thus we don't need + * to deal with concurrency issues. Same for msp_cic_irq_end. + */ +static inline void ack_msp_cic_irq(unsigned int irq) +{ + mask_msp_cic_irq(irq); + + /* + * only really necessary for 18, 16-14 and sometimes 3:0 (since + * these can be edge sensitive) but it doesn't hurt for the others. + */ + + /* check for PER interrupt range */ + if (irq < MSP_PER_INTBASE) + *CIC_STS_REG = (1 << (irq - MSP_CIC_INTBASE)); + else + *PER_INT_STS_REG = (1 << (irq - MSP_PER_INTBASE)); +} + +static struct irq_chip msp_cic_irq_controller = { + .name = "MSP_CIC", + .ack = ack_msp_cic_irq, + .mask = ack_msp_cic_irq, + .mask_ack = ack_msp_cic_irq, + .unmask = unmask_msp_cic_irq, +}; + + +void __init msp_cic_irq_init(void) +{ + int i; + + /* Mask/clear interrupts. */ + *CIC_VPE0_MSK_REG = 0x00000000; + *PER_INT_MSK_REG = 0x00000000; + *CIC_STS_REG = 0xFFFFFFFF; + *PER_INT_STS_REG = 0xFFFFFFFF; + +#if defined(CONFIG_PMC_MSP7120_GW) || \ + defined(CONFIG_PMC_MSP7120_EVAL) + /* + * The MSP7120 RG and EVBD boards use IRQ[6:4] for PCI. + * These inputs map to EXT_INT_POL[6:4] inside the CIC. + * They are to be active low, level sensitive. + */ + *CIC_EXT_CFG_REG &= 0xFFFF8F8F; +#endif + + /* initialize all the IRQ descriptors */ + for (i = MSP_CIC_INTBASE; i < MSP_PER_INTBASE + 32; i++) + set_irq_chip_and_handler(i, &msp_cic_irq_controller, + handle_level_irq); +} + +void msp_cic_irq_dispatch(void) +{ + u32 pending; + int intbase; + + intbase = MSP_CIC_INTBASE; + pending = *CIC_STS_REG & *CIC_VPE0_MSK_REG; + + /* check for PER interrupt */ + if (pending == (1 << (MSP_INT_PER - MSP_CIC_INTBASE))) { + intbase = MSP_PER_INTBASE; + pending = *PER_INT_STS_REG & *PER_INT_MSK_REG; + } + + /* check for spurious interrupt */ + if (pending == 0x00000000) { + printk(KERN_ERR + "Spurious %s interrupt? status %08x, mask %08x\n", + (intbase == MSP_CIC_INTBASE) ? "CIC" : "PER", + (intbase == MSP_CIC_INTBASE) ? + *CIC_STS_REG : *PER_INT_STS_REG, + (intbase == MSP_CIC_INTBASE) ? + *CIC_VPE0_MSK_REG : *PER_INT_MSK_REG); + return; + } + + /* check for the timer and dispatch it first */ + if ((intbase == MSP_CIC_INTBASE) && + (pending & (1 << (MSP_INT_VPE0_TIMER - MSP_CIC_INTBASE)))) + do_IRQ(MSP_INT_VPE0_TIMER); + else + do_IRQ(ffs(pending) + intbase - 1); +} + diff --git a/arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c b/arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c new file mode 100644 index 000000000000..f5f1b8d2bb9a --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c @@ -0,0 +1,109 @@ +/* + * This file define the irq handler for MSP SLM subsystem interrupts. + * + * Copyright 2005-2006 PMC-Sierra, Inc, derived from irq_cpu.c + * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/bitops.h> + +#include <asm/mipsregs.h> +#include <asm/system.h> + +#include <msp_slp_int.h> +#include <msp_regs.h> + +static inline void unmask_msp_slp_irq(unsigned int irq) +{ + /* check for PER interrupt range */ + if (irq < MSP_PER_INTBASE) + *SLP_INT_MSK_REG |= (1 << (irq - MSP_SLP_INTBASE)); + else + *PER_INT_MSK_REG |= (1 << (irq - MSP_PER_INTBASE)); +} + +static inline void mask_msp_slp_irq(unsigned int irq) +{ + /* check for PER interrupt range */ + if (irq < MSP_PER_INTBASE) + *SLP_INT_MSK_REG &= ~(1 << (irq - MSP_SLP_INTBASE)); + else + *PER_INT_MSK_REG &= ~(1 << (irq - MSP_PER_INTBASE)); +} + +/* + * While we ack the interrupt interrupts are disabled and thus we don't need + * to deal with concurrency issues. Same for msp_slp_irq_end. + */ +static inline void ack_msp_slp_irq(unsigned int irq) +{ + mask_slp_irq(irq); + + /* + * only really necessary for 18, 16-14 and sometimes 3:0 (since + * these can be edge sensitive) but it doesn't hurt for the others. + */ + + /* check for PER interrupt range */ + if (irq < MSP_PER_INTBASE) + *SLP_INT_STS_REG = (1 << (irq - MSP_SLP_INTBASE)); + else + *PER_INT_STS_REG = (1 << (irq - MSP_PER_INTBASE)); +} + +static struct irq_chip msp_slp_irq_controller = { + .name = "MSP_SLP", + .ack = ack_msp_slp_irq, + .mask = ack_msp_slp_irq, + .mask_ack = ack_msp_slp_irq, + .unmask = unmask_msp_slp_irq, +}; + +void __init msp_slp_irq_init(void) +{ + int i; + + /* Mask/clear interrupts. */ + *SLP_INT_MSK_REG = 0x00000000; + *PER_INT_MSK_REG = 0x00000000; + *SLP_INT_STS_REG = 0xFFFFFFFF; + *PER_INT_STS_REG = 0xFFFFFFFF; + + /* initialize all the IRQ descriptors */ + for (i = MSP_SLP_INTBASE; i < MSP_PER_INTBASE + 32; i++) + set_irq_chip_and_handler(i, &msp_slp_irq_controller + handle_level_irq); +} + +void msp_slp_irq_dispatch(void) +{ + u32 pending; + int intbase; + + intbase = MSP_SLP_INTBASE; + pending = *SLP_INT_STS_REG & *SLP_INT_MSK_REG; + + /* check for PER interrupt */ + if (pending == (1 << (MSP_INT_PER - MSP_SLP_INTBASE))) { + intbase = MSP_PER_INTBASE; + pending = *PER_INT_STS_REG & *PER_INT_MSK_REG; + } + + /* check for spurious interrupt */ + if (pending == 0x00000000) { + printk(KERN_ERR "Spurious %s interrupt?\n", + (intbase == MSP_SLP_INTBASE) ? "SLP" : "PER"); + return; + } + + /* dispatch the irq */ + do_IRQ(ffs(pending) + intbase - 1); +} diff --git a/arch/mips/pmc-sierra/msp71xx/msp_prom.c b/arch/mips/pmc-sierra/msp71xx/msp_prom.c new file mode 100644 index 000000000000..e5bd5481d8db --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_prom.c @@ -0,0 +1,566 @@ +/* + * BRIEF MODULE DESCRIPTION + * PROM library initialisation code, assuming a version of + * pmon is the boot code. + * + * Copyright 2000,2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * This file was derived from Carsten Langgaard's + * arch/mips/mips-boards/xx files. + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/mm.h> +#ifdef CONFIG_CRAMFS +#include <linux/cramfs_fs.h> +#endif +#ifdef CONFIG_SQUASHFS +#include <linux/squashfs_fs.h> +#endif + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm-generic/sections.h> +#include <asm/page.h> + +#include <msp_prom.h> +#include <msp_regs.h> + +/* global PROM environment variables and pointers */ +int prom_argc; +char **prom_argv, **prom_envp; +int *prom_vec; + +/* debug flag */ +int init_debug = 1; + +/* memory blocks */ +struct prom_pmemblock mdesc[PROM_MAX_PMEMBLOCKS]; + +/* default feature sets */ +static char msp_default_features[] = +#if defined(CONFIG_PMC_MSP4200_EVAL) \ + || defined(CONFIG_PMC_MSP4200_GW) + "ERER"; +#elif defined(CONFIG_PMC_MSP7120_EVAL) \ + || defined(CONFIG_PMC_MSP7120_GW) + "EMEMSP"; +#elif defined(CONFIG_PMC_MSP7120_FPGA) + "EMEM"; +#endif + +/* conversion functions */ +static inline unsigned char str2hexnum(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return 0; /* foo */ +} + +static inline int str2eaddr(unsigned char *ea, unsigned char *str) +{ + int index = 0; + unsigned char num = 0; + + while (*str != '\0') { + if ((*str == '.') || (*str == ':')) { + ea[index++] = num; + num = 0; + str++; + } else { + num = num << 4; + num |= str2hexnum(*str++); + } + } + + if (index == 5) { + ea[index++] = num; + return 0; + } else + return -1; +} +EXPORT_SYMBOL(str2eaddr); + +static inline unsigned long str2hex(unsigned char *str) +{ + int value = 0; + + while (*str) { + value = value << 4; + value |= str2hexnum(*str++); + } + + return value; +} + +/* function to query the system information */ +const char *get_system_type(void) +{ +#if defined(CONFIG_PMC_MSP4200_EVAL) + return "PMC-Sierra MSP4200 Eval Board"; +#elif defined(CONFIG_PMC_MSP4200_GW) + return "PMC-Sierra MSP4200 VoIP Gateway"; +#elif defined(CONFIG_PMC_MSP7120_EVAL) + return "PMC-Sierra MSP7120 Eval Board"; +#elif defined(CONFIG_PMC_MSP7120_GW) + return "PMC-Sierra MSP7120 Residential Gateway"; +#elif defined(CONFIG_PMC_MSP7120_FPGA) + return "PMC-Sierra MSP7120 FPGA"; +#else + #error "What is the type of *your* MSP?" +#endif +} + +int get_ethernet_addr(char *ethaddr_name, char *ethernet_addr) +{ + char *ethaddr_str; + + ethaddr_str = prom_getenv(ethaddr_name); + if (!ethaddr_str) { + printk(KERN_WARNING "%s not set in boot prom\n", ethaddr_name); + return -1; + } + + if (str2eaddr(ethernet_addr, ethaddr_str) == -1) { + printk(KERN_WARNING "%s badly formatted-<%s>\n", + ethaddr_name, ethaddr_str); + return -1; + } + + if (init_debug > 1) { + int i; + printk(KERN_DEBUG "get_ethernet_addr: for %s ", ethaddr_name); + for (i = 0; i < 5; i++) + printk(KERN_DEBUG "%02x:", + (unsigned char)*(ethernet_addr+i)); + printk(KERN_DEBUG "%02x\n", *(ethernet_addr+i)); + } + + return 0; +} +EXPORT_SYMBOL(get_ethernet_addr); + +static char *get_features(void) +{ + char *feature = prom_getenv(FEATURES); + + if (feature == NULL) { + /* default features based on MACHINE_TYPE */ + feature = msp_default_features; + } + + return feature; +} + +static char test_feature(char c) +{ + char *feature = get_features(); + + while (*feature) { + if (*feature++ == c) + return *feature; + feature++; + } + + return FEATURE_NOEXIST; +} + +unsigned long get_deviceid(void) +{ + char *deviceid = prom_getenv(DEVICEID); + + if (deviceid == NULL) + return *DEV_ID_REG; + else + return str2hex(deviceid); +} + +char identify_pci(void) +{ + return test_feature(PCI_KEY); +} +EXPORT_SYMBOL(identify_pci); + +char identify_pcimux(void) +{ + return test_feature(PCIMUX_KEY); +} + +char identify_sec(void) +{ + return test_feature(SEC_KEY); +} +EXPORT_SYMBOL(identify_sec); + +char identify_spad(void) +{ + return test_feature(SPAD_KEY); +} +EXPORT_SYMBOL(identify_spad); + +char identify_tdm(void) +{ + return test_feature(TDM_KEY); +} +EXPORT_SYMBOL(identify_tdm); + +char identify_zsp(void) +{ + return test_feature(ZSP_KEY); +} +EXPORT_SYMBOL(identify_zsp); + +static char identify_enetfeature(char key, unsigned long interface_num) +{ + char *feature = get_features(); + + while (*feature) { + if (*feature++ == key && interface_num-- == 0) + return *feature; + feature++; + } + + return FEATURE_NOEXIST; +} + +char identify_enet(unsigned long interface_num) +{ + return identify_enetfeature(ENET_KEY, interface_num); +} +EXPORT_SYMBOL(identify_enet); + +char identify_enetTxD(unsigned long interface_num) +{ + return identify_enetfeature(ENETTXD_KEY, interface_num); +} +EXPORT_SYMBOL(identify_enetTxD); + +unsigned long identify_family(void) +{ + unsigned long deviceid; + + deviceid = get_deviceid(); + + return deviceid & CPU_DEVID_FAMILY; +} +EXPORT_SYMBOL(identify_family); + +unsigned long identify_revision(void) +{ + unsigned long deviceid; + + deviceid = get_deviceid(); + + return deviceid & CPU_DEVID_REVISION; +} +EXPORT_SYMBOL(identify_revision); + +/* PROM environment functions */ +char *prom_getenv(char *env_name) +{ + /* + * Return a pointer to the given environment variable. prom_envp + * points to a null terminated array of pointers to variables. + * Environment variables are stored in the form of "memsize=64" + */ + + char **var = prom_envp; + int i = strlen(env_name); + + while (*var) { + if (strncmp(env_name, *var, i) == 0) { + return (*var + strlen(env_name) + 1); + } + var++; + } + + return NULL; +} + +/* PROM commandline functions */ +char *prom_getcmdline(void) +{ + return &(arcs_cmdline[0]); +} +EXPORT_SYMBOL(prom_getcmdline); + +void __init prom_init_cmdline(void) +{ + char *cp; + int actr; + + actr = 1; /* Always ignore argv[0] */ + + cp = &(arcs_cmdline[0]); + while (actr < prom_argc) { + strcpy(cp, prom_argv[actr]); + cp += strlen(prom_argv[actr]); + *cp++ = ' '; + actr++; + } + if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ + --cp; + *cp = '\0'; +} + +/* memory allocation functions */ +static int __init prom_memtype_classify(unsigned int type) +{ + switch (type) { + case yamon_free: + return BOOT_MEM_RAM; + case yamon_prom: + return BOOT_MEM_ROM_DATA; + default: + return BOOT_MEM_RESERVED; + } +} + +void __init prom_meminit(void) +{ + struct prom_pmemblock *p; + + p = prom_getmdesc(); + + while (p->size) { + long type; + unsigned long base, size; + + type = prom_memtype_classify(p->type); + base = p->base; + size = p->size; + + add_memory_region(base, size, type); + p++; + } +} + +void __init prom_free_prom_memory(void) +{ + int argc; + char **argv; + char **envp; + char *ptr; + int len = 0; + int i; + unsigned long addr; + + /* + * preserve environment variables and command line from pmon/bbload + * first preserve the command line + */ + for (argc = 0; argc < prom_argc; argc++) { + len += sizeof(char *); /* length of pointer */ + len += strlen(prom_argv[argc]) + 1; /* length of string */ + } + len += sizeof(char *); /* plus length of null pointer */ + + argv = kmalloc(len, GFP_KERNEL); + ptr = (char *) &argv[prom_argc + 1]; /* strings follow array */ + + for (argc = 0; argc < prom_argc; argc++) { + argv[argc] = ptr; + strcpy(ptr, prom_argv[argc]); + ptr += strlen(prom_argv[argc]) + 1; + } + argv[prom_argc] = NULL; /* end array with null pointer */ + prom_argv = argv; + + /* next preserve the environment variables */ + len = 0; + i = 0; + for (envp = prom_envp; *envp != NULL; envp++) { + i++; /* count number of environment variables */ + len += sizeof(char *); /* length of pointer */ + len += strlen(*envp) + 1; /* length of string */ + } + len += sizeof(char *); /* plus length of null pointer */ + + envp = kmalloc(len, GFP_KERNEL); + ptr = (char *) &envp[i+1]; + + for (argc = 0; argc < i; argc++) { + envp[argc] = ptr; + strcpy(ptr, prom_envp[argc]); + ptr += strlen(prom_envp[argc]) + 1; + } + envp[i] = NULL; /* end array with null pointer */ + prom_envp = envp; + + for (i = 0; i < boot_mem_map.nr_map; i++) { + if (boot_mem_map.map[i].type != BOOT_MEM_ROM_DATA) + continue; + + addr = boot_mem_map.map[i].addr; + free_init_pages("prom memory", + addr, addr + boot_mem_map.map[i].size); + } +} + +struct prom_pmemblock *__init prom_getmdesc(void) +{ + static char memsz_env[] __initdata = "memsize"; + static char heaptop_env[] __initdata = "heaptop"; + char *str; + unsigned int memsize; + unsigned int heaptop; +#ifdef CONFIG_MTD_PMC_MSP_RAMROOT + void *ramroot_start; + unsigned long ramroot_size; +#endif + int i; + + str = prom_getenv(memsz_env); + if (!str) { + ppfinit("memsize not set in boot prom, " + "set to default (32Mb)\n"); + memsize = 0x02000000; + } else { + memsize = simple_strtol(str, NULL, 0); + + if (memsize == 0) { + /* if memsize is a bad size, use reasonable default */ + memsize = 0x02000000; + } + + /* convert to physical address (removing caching bits, etc) */ + memsize = CPHYSADDR(memsize); + } + + str = prom_getenv(heaptop_env); + if (!str) { + heaptop = CPHYSADDR((u32)&_text); + ppfinit("heaptop not set in boot prom, " + "set to default 0x%08x\n", heaptop); + } else { + heaptop = simple_strtol(str, NULL, 16); + if (heaptop == 0) { + /* heaptop conversion bad, might have 0xValue */ + heaptop = simple_strtol(str, NULL, 0); + + if (heaptop == 0) { + /* heaptop still bad, use reasonable default */ + heaptop = CPHYSADDR((u32)&_text); + } + } + + /* convert to physical address (removing caching bits, etc) */ + heaptop = CPHYSADDR((u32)heaptop); + } + + /* the base region */ + i = 0; + mdesc[i].type = BOOT_MEM_RESERVED; + mdesc[i].base = 0x00000000; + mdesc[i].size = PAGE_ALIGN(0x300 + 0x80); + /* jtag interrupt vector + sizeof vector */ + + /* PMON data */ + if (heaptop > mdesc[i].base + mdesc[i].size) { + i++; /* 1 */ + mdesc[i].type = BOOT_MEM_ROM_DATA; + mdesc[i].base = mdesc[i-1].base + mdesc[i-1].size; + mdesc[i].size = heaptop - mdesc[i].base; + } + + /* end of PMON data to start of kernel -- probably zero .. */ + if (heaptop != CPHYSADDR((u32)_text)) { + i++; /* 2 */ + mdesc[i].type = BOOT_MEM_RAM; + mdesc[i].base = heaptop; + mdesc[i].size = CPHYSADDR((u32)_text) - mdesc[i].base; + } + + /* kernel proper */ + i++; /* 3 */ + mdesc[i].type = BOOT_MEM_RESERVED; + mdesc[i].base = CPHYSADDR((u32)_text); +#ifdef CONFIG_MTD_PMC_MSP_RAMROOT + if (get_ramroot(&ramroot_start, &ramroot_size)) { + /* + * Rootfs in RAM -- follows kernel + * Combine rootfs image with kernel block so a + * page (4k) isn't wasted between memory blocks + */ + mdesc[i].size = CPHYSADDR(PAGE_ALIGN( + (u32)ramroot_start + ramroot_size)) - mdesc[i].base; + } else +#endif + mdesc[i].size = CPHYSADDR(PAGE_ALIGN( + (u32)_end)) - mdesc[i].base; + + /* Remainder of RAM -- under memsize */ + i++; /* 5 */ + mdesc[i].type = yamon_free; + mdesc[i].base = mdesc[i-1].base + mdesc[i-1].size; + mdesc[i].size = memsize - mdesc[i].base; + + return &mdesc[0]; +} + +/* rootfs functions */ +#ifdef CONFIG_MTD_PMC_MSP_RAMROOT +bool get_ramroot(void **start, unsigned long *size) +{ + extern char _end[]; + + /* Check for start following the end of the kernel */ + void *check_start = (void *)_end; + + /* Check for supported rootfs types */ +#ifdef CONFIG_CRAMFS + if (*(__u32 *)check_start == CRAMFS_MAGIC) { + /* Get CRAMFS size */ + *start = check_start; + *size = PAGE_ALIGN(((struct cramfs_super *) + check_start)->size); + + return true; + } +#endif +#ifdef CONFIG_SQUASHFS + if (*((unsigned int *)check_start) == SQUASHFS_MAGIC) { + /* Get SQUASHFS size */ + *start = check_start; + *size = PAGE_ALIGN(((struct squashfs_super_block *) + check_start)->bytes_used); + + return true; + } +#endif + + return false; +} +EXPORT_SYMBOL(get_ramroot); +#endif diff --git a/arch/mips/pmc-sierra/msp71xx/msp_setup.c b/arch/mips/pmc-sierra/msp71xx/msp_setup.c new file mode 100644 index 000000000000..8f69b789be90 --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_setup.c @@ -0,0 +1,256 @@ +/* + * The generic setup file for PMC-Sierra MSP processors + * + * Copyright 2005-2007 PMC-Sierra, Inc, + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <asm/bootinfo.h> +#include <asm/cacheflush.h> +#include <asm/r4kcache.h> +#include <asm/reboot.h> +#include <asm/time.h> + +#include <msp_prom.h> +#include <msp_regs.h> + +#if defined(CONFIG_PMC_MSP7120_GW) +#include <msp_regops.h> +#include <msp_gpio.h> +#define MSP_BOARD_RESET_GPIO 9 +#endif + +extern void msp_timer_init(void); +extern void msp_serial_setup(void); +extern void pmctwiled_setup(void); + +#if defined(CONFIG_PMC_MSP7120_EVAL) || \ + defined(CONFIG_PMC_MSP7120_GW) || \ + defined(CONFIG_PMC_MSP7120_FPGA) +/* + * Performs the reset for MSP7120-based boards + */ +void msp7120_reset(void) +{ + void *start, *end, *iptr; + register int i; + + /* Diasble all interrupts */ + local_irq_disable(); +#ifdef CONFIG_SYS_SUPPORTS_MULTITHREADING + dvpe(); +#endif + + /* Cache the reset code of this function */ + __asm__ __volatile__ ( + " .set push \n" + " .set mips3 \n" + " la %0,startpoint \n" + " la %1,endpoint \n" + " .set pop \n" + : "=r" (start), "=r" (end) + : + ); + + for (iptr = (void *)((unsigned int)start & ~(L1_CACHE_BYTES - 1)); + iptr < end; iptr += L1_CACHE_BYTES) + cache_op(Fill, iptr); + + __asm__ __volatile__ ( + "startpoint: \n" + ); + + /* Put the DDRC into self-refresh mode */ + DDRC_INDIRECT_WRITE(DDRC_CTL(10), 0xb, 1 << 16); + + /* + * IMPORTANT! + * DO NOT do anything from here on out that might even + * think about fetching from RAM - i.e., don't call any + * non-inlined functions, and be VERY sure that any inline + * functions you do call do NOT access any sort of RAM + * anywhere! + */ + + /* Wait a bit for the DDRC to settle */ + for (i = 0; i < 100000000; i++); + +#if defined(CONFIG_PMC_MSP7120_GW) + /* + * Set GPIO 9 HI, (tied to board reset logic) + * GPIO 9 is the 4th GPIO of register 3 + * + * NOTE: We cannot use the higher-level msp_gpio_mode()/out() + * as GPIO char driver may not be enabled and it would look up + * data inRAM! + */ + set_value_reg32(GPIO_CFG3_REG, + basic_mode_mask(MSP_BOARD_RESET_GPIO), + basic_mode(MSP_GPIO_OUTPUT, MSP_BOARD_RESET_GPIO)); + set_reg32(GPIO_DATA3_REG, + basic_data_mask(MSP_BOARD_RESET_GPIO)); + + /* + * In case GPIO9 doesn't reset the board (jumper configurable!) + * fallback to device reset below. + */ +#endif + /* Set bit 1 of the MSP7120 reset register */ + *RST_SET_REG = 0x00000001; + + __asm__ __volatile__ ( + "endpoint: \n" + ); +} +#endif + +void msp_restart(char *command) +{ + printk(KERN_WARNING "Now rebooting .......\n"); + +#if defined(CONFIG_PMC_MSP7120_EVAL) || \ + defined(CONFIG_PMC_MSP7120_GW) || \ + defined(CONFIG_PMC_MSP7120_FPGA) + msp7120_reset(); +#else + /* No chip-specific reset code, just jump to the ROM reset vector */ + set_c0_status(ST0_BEV | ST0_ERL); + change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); + flush_cache_all(); + write_c0_wired(0); + + __asm__ __volatile__("jr\t%0"::"r"(0xbfc00000)); +#endif +} + +void msp_halt(void) +{ + printk(KERN_WARNING "\n** You can safely turn off the power\n"); + while (1) + /* If possible call official function to get CPU WARs */ + if (cpu_wait) + (*cpu_wait)(); + else + __asm__(".set\tmips3\n\t" "wait\n\t" ".set\tmips0"); +} + +void msp_power_off(void) +{ + msp_halt(); +} + +void __init plat_mem_setup(void) +{ + _machine_restart = msp_restart; + _machine_halt = msp_halt; + pm_power_off = msp_power_off; + + board_time_init = msp_timer_init; +} + +void __init prom_init(void) +{ + unsigned long family; + unsigned long revision; + + prom_argc = fw_arg0; + prom_argv = (char **)fw_arg1; + prom_envp = (char **)fw_arg2; + + /* + * Someday we can use this with PMON2000 to get a + * platform call prom routines for output etc. without + * having to use grody hacks. For now it's unused. + * + * struct callvectors *cv = (struct callvectors *) fw_arg3; + */ + family = identify_family(); + revision = identify_revision(); + + switch (family) { + case FAMILY_FPGA: + if (FPGA_IS_MSP4200(revision)) { + /* Old-style revision ID */ + mips_machgroup = MACH_GROUP_MSP; + mips_machtype = MACH_MSP4200_FPGA; + } else { + mips_machgroup = MACH_GROUP_MSP; + mips_machtype = MACH_MSP_OTHER; + } + break; + + case FAMILY_MSP4200: + mips_machgroup = MACH_GROUP_MSP; +#if defined(CONFIG_PMC_MSP4200_EVAL) + mips_machtype = MACH_MSP4200_EVAL; +#elif defined(CONFIG_PMC_MSP4200_GW) + mips_machtype = MACH_MSP4200_GW; +#else + mips_machtype = MACH_MSP_OTHER; +#endif + break; + + case FAMILY_MSP4200_FPGA: + mips_machgroup = MACH_GROUP_MSP; + mips_machtype = MACH_MSP4200_FPGA; + break; + + case FAMILY_MSP7100: + mips_machgroup = MACH_GROUP_MSP; +#if defined(CONFIG_PMC_MSP7120_EVAL) + mips_machtype = MACH_MSP7120_EVAL; +#elif defined(CONFIG_PMC_MSP7120_GW) + mips_machtype = MACH_MSP7120_GW; +#else + mips_machtype = MACH_MSP_OTHER; +#endif + break; + + case FAMILY_MSP7100_FPGA: + mips_machgroup = MACH_GROUP_MSP; + mips_machtype = MACH_MSP7120_FPGA; + break; + + default: + /* we don't recognize the machine */ + mips_machgroup = MACH_GROUP_UNKNOWN; + mips_machtype = MACH_UNKNOWN; + break; + } + + /* make sure we have the right initialization routine - sanity */ + if (mips_machgroup != MACH_GROUP_MSP) { + ppfinit("Unknown machine group in a " + "MSP initialization routine\n"); + panic("***Bogosity factor five***, exiting\n"); + } + + prom_init_cmdline(); + + prom_meminit(); + + /* + * Sub-system setup follows. + * Setup functions can either be called here or using the + * subsys_initcall mechanism (i.e. see msp_pci_setup). The + * order in which they are called can be changed by using the + * link order in arch/mips/pmc-sierra/msp71xx/Makefile. + * + * NOTE: Please keep sub-system specific initialization code + * in separate specific files. + */ + msp_serial_setup(); + +#ifdef CONFIG_PMCTWILED + /* + * Setup LED states before the subsys_initcall loads other + * dependant drivers/modules. + */ + pmctwiled_setup(); +#endif +} diff --git a/arch/mips/pmc-sierra/msp71xx/msp_time.c b/arch/mips/pmc-sierra/msp71xx/msp_time.c new file mode 100644 index 000000000000..2a2beac5a4f8 --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_time.c @@ -0,0 +1,94 @@ +/* + * Setting up the clock on MSP SOCs. No RTC typically. + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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/init.h> +#include <linux/kernel_stat.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/ptrace.h> + +#include <asm/mipsregs.h> +#include <asm/time.h> + +#include <msp_prom.h> +#include <msp_int.h> +#include <msp_regs.h> + +void __init msp_timer_init(void) +{ + char *endp, *s; + unsigned long cpu_rate = 0; + + if (cpu_rate == 0) { + s = prom_getenv("clkfreqhz"); + cpu_rate = simple_strtoul(s, &endp, 10); + if (endp != NULL && *endp != 0) { + printk(KERN_ERR + "Clock rate in Hz parse error: %s\n", s); + cpu_rate = 0; + } + } + + if (cpu_rate == 0) { + s = prom_getenv("clkfreq"); + cpu_rate = 1000 * simple_strtoul(s, &endp, 10); + if (endp != NULL && *endp != 0) { + printk(KERN_ERR + "Clock rate in MHz parse error: %s\n", s); + cpu_rate = 0; + } + } + + if (cpu_rate == 0) { +#if defined(CONFIG_PMC_MSP7120_EVAL) \ + || defined(CONFIG_PMC_MSP7120_GW) + cpu_rate = 400000000; +#elif defined(CONFIG_PMC_MSP7120_FPGA) + cpu_rate = 25000000; +#else + cpu_rate = 150000000; +#endif + printk(KERN_ERR + "Failed to determine CPU clock rate, " + "assuming %ld hz ...\n", cpu_rate); + } + + printk(KERN_WARNING "Clock rate set to %ld\n", cpu_rate); + + /* timer frequency is 1/2 clock rate */ + mips_hpt_frequency = cpu_rate/2; +} + + +void __init plat_timer_setup(struct irqaction *irq) +{ +#ifdef CONFIG_IRQ_MSP_CIC + /* we are using the vpe0 counter for timer interrupts */ + setup_irq(MSP_INT_VPE0_TIMER, irq); +#else + /* we are using the mips counter for timer interrupts */ + setup_irq(MSP_INT_TIMER, irq); +#endif +} diff --git a/arch/mips/pmc-sierra/msp71xx/msp_usb.c b/arch/mips/pmc-sierra/msp71xx/msp_usb.c new file mode 100644 index 000000000000..21f9c70b6923 --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_usb.c @@ -0,0 +1,150 @@ +/* + * The setup file for USB related hardware on PMC-Sierra MSP processors. + * + * Copyright 2006-2007 PMC-Sierra, Inc. + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> + +#include <asm/mipsregs.h> + +#include <msp_regs.h> +#include <msp_int.h> +#include <msp_prom.h> + +#if defined(CONFIG_USB_EHCI_HCD) +static struct resource msp_usbhost_resources [] = { + [0] = { + .start = MSP_USB_BASE_START, + .end = MSP_USB_BASE_END, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSP_INT_USB, + .end = MSP_INT_USB, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 msp_usbhost_dma_mask = DMA_32BIT_MASK; + +static struct platform_device msp_usbhost_device = { + .name = "pmcmsp-ehci", + .id = 0, + .dev = { + .dma_mask = &msp_usbhost_dma_mask, + .coherent_dma_mask = DMA_32BIT_MASK, + }, + .num_resources = ARRAY_SIZE (msp_usbhost_resources), + .resource = msp_usbhost_resources, +}; +#endif /* CONFIG_USB_EHCI_HCD */ + +#if defined(CONFIG_USB_GADGET) +static struct resource msp_usbdev_resources [] = { + [0] = { + .start = MSP_USB_BASE, + .end = MSP_USB_BASE_END, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSP_INT_USB, + .end = MSP_INT_USB, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 msp_usbdev_dma_mask = DMA_32BIT_MASK; + +static struct platform_device msp_usbdev_device = { + .name = "msp71xx_udc", + .id = 0, + .dev = { + .dma_mask = &msp_usbdev_dma_mask, + .coherent_dma_mask = DMA_32BIT_MASK, + }, + .num_resources = ARRAY_SIZE (msp_usbdev_resources), + .resource = msp_usbdev_resources, +}; +#endif /* CONFIG_USB_GADGET */ + +#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_GADGET) +static struct platform_device *msp_devs[1]; +#endif + + +static int __init msp_usb_setup(void) +{ +#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_GADGET) + char *strp; + char envstr[32]; + unsigned int val = 0; + int result = 0; + + /* + * construct environment name usbmode + * set usbmode <host/device> as pmon environment var + */ + snprintf((char *)&envstr[0], sizeof(envstr), "usbmode"); + +#if defined(CONFIG_USB_EHCI_HCD) + /* default to host mode */ + val = 1; +#endif + + /* get environment string */ + strp = prom_getenv((char *)&envstr[0]); + if (strp) { + if (!strcmp(strp, "device")) + val = 0; + } + + if (val) { +#if defined(CONFIG_USB_EHCI_HCD) + /* get host mode device */ + msp_devs[0] = &msp_usbhost_device; + ppfinit("platform add USB HOST done %s.\n", + msp_devs[0]->name); + + result = platform_add_devices(msp_devs, ARRAY_SIZE (msp_devs)); +#endif /* CONFIG_USB_EHCI_HCD */ + } +#if defined(CONFIG_USB_GADGET) + else { + /* get device mode structure */ + msp_devs[0] = &msp_usbdev_device; + ppfinit("platform add USB DEVICE done %s.\n", + msp_devs[0]->name); + + result = platform_add_devices(msp_devs, ARRAY_SIZE (msp_devs)); + } +#endif /* CONFIG_USB_GADGET */ +#endif /* CONFIG_USB_EHCI_HCD || CONFIG_USB_GADGET */ + + return result; +} + +subsys_initcall(msp_usb_setup); |