Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35832e26 authored by Marc St-Jean's avatar Marc St-Jean Committed by Ralf Baechle
Browse files

[MIPS] PMC MSP71xx core platform



Patch to add core platform support for the PMC-Sierra MSP71xx devices.

Signed-off-by: default avatarMarc St-Jean <Marc_St-Jean@pmc-sierra.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent a4b156d4
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
#
# 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
+46 −0
Original line number Diff line number Diff line
/*
 * 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);
+179 −0
Original line number Diff line number Diff line
/*
 * 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);
+124 −0
Original line number Diff line number Diff line
/*
 * 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
}
+134 −0
Original line number Diff line number Diff line
/*
 * 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);
}
Loading