u-boot-brain/arch/powerpc/cpu/mpc8xx/interrupts.c
Christophe Leroy 907208c452 powerpc: Partialy restore core of mpc8xx
CS Systemes d'Information (CSSI) manufactures 8xx boards for
critical communication systems. Those boards have been
running U-Boot since 2010 and will have to be maintained
until at least 2027.

commit 5b8e76c35e
("powerpc, 8xx: remove support for 8xx") orphaned those boards
by removing support for the mpc8xx CPU.

This commit partially restores support for the 8xx, with the
following limitations:
- Restores support for MPC866 and MPC885 only
- Does not restore IDE, PCMCIA, I2C, USB
- Does not restore examples
- Does not restore POST
- Does not restore Ethernet on SCC
- Does not restore console on SCC
- Does not restore bedbug and kgdb support

As the 866 and 885 do not support the following features,
they are not restored either:
- VIDEO / LCD
- RTC clock

The CPM uCODE patch is not restored either, because:
- 866 and 885 already have support for I2C and SPI relocation
without a uCODE patch
- relocation of SMC, I2C or SPI is only needed for using SCCs
for Ethernet or QMC

The dynamic setup/calculation of clocks is removed, we
expect the target being use with the clock and PLPRCR register
defined in the configuration.
All the clock settings for 8xx prior to 866 is removed as
well as we now only support 866 and 885.

This code is mature and addresses mature boards. Therefore
all code enclosed in '#if 0/#endif' and '#if XX_DEBUG/#endif'
is unneeded.

The following files are not restored by this patch:

- arch/powerpc/cpu/mpc8xx/bedbug_860.c
- arch/powerpc/cpu/mpc8xx/fec.h
- arch/powerpc/cpu/mpc8xx/kgdb.S
- arch/powerpc/cpu/mpc8xx/plprcr_write.S
- arch/powerpc/cpu/mpc8xx/scc.c
- arch/powerpc/cpu/mpc8xx/upatch.c
- arch/powerpc/cpu/mpc8xx/video.c
- arch/powerpc/include/asm/status_led.h
- arch/powerpc/lib/ide.c
- arch/powerpc/lib/ide.h
- doc/README.MPC866
- drivers/pcmcia/mpc8xx_pcmcia.c
- drivers/rtc/mpc8xx.c
- drivers/usb/gadget/mpc8xx_udc.c
- drivers/video/mpc8xx_lcd.c
- examples/standalone/test_burst.c
- examples/standalone/test_burst.h
- examples/standalone/test_burst_lib.S
- examples/standalone/timer.c
- include/mpc823_lcd.h
- include/usb/mpc8xx_udc.h
- post/cpu/mpc8xx/Makefile
- post/cpu/mpc8xx/cache.c
- post/cpu/mpc8xx/cache_8xx.S
- post/cpu/mpc8xx/ether.c
- post/cpu/mpc8xx/spr.c
- post/cpu/mpc8xx/uart.c
- post/cpu/mpc8xx/usb.c
- post/cpu/mpc8xx/watchdog.c

Some of the restored files are not located in a proper location.
In order to keep traceability of the changes, they will be
moved to their correct location and moved to Kconfig in a
followup patch.

This patch also declares CSSI as point of contact for the update
of the 8xx platform, as those boards are the only ones still
being maintained on the 8xx area. A later patch will add
those boards to the tree.

Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
2017-07-08 15:55:26 -04:00

260 lines
6.2 KiB
C

/*
* (C) Copyright 2000-2002
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mpc8xx.h>
#include <mpc8xx_irq.h>
#include <asm/processor.h>
#include <commproc.h>
/************************************************************************/
/*
* CPM interrupt vector functions.
*/
struct interrupt_action {
interrupt_handler_t *handler;
void *arg;
};
static struct interrupt_action cpm_vecs[CPMVEC_NR];
static struct interrupt_action irq_vecs[NR_IRQS];
static void cpm_interrupt_init (void);
static void cpm_interrupt (void *regs);
/************************************************************************/
int interrupt_init_cpu (unsigned *decrementer_count)
{
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
*decrementer_count = get_tbclk () / CONFIG_SYS_HZ;
/* disable all interrupts */
immr->im_siu_conf.sc_simask = 0;
/* Configure CPM interrupts */
cpm_interrupt_init ();
return (0);
}
/************************************************************************/
/*
* Handle external interrupts
*/
void external_interrupt (struct pt_regs *regs)
{
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
int irq;
ulong simask, newmask;
ulong vec, v_bit;
/*
* read the SIVEC register and shift the bits down
* to get the irq number
*/
vec = immr->im_siu_conf.sc_sivec;
irq = vec >> 26;
v_bit = 0x80000000UL >> irq;
/*
* Read Interrupt Mask Register and Mask Interrupts
*/
simask = immr->im_siu_conf.sc_simask;
newmask = simask & (~(0xFFFF0000 >> irq));
immr->im_siu_conf.sc_simask = newmask;
if (!(irq & 0x1)) { /* External Interrupt ? */
ulong siel;
/*
* Read Interrupt Edge/Level Register
*/
siel = immr->im_siu_conf.sc_siel;
if (siel & v_bit) { /* edge triggered interrupt ? */
/*
* Rewrite SIPEND Register to clear interrupt
*/
immr->im_siu_conf.sc_sipend = v_bit;
}
}
if (irq_vecs[irq].handler != NULL) {
irq_vecs[irq].handler (irq_vecs[irq].arg);
} else {
printf ("\nBogus External Interrupt IRQ %d Vector %ld\n",
irq, vec);
/* turn off the bogus interrupt to avoid it from now */
simask &= ~v_bit;
}
/*
* Re-Enable old Interrupt Mask
*/
immr->im_siu_conf.sc_simask = simask;
}
/************************************************************************/
/*
* CPM interrupt handler
*/
static void cpm_interrupt (void *regs)
{
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
uint vec;
/*
* Get the vector by setting the ACK bit
* and then reading the register.
*/
immr->im_cpic.cpic_civr = 1;
vec = immr->im_cpic.cpic_civr;
vec >>= 11;
if (cpm_vecs[vec].handler != NULL) {
(*cpm_vecs[vec].handler) (cpm_vecs[vec].arg);
} else {
immr->im_cpic.cpic_cimr &= ~(1 << vec);
printf ("Masking bogus CPM interrupt vector 0x%x\n", vec);
}
/*
* After servicing the interrupt,
* we have to remove the status indicator.
*/
immr->im_cpic.cpic_cisr |= (1 << vec);
}
/*
* The CPM can generate the error interrupt when there is a race
* condition between generating and masking interrupts. All we have
* to do is ACK it and return. This is a no-op function so we don't
* need any special tests in the interrupt handler.
*/
static void cpm_error_interrupt (void *dummy)
{
}
/************************************************************************/
/*
* Install and free an interrupt handler
*/
void irq_install_handler (int vec, interrupt_handler_t * handler,
void *arg)
{
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
if ((vec & CPMVEC_OFFSET) != 0) {
/* CPM interrupt */
vec &= 0xffff;
if (cpm_vecs[vec].handler != NULL) {
printf ("CPM interrupt 0x%x replacing 0x%x\n",
(uint) handler,
(uint) cpm_vecs[vec].handler);
}
cpm_vecs[vec].handler = handler;
cpm_vecs[vec].arg = arg;
immr->im_cpic.cpic_cimr |= (1 << vec);
} else {
/* SIU interrupt */
if (irq_vecs[vec].handler != NULL) {
printf ("SIU interrupt %d 0x%x replacing 0x%x\n",
vec,
(uint) handler,
(uint) cpm_vecs[vec].handler);
}
irq_vecs[vec].handler = handler;
irq_vecs[vec].arg = arg;
immr->im_siu_conf.sc_simask |= 1 << (31 - vec);
}
}
void irq_free_handler (int vec)
{
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
if ((vec & CPMVEC_OFFSET) != 0) {
/* CPM interrupt */
vec &= 0xffff;
immr->im_cpic.cpic_cimr &= ~(1 << vec);
cpm_vecs[vec].handler = NULL;
cpm_vecs[vec].arg = NULL;
} else {
/* SIU interrupt */
immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec));
irq_vecs[vec].handler = NULL;
irq_vecs[vec].arg = NULL;
}
}
/************************************************************************/
static void cpm_interrupt_init (void)
{
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
/*
* Initialize the CPM interrupt controller.
*/
immr->im_cpic.cpic_cicr =
(CICR_SCD_SCC4 |
CICR_SCC_SCC3 |
CICR_SCB_SCC2 |
CICR_SCA_SCC1) | ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK;
immr->im_cpic.cpic_cimr = 0;
/*
* Install the error handler.
*/
irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL);
immr->im_cpic.cpic_cicr |= CICR_IEN;
/*
* Install the cpm interrupt handler
*/
irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL);
}
/************************************************************************/
/*
* timer_interrupt - gets called when the decrementer overflows,
* with interrupts disabled.
* Trivial implementation - no need to be really accurate.
*/
void timer_interrupt_cpu (struct pt_regs *regs)
{
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
/* Reset Timer Expired and Timers Interrupt Status */
immr->im_clkrstk.cark_plprcrk = KAPWR_KEY;
__asm__ ("nop");
/*
Clear TEXPS (and TMIST on older chips). SPLSS (on older
chips) is cleared too.
Bitwise OR is a read-modify-write operation so ALL bits
which are cleared by writing `1' would be cleared by
operations like
immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS;
The same can be achieved by simple writing of the PLPRCR
to itself. If a bit value should be preserved, read the
register, ZERO the bit and write, not OR, the result back.
*/
immr->im_clkrst.car_plprcr = immr->im_clkrst.car_plprcr;
}
/************************************************************************/