diff options
Diffstat (limited to 'arch/ppc')
60 files changed, 10219 insertions, 1681 deletions
diff --git a/arch/ppc/8xx_io/.cvsignore b/arch/ppc/8xx_io/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/arch/ppc/8xx_io/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile new file mode 100644 index 000000000..b33919a5d --- /dev/null +++ b/arch/ppc/8xx_io/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the linux MPC8xx ppc-specific parts of comm processor +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := 8xx_io.a +O_OBJS = commproc.o uart.o enet.o + +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c new file mode 100644 index 000000000..7e95afd08 --- /dev/null +++ b/arch/ppc/8xx_io/commproc.c @@ -0,0 +1,222 @@ + +/* + * General Purpose functions for the global management of the + * Communication Processor Module. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * 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. + * The amount of space available is platform dependent. On the + * MBX, the EPPC software loads additional microcode into the + * communication processor, and uses some of the DP ram for this + * purpose. Current, the first 512 bytes and the last 256 bytes of + * memory are used. Right now I am conservative and only use the + * memory that can never be used for microcode. If there are + * applications that require more DP ram, we can expand the boundaries + * but then we have to be careful of any downloaded microcode. + */ +#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 <asm/irq.h> +#include <asm/mbx.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/8xx_immap.h> +#include "commproc.h" + +static uint dp_alloc_base; /* Starting offset in DP ram */ +static uint dp_alloc_top; /* Max offset + 1 */ +static uint host_buffer; /* One page of host buffer */ +static uint host_end; /* end + 1 */ +cpm8xx_t *cpmp; /* Pointer to comm processor space */ + +/* CPM interrupt vector functions. +*/ +struct cpm_action { + void (*handler)(void *); + void *dev_id; +}; +static struct cpm_action cpm_vecs[CPMVEC_NR]; +static void cpm_interrupt(int irq, void * dev, struct pt_regs * regs); +static void cpm_error_interrupt(void *); + +void +mbx_cpm_reset(uint host_page_addr) +{ + volatile immap_t *imp; + volatile cpm8xx_t *commproc; + pte_t *pte; + + imp = (immap_t *)MBX_IMAP_ADDR; + commproc = (cpm8xx_t *)&imp->im_cpm; + +#ifdef notdef + /* We can't do this. It seems to blow away the microcode + * patch that EPPC-Bug loaded for us. EPPC-Bug uses SCC1 for + * Ethernet, SMC1 for the console, and I2C for serial EEPROM. + * Our own drivers quickly reset all of these. + */ + + /* Perform a reset. + */ + commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (commproc->cp_cpcr & CPM_CR_FLG); +#endif + + /* Set SDMA Bus Request priority 5. + */ + imp->im_siu_conf.sc_sdcr = 1; + + /* Reclaim the DP memory for our use. + */ + dp_alloc_base = CPM_DATAONLY_BASE; + dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE; + + /* Set the host page for allocation. + */ + host_buffer = host_page_addr; /* Host virtual page address */ + host_end = host_page_addr + PAGE_SIZE; + pte = va_to_pte(&init_task, host_page_addr); + pte_val(*pte) |= _PAGE_NO_CACHE; + flush_tlb_page(current->mm->mmap, host_buffer); + + /* Tell everyone where the comm processor resides. + */ + cpmp = (cpm8xx_t *)commproc; + + /* Initialize the CPM interrupt controller. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr = + (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | + ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK; + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr = 0; + + /* Set our interrupt handler with the core CPU. + */ + if (request_irq(CPM_INTERRUPT, cpm_interrupt, 0, "cpm", NULL) != 0) + panic("Could not allocate CPM IRQ!"); + + /* Install our own error handler. + */ + cpm_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL); + + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN; +} + +/* CPM interrupt controller interrupt. +*/ +static void +cpm_interrupt(int irq, void * dev, struct pt_regs * regs) +{ + uint vec; + + /* Get the vector by setting the ACK bit and then reading + * the register. + */ + ((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr = 1; + vec = ((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr; + vec >>= 11; + + if (cpm_vecs[vec].handler != 0) + (*cpm_vecs[vec].handler)(cpm_vecs[vec].dev_id); + else + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec); + + /* After servicing the interrupt, we have to remove the status + * indicator. + */ + ((immap_t *)MBX_IMAP_ADDR)->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 *dev) +{ +} + +/* Install a CPM interrupt handler. +*/ +void +cpm_install_handler(int vec, void (*handler)(void *), void *dev_id) +{ + if (cpm_vecs[vec].handler != 0) + printk("CPM interrupt %x replacing %x\n", + (uint)handler, (uint)cpm_vecs[vec].handler); + cpm_vecs[vec].handler = handler; + cpm_vecs[vec].dev_id = dev_id; + ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec); +} + +/* Allocate some memory from the dual ported ram. We may want to + * enforce alignment restrictions, but right now everyone is a good + * citizen. + */ +uint +mbx_cpm_dpalloc(uint size) +{ + uint retloc; + + if ((dp_alloc_base + size) >= dp_alloc_top) + return(CPM_DP_NOSPACE); + + retloc = dp_alloc_base; + dp_alloc_base += size; + + return(retloc); +} + +/* We also own one page of host buffer space for the allocation of + * UART "fifos" and the like. + */ +uint +mbx_cpm_hostalloc(uint size) +{ + uint retloc; + + if ((host_buffer + size) >= host_end) + return(0); + + retloc = host_buffer; + host_buffer += size; + + return(retloc); +} + +/* Set a baud rate generator. This needs lots of work. There are + * four BRGs, any of which can be wired to any channel. + * The internal baud rate clock is the system clock divided by 16. + * I need to find a way to get this system clock frequency, which is + * part of the VPD....... + */ +#define BRG_INT_CLK (40000000/16) + +void +mbx_cpm_setbrg(uint brg, uint rate) +{ + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + bp = (uint *)&cpmp->cp_brgc1; + bp += brg; + *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN; +} diff --git a/arch/ppc/8xx_io/commproc.h b/arch/ppc/8xx_io/commproc.h new file mode 100644 index 000000000..38046a31c --- /dev/null +++ b/arch/ppc/8xx_io/commproc.h @@ -0,0 +1,464 @@ + +/* + * MPC8xx Communication Processor Module. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * This file contains structures and information for the communication + * processor channels. Some CPM control and status is available + * throught the MPC8xx internal memory map. See immap.h for details. + * This file only contains what I need for the moment, not the total + * CPM capabilities. I (or someone else) will add definitions as they + * are needed. -- Dan + * + * On the MBX board, EPPC-Bug loads CPM microcode into the first 512 + * bytes of the DP RAM and relocates the I2C parameter area to the + * IDMA1 space. The remaining DP RAM is available for buffer descriptors + * or other use. + */ +#ifndef __CPM_8XX__ +#define __CPM_8XX__ + +#include <asm/8xx_immap.h> + +/* CPM Command register. +*/ +#define CPM_CR_RST ((ushort)0x8000) +#define CPM_CR_OPCODE ((ushort)0x0f00) +#define CPM_CR_CHAN ((ushort)0x00f0) +#define CPM_CR_FLG ((ushort)0x0001) + +/* Some commands (there are more...later) +*/ +#define CPM_CR_INIT_TRX ((ushort)0x0000) +#define CPM_CR_INIT_RX ((ushort)0x0001) +#define CPM_CR_INIT_TX ((ushort)0x0002) +#define CPM_CR_STOP_TX ((ushort)0x0004) +#define CPM_CR_RESTART_TX ((ushort)0x0006) +#define CPM_CR_SET_GADDR ((ushort)0x0008) + +/* Channel numbers. +*/ +#define CPM_CR_CH_SCC1 ((ushort)0x0000) +#define CPM_CR_CH_I2C ((ushort)0x0001) /* I2C and IDMA1 */ +#define CPM_CR_CH_SCC2 ((ushort)0x0004) +#define CPM_CR_CH_SPI ((ushort)0x0005) /* SPI / IDMA2 / Timers */ +#define CPM_CR_CH_SCC3 ((ushort)0x0008) +#define CPM_CR_CH_SMC1 ((ushort)0x0009) /* SMC1 / DSP1 */ +#define CPM_CR_CH_SCC4 ((ushort)0x000c) +#define CPM_CR_CH_SMC2 ((ushort)0x000d) /* SMC2 / DSP2 */ + +#define mk_cr_cmd(CH, CMD) ((CMD << 8) | (CH << 4)) + +/* The dual ported RAM is multi-functional. Some areas can be (and are + * being) used for microcode. There is an area that can only be used + * as data ram for buffer descriptors, which is all we use right now. + * Currently the first 512 and last 256 bytes are used for microcode. + */ +#define CPM_DATAONLY_BASE ((uint)0x0800) +#define CPM_DATAONLY_SIZE ((uint)0x0700) +#define CPM_DP_NOSPACE ((uint)0x7fffffff) + +/* Export the base address of the communication processor registers + * and dual port ram. + */ +extern cpm8xx_t *cpmp; /* Pointer to comm processor */ +uint mbx_cpm_dpalloc(uint size); +uint mbx_cpm_hostalloc(uint size); +void mbx_cpm_setbrg(uint brg, uint rate); + +/* Buffer descriptors used by many of the CPM protocols. +*/ +typedef struct cpm_buf_desc { + ushort cbd_sc; /* Status and Control */ + ushort cbd_datlen; /* Data length in buffer */ + uint cbd_bufaddr; /* Buffer address in host memory */ +} cbd_t; + +#define BD_SC_EMPTY ((ushort)0x8000) /* Recieve is empty */ +#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */ +#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */ +#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */ +#define BD_SC_CM ((ushort)0x0200) /* Continous mode */ +#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */ +#define BD_SC_P ((ushort)0x0100) /* xmt preamble */ +#define BD_SC_BR ((ushort)0x0020) /* Break received */ +#define BD_SC_FR ((ushort)0x0010) /* Framing error */ +#define BD_SC_PR ((ushort)0x0008) /* Parity error */ +#define BD_SC_OV ((ushort)0x0002) /* Overrun */ +#define BD_SC_CD ((ushort)0x0001) /* ?? */ + +/* Define enough so I can at least use the MBX serial port as a UART. + * The MBX uses SMC1 as the host serial port. + */ +typedef struct smc_uart { + ushort smc_rbase; /* Rx Buffer descriptor base address */ + ushort smc_tbase; /* Tx Buffer descriptor base address */ + u_char smc_rfcr; /* Rx function code */ + u_char smc_tfcr; /* Tx function code */ + ushort smc_mrblr; /* Max receive buffer length */ + uint smc_rstate; /* Internal */ + uint smc_idp; /* Internal */ + ushort smc_rbptr; /* Internal */ + ushort smc_ibc; /* Internal */ + uint smc_rxtmp; /* Internal */ + uint smc_tstate; /* Internal */ + uint smc_tdp; /* Internal */ + ushort smc_tbptr; /* Internal */ + ushort smc_tbc; /* Internal */ + uint smc_txtmp; /* Internal */ + ushort smc_maxidl; /* Maximum idle characters */ + ushort smc_tmpidl; /* Temporary idle counter */ + ushort smc_brklen; /* Last received break length */ + ushort smc_brkec; /* rcv'd break condition counter */ + ushort smc_brkcr; /* xmt break count register */ + ushort smc_rmask; /* Temporary bit mask */ +} smc_uart_t; + +#define PROFF_SMC1 ((uint)0x0280) /* Offset in Parameter RAM */ +#define PROFF_SMC2 ((uint)0x0380) + +/* Function code bits. +*/ +#define SMC_EB ((u_char)0x10) /* Set big endian byte order */ + +/* SMC uart mode register. +*/ +#define SMCMR_REN ((ushort)0x0001) +#define SMCMR_TEN ((ushort)0x0002) +#define SMCMR_DM ((ushort)0x000c) +#define SMCMR_SM_GCI ((ushort)0x0000) +#define SMCMR_SM_UART ((ushort)0x0020) +#define SMCMR_SM_TRANS ((ushort)0x0030) +#define SMCMR_SM_MASK ((ushort)0x0030) +#define SMCMR_PM_EVEN ((ushort)0x0100) /* Even parity, else odd */ +#define SMCMR_PEN ((ushort)0x0200) /* Parity enable */ +#define SMCMR_SL ((ushort)0x0400) /* Two stops, else one */ +#define SMCR_CLEN_MASK ((ushort)0x7800) /* Character length */ +#define smcr_mk_clen(C) (((C) << 11) & SMCR_CLEN_MASK) + +/* SMC Event and Mask register. +*/ +#define SMCM_TXE ((unsigned char)0x10) +#define SMCM_BSY ((unsigned char)0x14) +#define SMCM_TX ((unsigned char)0x02) +#define SMCM_RX ((unsigned char)0x01) + +/* Baud rate generators. +*/ +#define CPM_BRG_RST ((uint)0x00020000) +#define CPM_BRG_EN ((uint)0x00010000) +#define CPM_BRG_EXTC_INT ((uint)0x00000000) +#define CPM_BRG_EXTC_CLK2 ((uint)0x00004000) +#define CPM_BRG_EXTC_CLK6 ((uint)0x00008000) +#define CPM_BRG_ATB ((uint)0x00002000) +#define CPM_BRG_CD_MASK ((uint)0x00001ffe) +#define CPM_BRG_DIV16 ((uint)0x00000001) + +/* SCCs. +*/ +#define SCC_GSMRH_IRP ((uint)0x00040000) +#define SCC_GSMRH_GDE ((uint)0x00010000) +#define SCC_GSMRH_TCRC_CCITT ((uint)0x00008000) +#define SCC_GSMRH_TCRC_BISYNC ((uint)0x00004000) +#define SCC_GSMRH_TCRC_HDLC ((uint)0x00000000) +#define SCC_GSMRH_REVD ((uint)0x00002000) +#define SCC_GSMRH_TRX ((uint)0x00001000) +#define SCC_GSMRH_TTX ((uint)0x00000800) +#define SCC_GSMRH_CDP ((uint)0x00000400) +#define SCC_GSMRH_CTSP ((uint)0x00000200) +#define SCC_GSMRH_CDS ((uint)0x00000100) +#define SCC_GSMRH_CTSS ((uint)0x00000080) +#define SCC_GSMRH_TFL ((uint)0x00000040) +#define SCC_GSMRH_RFW ((uint)0x00000020) +#define SCC_GSMRH_TXSY ((uint)0x00000010) +#define SCC_GSMRH_SYNL16 ((uint)0x0000000c) +#define SCC_GSMRH_SYNL8 ((uint)0x00000008) +#define SCC_GSMRH_SYNL4 ((uint)0x00000004) +#define SCC_GSMRH_RTSM ((uint)0x00000002) +#define SCC_GSMRH_RSYN ((uint)0x00000001) + +#define SCC_GSMRL_SIR ((uint)0x80000000) /* SCC2 only */ +#define SCC_GSMRL_EDGE_NONE ((uint)0x60000000) +#define SCC_GSMRL_EDGE_NEG ((uint)0x40000000) +#define SCC_GSMRL_EDGE_POS ((uint)0x20000000) +#define SCC_GSMRL_EDGE_BOTH ((uint)0x00000000) +#define SCC_GSMRL_TCI ((uint)0x10000000) +#define SCC_GSMRL_TSNC_3 ((uint)0x0c000000) +#define SCC_GSMRL_TSNC_4 ((uint)0x08000000) +#define SCC_GSMRL_TSNC_14 ((uint)0x04000000) +#define SCC_GSMRL_TSNC_INF ((uint)0x00000000) +#define SCC_GSMRL_RINV ((uint)0x02000000) +#define SCC_GSMRL_TINV ((uint)0x01000000) +#define SCC_GSMRL_TPL_128 ((uint)0x00c00000) +#define SCC_GSMRL_TPL_64 ((uint)0x00a00000) +#define SCC_GSMRL_TPL_48 ((uint)0x00800000) +#define SCC_GSMRL_TPL_32 ((uint)0x00600000) +#define SCC_GSMRL_TPL_16 ((uint)0x00400000) +#define SCC_GSMRL_TPL_8 ((uint)0x00200000) +#define SCC_GSMRL_TPL_NONE ((uint)0x00000000) +#define SCC_GSMRL_TPP_ALL1 ((uint)0x00180000) +#define SCC_GSMRL_TPP_01 ((uint)0x00100000) +#define SCC_GSMRL_TPP_10 ((uint)0x00080000) +#define SCC_GSMRL_TPP_ZEROS ((uint)0x00000000) +#define SCC_GSMRL_TEND ((uint)0x00040000) +#define SCC_GSMRL_TDCR_32 ((uint)0x00030000) +#define SCC_GSMRL_TDCR_16 ((uint)0x00020000) +#define SCC_GSMRL_TDCR_8 ((uint)0x00010000) +#define SCC_GSMRL_TDCR_1 ((uint)0x00000000) +#define SCC_GSMRL_RDCR_32 ((uint)0x0000c000) +#define SCC_GSMRL_RDCR_16 ((uint)0x00008000) +#define SCC_GSMRL_RDCR_8 ((uint)0x00004000) +#define SCC_GSMRL_RDCR_1 ((uint)0x00000000) +#define SCC_GSMRL_RENC_DFMAN ((uint)0x00003000) +#define SCC_GSMRL_RENC_MANCH ((uint)0x00002000) +#define SCC_GSMRL_RENC_FM0 ((uint)0x00001000) +#define SCC_GSMRL_RENC_NRZI ((uint)0x00000800) +#define SCC_GSMRL_RENC_NRZ ((uint)0x00000000) +#define SCC_GSMRL_TENC_DFMAN ((uint)0x00000600) +#define SCC_GSMRL_TENC_MANCH ((uint)0x00000400) +#define SCC_GSMRL_TENC_FM0 ((uint)0x00000200) +#define SCC_GSMRL_TENC_NRZI ((uint)0x00000100) +#define SCC_GSMRL_TENC_NRZ ((uint)0x00000000) +#define SCC_GSMRL_DIAG_LE ((uint)0x000000c0) /* Loop and echo */ +#define SCC_GSMRL_DIAG_ECHO ((uint)0x00000080) +#define SCC_GSMRL_DIAG_LOOP ((uint)0x00000040) +#define SCC_GSMRL_DIAG_NORM ((uint)0x00000000) +#define SCC_GSMRL_ENR ((uint)0x00000020) +#define SCC_GSMRL_ENT ((uint)0x00000010) +#define SCC_GSMRL_MODE_ENET ((uint)0x0000000c) +#define SCC_GSMRL_MODE_DDCMP ((uint)0x00000009) +#define SCC_GSMRL_MODE_BISYNC ((uint)0x00000008) +#define SCC_GSMRL_MODE_V14 ((uint)0x00000007) +#define SCC_GSMRL_MODE_AHDLC ((uint)0x00000006) +#define SCC_GSMRL_MODE_PROFIBUS ((uint)0x00000005) +#define SCC_GSMRL_MODE_UART ((uint)0x00000004) +#define SCC_GSMRL_MODE_SS7 ((uint)0x00000003) +#define SCC_GSMRL_MODE_ATALK ((uint)0x00000002) +#define SCC_GSMRL_MODE_HDLC ((uint)0x00000000) + +#define SCC_TODR_TOD ((ushort)0x8000) + +typedef struct scc_param { + ushort scc_rbase; /* Rx Buffer descriptor base address */ + ushort scc_tbase; /* Tx Buffer descriptor base address */ + u_char scc_rfcr; /* Rx function code */ + u_char scc_tfcr; /* Tx function code */ + ushort scc_mrblr; /* Max receive buffer length */ + uint scc_rstate; /* Internal */ + uint scc_idp; /* Internal */ + ushort scc_rbptr; /* Internal */ + ushort scc_ibc; /* Internal */ + uint scc_rxtmp; /* Internal */ + uint scc_tstate; /* Internal */ + uint scc_tdp; /* Internal */ + ushort scc_tbptr; /* Internal */ + ushort scc_tbc; /* Internal */ + uint scc_txtmp; /* Internal */ + uint scc_rcrc; /* Internal */ + uint scc_tcrc; /* Internal */ +} sccp_t; + +/* Function code bits. +*/ +#define SCC_EB ((u_char)0x10) /* Set big endian byte order */ + +/* CPM Ethernet through SCC1. + */ +typedef struct scc_enet { + sccp_t sen_genscc; + uint sen_cpres; /* Preset CRC */ + uint sen_cmask; /* Constant mask for CRC */ + uint sen_crcec; /* CRC Error counter */ + uint sen_alec; /* alignment error counter */ + uint sen_disfc; /* discard frame counter */ + ushort sen_pads; /* Tx short frame pad character */ + ushort sen_retlim; /* Retry limit threshold */ + ushort sen_retcnt; /* Retry limit counter */ + ushort sen_maxflr; /* maximum frame length register */ + ushort sen_minflr; /* minimum frame length register */ + ushort sen_maxd1; /* maximum DMA1 length */ + ushort sen_maxd2; /* maximum DMA2 length */ + ushort sen_maxd; /* Rx max DMA */ + ushort sen_dmacnt; /* Rx DMA counter */ + ushort sen_maxb; /* Max BD byte count */ + ushort sen_gaddr1; /* Group address filter */ + ushort sen_gaddr2; + ushort sen_gaddr3; + ushort sen_gaddr4; + uint sen_tbuf0data0; /* Save area 0 - current frame */ + uint sen_tbuf0data1; /* Save area 1 - current frame */ + uint sen_tbuf0rba; /* Internal */ + uint sen_tbuf0crc; /* Internal */ + ushort sen_tbuf0bcnt; /* Internal */ + ushort sen_paddrh; /* physical address (MSB) */ + ushort sen_paddrm; + ushort sen_paddrl; /* physical address (LSB) */ + ushort sen_pper; /* persistence */ + ushort sen_rfbdptr; /* Rx first BD pointer */ + ushort sen_tfbdptr; /* Tx first BD pointer */ + ushort sen_tlbdptr; /* Tx last BD pointer */ + uint sen_tbuf1data0; /* Save area 0 - current frame */ + uint sen_tbuf1data1; /* Save area 1 - current frame */ + uint sen_tbuf1rba; /* Internal */ + uint sen_tbuf1crc; /* Internal */ + ushort sen_tbuf1bcnt; /* Internal */ + ushort sen_txlen; /* Tx Frame length counter */ + ushort sen_iaddr1; /* Individual address filter */ + ushort sen_iaddr2; + ushort sen_iaddr3; + ushort sen_iaddr4; + ushort sen_boffcnt; /* Backoff counter */ + + /* NOTE: Some versions of the manual have the following items + * incorrectly documented. Below is the proper order. + */ + ushort sen_taddrh; /* temp address (MSB) */ + ushort sen_taddrm; + ushort sen_taddrl; /* temp address (LSB) */ +} scc_enet_t; + +#define PROFF_SCC1 ((uint)0x0000) /* Offset in Parameter RAM */ + +/* Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC1 use. The TCLK and RCLK seem unique + * to the MBX860 board. Any two of the four available clocks could be + * used, and the MPC860 cookbook manual has an example using different + * clock pins. + */ +#define PA_ENET_RXD ((ushort)0x0001) +#define PA_ENET_TXD ((ushort)0x0002) +#define PA_ENET_TCLK ((ushort)0x0200) +#define PA_ENET_RCLK ((ushort)0x0800) +#define PC_ENET_TENA ((ushort)0x0001) +#define PC_ENET_CLSN ((ushort)0x0010) +#define PC_ENET_RENA ((ushort)0x0020) + +/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to + * SCC1. Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero. + */ +#define SICR_ENET_MASK ((uint)0x000000ff) +#define SICR_ENET_CLKRT ((uint)0x0000003d) + +/* SCC Event register as used by Ethernet. +*/ +#define SCCE_ENET_GRA ((ushort)0x0080) /* Graceful stop complete */ +#define SCCE_ENET_TXE ((ushort)0x0010) /* Transmit Error */ +#define SCCE_ENET_RXF ((ushort)0x0008) /* Full frame received */ +#define SCCE_ENET_BSY ((ushort)0x0004) /* All incoming buffers full */ +#define SCCE_ENET_TXB ((ushort)0x0002) /* A buffer was transmitted */ +#define SCCE_ENET_RXB ((ushort)0x0001) /* A buffer was received */ + +/* SCC Mode Register (PMSR) as used by Ethernet. +*/ +#define SCC_PMSR_HBC ((ushort)0x8000) /* Enable heartbeat */ +#define SCC_PMSR_FC ((ushort)0x4000) /* Force collision */ +#define SCC_PMSR_RSH ((ushort)0x2000) /* Receive short frames */ +#define SCC_PMSR_IAM ((ushort)0x1000) /* Check individual hash */ +#define SCC_PMSR_ENCRC ((ushort)0x0800) /* Ethernet CRC mode */ +#define SCC_PMSR_PRO ((ushort)0x0200) /* Promiscuous mode */ +#define SCC_PMSR_BRO ((ushort)0x0100) /* Catch broadcast pkts */ +#define SCC_PMSR_SBT ((ushort)0x0080) /* Special backoff timer */ +#define SCC_PMSR_LPB ((ushort)0x0040) /* Set Loopback mode */ +#define SCC_PMSR_SIP ((ushort)0x0020) /* Sample Input Pins */ +#define SCC_PMSR_LCW ((ushort)0x0010) /* Late collision window */ +#define SCC_PMSR_NIB22 ((ushort)0x000a) /* Start frame search */ +#define SCC_PMSR_FDE ((ushort)0x0001) /* Full duplex enable */ + +/* Buffer descriptor control/status used by Ethernet receive. +*/ +#define BD_ENET_RX_EMPTY ((ushort)0x8000) +#define BD_ENET_RX_WRAP ((ushort)0x2000) +#define BD_ENET_RX_INTR ((ushort)0x1000) +#define BD_ENET_RX_LAST ((ushort)0x0800) +#define BD_ENET_RX_FIRST ((ushort)0x0400) +#define BD_ENET_RX_MISS ((ushort)0x0100) +#define BD_ENET_RX_LG ((ushort)0x0020) +#define BD_ENET_RX_NO ((ushort)0x0010) +#define BD_ENET_RX_SH ((ushort)0x0008) +#define BD_ENET_RX_CR ((ushort)0x0004) +#define BD_ENET_RX_OV ((ushort)0x0002) +#define BD_ENET_RX_CL ((ushort)0x0001) +#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ + +/* Buffer descriptor control/status used by Ethernet transmit. +*/ +#define BD_ENET_TX_READY ((ushort)0x8000) +#define BD_ENET_TX_PAD ((ushort)0x4000) +#define BD_ENET_TX_WRAP ((ushort)0x2000) +#define BD_ENET_TX_INTR ((ushort)0x1000) +#define BD_ENET_TX_LAST ((ushort)0x0800) +#define BD_ENET_TX_TC ((ushort)0x0400) +#define BD_ENET_TX_DEF ((ushort)0x0200) +#define BD_ENET_TX_HB ((ushort)0x0100) +#define BD_ENET_TX_LC ((ushort)0x0080) +#define BD_ENET_TX_RL ((ushort)0x0040) +#define BD_ENET_TX_RCMASK ((ushort)0x003c) +#define BD_ENET_TX_UN ((ushort)0x0002) +#define BD_ENET_TX_CSL ((ushort)0x0001) +#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ + +/* SCC Event and Mask registers when it is used as a UART. +*/ +#define UART_SCCM_GLR ((ushort)0x1000) +#define UART_SCCM_GLT ((ushort)0x0800) +#define UART_SCCM_AB ((ushort)0x0200) +#define UART_SCCM_IDL ((ushort)0x0100) +#define UART_SCCM_GRA ((ushort)0x0080) +#define UART_SCCM_BRKE ((ushort)0x0040) +#define UART_SCCM_BRKS ((ushort)0x0020) +#define UART_SCCM_CCR ((ushort)0x0008) +#define UART_SCCM_BSY ((ushort)0x0004) +#define UART_SCCM_TX ((ushort)0x0002) +#define UART_SCCM_RX ((ushort)0x0001) + +/* CPM interrupts. There are nearly 32 interrupts generated by CPM + * channels or devices. All of these are presented to the PPC core + * as a single interrupt. The CPM interrupt handler dispatches its + * own handlers, in a similar fashion to the PPC core handler. We + * use the table as defined in the manuals (i.e. no special high + * priority and SCC1 == SCCa, etc...). + */ +#define CPMVEC_NR 32 +#define CPMVEC_PIO_PC15 ((ushort)0x1f) +#define CPMVEC_SCC1 ((ushort)0x1e) +#define CPMVEC_SCC2 ((ushort)0x1d) +#define CPMVEC_SCC3 ((ushort)0x1c) +#define CPMVEC_SCC4 ((ushort)0x1b) +#define CPMVEC_PIO_PC14 ((ushort)0x1a) +#define CPMVEC_TIMER1 ((ushort)0x19) +#define CPMVEC_PIO_PC13 ((ushort)0x18) +#define CPMVEC_PIO_PC12 ((ushort)0x17) +#define CPMVEC_SDMA_CB_ERR ((ushort)0x16) +#define CPMVEC_IDMA1 ((ushort)0x15) +#define CPMVEC_IDMA2 ((ushort)0x14) +#define CPMVEC_TIMER2 ((ushort)0x12) +#define CPMVEC_RISCTIMER ((ushort)0x11) +#define CPMVEC_I2C ((ushort)0x10) +#define CPMVEC_PIO_PC11 ((ushort)0x0f) +#define CPMVEC_PIO_PC10 ((ushort)0x0e) +#define CPMVEC_TIMER3 ((ushort)0x0c) +#define CPMVEC_PIO_PC9 ((ushort)0x0b) +#define CPMVEC_PIO_PC8 ((ushort)0x0a) +#define CPMVEC_PIO_PC7 ((ushort)0x09) +#define CPMVEC_TIMER4 ((ushort)0x07) +#define CPMVEC_PIO_PC6 ((ushort)0x06) +#define CPMVEC_SPI ((ushort)0x05) +#define CPMVEC_SMC1 ((ushort)0x04) +#define CPMVEC_SMC2 ((ushort)0x03) +#define CPMVEC_PIO_PC5 ((ushort)0x02) +#define CPMVEC_PIO_PC4 ((ushort)0x01) +#define CPMVEC_ERROR ((ushort)0x00) + +extern void cpm_install_handler(int vec, void (*handler)(void *), void *dev_id); + +/* CPM interrupt configuration vector. +*/ +#define CICR_SCD_SCC4 ((uint)0x00c00000) /* SCC4 @ SCCd */ +#define CICR_SCC_SCC3 ((uint)0x00200000) /* SCC3 @ SCCc */ +#define CICR_SCB_SCC2 ((uint)0x00040000) /* SCC2 @ SCCb */ +#define CICR_SCA_SCC1 ((uint)0x00000000) /* SCC1 @ SCCa */ +#define CICR_IRL_MASK ((uint)0x0000e000) /* Core interrrupt */ +#define CICR_HP_MASK ((uint)0x00001f00) /* Hi-pri int. */ +#define CICR_IEN ((uint)0x00000080) /* Int. enable */ +#define CICR_SPS ((uint)0x00000001) /* SCC Spread */ +#endif /* __CPM_8XX__ */ diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c new file mode 100644 index 000000000..1c43ddce0 --- /dev/null +++ b/arch/ppc/8xx_io/enet.c @@ -0,0 +1,912 @@ +/* + * Ethernet driver for Motorola MPC8xx. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * I copied the basic skeleton from the lance driver, because I did not + * know how to write the Linux driver, but I did know how the LANCE worked. + * This version of the driver is specific to the MBX implementation, + * since the board contains control registers external to the processor + * for the control of the MC68160 SIA/transceiver. The MPC860 manual + * describes connections using the internal parallel port I/O. + * + * The MBX860 uses the CPM SCC1 serial port for the Ethernet interface. + * Buffer descriptors are kept in the CPM dual port RAM, and the frame + * buffers are in the host memory. + * + * Right now, I am very watseful with the buffers. I allocate memory + * pages and then divide them into 2K frame buffers. This way I know I + * have buffers large enough to hold one frame within one buffer descriptor. + * Once I get this working, I will use 64 or 128 byte CPM buffers, which + * will be much more memory efficient and will easily handle lots of + * small packets. + * + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/init.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <asm/8xx_immap.h> +#include <asm/pgtable.h> +#include <asm/mbx.h> +#include "commproc.h" +#include <linux/delay.h> + +/* + * Theory of Operation + * + * The MPC8xx CPM performs the Ethernet processing on SCC1. It can use + * an aribtrary number of buffers on byte boundaries, but must have at + * least two receive buffers to prevent constand overrun conditions. + * + * The buffer descriptors are allocated from the CPM dual port memory + * with the data buffers allocated from host memory, just like all other + * serial communication protocols. The host memory buffers are allocated + * from the free page pool, and then divided into smaller receive and + * transmit buffers. The size of the buffers should be a power of two, + * since that nicely divides the page. This creates a ring buffer + * structure similar to the LANCE and other controllers. + * + * Like the LANCE driver: + * The driver runs as two independent, single-threaded flows of control. One + * is the send-packet routine, which enforces single-threaded use by the + * dev->tbusy flag. The other thread is the interrupt handler, which is single + * threaded by the hardware and other software. + * + * The send packet thread has partial control over the Tx ring and 'dev->tbusy' + * flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next + * queue slot is empty, it clears the tbusy flag when finished otherwise it sets + * the 'lp->tx_full' flag. + * + * The MBX has a control register external to the MPC8xx that has some + * control of the Ethernet interface. Control Register 1 has the + * following format: + * bit 0 - Set to enable Ethernet transceiver + * bit 1 - Set to enable Ethernet internal loopback + * bit 2 - Set to auto select AUI or TP port + * bit 3 - if bit 2 is 0, set to select TP port + * bit 4 - Set to disable full duplex (loopback) + * bit 5 - Set to disable XCVR collision test + * bit 6, 7 - Used for RS-232 control. + * + * EPPC-Bug sets this register to 0x98 for normal Ethernet operation, + * so we should not have to touch it. + * + * The following I/O is used by the MBX implementation of the MPC8xx to + * the MC68160 transceiver. It DOES NOT exactly follow the cookbook + * example from the MPC860 manual. + * Port A, 15 - SCC1 Ethernet Rx + * Port A, 14 - SCC1 Ethernet Tx + * Port A, 6 (CLK2) - SCC1 Ethernet Tx Clk + * Port A, 4 (CLK4) - SCC1 Ethernet Rx Clk + * Port C, 15 - SCC1 Ethernet Tx Enable + * Port C, 11 - SCC1 Ethernet Collision + * Port C, 10 - SCC1 Ethernet Rx Enable + */ + +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it it best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ +#define CPM_ENET_RX_PAGES 4 +#define CPM_ENET_RX_FRSIZE 2048 +#define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE) +#define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES) +#define TX_RING_SIZE 8 /* Must be power of two */ +#define TX_RING_MOD_MASK 7 /* for this to work */ + +/* The CPM stores dest/src/type, data, and checksum for receive packets. + */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + +/* The CPM buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct cpm_enet_private { + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* CPM dual port RAM relative addresses. + */ + cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ + cbd_t *tx_bd_base; + cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + scc_t *sccp; + struct net_device_stats stats; + char tx_full; + unsigned long lock; +}; + +static int cpm_enet_open(struct device *dev); +static int cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev); +static int cpm_enet_rx(struct device *dev); +static void cpm_enet_interrupt(void *dev_id); +static int cpm_enet_close(struct device *dev); +static struct net_device_stats *cpm_enet_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + +/* GET THIS FROM THE VPD!!!! +*/ +static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 }; + +/* Initialize the CPM Ethernet on SCC1. If EPPC-Bug loaded us, or performed + * some other network I/O, a whole bunch of this has already been set up. + * It is no big deal if we do it again, we just have to disable the + * transmit and receive to make sure we don't catch the CPM with some + * inconsistent control information. + */ +__initfunc(int cpm_enet_init(void)) +{ + struct device *dev; + struct cpm_enet_private *cep; + int i, j; + unsigned char *eap; + unsigned long mem_addr; + pte_t *pte; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile scc_t *sccp; + volatile scc_enet_t *ep; + volatile immap_t *immap; + + cp = cpmp; /* Get pointer to Communication Processor */ + + immap = (immap_t *)MBX_IMAP_ADDR; /* and to internal registers */ + + /* Allocate some private information. + */ + cep = (struct cpm_enet_private *)kmalloc(sizeof(*cep), GFP_KERNEL); + memset(cep, 0, sizeof(*cep)); + + /* Create an Ethernet device instance. + */ + dev = init_etherdev(0, 0); + + /* Get pointer to SCC1 area in parameter RAM. + */ + ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_SCC1]); + + /* And another to the SCC register area. + */ + sccp = (volatile scc_t *)(&cp->cp_scc[0]); + cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */ + + /* Disable receive and transmit in case EPPC-Bug started it. + */ + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* Cookbook style from the MPC860 manual..... + * Not all of this is necessary if EPPC-Bug has initialized + * the network. + */ + + /* Configure port A pins for Txd and Rxd. + */ + immap->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD); + immap->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD); + immap->im_ioport.iop_paodr &= ~PA_ENET_TXD; + + /* Configure port C pins to enable CLSN and RENA. + */ + immap->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immap->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immap->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA); + + /* Configure port A for TCLK and RCLK. + */ + immap->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK); + immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); + + /* Configure Serial Interface clock routing. + * First, clear all SCC1 bits to zero, then set the ones we want. + */ + cp->cp_sicr &= ~SICR_ENET_MASK; + cp->cp_sicr |= SICR_ENET_CLKRT; + + /* Manual says set SDDR, but I can't find anything with that + * name. I think it is a misprint, and should be SDCR. This + * has already been set by the communication processor initialization. + */ + + /* Allocate space for the buffer descriptors in the DP ram. + * These are relative offsets in the DP ram address space. + * Initialize base addresses for the buffer descriptors. + */ + i = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE); + ep->sen_genscc.scc_rbase = i; + cep->rx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; + + i = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE); + ep->sen_genscc.scc_tbase = i; + cep->tx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; + + cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; + cep->cur_rx = cep->rx_bd_base; + + /* Issue init Rx BD command for SCC1. + * Manual says to perform an Init Rx parameters here. We have + * to perform both Rx and Tx because the SCC may have been + * already running. + * In addition, we have to do it later because we don't yet have + * all of the BD control/status set properly. + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_RX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + */ + + /* Initialize function code registers for big-endian. + */ + ep->sen_genscc.scc_rfcr = SCC_EB; + ep->sen_genscc.scc_tfcr = SCC_EB; + + /* Set maximum bytes per receive buffer. + * This appears to be an Ethernet frame size, not the buffer + * fragment size. It must be a multiple of four. + */ + ep->sen_genscc.scc_mrblr = PKT_MAXBLR_SIZE; + + /* Set CRC preset and mask. + */ + ep->sen_cpres = 0xffffffff; + ep->sen_cmask = 0xdebb20e3; + + ep->sen_crcec = 0; /* CRC Error counter */ + ep->sen_alec = 0; /* alignment error counter */ + ep->sen_disfc = 0; /* discard frame counter */ + + ep->sen_pads = 0x8888; /* Tx short frame pad character */ + ep->sen_retlim = 15; /* Retry limit threshold */ + + ep->sen_maxflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + ep->sen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + + ep->sen_maxd1 = PKT_MAXBUF_SIZE; /* maximum DMA1 length */ + ep->sen_maxd2 = PKT_MAXBUF_SIZE; /* maximum DMA2 length */ + + /* Clear hash tables. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + ep->sen_iaddr1 = 0; + ep->sen_iaddr2 = 0; + ep->sen_iaddr3 = 0; + ep->sen_iaddr4 = 0; + + /* Set Ethernet station address. This must come from the + * Vital Product Data (VPD) EEPROM.....as soon as I get the + * I2C interface working..... + * + * Since we performed a diskless boot, the Ethernet controller + * has been initialized and we copy the address out into our + * own structure. + */ +#ifdef notdef + ep->sen_paddrh = my_enet_addr[0]; + ep->sen_paddrm = my_enet_addr[1]; + ep->sen_paddrl = my_enet_addr[2]; +#else + eap = (unsigned char *)&(ep->sen_paddrh); + for (i=5; i>=0; i--) + dev->dev_addr[i] = *eap++; +#endif + + ep->sen_pper = 0; /* 'cause the book says so */ + ep->sen_taddrl = 0; /* temp address (LSB) */ + ep->sen_taddrm = 0; + ep->sen_taddrh = 0; /* temp address (MSB) */ + + /* Now allocate the host memory pages and initialize the + * buffer descriptors. + */ + bdp = cep->tx_bd_base; + for (i=0; i<TX_RING_SIZE; i++) { + + /* Initialize the BD for every fragment in the page. + */ + bdp->cbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + bdp = cep->rx_bd_base; + for (i=0; i<CPM_ENET_RX_PAGES; i++) { + + /* Allocate a page. + */ + mem_addr = __get_free_page(GFP_KERNEL); + + /* Make it uncached. + */ + pte = va_to_pte(&init_task, mem_addr); + pte_val(*pte) |= _PAGE_NO_CACHE; + flush_tlb_page(current->mm->mmap, mem_addr); + + /* Initialize the BD for every fragment in the page. + */ + for (j=0; j<CPM_ENET_RX_FRPPG; j++) { + bdp->cbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; + bdp->cbd_bufaddr = __pa(mem_addr); + mem_addr += CPM_ENET_RX_FRSIZE; + bdp++; + } + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + cep->skb_cur = cep->skb_dirty = 0; + + sccp->scc_scce = 0xffff; /* Clear any pending events */ + + /* Enable interrupts for transmit error, complete frame + * received, and any transmit buffer we have also set the + * interrupt flag. + */ + sccp->scc_sccm = (SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB); + + /* Install our interrupt handler. + */ + cpm_install_handler(CPMVEC_SCC1, cpm_enet_interrupt, dev); + + /* Set GSMR_H to enable all normal operating modes. + * Set GSMR_L to enable Ethernet to MC68160. + */ + sccp->scc_gsmrh = 0; + sccp->scc_gsmrl = (SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 | SCC_GSMRL_MODE_ENET); + + /* Set sync/delimiters. + */ + sccp->scc_dsr = 0xd555; + + /* Set processing mode. Use Ethernet CRC, catch broadcast, and + * start frame search 22 bit times after RENA. + */ + sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_BRO | SCC_PMSR_NIB22); + + /* It is now OK to enable the Ethernet transmitter. + */ + immap->im_ioport.iop_pcpar |= PC_ENET_TENA; + immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA; + + dev->base_addr = (unsigned long)ep; + dev->priv = cep; + dev->name = "CPM_ENET"; + + /* The CPM Ethernet specific entries in the device structure. */ + dev->open = cpm_enet_open; + dev->hard_start_xmit = cpm_enet_start_xmit; + dev->stop = cpm_enet_close; + dev->get_stats = cpm_enet_get_stats; + dev->set_multicast_list = set_multicast_list; + + /* And last, enable the transmit and receive processing. + */ + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + printk("CPM ENET Version 0.1, "); + for (i=0; i<5; i++) + printk("%02x:", dev->dev_addr[i]); + printk("%02x\n", dev->dev_addr[5]); + + return 0; +} + +static int +cpm_enet_open(struct device *dev) +{ + + /* I should reset the ring buffers here, but I don't yet know + * a simple way to do that. + */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + return 0; /* Always succeed */ +} + +static int +cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv; + volatile cbd_t *bdp; + unsigned long flags; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 20) + return 1; + printk("%s: transmit timed out.\n", dev->name); + cep->stats.tx_errors++; +#ifndef final_version + { + int i; + cbd_t *bdp; + printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n", + cep->cur_tx, cep->tx_full ? " (full)" : "", + cep->cur_rx); + bdp = cep->tx_bd_base; + for (i = 0 ; i < TX_RING_SIZE; i++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp = cep->rx_bd_base; + for (i = 0 ; i < RX_RING_SIZE; i++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + } +#endif + + dev->tbusy=0; + dev->trans_start = jiffies; + + return 0; + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + return 1; + } + + if (test_and_set_bit(0, (void*)&cep->lock) != 0) { + printk("%s: tx queue lock!.\n", dev->name); + /* don't clear dev->tbusy flag. */ + return 1; + } + + /* Fill in a Tx ring entry */ + bdp = cep->cur_tx; + +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) { + /* Ooops. All transmit buffers are full. Bail out. + * This should not happen, since dev->tbusy should be set. + */ + printk("%s: tx queue full!.\n", dev->name); + cep->lock = 0; + return 1; + } +#endif + + /* Clear all of the status flags. + */ + bdp->cbd_sc &= ~BD_ENET_TX_STATS; + + /* If the frame is short, tell CPM to pad it. + */ + if (skb->len <= ETH_ZLEN) + bdp->cbd_sc |= BD_ENET_TX_PAD; + else + bdp->cbd_sc &= ~BD_ENET_TX_PAD; + + /* Set buffer length and buffer pointer. + */ + bdp->cbd_datlen = skb->len; + bdp->cbd_bufaddr = __pa(skb->data); + + /* Save skb pointer. + */ + cep->tx_skbuff[cep->skb_cur] = skb; + + cep->stats.tx_bytes += skb->len; + cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; + + /* Push the data cache so the CPM does not get stale memory + * data. + */ + /*flush_dcache_range(skb->data, skb->data + skb->len);*/ + + /* Send it on its way. Tell CPM its ready, interrupt when done, + * its the last BD of the frame, and to put the CRC on the end. + */ + bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); + + dev->trans_start = jiffies; + + /* If this was the last BD in the ring, start at the beginning again. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + save_flags(flags); + cli(); + cep->lock = 0; + if (bdp->cbd_sc & BD_ENET_TX_READY) + cep->tx_full = 1; + else + dev->tbusy=0; + restore_flags(flags); + + cep->cur_tx = (cbd_t *)bdp; + + return 0; +} + +/* The interrupt handler. + * This is called from the CPM handler, not the MPC core interrupt. + */ +static void +cpm_enet_interrupt(void *dev_id) +{ + struct device *dev = dev_id; + struct cpm_enet_private *cep; + volatile cbd_t *bdp; + ushort int_events; + int must_restart; + + cep = (struct cpm_enet_private *)dev->priv; + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + dev->interrupt = 1; + + /* Get the interrupt events that caused us to be here. + */ + int_events = cep->sccp->scc_scce; + must_restart = 0; + + /* Handle receive event in its own function. + */ + if (int_events & SCCE_ENET_RXF) + cpm_enet_rx(dev_id); + + /* Check for a transmit error. The manual is a little unclear + * about this, so the debug code until I get it figured out. It + * appears that if TXE is set, then TXB is not set. However, + * if carrier sense is lost during frame transmission, the TXE + * bit is set, "and continues the buffer transmission normally." + * I don't know if "normally" implies TXB is set when the buffer + * descriptor is closed.....trial and error :-). + */ + if (int_events & SCCE_ENET_TXE) { + + /* Transmission errors. + */ + bdp = cep->dirty_tx; +#ifndef final_version + printk("CPM ENET xmit error %x\n", bdp->cbd_sc); + if (bdp->cbd_sc & BD_ENET_TX_READY) + printk("HEY! Enet xmit interrupt and TX_READY.\n"); +#endif + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + cep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + cep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + cep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + cep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + cep->stats.tx_carrier_errors++; + + cep->stats.tx_errors++; + + /* No heartbeat or Lost carrier are not really bad errors. + * The others require a restart transmit command. + */ + if (bdp->cbd_sc & + (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) + must_restart = 1; + } + + /* Transmit OK, or non-fatal error. Update the buffer descriptors. + */ + if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) { + cep->stats.tx_packets++; + bdp = cep->dirty_tx; +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) + printk("HEY! Enet xmit interrupt and TX_READY.\n"); +#endif + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (bdp->cbd_sc & BD_ENET_TX_DEF) + cep->stats.collisions++; + + /* Free the sk buffer associated with this last transmit. + */ + dev_kfree_skb(cep->tx_skbuff[cep->skb_dirty]/*, FREE_WRITE*/); + cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; + + /* Update pointer to next buffer descriptor to be transmitted. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + /* I don't know if we can be held off from processing these + * interrupts for more than one frame time. I really hope + * not. In such a case, we would now want to check the + * currently available BD (cur_tx) and determine if any + * buffers between the dirty_tx and cur_tx have also been + * sent. We would want to process anything in between that + * does not have BD_ENET_TX_READY set. + */ + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (cep->tx_full && dev->tbusy) { + cep->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + cep->dirty_tx = (cbd_t *)bdp; + } + + if (must_restart) { + volatile cpm8xx_t *cp; + + /* Some transmit errors cause the transmitter to shut + * down. We now issue a restart transmit. Since the + * errors close the BD and update the pointers, the restart + * _should_ pick up without having to reset any of our + * pointers either. + */ + cp = cpmp; + cp->cp_cpcr = + mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + + /* Check for receive busy, i.e. packets coming but no place to + * put them. This "can't happen" because the receive interrupt + * is tossing previous frames. + */ + if (int_events & SCCE_ENET_BSY) { + cep->stats.rx_dropped++; + printk("CPM ENET: BSY can't happen.\n"); + } + + /* Write the SCC event register with the events we have handled + * to clear them. Maybe we should do this sooner? + */ + cep->sccp->scc_scce = int_events; + + dev->interrupt = 0; + + return; +} + +/* During a receive, the cur_rx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static int +cpm_enet_rx(struct device *dev) +{ + struct cpm_enet_private *cep; + volatile cbd_t *bdp; + struct sk_buff *skb; + ushort pkt_len; + + cep = (struct cpm_enet_private *)dev->priv; + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = cep->cur_rx; + +for (;;) { + if (bdp->cbd_sc & BD_ENET_RX_EMPTY) + break; + +#ifndef final_version + /* Since we have allocated space to hold a complete frame, both + * the first and last indicators should be set. + */ + if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) != + (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) + printk("CPM ENET: rcv is not first+last\n"); +#endif + + /* Frame too long or too short. + */ + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) + cep->stats.rx_length_errors++; + if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ + cep->stats.rx_frame_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ + cep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ + cep->stats.rx_crc_errors++; + + /* Report late collisions as a frame error. + * On this error, the BD is closed, but we don't know what we + * have in the buffer. So, just drop this frame on the floor. + */ + if (bdp->cbd_sc & BD_ENET_RX_CL) { + cep->stats.rx_frame_errors++; + } + else { + + /* Process the incoming frame. + */ + cep->stats.rx_packets++; + pkt_len = bdp->cbd_datlen; + cep->stats.rx_bytes += pkt_len; + + /* This does 16 byte alignment, much more than we need. + */ + skb = dev_alloc_skb(pkt_len); + + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + cep->stats.rx_dropped++; + } + else { + skb->dev = dev; + skb_put(skb,pkt_len); /* Make room */ + eth_copy_and_sum(skb, + (unsigned char *)__va(bdp->cbd_bufaddr), + pkt_len, 0); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + } + } + + /* Clear the status flags for this buffer. + */ + bdp->cbd_sc &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty. + */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + + /* Update BD pointer to next entry. + */ + if (bdp->cbd_sc & BD_ENET_RX_WRAP) + bdp = cep->rx_bd_base; + else + bdp++; + + } + cep->cur_rx = (cbd_t *)bdp; + + return 0; +} + +static int +cpm_enet_close(struct device *dev) +{ + /* Don't know what to do yet. + */ + + return 0; +} + +static struct net_device_stats *cpm_enet_get_stats(struct device *dev) +{ + struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv; + + return &cep->stats; +} + +/* Set or clear the multicast filter for this adaptor. + * Skeleton taken from sunlance driver. + * The CPM Ethernet implementation allows Multicast as well as individual + * MAC address filtering. Some of the drivers check to make sure it is + * a group multicast address, and discard those that are not. I guess I + * will do the same for now, but just remove the test if you want + * individual filtering as well (do the upper net layers want or support + * this kind of feature?). + */ + +static void set_multicast_list(struct device *dev) +{ + struct cpm_enet_private *cep; + struct dev_mc_list *dmi; + u_char *mcptr, *tdptr; + volatile scc_enet_t *ep; + int i, j; + + cep = (struct cpm_enet_private *)dev->priv; + + /* Get pointer to SCC1 area in parameter RAM. + */ + ep = (scc_enet_t *)dev->base_addr; + + if (dev->flags&IFF_PROMISC) { + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + cep->sccp->scc_pmsr |= SCC_PMSR_PRO; + } else { + + cep->sccp->scc_pmsr &= ~SCC_PMSR_PRO; + + if (dev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's. + */ + ep->sen_gaddr1 = 0xffff; + ep->sen_gaddr2 = 0xffff; + ep->sen_gaddr3 = 0xffff; + ep->sen_gaddr4 = 0xffff; + } + else { + /* Clear filter and add the addresses in the list. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + + dmi = dev->mc_list; + + for (i=0; i<dev->mc_count; i++) { + + /* Only support group multicast for now. + */ + if (!(dmi->dmi_addr[0] & 1)) + continue; + + /* The address in dmi_addr is LSB first, + * and taddr is MSB first. We have to + * copy bytes MSB first from dmi_addr. + */ + mcptr = (u_char *)dmi->dmi_addr + 5; + tdptr = (u_char *)&ep->sen_taddrh; + for (j=0; j<6; j++) + *tdptr++ = *mcptr--; + + /* Ask CPM to run CRC and set bit in + * filter mask. + */ + cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG; + while (cpmp->cp_cpcr & CPM_CR_FLG); + } + } + } +} diff --git a/arch/ppc/8xx_io/uart.c b/arch/ppc/8xx_io/uart.c new file mode 100644 index 000000000..a60eda695 --- /dev/null +++ b/arch/ppc/8xx_io/uart.c @@ -0,0 +1,2530 @@ +/* + * UART driver for MPC860 CPM SCC or SMC + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * I used the serial.c driver as the framework for this driver. + * Give credit to those guys. + * The original code was written for the MBX860 board. I tried to make + * it generic, but there may be some assumptions in the structures that + * have to be fixed later. + * To save porting time, I did not bother to change any object names + * that are not accessed outside of this file. + * It still needs lots of work........When it was easy, I included code + * to support the SCCs, but this has never been tested, nor is it complete. + * Only the SCCs support modem control, so that is not complete either. + * + * This module exports the following rs232 io functions: + * + * int rs_8xx_init(void); + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <asm/uaccess.h> + +#include "commproc.h" + +#ifdef CONFIG_SERIAL_CONSOLE +#include <linux/console.h> + +/* this defines the index into rs_table for the port to use +*/ +#ifndef CONFIG_SERIAL_CONSOLE_PORT +#define CONFIG_SERIAL_CONSOLE_PORT 0 +#endif +#endif + +#define TX_WAKEUP ASYNC_SHARE_IRQ + +static char *serial_name = "CPM UART driver"; +static char *serial_version = "0.01"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* + * Serial driver configuration section. Here are the various options: + */ +#define SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + +#define _INLINE_ inline + +#define DBG_CNT(s) + +/* We overload some of the items in the data structure to meet our + * needs. For example, the port address is the CPM parameter ram + * offset for the SCC or SMC. The maximum number of ports is 4 SCCs and + * 2 SMCs. The "hub6" field is used to indicate the channel number, with + * 0 and 1 indicating the SMCs and 2, 3, 4, and 5 are the SCCs. + * Since these ports are so versatile, I don't yet have a strategy for + * their management. For example, SCC1 is used for Ethernet. Right + * now, just don't put them in the table. Of course, right now I just + * want the SMC to work as a uart :-).. + * The "type" field is currently set to 0, for PORT_UNKNOWN. It is + * not currently used. I should probably use it to indicate the port + * type of CMS or SCC. + * The SMCs do not support any modem control signals. + */ +#define smc_scc_num hub6 +#define SCC_NUM_BASE 2 + +static struct serial_state rs_table[] = { + /* UART CLK PORT IRQ FLAGS NUM */ + { 0, 0, PROFF_SMC1, CPMVEC_SMC1, 0, 0 }, /* SMC1 ttyS0 */ + { 0, 0, PROFF_SMC2, CPMVEC_SMC2, 0, 1 }, /* SMC1 ttyS0 */ +}; + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + +/* The number of buffer descriptors and their sizes. +*/ +#define RX_NUM_FIFO 4 +#define RX_BUF_SIZE 32 +#define TX_NUM_FIFO 4 +#define TX_BUF_SIZE 32 + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* The async_struct in serial.h does not really give us what we + * need, so define our own here. + */ +typedef struct serial_info { + int magic; + int flags; + struct serial_state *state; + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int timeout; + int line; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + + /* CPM Buffer Descriptor pointers. + */ + cbd_t *rx_bd_base; + cbd_t *rx_cur; + cbd_t *tx_bd_base; + cbd_t *tx_cur; +} ser_info_t; + +static void change_speed(ser_info_t *info); +static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout); + +static inline int serial_paranoia_check(ser_info_t *info, + kdev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/* + * This is used to figure out the divisor speeds and the timeouts, + * indexed by the termio value. The generic CPM functions are responsible + * for setting and assigning baud rate generators for us. + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; + + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_8xx_stop(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int idx; + unsigned long flags; + volatile scc_t *sccp; + volatile smc_t *smcp; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + + save_flags(flags); cli(); + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + smcp->smc_smcm &= ~SMCM_TX; + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_TX; + } + restore_flags(flags); +} + +static void rs_8xx_start(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int idx; + unsigned long flags; + volatile scc_t *sccp; + volatile smc_t *smcp; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + + save_flags(flags); cli(); + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + smcp->smc_smcm |= SMCM_TX; + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm |= UART_SCCM_TX; + } + restore_flags(flags); +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static _INLINE_ void rs_sched_event(ser_info_t *info, + int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(ser_info_t *info) +{ + struct tty_struct *tty = info->tty; + unsigned char ch, *cp; + int ignored = 0; + int i; + ushort status; + struct async_icount *icount; + volatile cbd_t *bdp; + + icount = &info->state->icount; + + /* Just loop through the closed BDs and copy the characters into + * the buffer. + */ + bdp = info->rx_cur; + for (;;) { + if (bdp->cbd_sc & BD_SC_EMPTY) /* If this one is empty */ + break; /* we are all done */ + + /* The read status mask tell us what we should do with + * incoming characters, especially if errors occur. + * One special case is the use of BD_SC_EMPTY. If + * this is not set, we are supposed to be ignoring + * inputs. In this case, just mark the buffer empty and + * continue. + if (!(info->read_status_mask & BD_SC_EMPTY)) { + bdp->cbd_sc |= BD_SC_EMPTY; + bdp->cbd_sc &= + ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->rx_bd_base; + else + bdp++; + continue; + } + */ + + /* Get the number of characters and the buffer pointer. + */ + i = bdp->cbd_datlen; + cp = (unsigned char *)__va(bdp->cbd_bufaddr); + status = bdp->cbd_sc; + + /* Check to see if there is room in the tty buffer for + * the characters in our BD buffer. If not, we exit + * now, leaving the BD with the characters. We'll pick + * them up again on the next receive interrupt (which could + * be a timeout). + */ + if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE) + break; + + while (i-- > 0) { + ch = *cp++; + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (status & (BD_SC_BR | BD_SC_FR | + BD_SC_PR | BD_SC_OV)) { + /* + * For statistics only + */ + if (status & BD_SC_BR) + icount->brk++; + else if (status & BD_SC_PR) + icount->parity++; + else if (status & BD_SC_FR) + icount->frame++; + if (status & BD_SC_OV) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + if (status & info->ignore_status_mask) { + if (++ignored > 100) + break; + continue; + } + */ + status &= info->read_status_mask; + + if (status & (BD_SC_BR)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (status & BD_SC_PR) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (status & BD_SC_FR) + *tty->flip.flag_buf_ptr = TTY_FRAME; + if (status & BD_SC_OV) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = + TTY_OVERRUN; + } + } + } + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + /* This BD is ready to be used again. Clear status. + * Get next BD. + */ + bdp->cbd_sc |= BD_SC_EMPTY; + bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->rx_bd_base; + else + bdp++; + } + + info->rx_cur = (cbd_t *)bdp; + + queue_task(&tty->flip.tqueue, &tq_timer); +} + +static _INLINE_ void transmit_chars(ser_info_t *info) +{ + + if (info->flags & TX_WAKEUP) { + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + } + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif +} + +#ifdef notdef + /* I need to do this for the SCCs, so it is left as a reminder. + */ +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + icount->cts++; + wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SERIAL_DEBUG_OPEN + printk("scheduling hangup..."); +#endif + queue_task(&info->tqueue_hangup, + &tq_scheduler); + } + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + } + } +} +#endif + +/* + * This is the serial driver's interrupt routine for a single port + */ +static void rs_8xx_interrupt(void *dev_id) +{ + u_char events; + int idx; + ser_info_t *info; + volatile smc_t *smcp; + + info = (ser_info_t *)dev_id; + + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + } + else { + panic("SCC UART Interrupt....not ready"); + } + + events = smcp->smc_smce; +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d, %x)...", + info->state->smc_scc_num, events); +#endif + if (events & SMCM_RX) + receive_chars(info); + if (events & SMCM_TX) + transmit_chars(info); + smcp->smc_smce = events; +#ifdef modem_control + check_modem_status(info); +#endif + info->last_active = jiffies; +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void do_softint(void *private_) +{ + ser_info_t *info = (ser_info_t *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_serial_hangup() -> tty->hangup() -> rs_hangup() + * + */ +static void do_serial_hangup(void *private_) +{ + struct async_struct *info = (struct async_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + tty_hangup(tty); +} + +static void rs_8xx_timer(void) +{ + printk("rs_8xx_timer\n"); +} + + +static int startup(ser_info_t *info) +{ + unsigned long flags; + int retval=0; + int idx; + struct serial_state *state= info->state; + volatile smc_t *smcp; + volatile scc_t *sccp; + volatile smc_uart_t *up; + + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + goto errout; + } + +#ifdef maybe + if (!state->port || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + goto errout; + } +#endif + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttys%d (irq %d)...", info->line, state->irq); +#endif + + +#ifdef modem_control + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#endif + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + + /* + * and set the speed of the serial port + */ + change_speed(info); + + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + + /* Enable interrupts and I/O. + */ + smcp->smc_smcm |= (SMCM_RX | SMCM_TX); + smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN); + + /* We can tune the buffer length and idle characters + * to take advantage of the entire incoming buffer size. + * If mrblr is something other than 1, maxidl has to be + * non-zero or we never get an interrupt. The maxidl + * is the number of character times we wait after reception + * of the last character before we decide no more characters + * are coming. + */ + up = (smc_uart_t *)&cpmp->cp_dparam[state->port]; + up->smc_mrblr = 1; /* receive buffer length */ + up->smc_maxidl = 0; /* wait forever for next char */ + up->smc_brkcr = 1; /* number of break chars */ + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm |= UART_SCCM_RX; + } + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(ser_info_t * info) +{ + unsigned long flags; + struct serial_state *state; + int idx; + volatile smc_t *smcp; + volatile scc_t *sccp; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + + /* Disable interrupts and I/O. + */ + smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); +#ifdef CONFIG_SERIAL_CONSOLE + /* We can't disable the transmitter if this is the + * system console. + */ + if (idx != CONFIG_SERIAL_CONSOLE_PORT) +#endif + smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_RX; + } + + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(ser_info_t *info) +{ + int baud_rate; + unsigned cflag, cval, prev_mode; + int i, bits, idx; + unsigned long flags; + volatile smc_t *smcp; + volatile scc_t *sccp; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + + /* Character length programmed into the mode register is the + * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, + * 1 or 2 stop bits, minus 1. + * The value 'bits' counts this for us. + */ + cval = 0; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: bits = 5; break; + case CS6: bits = 6; break; + case CS7: bits = 7; break; + case CS8: bits = 8; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: bits = 8; break; + } + if (cflag & CSTOPB) { + cval |= SMCMR_SL; /* Two stops */ + bits++; + } + if (cflag & PARENB) { + cval |= SMCMR_PEN; + bits++; + } + if (!(cflag & PARODD)) + cval |= SMCMR_PM_EVEN; + + /* Determine divisor based on baud rate */ + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + + baud_rate = baud_table[i]; + + info->timeout = (TX_BUF_SIZE*HZ*bits); + info->timeout += HZ/50; /* Add .02 seconds of slop */ + +#ifdef modem_control + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); +#endif + + /* + * Set up parity check flag + */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); + if (I_INPCK(info->tty)) + info->read_status_mask |= BD_SC_FR | BD_SC_PR; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= BD_SC_BR; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= BD_SC_PR | BD_SC_FR; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= BD_SC_BR; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= BD_SC_OV; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + info->read_status_mask &= ~BD_SC_EMPTY; + save_flags(flags); cli(); + + /* Start bit has not been added (so don't, because we would just + * subtract it later), and we need to add one for the number of + * stops bits (there is always at least one). + */ + bits++; + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + + /* Set the mode register. We want to keep a copy of the + * enables, because we want to put them back if they were + * present. + */ + prev_mode = smcp->smc_smcmr; + smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART; + smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN)); + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_RX; + } + + mbx_cpm_setbrg(info->state->smc_scc_num, baud_rate); + + restore_flags(flags); +} + +static void rs_8xx_put_char(struct tty_struct *tty, unsigned char ch) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + volatile cbd_t *bdp; + + if (serial_paranoia_check(info, tty->device, "rs_put_char")) + return; + + if (!tty) + return; + + bdp = info->tx_cur; + while (bdp->cbd_sc & BD_SC_READY); + + *((char *)__va(bdp->cbd_bufaddr)) = ch; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + + info->tx_cur = (cbd_t *)bdp; + +} + +static int rs_8xx_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + ser_info_t *info = (ser_info_t *)tty->driver_data; + volatile cbd_t *bdp; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty) + return 0; + + bdp = info->tx_cur; + + while (1) { + c = MIN(count, TX_BUF_SIZE); + + if (c <= 0) + break; + + if (bdp->cbd_sc & BD_SC_READY) { + info->flags |= TX_WAKEUP; + break; + } + + if (from_user) { + if (c != + copy_from_user(__va(bdp->cbd_bufaddr), buf, c)) { + if (!ret) + ret = -EFAULT; + break; + } + } else { + memcpy(__va(bdp->cbd_bufaddr), buf, c); + } + + bdp->cbd_datlen = c; + bdp->cbd_sc |= BD_SC_READY; + + buf += c; + count -= c; + ret += c; + + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + info->tx_cur = (cbd_t *)bdp; + } + return ret; +} + +static int rs_8xx_write_room(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int ret; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + + if ((info->tx_cur->cbd_sc & BD_SC_READY) == 0) { + info->flags &= ~TX_WAKEUP; + ret = TX_BUF_SIZE; + } + else { + info->flags |= TX_WAKEUP; + ret = 0; + } + return ret; +} + +/* I could track this with transmit counters....maybe later. +*/ +static int rs_8xx_chars_in_buffer(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return 0; +} + +static void rs_8xx_flush_buffer(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + + /* There is nothing to "flush", whatever we gave the CPM + * is on its way out. + */ + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + info->flags &= ~TX_WAKEUP; +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_8xx_send_xchar(struct tty_struct *tty, char ch) +{ + volatile cbd_t *bdp; + + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + bdp = info->tx_cur; + while (bdp->cbd_sc & BD_SC_READY); + + *((char *)__va(bdp->cbd_bufaddr)) = ch; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + + info->tx_cur = (cbd_t *)bdp; +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_8xx_throttle(struct tty_struct * tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + rs_8xx_send_xchar(tty, STOP_CHAR(tty)); + +#ifdef modem_control + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); +#endif +} + +static void rs_8xx_unthrottle(struct tty_struct * tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_8xx_send_xchar(tty, START_CHAR(tty)); + } +#ifdef modem_control + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +#ifdef maybe +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct async_struct * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + + cli(); + status = serial_in(info, UART_LSR); + sti(); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + return put_user(result,value); +} +#endif + +static int get_modem_info(ser_info_t *info, unsigned int *value) +{ + unsigned int result = 0; +#ifdef modem_control + unsigned char control, status; + + control = info->MCR; + cli(); + status = serial_in(info, UART_MSR); + sti(); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); +#endif + return put_user(result,value); +} + +static int set_modem_info(ser_info_t *info, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + + error = get_user(arg, value); + if (error) + return error; +#ifdef modem_control + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); +#endif + return 0; +} + +/* Sending a break is a two step process on the SMC/SCC. It is accomplished + * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT + * command. We take advantage of the begin/end functions to make this + * happen. + */ +static void begin_break(ser_info_t *info) +{ + volatile cpm8xx_t *cp; + ushort chan; + ushort num; + + cp = cpmp; + + if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) { + if (num == 0) + chan = CPM_CR_CH_SMC1; + else + chan = CPM_CR_CH_SMC2; + } + else { + num -= SCC_NUM_BASE; + switch (num) { + case 0: chan = CPM_CR_CH_SCC1; break; + case 1: chan = CPM_CR_CH_SCC2; break; + case 2: chan = CPM_CR_CH_SCC3; break; + case 3: chan = CPM_CR_CH_SCC4; break; + default: return; + } + } + cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + +static void end_break(ser_info_t *info) +{ + volatile cpm8xx_t *cp; + ushort chan; + ushort num; + + cp = cpmp; + + if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) { + if (num == 0) + chan = CPM_CR_CH_SMC1; + else + chan = CPM_CR_CH_SMC2; + } + else { + num -= SCC_NUM_BASE; + switch (num) { + case 0: chan = CPM_CR_CH_SCC1; break; + case 1: chan = CPM_CR_CH_SCC2; break; + case 2: chan = CPM_CR_CH_SCC3; break; + case 3: chan = CPM_CR_CH_SCC4; break; + default: return; + } + } + cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + +/* + * This routine sends a break character out the serial port. + */ +static void send_break(ser_info_t *info, int duration) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; +#ifdef SERIAL_DEBUG_SEND_BREAK + printk("rs_send_break(%d) jiff=%lu...", duration, jiffies); +#endif + begin_break(info); + schedule(); + end_break(info); +#ifdef SERIAL_DEBUG_SEND_BREAK + printk("done jiffies=%lu\n", jiffies); +#endif +} + + +static int rs_8xx_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + ser_info_t *info = (ser_info_t *)tty->driver_data; + int retval; + struct async_icount cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); /* 1/4 second */ + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCSBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + begin_break(info); + return 0; + case TIOCCBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + end_break(info); + return 0; + case TIOCGSOFTCAR: + return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); + case TIOCSSOFTCAR: + error = get_user(arg, (unsigned int *) arg); + if (error) + return error; + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); +#ifdef maybe + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); +#endif + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: +#ifdef modem_control + cli(); + /* note the counters on entry */ + cprev = info->state->icount; + sti(); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + cli(); + cnow = info->state->icount; /* atomic copy */ + sti(); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ +#else + return 0; +#endif + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + cli(); + cnow = info->state->icount; + sti(); + p_cuser = (struct serial_icounter_struct *) arg; + error = put_user(cnow.cts, &p_cuser->cts); + if (error) return error; + error = put_user(cnow.dsr, &p_cuser->dsr); + if (error) return error; + error = put_user(cnow.rng, &p_cuser->rng); + if (error) return error; + error = put_user(cnow.dcd, &p_cuser->dcd); + if (error) return error; + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* FIX UP modem control here someday...... +*/ +static void rs_8xx_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if ( (tty->termios->c_cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info); + +#ifdef modem_control + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(tty->termios->c_cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (tty->termios->c_cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!tty->hw_stopped || + !(tty->termios->c_cflag & CRTSCTS)) { + info->MCR |= UART_MCR_RTS; + } + cli(); + serial_out(info, UART_MCR, info->MCR); + sti(); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_8xx_start(tty); + } +#endif + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_8xx_close(struct tty_struct *tty, struct file * filp) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + struct serial_state *state; + unsigned long flags; + int idx; + volatile smc_t *smcp; + volatile scc_t *sccp; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->read_status_mask &= ~BD_SC_EMPTY; + if (info->flags & ASYNC_INITIALIZED) { + if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { + smcp = &cpmp->cp_smc[idx]; + smcp->smc_smcm &= ~SMCM_RX; + smcp->smc_smcmr &= ~SMCMR_REN; + } + else { + sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp->scc_sccm &= ~UART_SCCM_RX; + } + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + rs_8xx_wait_until_sent(tty, info->timeout); + } + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; + restore_flags(flags); +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + volatile cbd_t *bdp; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + +#ifdef maybe + if (info->state->type == PORT_UNKNOWN) + return; +#endif + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = 1; + if (timeout) + char_time = MIN(char_time, timeout); +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + + /* We go through the loop at least once because we can't tell + * exactly when the last character exits the shifter. There can + * be at least two characters waiting to be sent after the buffers + * are empty. + */ + do { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + current->state = TASK_INTERRUPTIBLE; +/* current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + char_time; + schedule(); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + bdp = info->tx_cur; + } while (bdp->cbd_sc & BD_SC_READY); + current->state = TASK_RUNNING; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_8xx_hangup(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_8xx_flush_buffer(tty); + shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + ser_info_t *info) +{ +#ifdef DO_THIS_LATER + struct wait_queue wait = { current, NULL }; +#endif + struct serial_state *state = info->state; + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + * If this is an SMC port, we don't have modem control to wait + * for, so just get out here. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR)) || + (info->state->smc_scc_num < SCC_NUM_BASE)) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (state->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, state->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; +#ifdef DO_THIS_LATER + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + state->line, state->count); +#endif + cli(); + if (!tty_hung_up_p(filp)) + state->count--; + sti(); + info->blocked_open++; + while (1) { + cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif +#endif /* DO_THIS_LATER */ + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, ser_info_t **ret_info) +{ + struct serial_state *sstate; + + sstate = rs_table + line; + if (sstate->info) { + sstate->count++; + *ret_info = (ser_info_t *)sstate->info; + return 0; + } + else { + return -ENOMEM; + } +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int rs_8xx_open(struct tty_struct *tty, struct file * filp) +{ + ser_info_t *info; + int retval, line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + return -ENODEV; + retval = get_async_struct(line, &info); + if (retval) + return retval; + if (serial_paranoia_check(info, tty->device, "rs_open")) + return -ENODEV; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif + tty->driver_data = info; + info->tty = tty; + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + MOD_INC_USE_COUNT; + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info); + } + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ + +static int inline line_info(char *buf, struct serial_state *state) +{ +#ifdef notdef + struct async_struct *info = state->info, scr_info; + char stat_buf[30], control, status; +#endif + int ret; + + ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", + state->line, + (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC", + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + +#ifdef notdef + /* + * Figure out the current RS-232 lines + */ + if (!info) { + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->quot = 0; + info->tty = 0; + } + cli(); + status = serial_in(info, UART_MSR); + control = info ? info->MCR : serial_in(info, UART_MCR); + sti(); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); + + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); +#endif + return ret; +} + +int rs_8xx_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + len += line_info(page + len, &rs_table[i]); + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * rs_init() and friends + * + * rs_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s\n", serial_name, serial_version); +} + +/* + * The serial driver boot-time initialization code! + */ +__initfunc(int rs_8xx_init(void)) +{ + struct serial_state * state; + ser_info_t *info; + uint mem_addr, dp_addr; + int i, j; + ushort chan; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + volatile smc_uart_t *up; + + init_bh(SERIAL_BH, do_serial_bh); +#if 0 + timer_table[RS_TIMER].fn = rs_8xx_timer; + timer_table[RS_TIMER].expires = 0; +#endif + + show_serial_version(); + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "serial"; + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_8xx_open; + serial_driver.close = rs_8xx_close; + serial_driver.write = rs_8xx_write; + serial_driver.put_char = rs_8xx_put_char; + serial_driver.write_room = rs_8xx_write_room; + serial_driver.chars_in_buffer = rs_8xx_chars_in_buffer; + serial_driver.flush_buffer = rs_8xx_flush_buffer; + serial_driver.ioctl = rs_8xx_ioctl; + serial_driver.throttle = rs_8xx_throttle; + serial_driver.unthrottle = rs_8xx_unthrottle; + serial_driver.send_xchar = rs_8xx_send_xchar; + serial_driver.set_termios = rs_8xx_set_termios; + serial_driver.stop = rs_8xx_stop; + serial_driver.start = rs_8xx_start; + serial_driver.hangup = rs_8xx_hangup; + serial_driver.wait_until_sent = rs_8xx_wait_until_sent; + serial_driver.read_proc = rs_8xx_read_proc; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + cp = cpmp; /* Get pointer to Communication Processor */ + + /* Configure SMCs Tx/Rx instead of port B parallel I/O. + */ + cp->cp_pbpar |= 0x00000cc0; + cp->cp_pbdir &= ~0x00000cc0; + cp->cp_pbodr &= ~0x00000cc0; + + /* Wire BRG1 to SMC1 and BRG2 to SMC2. + */ + cp->cp_simode = 0x10000000; + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n", + i, state->port, + (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC"); +#ifdef CONFIG_SERIAL_CONSOLE + /* If we just printed the message on the console port, and + * we are about to initialize it for general use, we have + * to wait a couple of character times for the CR/NL to + * make it out of the transmit buffer. + */ + if (i == CONFIG_SERIAL_CONSOLE_PORT) + udelay(2000); +#endif + info = kmalloc(sizeof(ser_info_t), GFP_KERNEL); + if (info) { + memset(info, 0, sizeof(ser_info_t)); + info->magic = SERIAL_MAGIC; + info->flags = state->flags; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->tqueue_hangup.routine = do_serial_hangup; + info->tqueue_hangup.data = info; + info->line = i; + info->state = state; + state->info = (struct async_struct *)info; + + /* Right now, assume we are using SMCs. + */ + sp = &cp->cp_smc[state->smc_scc_num]; + + up = (smc_uart_t *)&cp->cp_dparam[state->port]; + + /* We need to allocate a transmit and receive buffer + * descriptors from dual port ram, and a character + * buffer area from host mem. + */ + dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_NUM_FIFO); + + /* Allocate space for FIFOs in the host memory. + */ + mem_addr = mbx_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_rbase = dp_addr; + info->rx_cur = info->rx_bd_base = (cbd_t *)bdp; + + for (j=0; j<(RX_NUM_FIFO-1); j++) { + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; + mem_addr += RX_BUF_SIZE; + bdp++; + } + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; + + dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_NUM_FIFO); + + /* Allocate space for FIFOs in the host memory. + */ + mem_addr = mbx_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_tbase = dp_addr; + info->tx_cur = info->tx_bd_base = (cbd_t *)bdp; + + for (j=0; j<(TX_NUM_FIFO-1); j++) { + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = BD_SC_INTRPT; + mem_addr += TX_BUF_SIZE; + bdp++; + } + bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single character + * interrupts. Using idle charater time requires + * some additional tuning. + */ + up->smc_mrblr = 1; /* receive buffer length */ + up->smc_maxidl = 0; /* wait forever for next char */ + up->smc_brkcr = 1; /* number of break chars */ + + /* Send the CPM an initialize command. + */ + if (state->smc_scc_num == 0) + chan = CPM_CR_CH_SMC1; + else + chan = CPM_CR_CH_SMC2; + cp->cp_cpcr = mk_cr_cmd(chan, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Disable all interrupts and clear all pending + * events. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Install interrupt handler. + */ + cpm_install_handler(state->irq, rs_8xx_interrupt, info); + + /* Set up the baud rate generator. + */ + mbx_cpm_setbrg(state->smc_scc_num, 9600); + + /* If the port is the console, enable Rx and Tx. + */ +#ifdef CONFIG_SERIAL_CONSOLE + if (i == CONFIG_SERIAL_CONSOLE_PORT) + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; +#endif + } + } + return 0; +} + +/* + * The serial console driver used during boot. Note that these names + * clash with those found in "serial.c", so we currently can't support + * the 16xxx uarts and these at the same time. I will fix this to become + * an indirect function call from tty_io.c (or something). + */ + +#ifdef CONFIG_SERIAL_CONSOLE + +/* + * Print a string to the serial port trying not to disturb any possible + * real use of the port... + */ +static void serial_console_write(struct console *c, const char *s, + unsigned count) +{ + struct serial_state *ser; + ser_info_t *info; + unsigned i; + volatile cbd_t *bdp, *bdbase; + volatile smc_uart_t *up; + volatile u_char *cp; + + ser = rs_table + c->index; + + /* If the port has been initialized for general use, we have + * to use the buffer descriptors allocated there. Otherwise, + * we simply use the single buffer allocated. + */ + if ((info = (ser_info_t *)ser->info) != NULL) { + bdp = info->tx_cur; + bdbase = info->tx_bd_base; + } + else { + /* Pointer to UART in parameter ram. + */ + up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; + + /* Get the address of the host memory buffer. + */ + bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + } + + /* + * We need to gracefully shut down the transmitter, disable + * interrupts, then send our bytes out. + */ + + /* + * Now, do each character. This is not as bad as it looks + * since this is a holding FIFO and not a transmitting FIFO. + * We could add the complexity of filling the entire transmit + * buffer, but we would just wait longer between accesses...... + */ + for (i = 0; i < count; i++, s++) { + + /* Wait for transmitter fifo to empty. + * Ready indicates output is ready, and xmt is doing + * that, not that it is ready for us to send. + */ + while (bdp->cbd_sc & BD_SC_READY); + + /* Send the character out. */ + cp = __va(bdp->cbd_bufaddr); + *cp = *s; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = bdbase; + else + bdp++; + + /* if a LF, also do CR... */ + if (*s == 10) { + while (bdp->cbd_sc & BD_SC_READY); + cp = __va(bdp->cbd_bufaddr); + *cp = 13; + bdp->cbd_datlen = 1; + bdp->cbd_sc |= BD_SC_READY; + + if (bdp->cbd_sc & BD_SC_WRAP) { + bdp = bdbase; + } + else { + bdp++; + } + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + while (bdp->cbd_sc & BD_SC_READY); + + if (info) + info->tx_cur = (cbd_t *)bdp; +} + +/* + * Receive character from the serial port. This only works well + * before the port is initialize for real use. + */ +static int serial_console_wait_key(struct console *co) +{ + struct serial_state *ser; + u_char c, *cp; + ser_info_t *info; + volatile cbd_t *bdp; + volatile smc_uart_t *up; + + ser = rs_table + co->index; + + /* Pointer to UART in parameter ram. + */ + up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; + + /* Get the address of the host memory buffer. + * If the port has been initialized for general use, we must + * use information from the port structure. + */ + if ((info = ser->info)) + bdp = info->rx_cur; + else + bdp = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* + * We need to gracefully shut down the receiver, disable + * interrupts, then read the input. + */ + while (bdp->cbd_sc & BD_SC_EMPTY); /* Wait for a character */ + cp = __va(bdp->cbd_bufaddr); + + if (info) { + if (bdp->cbd_sc & BD_SC_WRAP) { + bdp = info->rx_bd_base; + } + else { + bdp++; + } + info->rx_cur = (cbd_t *)bdp; + } + + c = *cp; + return((int)c); +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 64 + c->index); +} + +/* This must always be called before the rs_8xx_init() function, otherwise + * it blows away the port control information. +*/ +__initfunc(static int serial_console_setup(struct console *co, char *options)) +{ + struct serial_state *ser; + uint mem_addr, dp_addr; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + volatile smc_uart_t *up; + + co->cflag = CREAD|CLOCAL|B9600|CS8; + + ser = rs_table + co->index; + + cp = cpmp; /* Get pointer to Communication Processor */ + + /* Right now, assume we are using SMCs. + */ + sp = &cp->cp_smc[ser->smc_scc_num]; + + /* When we get here, the CPM has been reset, so we need + * to configure the port. + * We need to allocate a transmit and receive buffer descriptor + * from dual port ram, and a character buffer area from host mem. + */ + up = (smc_uart_t *)&cp->cp_dparam[ser->port]; + cp->cp_pbpar = 0x00c0; /* Enable SMC1 instead of Port B I/O */ + + /* Allocate space for two buffer descriptors in the DP ram. + */ + dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * 2); + + /* Allocate space for two 2 byte FIFOs in the host memory. + */ + mem_addr = mbx_cpm_hostalloc(4); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + bdp->cbd_bufaddr = __pa(mem_addr); + (bdp+1)->cbd_bufaddr = __pa(mem_addr+2); + + /* For the receive, set empty and wrap. + * For transmit, set wrap. + */ + bdp->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + (bdp+1)->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dp_addr; /* Base of receive buffer desc. */ + up->smc_tbase = dp_addr+sizeof(cbd_t); /* Base of xmt buffer desc. */ + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single character interrupts. + */ + up->smc_mrblr = 1; /* receive buffer length */ + up->smc_maxidl = 0; /* wait forever for next char */ + + /* Send the CPM an initialize command. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Set up the baud rate generator. + */ + mbx_cpm_setbrg(ser->smc_scc_num, 9600); + + /* And finally, enable Rx and Tx. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + return 0; +} + +static struct console sercons = { + "ttyS", + serial_console_write, + NULL, + serial_console_device, + serial_console_wait_key, + NULL, + serial_console_setup, + CON_PRINTBUFFER, + CONFIG_SERIAL_CONSOLE_PORT, + 0, + NULL +}; + +/* + * Register console. + */ +__initfunc (long console_8xx_init(long kmem_start, long kmem_end)) +{ + register_console(&sercons); + return kmem_start; +} + +#endif diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index 832513c8d..cd81e45a4 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -12,14 +12,7 @@ # Rewritten by Cort Dougan and Paul Mackerras # -ifdef CONFIG_CHRP -# XXX for now -KERNELBASE =0x90000000 -KERNELLOAD =0x90010000 -else -KERNELBASE =0xc0000000 -KERNELLOAD =0xc0000000 -endif +KERNELLOAD =0xc0000000 # PowerPC (cross) tools ifneq ($(shell uname -m),ppc) @@ -32,21 +25,13 @@ ASFLAGS = LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__ CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \ - -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring \ - -DKERNELBASE=$(KERNELBASE) + -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring CPP = $(CC) -E $(CFLAGS) -ifdef CONFIG_601 -CFLAGS := $(CFLAGS) -mcpu=601 -DCPU=601 +ifdef CONFIG_8xx +CFLAGS := $(CFLAGS) -mcpu=860 endif -ifdef CONFIG_603 -CFLAGS := $(CFLAGS) -mcpu=603 -DCPU=603 -endif - -ifdef CONFIG_604 -CFLAGS := $(CFLAGS) -mcpu=604 -DCPU=604 -endif HEAD := arch/ppc/kernel/head.o @@ -64,6 +49,11 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot +ifdef CONFIG_8xx +SUBDIRS += arch/ppc/8xx_io +DRIVERS += arch/ppc/8xx_io/8xx_io.a drivers/net/net.a +endif + checks: @$(MAKE) -C arch/$(ARCH)/kernel checks @@ -83,6 +73,19 @@ prep_config: rm -f .config arch/ppc/defconfig ln -s prep_defconfig arch/ppc/defconfig +chrp_config: + rm -f .config arch/ppc/defconfig + ln -s chrp_defconfig arch/ppc/defconfig + +common_config: + rm -f .config arch/ppc/common_defconfig + ln -s common_defconfig arch/ppc/defconfig + +mbx_config: + rm -f .config arch/ppc/defconfig + ln -s mbx_defconfig arch/ppc/defconfig + + tags: etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h} diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index 02f95f577..84be805b2 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -25,31 +25,70 @@ ZOFF = 0 ZSZ = 0 IOFF = 0 ISZ = 0 +ifeq ($(CONFIG_MBX),y) +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000 +else #ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000 +endif GZIP_FLAGS = -v9 -OBJECTS := head.o misc.o vreset.o kbd.o ../coffboot/zlib.o # inflate.o unzip.o -#OBJECTS := crt0.o start.o vreset.o +OBJECTS := head.o misc.o ../coffboot/zlib.o # inflate.o unzip.o CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc +ifeq ($(CONFIG_MBX),y) +OBJECTS += mbxtty.o +CFLAGS += -DCONFIG_MBX +else +OBJECTS += vreset.o kbd.o +endif + all: zImage +ifeq ($(CONFIG_ALL_PPC),y) +CONFIG_PREP = y +endif ifeq ($(CONFIG_PREP),y) -mkprep : mkprep.c - $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c +zvmlinux.initrd: zvmlinux + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp zvmlinux.initrd + $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset zvmlinux.initrd initrd` \ + -DINITRD_SIZE=`sh size zvmlinux.initrd initrd` \ + -DZIMAGE_OFFSET=`sh offset zvmlinux.initrd image` \ + -DZIMAGE_SIZE=`sh size zvmlinux.initrd image` \ + -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp $@ + rm zvmlinux.initrd.tmp -floppy: $(TOPDIR)/vmlinux zImage - dd if=zImage of=/dev/fd0H1440 bs=64b +else +zvmlinux.initrd: +endif -znetboot : zImage - cp zImage /tftpboot/zImage.prep +zImage: zvmlinux mkprep +ifeq ($(CONFIG_PREP),y) + ./mkprep -pbp zvmlinux zImage +endif +ifeq ($(CONFIG_MBX),y) + ln -sf zvmlinux zImage +endif -znetboot.initrd : zImage.initrd - cp zImage.initrd /tftpboot/zImage.prep +zImage.initrd: zvmlinux.initrd mkprep +ifeq ($(CONFIG_PREP),y) + ./mkprep -pbp zvmlinux.initrd zImage.initrd +endif +ifeq ($(CONFIG_MBX),y) + ln -sf zvmlinux.initrd zImage.initrd +endif zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # @@ -63,61 +102,39 @@ zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # then with the offset rebuild the bootloader so we know where the kernel is # $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ - -DZIMAGE_OFFSET=`./offset zvmlinux image` \ - -DZIMAGE_SIZE=`./size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ + -DZIMAGE_OFFSET=`sh offset zvmlinux image` \ + -DZIMAGE_SIZE=`sh size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ -c -o misc.o misc.c $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ zvmlinux.tmp $@ rm zvmlinux.tmp -zvmlinux.initrd: zvmlinux - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp zvmlinux.initrd - $(CC) $(CFLAGS) -DINITRD_OFFSET=`./offset zvmlinux.initrd initrd` \ - -DINITRD_SIZE=`./size zvmlinux.initrd initrd` \ - -DZIMAGE_OFFSET=`./offset zvmlinux.initrd image` \ - -DZIMAGE_SIZE=`./size zvmlinux.initrd image` \ - -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp $@ - rm zvmlinux.initrd.tmp - -zImage: zvmlinux mkprep - ./mkprep -pbp zvmlinux zImage - -zImage.initrd: zvmlinux.initrd mkprep - ./mkprep -pbp zvmlinux.initrd zImage.initrd -else -mkprep: - -floppy: - -znetboot: - -znetboot.initrd: - -zvmlinux: - -zvmlinux.initrd: - -zImage: - -zImage.initrd: +floppy: $(TOPDIR)/vmlinux zImage +ifeq ($(CONFIG_PREP),y) + dd if=zImage of=/dev/fd0H1440 bs=64b endif +mkprep : mkprep.c +ifeq ($(CONFIG_PREP),y) + $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c +endif +znetboot : zImage +ifeq ($(CONFIG_PREP),y) + cp zImage /tftpboot/zImage.prep +endif +ifeq ($(CONFIG_MBX),y) + cp zImage /tftpboot/zImage.mbx +endif -# just here to match coffboot/Makefile -vmlinux.coff: - -vmlinux.coff.initrd: +znetboot.initrd : zImage.initrd +ifeq ($(CONFIG_PREP),y) + cp zImage.initrd /tftpboot/zImage.prep +endif +ifeq ($(CONFIG_MBX),y) + cp zImage.initrd /tftpboot/zImage.mbx +endif clean: rm -f vmlinux* zvmlinux* mkprep zImage* @@ -128,3 +145,7 @@ fastdep: dep: $(CPP) -M *.S *.c > .depend +# just here to match coffboot/Makefile +vmlinux.coff: + +vmlinux.coff.initrd: diff --git a/arch/ppc/boot/head.S b/arch/ppc/boot/head.S index ff0030154..26a0be180 100644 --- a/arch/ppc/boot/head.S +++ b/arch/ppc/boot/head.S @@ -1,19 +1,43 @@ +#include <linux/config.h> #include "../kernel/ppc_defs.h" #include "../kernel/ppc_asm.tmpl" #include <asm/processor.h> +#include <asm/cache.h> .text /* * This code is loaded by the ROM loader at some arbitrary location. * Move it to high memory so that it can load the kernel at 0x0000. + * + * The MBX EPPC-Bug understands ELF, so it loads us into the location + * specified in the header. This is a two step process. First, EPPC-Bug + * loads the file into the intermediate buffer memory location specified + * by the environment parameters. When it discovers this is an ELF + * binary, it relocates to the link address for us. Unfortunately, the + * header does not move with the file, so we have to find the + * intermediate load location and read the header from there. From + * information provided by Motorola (thank you), we know this intermediate + * location can be found from the NVRAM environment. + * All of these addresses must be somewhat carefully chosen to make sure + * we don't overlap the regions. I chose to load the kernel at 0, the + * compressed image loads at 0x00100000, and the MBX intermediate buffer + * was set to 0x00200000. Provided the loaded kernel image never grows + * over one megabyte (which I am going to ensure never happens :-), these + * will work fine. When we get called from EPPC-Bug, registers are: + * R1 - Stack pointer at a high memory address. + * R3 - Pointer to Board Information Block. + * R4 - Pointer to argument string. + * Interrupts masked, cache and MMU disabled. */ .globl start start: bl start_ start_: - mr r11,r3 /* Save pointer to residual data */ + mr r11,r3 /* Save pointer to residual/board data */ + +#ifndef CONFIG_MBX mfmsr r3 /* Turn off interrupts */ li r4,0 ori r4,r4,MSR_EE @@ -33,7 +57,6 @@ start_: bne 00b mflr r21 mfctr r22 - bl flush_instruction_cache mtlr r21 mtctr r22 bctr /* Jump to code */ @@ -70,6 +93,8 @@ relocate: mtlr r3 /* Easiest way to do an absolute jump */ blr start_ldr: +#endif /* ndef CONFIG_MBX */ + /* Clear all of BSS */ lis r3,edata@h ori r3,r3,edata@l @@ -89,12 +114,21 @@ start_ldr: li r2,0x000F /* Mask pointer to 16-byte boundary */ andc r1,r1,r2 /* Run loader */ +#ifdef CONFIG_MBX + bl serial_init /* Init MBX serial port */ +#define ILAP_ADDRESS 0xfa000020 + lis r8, ILAP_ADDRESS@h + lwz r8, ILAP_ADDRESS@l(r8) + li r9,end@h + ori r9,r9,end@l + sub r7,r8,r9 + addis r8, r8, 1 /* Add 64K */ +#endif mr r3,r8 /* Load point */ mr r4,r7 /* Program length */ mr r5,r6 /* Checksum */ mr r6,r11 /* Residual data */ bl decompress_kernel - /* changed to use r3 (as firmware does) for kernel as ptr to residual -- Cort*/ lis r6,cmd_line@h @@ -111,45 +145,25 @@ start_ldr: lis r2,initrd_end@h ori r2,r2,initrd_end@l lwz r5,0(r2) - - li r9,0x00c /* Kernel code starts here */ + + /* tell kernel we're prep */ + /* + * get start address of kernel code which is stored as a coff + * entry. see boot/head.S -- Cort + */ + li r9,0x0 + lwz r9,0(r9) mtlr r9 +#ifndef CONFIG_MBX + li r9,0 + lis r10,0xdeadc0de@h + ori r10,r10,0xdeadc0de@l + stw r10,0(r9) +#endif blr hang: b hang - .globl _get_SP -_get_SP: - mr r3,r1 - blr - - .globl _get_PVR -_get_PVR: - mfspr r3,PVR - blr - - .globl _get_MSR -_get_MSR: - mfmsr r3 - blr - - .globl _put_MSR -_put_MSR: - sync - mtmsr r3 - blr - - .globl _get_HID0 -_get_HID0: - mfspr r3,HID0 - blr - - .globl _put_HID0 -_put_HID0: - sync - mtspr HID0,r3 - blr - /* * Delay for a number of microseconds * -- Use the BUS timer (assumes 66MHz) @@ -189,98 +203,4 @@ udelay: blt 2b 3: blr -/* - * This space [buffer] is used to forceably flush the data cache when - * running in copyback mode. This is necessary IFF the data cache could - * contain instructions for which the instruction cache has stale data. - * Since the instruction cache NEVER snoops the data cache, memory must - * be made coherent with the data cache to insure that the instruction - * cache gets a valid instruction stream. Note that this flushing is - * only performed when switching from system to user mode since this is - * the only juncture [as far as the OS goes] where the data cache may - * contain instructions, e.g. after a disk read. - */ -#define NUM_CACHE_LINES 128*8 -#define CACHE_LINE_SIZE 32 -#if 0 -cache_flush_buffer: - .space NUM_CACHE_LINES*CACHE_LINE_SIZE /* CAUTION! these need to match hardware */ -#else -#define cache_flush_buffer 0x1000 -#endif - - -/* - * Flush instruction cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_instruction_cache) - mflr r5 - bl flush_data_cache - mfspr r3,HID0 /* Caches are controlled by this register */ - li r4,0 - ori r4,r4,(HID0_ICE|HID0_ICFI) - or r3,r3,r4 /* Need to enable+invalidate to clear */ - mtspr HID0,r3 - andc r3,r3,r4 - ori r3,r3,HID0_ICE /* Enable cache */ - mtspr HID0,r3 - mtlr r5 - blr - -/* - * Flush data cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_data_cache) - lis r3,cache_flush_buffer@h - ori r3,r3,cache_flush_buffer@l - li r4,NUM_CACHE_LINES - mtctr r4 -#if 0 -00: dcbz 0,r3 /* Flush cache line with minimal BUS traffic */ -#else -00: lwz r4,0(r3) -#endif - addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ - bdnz 00b -10: blr - -/* - * Flush a particular page from the DATA cache - * Note: this is necessary because the instruction cache does *not* - * snoop from the data cache. - * void flush_page(void *page) - */ -_GLOBAL(flush_page) - li r4,0x0FFF - andc r3,r3,r4 /* Get page base address */ - li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ - mtctr r4 -00: dcbf 0,r3 /* Clear line */ - icbi 0,r3 - addi r3,r3,CACHE_LINE_SIZE - bdnz 00b - blr - -/* - * Execute a [foreign] function - * - * run(p1, p2, cp, ep, entry) - * - */ - .globl run -run: - mtctr r7 /* Entry point */ -#define IS_PreP 0x50726550 /* 'PreP' */ - lis r30,IS_PreP>>16 - ori r30,r30,IS_PreP&0xFFFF - mr 11,r5 - mr 12,r6 - mr r28,r5 - mr r29,r6 - mr 11,r5 - mr 12,r6 - bctr - .comm .stack,4096*2,4 diff --git a/arch/ppc/boot/mbxtty.c b/arch/ppc/boot/mbxtty.c new file mode 100644 index 000000000..ee6c59c90 --- /dev/null +++ b/arch/ppc/boot/mbxtty.c @@ -0,0 +1,118 @@ + + +/* Minimal serial functions needed to send messages out the serial + * port on the MBX console. + * + * The MBX uxes SMC1 for the serial port. We reset the port and use + * only the first BD that EPPC-Bug set up as a character FIFO. + * + * It's a big hack, but I don't have time right now....I want a kernel + * that boots. + */ +#include <linux/types.h> +#include <asm/mbx.h> +#include "../8xx_io/commproc.h" + +#define CPM_CPCR ((volatile ushort *)0xfa2009c0) +#define SMC1_MODE ((volatile ushort *)0xfa200a82) +#define SMC1_TBDF ((volatile bd_t *)0xfa202c90) +#define SMC1_RBDF ((volatile bd_t *)0xfa202c10) + +static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)MBX_IMAP_ADDR)->im_cpm); + +void +serial_init(void) +{ + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp; + + cp = cpmp; + sp = (smc_t*)&(cp->cp_smc[0]); + up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcm &= ~(SMCMR_REN | SMCMR_TEN); + + tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase]; + rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase]; + + /* Issue a stop transmit, and wait for it. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Enable transmitter/receiver. + */ + sp->smc_smcm |= SMCMR_REN | SMCMR_TEN; +} + +void +serial_putchar(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY); + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +char +serial_getc() +{ + volatile cbd_t *rbdf; + volatile char *buf; + volatile smc_uart_t *up; + char c; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY); + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c index bc6aa088f..33c7c3e0d 100644 --- a/arch/ppc/boot/misc.c +++ b/arch/ppc/boot/misc.c @@ -4,25 +4,35 @@ * Adapted for PowerPC by Gary Thomas * * Rewritten by Cort Dougan (cort@cs.nmt.edu) - * Soon to be replaced by a single bootloader for chrp/prep/pmac. -- Cort + * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort */ #include "../coffboot/zlib.h" #include "asm/residual.h" #include <elf.h> +#include <linux/config.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +bd_t hold_board_info; +#endif /* this is where the INITRD gets moved to for safe keeping */ -#define INITRD_DESTINATION /*0x00f00000*/ 0x01f00000 +#define INITRD_DESTINATION /*0x00f00000*/ 0x01800000 +#ifdef CONFIG_8xx +char *avail_ram = (char *) 0x00200000; +char *end_avail = (char *) 0x00400000; +#else /* CONFIG_8xx */ /* this will do for now - Cort */ char *avail_ram = (char *) 0x00800000; /* start with 8M */ /* assume 15M max since this is where we copy the initrd to -- Cort */ char *end_avail = (char *) INITRD_DESTINATION; +#endif /* CONFIG_8xx */ +char cmd_line[256]; RESIDUAL hold_residual; unsigned long initrd_start = 0, initrd_end = 0; char *zimage_start; int zimage_size; -char cmd_line[256]; char *vidmem = (char *)0xC00B8000; int lines, cols; @@ -47,6 +57,7 @@ void exit() while(1); } +#ifndef CONFIG_MBX static void clear_screen() { int i, j; @@ -144,6 +155,39 @@ void puts(const char *s) orig_x = x; orig_y = y; } +#else +/* The MBX is just the serial port. +*/ +tstc(void) +{ + return (serial_tstc()); +} + +getc(void) +{ + while (1) { + if (serial_tstc()) return (serial_getc()); + } +} + +void +putc(const char c) +{ + serial_putchar(c); +} + +void puts(const char *s) +{ + char c; + + while ( ( c = *s++ ) != '\0' ) { + serial_putchar(c); + if ( c == '\n' ) + serial_putchar('\r'); + } +} + +#endif /* CONFIG_MBX */ void * memcpy(void * __dest, __const void * __src, int __n) @@ -253,6 +297,8 @@ void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) inflateEnd(&s); } +unsigned char sanity[0x2000]; + unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) { @@ -260,31 +306,60 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R extern unsigned long start; char *cp, ch; unsigned long i; - - + lines = 25; cols = 80; orig_x = 0; orig_y = 24; - /* Turn off MMU. Since we are mapped 1-1, this is OK. */ - flush_instruction_cache(); - _put_HID0(_get_HID0() & ~0x0000C000); - _put_MSR(_get_MSR() & ~0x0030); - +#ifndef CONFIG_8xx vga_init(0xC0000000); - /*clear_screen();*/ + /* copy the residual data */ + if (residual) + memcpy(&hold_residual,residual,sizeof(RESIDUAL)); +#endif /* CONFIG_8xx */ +#ifdef CONFIG_MBX + /* copy board data */ + if (residual) + _bcopy((char *)residual, (char *)&hold_board_info, + sizeof(hold_board_info)); +#endif /* CONFIG_8xx */ + - puts("loaded at: "); puthex(load_addr); + puts("loaded at: "); puthex(load_addr); puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); - - puts("relocated to: "); puthex((unsigned long)&start); + puts("relocated to: "); puthex((unsigned long)&start); puts(" "); puthex((unsigned long)((unsigned long)&start + (4*num_words))); puts("\n"); - + + if ( residual ) + { + puts("board data at: "); puthex((unsigned long)residual); + puts(" "); +#ifdef CONFIG_MBX + puthex((unsigned long)((unsigned long)residual + sizeof(bd_t))); +#else + puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL))); +#endif + puts("\n"); + puts("relocated to: "); +#ifdef CONFIG_MBX + puthex((unsigned long)&hold_board_info); +#else + puthex((unsigned long)&hold_residual); +#endif + puts(" "); +#ifdef CONFIG_MBX + puthex((unsigned long)((unsigned long)&hold_board_info + sizeof(bd_t))); +#else + puthex((unsigned long)((unsigned long)&hold_residual + sizeof(RESIDUAL))); +#endif + puts("\n"); + } + zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); zimage_size = ZIMAGE_SIZE; - puts("zimage at: "); puthex((unsigned long)zimage_start); + puts("zimage at: "); puthex((unsigned long)zimage_start); puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); if ( INITRD_OFFSET ) @@ -296,18 +371,19 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R /* relocate initrd */ if ( initrd_start ) { - puts("initrd at: "); puthex(initrd_start); + puts("initrd at: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); memcpy ((void *)INITRD_DESTINATION,(void *)initrd_start, INITRD_SIZE ); initrd_end = INITRD_DESTINATION + INITRD_SIZE; initrd_start = INITRD_DESTINATION; - puts("Moved initrd to: "); puthex(initrd_start); + puts("Moved initrd to: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); } - +#ifndef CONFIG_MBX CRT_tstc(); /* Forces keyboard to be initialized */ +#endif puts("\nLinux/PPC load: "); timer = 0; cp = cmd_line; @@ -339,9 +415,12 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R if ( initrd_start > (16<<20)) puts("initrd_start located > 16M\n"); - puts("Uncompressing Linux..."); - gunzip(0, 0x400000, zimage_start, &zimage_size); + + /* these _bcopy() calls are here so I can add breakpoints to the boot for mbx -- Cort */ + /*_bcopy( (char *)0x100,(char *)&sanity, 0x2000-0x100);*/ + gunzip(0, 0x400000, zimage_start, &zimage_size); + /*_bcopy( (char *)&sanity,(char *)0x100,0x2000-0x100);*/ puts("done.\n"); puts("Now booting the kernel\n"); return (unsigned long)&hold_residual; diff --git a/arch/ppc/boot/vreset.c b/arch/ppc/boot/vreset.c index 3bc3936c6..6086372a0 100644 --- a/arch/ppc/boot/vreset.c +++ b/arch/ppc/boot/vreset.c @@ -434,16 +434,15 @@ vga_init(unsigned char *ISA_mem) { int slot; struct VgaRegs *VgaTextRegs; - +#if 0 if ((_get_PVR()>>16) == PPC_601) { return(old_vga_init(ISA_mem)); } - -#if 1 +#endif + /* See if VGA already in TEXT mode - exit if so! */ outb(0x3CE, 0x06); if ((inb(0x3CF) & 0x01) == 0){puts("VGA already in text mode\n"); return;} -#endif /* If no VGA responding in text mode, then we have some work to do... */ @@ -522,11 +521,6 @@ vga_init(unsigned char *ISA_mem) return (1); /* 'CRT' I/O supported */ } -static int -NOP(int x) -{ -} - /* * Write to VGA Attribute registers. */ @@ -852,166 +846,5 @@ void printslots(void) puts(" Vendor ID: "); puthex(PCIVendor(i)); puts("\n"); #endif - - } -} - -/* - * OLD vreset.c - * - * Initialize the VGA control registers to 80x25 text mode. - * - * Adapted from a program by: - * Steve Sellgren - * San Francisco Indigo Company - * sfindigo!sellgren@uunet.uu.net - */ - -unsigned char CRTC[24] = { - 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, - 0x00, 0x4F, 0x0D, 0x0E, 0x00, 0x00, 0x00, 0x00, /*0x07, 0x80, */ - 0x9C, 0xAE, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3}; -unsigned char SEQ[5] = {0x3, 0x0, 0x3, 0x0, 0x2}; -unsigned char GC[9] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0xE, 0x0, 0xFF}; - -#if 0 -static const unsigned char color_LUT[] = - { - 0x00, 0x00, 0x00, /* 0 - black */ - 0x00, 0x00, 0x2A, /* 1 - blue */ - 0x00, 0x2A, 0x00, /* 2 - green */ - 0x00, 0x2A, 0x2A, /* 3 - cyan */ - 0x2A, 0x00, 0x00, /* 4 - red */ - 0x2A, 0x00, 0x2A, /* 5 - magenta */ - 0x2A, 0x2A, 0x00, /* 6 - brown */ - 0x2A, 0x2A, 0x2A, /* 7 - white */ - 0x00, 0x00, 0x15, /* 8 - gray */ - 0x00, 0x00, 0x3F, /* 9 - light blue */ - 0x00, 0x2A, 0x15, /* 10 - light green */ - 0x00, 0x2A, 0x3F, /* 11 - light cyan */ - 0x2A, 0x00, 0x15, /* 12 - light red */ - 0x2A, 0x00, 0x3F, /* 13 - light magenta */ - 0x2A, 0x2A, 0x15, /* 14 - yellow */ - 0x2A, 0x2A, 0x3F, /* 15 - bright white */ - }; -#endif - -old_vga_init(unsigned char *ISA_mem) -{ - int i, j; - int value; - unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000]; - - /* See if VGA already in TEXT mode - exit if so! */ - outb(0x3CE, 0x06); - if ((inb(0x3CF) & 0x01) == 0) return; - - /* From the S3 manual */ - outb(0x46E8, 0x10); /* Put into setup mode */ - outb(0x3C3, 0x10); - outb(0x102, 0x01); /* Enable registers */ - outb(0x46E8, 0x08); /* Enable video */ - outb(0x3C3, 0x08); - outb(0x4AE8, 0x00); - -#if 0 - outb(0x42E8, 0x80); /* Reset graphics engine? */ -#endif - - outb(0x3D4, 0x38); /* Unlock all registers */ - outb(0x3D5, 0x48); - outb(0x3D4, 0x39); - outb(0x3D5, 0xA5); - outb(0x3D4, 0x40); - outb(0x3D5, inb(0x3D5)|0x01); - outb(0x3D4, 0x33); - outb(0x3D5, inb(0x3D5)&~0x52); - outb(0x3D4, 0x35); - outb(0x3D5, inb(0x3D5)&~0x30); - outb(0x3D4, 0x3A); - outb(0x3D5, 0x00); - outb(0x3D4, 0x53); - outb(0x3D5, 0x00); - outb(0x3D4, 0x31); - outb(0x3D5, inb(0x3D5)&~0x4B); - outb(0x3D4, 0x58); - outb(0x3D5, 0); - - outb(0x3D4, 0x54); - outb(0x3D5, 0x38); - outb(0x3D4, 0x60); - outb(0x3D5, 0x07); - outb(0x3D4, 0x61); - outb(0x3D5, 0x80); - outb(0x3D4, 0x62); - outb(0x3D5, 0xA1); - outb(0x3D4, 0x69); /* High order bits for cursor address */ - outb(0x3D5, 0); - - outb(0x3D4, 0x32); - outb(0x3D5, inb(0x3D5)&~0x10); - - outb(0x3C2, 0x67); - -#if 0 - /* Initialize DAC */ - outb(0x3C6,0xFF); - inb(0x3C7); - outb(0x3C8,0x00); - inb(0x3C7); - for (i=0; i<sizeof(color_LUT); i++) { - outb(0x3C9, color_LUT[i]); - } - for (i; i<768; i += 3) { - outb(0x3C9, 0x3F); /* White? */ - outb(0x3C9, 0x3F); /* White? */ - outb(0x3C9, 0x3F); /* White? */ - } - - /* Load font */ - NOP(inb(0x3DA)); /* Reset Address/Data FlipFlop for Attribute ctlr */ - outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */ - outw(0x3C4, 0x0001); /* reset sequencer */ - outw(0x3C4, 0x0204); /* write to plane 2 */ - outw(0x3C4, 0x0407); /* enable plane graphics */ - outw(0x3C4, 0x0003); /* reset sequencer */ - outw(0x3CE, 0x0402); /* read plane 2 */ - outw(0x3CE, 0x0500); /* write mode 0, read mode 0 */ - outw(0x3CE, 0x0600); /* set graphics */ - for (i = 0; i < sizeof(font); i += 16) { - for (j = 0; j < 16; j++) { - font_page[(2*i)+j] = font[i+j]; - } - } -#else - outw(0x3C4, 0x0120); /* disable video */ - setTextCLUT(2); /* load color lookup table */ - loadFont(ISA_mem); /* load font */ -#endif - - for (i = 0; i < 24; i++) { - outb(0x3D4, i); - outb(0x3D5, CRTC[i]); - } - for (i = 0; i < 5; i++) { - outb(0x3C4, i); - outb(0x3C5, SEQ[i]); - } - for (i = 0; i < 9; i++) { - outb(0x3CE, i); - outb(0x3CF, GC[i]); - } - value = inb(0x3DA); /* reset flip-flop */ - for (i = 0; i < 16; i++) { - outb(0x3C0, i); - outb(0x3C0, AC[i]); - } - for (i = 16; i < 21; i++) { - outb(0x3C0, i | 0x20); - outb(0x3C0, AC[i]); } - clearVideoMemory(); - outw(0x3C4, 0x0100); /* re-enable video */ - outb(0x3C2, 0x23); - return (1); /* Keyboard should work */ } diff --git a/arch/ppc/chrp_defconfig b/arch/ppc/chrp_defconfig new file mode 100644 index 000000000..ccbc0f942 --- /dev/null +++ b/arch/ppc/chrp_defconfig @@ -0,0 +1,321 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_6xx=y +# CONFIG_8xx is not set +# CONFIG_PMAC is not set +# CONFIG_PREP is not set +CONFIG_CHRP=y +# CONFIG_ALL_PPC is not set +# CONFIG_MBX is not set +CONFIG_MACH_SPECIFIC=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +# CONFIG_ABSTRACT_CONSOLE is not set +CONFIG_PMAC_CONSOLE=y +CONFIG_MAC_KEYBOARD=y +# CONFIG_MAC_FLOPPY is not set +CONFIG_MACMOUSE=y +CONFIG_PROC_DEVICETREE=y +# CONFIG_XMON is not set +CONFIG_CONTROL_VIDEO=y +CONFIG_PLATINUM_VIDEO=y +CONFIG_VALKYRIE_VIDEO=y +CONFIG_ATY_VIDEO=y +CONFIG_IMSTT_VIDEO=y +CONFIG_CHIPS_VIDEO=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +CONFIG_NET_ALIAS=y +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_ALIAS=y +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_RARP=y +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +CONFIG_SCSI_AIC7XXX=m +# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set +# CONFIG_OVERRIDE_CMDS is not set +# CONFIG_AIC7XXX_PAGE_ENABLE is not set +CONFIG_AIC7XXX_PROC_STATS=y +CONFIG_AIC7XXX_RESET_DELAY=15 +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_DEBUG is not set +CONFIG_SCSI_MESH=y +CONFIG_SCSI_MESH_SYNC_RATE=10 +CONFIG_SCSI_MAC53C94=y + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +# CONFIG_PCNET32 is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=y +# CONFIG_DEC_ELCP is not set +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set +CONFIG_CDROM=y + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set +# CONFIG_SMB_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=y +# CONFIG_ROMFS_FS is not set +CONFIG_AUTOFS_FS=y +# CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set +CONFIG_MAC_PARTITION=y +CONFIG_NLS=y + +# +# Native Language Support +# +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_MOUSE is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +CONFIG_NVRAM=y +# CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/chrpboot/main.c b/arch/ppc/chrpboot/main.c index 629256eaa..05a2f85d3 100644 --- a/arch/ppc/chrpboot/main.c +++ b/arch/ppc/chrpboot/main.c @@ -16,11 +16,11 @@ void gunzip(void *, int, unsigned char *, int *); #define get_16be(x) (*(unsigned short *)(x)) #define get_32be(x) (*(unsigned *)(x)) -#define RAM_START 0x90000000 -#define RAM_END 0x90800000 /* only 8M mapped with BATs */ +#define RAM_START 0x00000000 +#define RAM_END 0x00800000 /* only 8M mapped with BATs */ -#define RAM_FREE 0x90540000 /* after image of chrpboot */ -#define PROG_START 0x90010000 +#define RAM_FREE 0x00540000 /* after image of chrpboot */ +#define PROG_START 0x00010000 char *avail_ram; char *end_avail; @@ -40,7 +40,7 @@ chrpboot(int a1, int a2, void *prom) unsigned initrd_start, initrd_size; printf("chrpboot starting\n\r"); - setup_bats(); + /* setup_bats(); */ if (initrd_len) { initrd_size = initrd_len; diff --git a/arch/ppc/chrpboot/misc.S b/arch/ppc/chrpboot/misc.S index 11a612af1..b2094b9d3 100644 --- a/arch/ppc/chrpboot/misc.S +++ b/arch/ppc/chrpboot/misc.S @@ -23,10 +23,10 @@ setup_bats: b 5f 4: ori 4,4,0xff /* set up BAT registers for 604 */ li 5,2 - mtdbatu 0,4 - mtdbatl 0,5 -5: mtibatu 0,4 - mtibatl 0,5 + mtdbatu 3,4 + mtdbatl 3,5 +5: mtibatu 3,4 + mtibatl 3,5 isync blr diff --git a/arch/ppc/coffboot/main.c b/arch/ppc/coffboot/main.c index ab0ee6c4a..e70844b62 100644 --- a/arch/ppc/coffboot/main.c +++ b/arch/ppc/coffboot/main.c @@ -18,9 +18,10 @@ void gunzip(void *, int, unsigned char *, int *); #define get_32be(x) (*(unsigned *)(x)) #define RAM_START 0xc0000000 -#define RAM_END 0xc0800000 /* only 8M mapped with BATs */ +#define PROG_START RAM_START +#define RAM_END (RAM_START + 0x800000) /* only 8M mapped with BATs */ -#define RAM_FREE 0xc0540000 /* after image of coffboot */ +#define RAM_FREE (RAM_START + 0x540000) /* after image of coffboot */ char *avail_ram; char *end_avail; @@ -47,7 +48,7 @@ coffboot(int a1, int a2, void *prom) printf("error getting load-base\n"); exit(); } - setup_bats(); + setup_bats(RAM_START); eh = (struct external_filehdr *) loadbase; ns = get_16be(eh->f_nscns); @@ -82,7 +83,7 @@ coffboot(int a1, int a2, void *prom) im = (unsigned char *)(loadbase + get_32be(isect->s_scnptr)); len = get_32be(isect->s_size); - dst = (void *) RAM_START; + dst = (void *) PROG_START; if (im[0] == 0x1f && im[1] == 0x8b) { void *cp = (void *) RAM_FREE; @@ -98,7 +99,7 @@ coffboot(int a1, int a2, void *prom) flush_cache(dst, len); - sa = *(unsigned *)dst + RAM_START; + sa = *(unsigned *)dst + PROG_START; printf("start address = 0x%x\n", sa); #if 0 diff --git a/arch/ppc/coffboot/misc.S b/arch/ppc/coffboot/misc.S index ae08ee2fa..1a9f07129 100644 --- a/arch/ppc/coffboot/misc.S +++ b/arch/ppc/coffboot/misc.S @@ -9,24 +9,25 @@ .text /* - * Use the BAT0 registers to map the 1st 8MB of RAM to 0xc0000000. + * Use the BAT0 registers to map the 1st 8MB of RAM to + * the address given as the 1st argument. */ .globl setup_bats setup_bats: + mr 4,3 mfpvr 3 rlwinm 3,3,16,16,31 /* r3 = 1 for 601, 4 for 604 */ cmpi 0,3,1 - lis 4,0xc000 bne 4f ori 4,4,4 /* set up BAT registers for 601 */ li 5,0x7f b 5f 4: ori 4,4,0xff /* set up BAT registers for 604 */ li 5,2 - mtdbatu 0,4 - mtdbatl 0,5 -5: mtibatu 0,4 - mtibatl 0,5 + mtdbatu 3,4 + mtdbatl 3,5 +5: mtibatu 3,4 + mtibatl 3,5 isync blr diff --git a/arch/ppc/common_defconfig b/arch/ppc/common_defconfig new file mode 100644 index 000000000..4f9983126 --- /dev/null +++ b/arch/ppc/common_defconfig @@ -0,0 +1,331 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_6xx=y +# CONFIG_8xx is not set +# CONFIG_PMAC is not set +# CONFIG_PREP is not set +# CONFIG_CHRP is not set +CONFIG_ALL_PPC=y +# CONFIG_MBX is not set + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +CONFIG_ABSTRACT_CONSOLE=y +CONFIG_FB=y +CONFIG_VGA_CONSOLE=y +CONFIG_FB_COMPAT_XPMAC=y +CONFIG_MAC_KEYBOARD=y +CONFIG_MAC_FLOPPY=y +CONFIG_MACMOUSE=y +CONFIG_PROC_DEVICETREE=y +# CONFIG_XMON is not set + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +CONFIG_IP_ACCT=y +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_RARP=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +# CONFIG_CHR_DEV_SG is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y +CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 +CONFIG_SCSI_NCR53C8XX_SYNC=5 +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +CONFIG_SCSI_MESH=y +CONFIG_SCSI_MESH_SYNC_RATE=5 +CONFIG_SCSI_MAC53C94=y + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +CONFIG_LANCE=y +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +CONFIG_PCNET32=y +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=y +# CONFIG_DEC_ELCP is not set +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +CONFIG_PPP=y +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set +CONFIG_CDROM=y + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set +# CONFIG_SMB_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=y +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set +CONFIG_MAC_PARTITION=y +CONFIG_NLS=y + +# +# Native Language Support +# +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set + +# +# Frame buffer devices +# +CONFIG_FB_OPEN_FIRMWARE=y +# CONFIG_FB_S3TRIO is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FBCON_ADVANCED is not set +CONFIG_FBCON_MFB=y +CONFIG_FBCON_CFB8=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_MOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 72bab8928..4ab631988 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -1,30 +1,37 @@ -# $Id: config.in,v 1.36 1997/12/29 21:36:52 geert Exp $ +# $Id: config.in,v 1.47 1998/04/10 10:19:04 geert Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # mainmenu_name "Linux/PowerPC Kernel Configuration" + + mainmenu_option next_comment comment 'Platform support' define_bool CONFIG_PPC y -if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then - define_bool CONFIG_CROSSCOMPILE y -else - define_bool CONFIG_NATIVE y -fi - -define_bool CONFIG_MACH_SPECIFIC y -bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC -bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP -bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP +#if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then +# define_bool CONFIG_CROSSCOMPILE y +#else +# define_bool CONFIG_NATIVE y +#fi choice 'Processor type' \ - "Common CONFIG_COMMON \ - 601 CONFIG_601 \ - 603 CONFIG_603 \ - 604 CONFIG_604" Common + "6xx/7xx CONFIG_6xx \ + 860/821 CONFIG_8xx" 6xx/7xx + +choice 'Machine Type' \ + "PowerMac CONFIG_PMAC \ + PReP CONFIG_PREP \ + CHRP CONFIG_CHRP \ + PowerMac/PReP/CHRP CONFIG_ALL_PPC \ + APUS CONFIG_APUS \ + MBX CONFIG_MBX" PReP endmenu +if [ "$CONFIG_ALL_PPC" != "y" ]; then + define_bool CONFIG_MACH_SPECIFIC y +fi + mainmenu_option next_comment comment 'General setup' @@ -35,9 +42,16 @@ if [ "$CONFIG_MODULES" = "y" ]; then bool 'Kernel module loader' CONFIG_KMOD fi -define_bool CONFIG_PCI y +if [ "$CONFIG_APUS" = "y" ]; then + define_bool CONFIG_PCI n +else + define_bool CONFIG_PCI y +fi if [ "$CONFIG_PREP" = "y" ]; then - bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE + bool 'PCI quirks' CONFIG_PCI_QUIRKS + if [ "$CONFIG_PCI_QUIRKS" = "y" ]; then + bool ' PCI bridge optimization' CONFIG_PCI_OPTIMIZE + fi fi bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC bool 'Networking support' CONFIG_NET @@ -51,6 +65,14 @@ define_bool CONFIG_KERNEL_ELF y tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA +tristate 'Parallel port support' CONFIG_PARPORT +if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT + if [ "$CONFIG_PARPORT_PC" != "n" ]; then + bool ' Support foreign hardware' CONFIG_PARPORT_OTHER + fi +fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'New unified console driver (EXPERIMENTAL)' CONFIG_ABSTRACT_CONSOLE fi @@ -68,6 +90,7 @@ else # if compiling specifically for prep or chrp, or supporting all arch's if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then bool 'Support for frame buffer devices' CONFIG_FB + bool 'Support for VGA devices' CONFIG_VGA_CONSOLE bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC else bool 'Support for PowerMac console' CONFIG_PMAC_CONSOLE @@ -79,13 +102,10 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Support for PowerMac mouse (EXPERIMENTAL)' CONFIG_MACMOUSE fi bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE +bool 'Include kgdb kernel debugger' CONFIG_KGDB bool 'Include xmon kernel debugger' CONFIG_XMON -if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then - if [ "$CONFIG_FB" != "y" ]; then - define_bool CONFIG_VGA_CONSOLE y - fi -else +if [ "$CONFIG_ABSTRACT_CONSOLE" != "y" ]; then if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then bool 'Support for Apple "control" display' CONFIG_CONTROL_VIDEO bool 'Support for Apple "platinum" display' CONFIG_PLATINUM_VIDEO @@ -149,17 +169,6 @@ if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then fi endmenu -# Conditionally compile in the Uniform CD-ROM driver -if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then - define_bool CONFIG_CDROM y -else - if [ "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then - define_bool CONFIG_CDROM m - else - define_bool CONFIG_CDROM n - fi -fi - source fs/Config.in source fs/nls/Config.in diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index 7e5caa71d..e498b5249 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -6,22 +6,21 @@ # Platform support # CONFIG_PPC=y -CONFIG_NATIVE=y -CONFIG_MACH_SPECIFIC=y +# CONFIG_6xx is not set +CONFIG_8xx=y # CONFIG_PMAC is not set -CONFIG_PREP=y +# CONFIG_PREP is not set # CONFIG_CHRP is not set -CONFIG_COMMON=y +# CONFIG_ALL_PPC is not set +CONFIG_MBX=y +CONFIG_MACH_SPECIFIC=y # # General setup # -CONFIG_EXPERIMENTAL=y -CONFIG_MODULES=y -CONFIG_MODVERSIONS=y -CONFIG_KMOD=y +# CONFIG_EXPERIMENTAL is not set +# CONFIG_MODULES is not set CONFIG_PCI=y -# CONFIG_PCI_OPTIMIZE is not set CONFIG_PCI_OLD_PROC=y CONFIG_NET=y # CONFIG_SYSCTL is not set @@ -31,6 +30,7 @@ CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set # CONFIG_PMAC_CONSOLE is not set # CONFIG_MAC_KEYBOARD is not set # CONFIG_MAC_FLOPPY is not set @@ -46,25 +46,17 @@ CONFIG_VGA_CONSOLE=y # # Floppy, IDE, and other block devices # -CONFIG_BLK_DEV_FD=y -CONFIG_BLK_DEV_IDE=y -# CONFIG_BLK_DEV_HD_IDE is not set -CONFIG_BLK_DEV_IDEDISK=y -CONFIG_BLK_DEV_IDECD=y -# CONFIG_BLK_DEV_IDETAPE is not set -# CONFIG_BLK_DEV_IDEFLOPPY is not set -# CONFIG_BLK_DEV_IDESCSI is not set -# CONFIG_BLK_DEV_CMD640 is not set -# CONFIG_BLK_DEV_RZ1000 is not set -# CONFIG_BLK_DEV_TRITON is not set -# CONFIG_IDE_CHIPSETS is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_IDE is not set +# CONFIG_BLK_DEV_HD_ONLY is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_EZ is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # @@ -79,83 +71,30 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set # CONFIG_IP_ACCT is not set -# CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_ALIAS is not set # CONFIG_SYN_COOKIES is not set -CONFIG_INET_RARP=y -# CONFIG_IP_NOSR is not set -CONFIG_SKB_LARGE=y -# CONFIG_IPV6 is not set +# CONFIG_INET_RARP is not set +CONFIG_IP_NOSR=y +# CONFIG_SKB_LARGE is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set -# CONFIG_AX25 is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_BRIDGE is not set -# CONFIG_LLC is not set -# CONFIG_WAN_ROUTER is not set -# CONFIG_CPU_IS_SLOW is not set -# CONFIG_NET_SCHED is not set # # SCSI support # -CONFIG_SCSI=y -CONFIG_BLK_DEV_SD=y -CONFIG_CHR_DEV_ST=y -CONFIG_BLK_DEV_SR=y -CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set -# CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_CONSTANTS is not set - -# -# SCSI low-level drivers -# -# CONFIG_SCSI_7000FASST is not set -# CONFIG_SCSI_AHA152X is not set -# CONFIG_SCSI_AHA1542 is not set -# CONFIG_SCSI_AHA1740 is not set -# CONFIG_SCSI_AIC7XXX is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_IN2000 is not set -# CONFIG_SCSI_AM53C974 is not set -# CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_DTC3280 is not set -# CONFIG_SCSI_EATA_DMA is not set -# CONFIG_SCSI_EATA_PIO is not set -# CONFIG_SCSI_EATA is not set -# CONFIG_SCSI_FUTURE_DOMAIN is not set -# CONFIG_SCSI_GENERIC_NCR5380 is not set -# CONFIG_SCSI_NCR53C406A is not set -# CONFIG_SCSI_NCR53C7xx is not set -CONFIG_SCSI_NCR53C8XX=y -# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set -CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y -CONFIG_SCSI_NCR53C8XX_IOMAPPED=y -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 -CONFIG_SCSI_NCR53C8XX_SYNC=5 -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set -# CONFIG_SCSI_PPA is not set -# CONFIG_SCSI_PAS16 is not set -# CONFIG_SCSI_QLOGIC_FAS is not set -# CONFIG_SCSI_QLOGIC_ISP is not set -# CONFIG_SCSI_SEAGATE is not set -# CONFIG_SCSI_DC390T is not set -# CONFIG_SCSI_T128 is not set -# CONFIG_SCSI_U14_34F is not set -# CONFIG_SCSI_ULTRASTOR is not set -# CONFIG_SCSI_MESH is not set -# CONFIG_SCSI_MAC53C94 is not set +# CONFIG_SCSI is not set # # Network device support @@ -164,34 +103,28 @@ CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set -# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_3COM is not set -CONFIG_LANCE=y +# CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_NET_ISA is not set -CONFIG_NET_EISA=y -CONFIG_PCNET32=y -# CONFIG_AC3200 is not set -# CONFIG_APRICOT is not set -# CONFIG_CS89x0 is not set -CONFIG_DE4X5=y -# CONFIG_DEC_ELCP is not set -# CONFIG_DGRS is not set -# CONFIG_EEXPRESS_PRO100 is not set -# CONFIG_TLAN is not set -# CONFIG_ES3210 is not set -# CONFIG_ZNET is not set +# CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set -# CONFIG_PLIP is not set -CONFIG_PPP=m -# CONFIG_NET_RADIO is not set +# CONFIG_PPP is not set # CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set -# CONFIG_SHAPER is not set +# CONFIG_WAN_DRIVERS is not set +# CONFIG_LAPBETHER is not set +# CONFIG_X25_ASY is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set # # ISDN subsystem @@ -202,6 +135,7 @@ CONFIG_PPP=m # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set +# CONFIG_CDROM is not set # # Filesystems @@ -209,63 +143,52 @@ CONFIG_PPP=m # CONFIG_QUOTA is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set +# CONFIG_ISO9660_FS is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set -CONFIG_PROC_FS=y +# CONFIG_PROC_FS is not set CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set # CONFIG_MAC_PARTITION is not set - -# -# Native Language Support -# # CONFIG_NLS is not set # # Character devices # -CONFIG_VT=y -CONFIG_VT_CONSOLE=y -# CONFIG_SOFTCURSOR is not set +# CONFIG_VT is not set CONFIG_SERIAL=y -CONFIG_SERIAL_EXTENDED=y -# CONFIG_SERIAL_MANY_PORTS is not set -# CONFIG_SERIAL_SHARE_IRQ is not set -# CONFIG_SERIAL_MULTIPORT is not set -# CONFIG_HUB6 is not set CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_PRINTER is not set -CONFIG_MOUSE=y -# CONFIG_ATIXL_BUSMOUSE is not set -# CONFIG_BUSMOUSE is not set -# CONFIG_MS_BUSMOUSE is not set -CONFIG_PSMOUSE=y -# CONFIG_82C710_MOUSE is not set -# CONFIG_PC110_PAD is not set -# CONFIG_UMISC is not set +# CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set -# CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set # CONFIG_VIDEO_DEV is not set -# CONFIG_VIDEO_BT848 is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 37776109d..3561fd26a 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -11,14 +11,32 @@ $(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $*.o O_TARGET := kernel.o -O_OBJS := misc.o traps.o process.o signal.o syscalls.o \ - align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \ - time.o prep_time.o pmac_time.o chrp_time.o \ - setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \ - pci.o prep_pci.o pmac_pci.o chrp_pci.o \ - residual.o prom.o OX_OBJS := ppc_ksyms.o + +O_OBJS := traps.o irq.o idle.o time.o process.o signal.o syscalls.o misc.o \ + bitops.o ppc_htab.o setup.o ptrace.o align.o + +ifdef CONFIG_PCI +O_OBJS += pci.o +endif +ifdef CONFIG_KGDB +O_OBJS += ppc-stub.o +endif + +ifeq ($(CONFIG_MBX),y) +O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o +else +ifeq ($(CONFIG_APUS),y) +O_OBJS += prom.o openpic.o +else +O_OBJS += prep_time.o pmac_time.o chrp_time.o \ + prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \ + prep_pci.o pmac_pci.o chrp_pci.o \ + residual.o prom.o openpic.o +endif +endif + ifdef SMP O_OBJS += smp.o endif @@ -38,10 +56,10 @@ ppc_defs.h: mk_defs.c ppc_defs.head \ rm mk_defs.s find_name : find_name.c - $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o find_name find_name.c + $(HOSTCC) -o find_name find_name.c checks: checks.c - $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c + $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c ./checks include $(TOPDIR)/Rules.make diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c index 39cb04f77..70284674b 100644 --- a/arch/ppc/kernel/align.c +++ b/arch/ppc/kernel/align.c @@ -194,9 +194,13 @@ fix_alignment(struct pt_regs *regs) return -EFAULT; /* bad address */ } +#ifdef __SMP__ + if ((flags & F) && (regs->msr & MSR_FP) ) + smp_giveup_fpu(current); +#else if ((flags & F) && last_task_used_math == current) giveup_fpu(); - +#endif if (flags & M) return 0; /* too hard for now */ @@ -255,12 +259,22 @@ fix_alignment(struct pt_regs *regs) * the kernel with -msoft-float so it doesn't use the * fp regs for copying 8-byte objects. */ case LD+F+S: +#ifdef __SMP__ + if (regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else giveup_fpu(); +#endif cvt_fd(&data.f, ¤t->tss.fpr[reg]); /* current->tss.fpr[reg] = data.f; */ break; case ST+F+S: +#ifdef __SMP__ + if (regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else giveup_fpu(); +#endif cvt_df(¤t->tss.fpr[reg], &data.f); /* data.f = current->tss.fpr[reg]; */ break; diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index 16b379254..6036efc28 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -4,13 +4,13 @@ #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> #include <linux/openpic.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/irq.h> #include <asm/hydra.h> #include <asm/prom.h> @@ -22,8 +22,12 @@ volatile struct Hydra *Hydra = NULL; - #if 1 +/* + * The VLSI Golden Gate II has only 512K of PCI configuration space, so we + * limit the bus number to 3 bits + */ + int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { @@ -32,11 +36,6 @@ int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, return PCIBIOS_DEVICE_NOT_FOUND; } *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset)); - if (offset == PCI_INTERRUPT_LINE) { - /* PCI interrupts are controlled by the OpenPIC */ - if (*val) - *val = openpic_to_irq(*val); - } return PCIBIOS_SUCCESSFUL; } @@ -228,10 +227,11 @@ __initfunc(int w83c553f_init(void)) unsigned char t8; unsigned short t16; unsigned int t32; - if (pcibios_find_device(PCI_VENDOR_ID_WINBOND, - PCI_DEVICE_ID_WINBOND_83C553, 0, &bus, &dev) - == PCIBIOS_SUCCESSFUL) { - dev++; + struct pci_dev *pdev; + if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, NULL))) { + bus = pdev->bus->number; + dev = pdev->devfn + 1; chrp_pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32); if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) { #if 0 diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 6c5d2afa5..91ca5d595 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -60,21 +60,6 @@ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ extern int rd_image_start; /* starting block # of image */ #endif - -int chrp_ide_irq = 0; - -void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) -{ - ide_ioreg_t port = base; - int i = 8; - - while (i--) - *p++ = port++; - *p++ = base + 0x206; - if (irq != NULL) - *irq = chrp_ide_irq; -} - static const char *gg2_memtypes[4] = { "FPM", "SDRAM", "EDO", "BEDO" }; @@ -89,6 +74,34 @@ static const char *gg2_cachemodes[4] = { "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" }; +#if 0 +#ifdef CONFIG_BLK_DEV_IDE +int chrp_ide_ports_known; +ide_ioreg_t chrp_ide_regbase[MAX_HWIFS]; +ide_ioreg_t chrp_idedma_regbase; /* one for both channels */ +unsigned int chrp_ide_irq; + +void chrp_ide_probe(void) +{ +} + +void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + int i; + + *p = 0; + if (base == 0) + return; + for (i = 0; i < 8; ++i) + *p++ = base + i * 0x10; + *p = base + 0x160; + if (irq != NULL) { + *irq = chrp_ide_irq; + } +} +#endif /* CONFIG_BLK_DEV_IDE */ +#endif + int chrp_get_cpuinfo(char *buffer) { @@ -139,12 +152,63 @@ chrp_get_cpuinfo(char *buffer) } /* L2 cache */ t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); - len += sprintf(buffer+len, "l2\t\t: %s %s (%s)\n", + len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n", gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3], gg2_cachemodes[t & 3]); return len; } + /* + * Fixes for the National Semiconductor PC78308VUL SuperI/O + * + * Some versions of Open Firmware incorrectly initialize the IRQ settings + * for keyboard and mouse + */ + +__initfunc(static inline void sio_write(u8 val, u8 index)) +{ + outb(index, 0x15c); + outb(val, 0x15d); +} + +__initfunc(static inline u8 sio_read(u8 index)) +{ + outb(index, 0x15c); + return inb(0x15d); +} + +__initfunc(static void sio_init(void)) +{ + u8 irq, type; + + /* select logical device 0 (KBC/Keyboard) */ + sio_write(0, 0x07); + irq = sio_read(0x70); + type = sio_read(0x71); + printk("sio: Keyboard irq %d, type %d: ", irq, type); + if (irq == 1 && type == 3) + printk("OK\n"); + else { + printk("remapping to irq 1, type 3\n"); + sio_write(1, 0x70); + sio_write(3, 0x71); + } + + /* select logical device 1 (KBC/Mouse) */ + sio_write(1, 0x07); + irq = sio_read(0x70); + type = sio_read(0x71); + printk("sio: Mouse irq %d, type %d: ", irq, type); + if (irq == 12 && type == 3) + printk("OK\n"); + else { + printk("remapping to irq 12, type 3\n"); + sio_write(12, 0x70); + sio_write(3, 0x71); + } +} + + __initfunc(void chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { @@ -191,7 +255,12 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) hydra_init(); /* Mac I/O */ w83c553f_init(); /* PCI-ISA bridge and IDE */ -#ifdef CONFIG_ABSTRACT_CONSOLE + /* + * Fix the Super I/O configuration + */ + sio_init(); + +#ifdef CONFIG_FB /* Frame buffer device based console */ conswitchp = &fb_con; #endif diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index 73dcf9844..7109c0e5d 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -38,7 +38,7 @@ void chrp_time_init(void) rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); if (rtcs == NULL || rtcs->addrs == NULL) return; - base = ((int *)rtcs->addrs)[2]; + base = rtcs->addrs[0].address; nvram_as1 = 0; nvram_as0 = base; nvram_data = base + 1; @@ -69,7 +69,7 @@ int chrp_set_rtc_time(unsigned long nowtime) unsigned char save_control, save_freq_select; struct rtc_time tm; - to_tm(nowtime + 10*60*60, &tm); /* XXX for now */ + to_tm(nowtime, &tm); save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ @@ -146,7 +146,7 @@ unsigned long chrp_get_rtc_time(void) } if ((year += 1900) < 1970) year += 100; - return mktime(year, mon, day, hour, min, sec) - 10*60*60 /* XXX for now */; + return mktime(year, mon, day, hour, min, sec); } @@ -155,6 +155,9 @@ void chrp_calibrate_decr(void) struct device_node *cpu; int freq, *fp, divisor; + if (via_calibrate_decr()) + return; + /* * The cpu node should have a timebase-frequency property * to tell us the rate at which the decrementer counts. diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 2924febc5..d8540e68d 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -9,6 +9,8 @@ * Low-level exception handlers and MMU support * rewritten by Paul Mackerras. * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This file contains the low-level support and setup for the * PowerPC platform, including trap and interrupt dispatch. @@ -29,12 +31,14 @@ #include <linux/sys.h> #include <linux/errno.h> #include <linux/config.h> +#ifdef CONFIG_8xx +#include <asm/mmu.h> +#include <asm/pgtable.h> +#include <asm/cache.h> +#endif #ifdef CONFIG_APUS -/* At CYBERBASEp we'll find the following sum: - * -KERNELBASE+CyberStormMemoryBase - */ -#define CYBERBASEp (0xfff00000) +#include <asm/amigappc.h> #endif /* optimization for 603 to load the tlb directly from the linux table */ @@ -78,6 +82,8 @@ LG_CACHE_LINE_SIZE = 5 isync /* This instruction is not implemented on the PPC 603 or 601 */ +#ifndef CONFIG_8xx +/* This instruction is not implemented on the PPC 603 or 601 */ #define tlbia \ li r4,128; \ mtctr r4; \ @@ -85,6 +91,7 @@ LG_CACHE_LINE_SIZE = 5 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b +#endif #define LOAD_BAT(n, offset, reg, RA, RB) \ lwz RA,offset+0(reg); \ @@ -96,6 +103,20 @@ LG_CACHE_LINE_SIZE = 5 mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB +#ifndef CONFIG_APUS +#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h +#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h +#else +#define tophys(rd,rs,rt) \ + lis rt,CYBERBASEp@h; \ + lwz rt,0(rt); \ + add rd,rs,rt +#define tovirt(rd,rs,rt) \ + lis rt,CYBERBASEp@h; \ + lwz rt,0(rt); \ + sub rd,rs,rt +#endif + .text .globl _stext _stext: @@ -130,12 +151,44 @@ _start: * * This just gets a minimal mmu environment setup so we can call * start_here() to do the real work. - * -- Cort + * -- Cort + * + * MPC8xx + * This port was done on an MBX board with an 860. Right now I only + * support an ELF compressed (zImage) boot from EPPC-Bug because the + * code there loads up some registers before calling us: + * r3: ptr to board info data + * r4: initrd_start or if no initrd then 0 + * r5: initrd_end - unused if r4 is 0 + * r6: Start of command line string + * r7: End of command line string + * + * I decided to use conditional compilation instead of checking PVR and + * adding more processor specific branches around code I don't need. + * Since this is an embedded processor, I also appreciate any memory + * savings I can get. + * + * The MPC8xx does not have any BATs, but it supports large page sizes. + * We first initialize the MMU to support 8M byte pages, then load one + * entry into each of the instruction and data TLBs to map the first + * 8M 1:1. I also mapped an additional I/O space 1:1 so we can get to + * the "internal" processor registers before MMU_init is called. + * + * The TLB code currently contains a major hack. Since I use the condition + * code register, I have to save and restore it. I am out of registers, so + * I just store it in memory location 0 (the TLB handlers are not reentrant). + * To avoid making any decisions, I need to use the "segment" valid bit + * in the first level table, but that would require many changes to the + * Linux page directory/table functions that I don't want to do right now. + * + * I used to use SPRG2 for a temporary register in the TLB handler, but it + * has since been put to other uses. I now use a hack to save a register + * and the CCR at memory location 0.....Someday I'll fix this..... + * -- Dan */ .globl __start __start: - /* * We have to do any OF calls before we map ourselves to KERNELBASE, * because OF may have I/O devices mapped in in that area @@ -145,12 +198,16 @@ __start: mr r30,r4 mr r29,r5 mr r28,r6 - mr r29,r7 + mr r27,r7 +#ifndef CONFIG_8xx +#ifndef CONFIG_APUS bl prom_init +#endif /* * Use the first pair of BAT registers to map the 1st 16MB - * of RAM to KERNELBASE. + * of RAM to KERNELBASE. From this point on we can't safely + * call OF any more. */ mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ @@ -173,17 +230,134 @@ __start: addis r8,r8,KERNELBASE@h addi r8,r8,2 #endif - mtspr DBAT0U,r11 +5: mtspr DBAT0U,r11 mtspr DBAT0L,r8 -5: mtspr IBAT0U,r11 + mtspr IBAT0U,r11 mtspr IBAT0L,r8 isync + +/* + * We need to run with _start at physical address 0. + * On CHRP, we are loaded at 0x10000 since OF on CHRP uses + * the exception vectors at 0 (and therefore this copy + * overwrites OF's exception vectors with our own). + * If the MMU is already turned on, we copy stuff to KERNELBASE, + * otherwise we copy it to 0. + */ + bl reloc_offset + mr r26,r3 + addis r4,r3,KERNELBASE@h /* current address of _start */ + cmpwi 0,r4,0 /* are we already running at 0? */ + beq 2f /* assume it's OK if so */ + li r3,0 + mfmsr r0 + andi. r0,r0,MSR_DR /* MMU enabled? */ + beq 7f + lis r3,KERNELBASE@h /* if so, are we */ + cmpw 0,r4,r3 /* already running at KERNELBASE? */ + beq 2f + rlwinm r4,r4,0,8,31 /* translate source address */ + add r4,r4,r3 /* to region mapped with BATs */ +7: addis r9,r26,klimit@ha /* fetch klimit */ + lwz r25,klimit@l(r9) + addis r25,r25,-KERNELBASE@h + li r6,0 /* Destination */ +#ifdef CONFIG_APUS + lis r9,0x6170 + ori r9,r9,0x7573 + cmpw 0,r9,r31 + bne 8f + lis r6,0xfff0 /* Copy to 0xfff00000 on APUS */ +8: +#endif + li r5,0x4000 /* # bytes of memory to copy */ + bl copy_and_flush /* copy the first 0x4000 bytes */ +#ifdef CONFIG_APUS + cmpw 0,r9,r31 /* That's all we need on APUS. */ + beq 2f +#endif + addi r0,r3,4f@l /* jump to the address of 4f */ + mtctr r0 /* in copy and do the rest. */ + bctr /* jump to the copy */ +4: mr r5,r25 + bl copy_and_flush /* copy the rest */ +2: /* * we now have the 1st 16M of ram mapped with the bats. * prep needs the mmu to be turned on here, but pmac already has it on. * this shouldn't bother the pmac since it just gets turned on again * as we jump to our code at KERNELBASE. -- Cort */ + +#else /* CONFIG_8xx */ + tlbia /* Invalidate all TLB entries */ + li r8, 0 + mtspr MI_CTR, r8 /* Set instruction control to zero */ + lis r8, MD_RESETVAL@h + mtspr MD_CTR, r8 /* Set data TLB control */ + + /* Now map the lower 8 Meg into the TLBs. For this quick hack, + * we can load the instruction and data TLB registers with the + * same values. + */ + lis r8, KERNELBASE@h /* Create vaddr for TLB */ + ori r8, r8, MI_EVALID /* Mark it valid */ + mtspr MI_EPN, r8 + mtspr MD_EPN, r8 + li r8, MI_PS8MEG /* Set 8M byte page */ + ori r8, r8, MI_SVALID /* Make it valid */ + mtspr MI_TWC, r8 + mtspr MD_TWC, r8 + li r8, MI_BOOTINIT /* Create RPN for address 0 */ + mtspr MI_RPN, r8 /* Store TLB entry */ + mtspr MD_RPN, r8 + lis r8, MI_Kp@h /* Set the protection mode */ + mtspr MI_AP, r8 + mtspr MD_AP, r8 +#ifdef CONFIG_MBX + /* Map another 8 MByte at 0xfa000000 to get the processor + * internal registers (among other things). + */ + lis r8, 0xfa000000@h /* Create vaddr for TLB */ + ori r8, r8, MD_EVALID /* Mark it valid */ + mtspr MD_EPN, r8 + li r8, MD_PS8MEG /* Set 8M byte page */ + ori r8, r8, MD_SVALID /* Make it valid */ + mtspr MD_TWC, r8 + lis r8, 0xfa000000@h /* Create paddr for TLB */ + ori r8, r8, MI_BOOTINIT + mtspr MD_RPN, r8 +#endif + + /* Since the cache is enabled according to the information we + * just loaded into the TLB, invalidate and enable the caches here. + * We should probably check/set other modes....later. + */ + lis r8, IDC_INVALL@h + mtspr IC_CST, r8 + mtspr DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr IC_CST, r8 +#ifdef notdef + mtspr DC_CST, r8 +#else + /* I still have a bug somewhere because the Ethernet driver + * does not want to work with copyback enabled. For now, + * at least enable write through. + */ +#if 0 + lis r8, DC_SFWT@h + mtspr DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr DC_CST, r8 +#endif +#endif + +/* We now have the lower 8 Meg mapped into TLB entries, and the caches + * ready to work. + */ +#endif /* CONFIG_8xx */ + mfmsr r0 ori r0,r0,MSR_DR|MSR_IR mtspr SRR1,r0 @@ -202,48 +376,6 @@ __start: #define STACK_UNDERHEAD 64 /* - * Macros for storing registers into and loading registers from - * exception frames. - */ -#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) -#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) -#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) -#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) -#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) -#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) -#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) -#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) -#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) -#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) - -#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base) -#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) -#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) -#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) -#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) -#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) -#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base) -#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) -#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) -#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) -#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) -#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) - -#ifndef CONFIG_APUS -#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h -#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h -#else -#define tophys(rd,rs,rt) \ - lis rt,CYBERBASEp@h; \ - lwz rt,0(rt); \ - add rd,rs,rt -#define tovirt(rd,rs,rt) \ - lis rt,CYBERBASEp@h; \ - lwz rt,0(rt); \ - sub rd,rs,rt -#endif - -/* * Exception entry code. This code runs with address translation * turned off, i.e. using physical addresses. * We assume sprg3 has the physical address of the current @@ -303,11 +435,15 @@ label: \ /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) -/* Data access exception */ +/* Data access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ . = 0x300 DataAccess: EXCEPTION_PROLOG mfspr r20,DSISR +#ifndef CONFIG_8xx andis. r0,r20,0xa470 /* weird error? */ bne 1f /* if not, try to put a PTE */ mfspr r3,DAR /* into the hash table */ @@ -315,6 +451,7 @@ DataAccess: rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ mfspr r5,SPRG3 /* phys addr of TSS */ bl hash_page +#endif 1: stw r20,_DSISR(r21) mr r5,r20 mfspr r4,DAR @@ -326,10 +463,14 @@ DataAccess: .long do_page_fault .long int_return -/* Instruction access exception */ +/* Instruction access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ . = 0x400 InstructionAccess: EXCEPTION_PROLOG +#ifndef CONFIG_8xx andis. r0,r23,0x4000 /* no pte found? */ beq 1f /* if so, try to put a PTE */ mr r3,r22 /* into the hash table */ @@ -337,6 +478,7 @@ InstructionAccess: mr r20,r23 /* SRR1 has reason bits */ mfspr r5,SPRG3 /* phys addr of TSS */ bl hash_page +#endif 1: addi r3,r1,STACK_FRAME_OVERHEAD mr r4,r22 mr r5,r23 @@ -347,7 +489,38 @@ InstructionAccess: .long int_return /* External interrupt */ - STD_EXCEPTION(0x500, HardwareInterrupt, do_IRQ) + . = 0x500; +HardwareInterrupt: + EXCEPTION_PROLOG; +#ifdef CONFIG_APUS + mfmsr 20 + xori r20,r20,MSR_DR + sync + mtmsr r20 + sync + + lis r3,APUS_IPL_EMU@h + + li r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT) + stb r20,APUS_IPL_EMU@l(r3) + sync + + lbz r3,APUS_IPL_EMU@l(r3) + + mfmsr r20 + xori r20,r20,MSR_DR + sync + mtmsr r20 + sync + + stw r3,(_CCR+4)(r21); +#endif + addi r3,r1,STACK_FRAME_OVERHEAD; + li r20,MSR_KERNEL; + bl transfer_to_handler; + .long do_IRQ; + .long int_return + /* Alignment exception */ . = 0x600 @@ -375,6 +548,7 @@ ProgramCheck: .long ProgramCheckException .long int_return +#ifndef CONFIG_8xx /* Floating-point unavailable */ . = 0x800 FPUnavailable: @@ -384,6 +558,11 @@ FPUnavailable: bl transfer_to_handler /* if from kernel, take a trap */ .long KernelFP .long int_return +#else +/* No FPU on MPC8xx. This exception is not supposed to happen. +*/ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) +#endif STD_EXCEPTION(0x900, Decrementer, timer_interrupt) STD_EXCEPTION(0xa00, Trap_0a, UnknownException) @@ -406,6 +585,7 @@ SystemCall: STD_EXCEPTION(0xe00, Trap_0e, UnknownException) STD_EXCEPTION(0xf00, Trap_0f, UnknownException) +#ifndef CONFIG_8xx /* * Handle TLB miss for instruction on 603/603e. * Note: we get an alternate set of r0 - r3 to use automatically. @@ -502,7 +682,14 @@ InstructionAddressInvalid: sync /* Some chip revs have problems here... */ mtmsr r0 b InstructionAccess +#else +/* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation) +#endif +#ifndef CONFIG_8xx /* * Handle TLB miss for DATA Load operation on 603/603e */ @@ -598,12 +785,78 @@ DataAddressInvalid: sync /* Some chip revs have problems here... */ mtmsr r0 b DataAccess +#else +/* + * For the MPC8xx, this is a software tablewalk to load the instruction + * TLB. It is modelled after the example in the Motorola manual. The task + * switch loads the M_TWB register with the pointer to the first level table. + * If we discover there is no second level table (the value is zero), the + * plan was to load that into the TLB, which causes another fault into the + * TLB Error interrupt where we can handle such problems. However, that did + * not work, so if we discover there is no second level table, we restore + * registers and branch to the error exception. We have to use the MD_xxx + * registers for the tablewalk because the equivalent MI_xxx registers + * only perform the attribute functions. + */ +InstructionTLBMiss: + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + mfspr r20, SRR0 /* Get effective address of fault */ + mtspr MD_EPN, r20 /* Have to use MD_EPN for walk, MI_EPN can't */ + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load the MI_TWC with the attributes + * for this page, which has only bit 31 set. + */ + tophys(r21,r21,0) + ori r21,r21,1 /* Set valid bit */ + mtspr MI_TWC, r21 /* Set page attributes */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MI_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + mtspr MI_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi + +2: mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b InstructionAccess +#endif /* CONFIG_8xx */ + /* * Handle TLB miss for DATA Store on 603/603e */ . = 0x1200 DataStoreTLBMiss: +#ifndef CONFIG_8xx #ifdef NO_RELOAD_HTAB /* * r0: stored ctr @@ -671,27 +924,164 @@ DataStoreTLBMiss: ori r3,r3,0x40 /* Set secondary hash */ b 00b /* Try lookup again */ #endif /* NO_RELOAD_HTAB */ - +#else /* CONFIG_8xx */ + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load fetch the pte from the table. + */ + tophys(r21, r21, 0) + ori r21, r21, 1 /* Set valid bit in physical L2 page */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MD_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + mtspr MD_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi + +2: mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b DataAccess +#endif /* CONFIG_8xx */ + +#ifndef CONFIG_8xx /* Instruction address breakpoint exception (on 603/604) */ STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) +#else + +/* This is an instruction TLB error on the MPC8xx. This could be due + * to many reasons, such as executing guarded memory or illegal instruction + * addresses. There is nothing to do but handle a big time error fault. + */ + . = 0x1300 +InstructionTLBError: + b InstructionAccess +#endif /* System management exception (603?) */ +#ifndef CONFIG_8xx STD_EXCEPTION(0x1400, Trap_14, UnknownException) +#else + +/* This is the data TLB error on the MPC8xx. This could be due to + * many reasons, including a dirty update to a pte. We can catch that + * one here, but anything else is an error. First, we track down the + * Linux pte. If it is valid, write access is allowed, but the + * page dirty bit is not set, we will set it and reload the TLB. For + * any other case, we bail out to a higher level function that can + * handle it. + */ + . = 0x1400 +DataTLBError: + mtspr M_TW, r20 /* Save a couple of working registers */ + mfcr r20 + stw r20, 0(r0) + stw r21, 4(r0) + + /* First, make sure this was a store operation. + */ + mfspr r20, DSISR + andis. r21, r20, 0x0200 /* If set, indicates store op */ + beq 2f + + mfspr r20, M_TWB /* Get level 1 table entry address */ + lwz r21, 0(r20) /* Get the level 1 entry */ + rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */ + beq 2f /* If zero, bail */ + + /* We have a pte table, so fetch the pte from the table. + */ + tophys(r21, r21, 0) + ori r21, r21, 1 /* Set valid bit in physical L2 page */ + mtspr MD_TWC, r21 /* Load pte table base address */ + mfspr r21, MD_TWC /* ....and get the pte address */ + lwz r21, 0(r21) /* Get the pte */ + + andi. r20, r21, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail out if not */ + + ori r21, r21, _PAGE_DIRTY /* Update changed bit */ + mfspr r20, MD_TWC /* Get pte address again */ + stw r21, 0(r20) /* and update pte in table */ + + /* Set four subpage valid bits (24, 25, 26, and 27). + * Since we currently run MD_CTR.PPCS = 0, the manual says, + * "If the page size is larger than 4k byte, then all the + * 4 bits should have the same value." + * I don't really know what to do if the page size is 4k Bytes, + * but I know setting them all to 0 does not work, and setting them + * all to 1 does, so that is the way it is right now. + * BTW, these four bits map to the software only bits in the + * linux page table. I used to turn them all of, but now just + * set them all for the hardware. + li r20, 0x00f0 + andc r20, r21, r20 + ori r20, r20, 0x0080 + */ + ori r20, r21, 0x00f0 + + mtspr MD_RPN, r20 /* Update TLB entry */ + + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + rfi +2: + mfspr r20, M_TW /* Restore registers */ + lwz r21, 0(r0) + mtcr r21 + lwz r21, 4(r0) + b DataAccess +#endif /* CONFIG_8xx */ STD_EXCEPTION(0x1500, Trap_15, UnknownException) STD_EXCEPTION(0x1600, Trap_16, UnknownException) - STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, TAUException) STD_EXCEPTION(0x1800, Trap_18, UnknownException) STD_EXCEPTION(0x1900, Trap_19, UnknownException) STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) +/* On the MPC8xx, these next four traps are used for development + * support of breakpoints and such. Someday I will get around to + * using them. + */ STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) -/* Run mode exception */ +#ifndef CONFIG_8xx + /* Run mode exception */ STD_EXCEPTION(0x2000, RunMode, RunModeException) STD_EXCEPTION(0x2100, Trap_21, UnknownException) @@ -711,6 +1101,9 @@ DataStoreTLBMiss: STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) . = 0x3000 +#else + . = 0x2000 +#endif /* * This code finishes saving the registers to the exception frame @@ -720,6 +1113,8 @@ DataStoreTLBMiss: .globl transfer_to_handler transfer_to_handler: stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 stw r23,_MSR(r21) SAVE_GPR(7, r21) SAVE_4GPRS(8, r21) @@ -768,6 +1163,7 @@ stack_ovf: SYNC rfi +#ifndef CONFIG_8xx /* * Continuation of the floating-point unavailable handler. */ @@ -790,9 +1186,18 @@ load_up_fpu: ori r5,r5,MSR_FP SYNC mtmsr r5 /* enable use of fpu now */ +#ifndef __SMP__ SYNC cmpi 0,r4,0 beq 1f +#else +/* + * All the saving of last_task_used_math is handled + * by a switch_to() call to smp_giveup_fpu() in SMP so + * last_task_used_math is not used. -- Cort + */ + b 1f +#endif add r4,r4,r6 addi r4,r4,TSS /* want TSS of last_task_used_math */ SAVE_32FPRS(0, r4) @@ -810,9 +1215,11 @@ load_up_fpu: lfd fr0,TSS_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) +#ifndef __SMP__ subi r4,r5,TSS sub r4,r4,r6 stw r4,last_task_used_math@l(r3) +#endif /* __SMP__ */ /* restore registers and return */ lwz r3,_CCR(r21) lwz r4,_LINK(r21) @@ -859,16 +1266,6 @@ Hash_msk = (((1 << Hash_bits) - 1) * 64) .globl hash_page hash_page: -#ifdef __SMP__ - lis r6,hash_table_lock@h - ori r6,r6,hash_table_lock@l - tophys(r6,r6,r2) -1011: lwarx r0,0,r6 - stwcx. r6,0,r6 - bne- 1011b - cmpi 0,r0,0 - bne 1011b -#endif /* __SMP__ */ /* Get PTE (linux-style) and check access */ lwz r5,PG_TABLES(r5) tophys(r5,r5,r2) /* convert to phys addr */ @@ -1018,7 +1415,6 @@ found_slot: lwz r3,0(r2) addi r3,r3,1 stw r3,0(r2) - SYNC /* Return from the exception */ lwz r3,_CCR(r21) @@ -1027,19 +1423,14 @@ found_slot: mtcrf 0xff,r3 mtlr r4 mtctr r5 -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - tophys(r5,r5,r6) - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ REST_GPR(0, r21) REST_2GPRS(1, r21) REST_4GPRS(3, r21) /* we haven't used xer */ + SYNC mtspr SRR1,r23 mtspr SRR0,r22 + SYNC REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) @@ -1047,16 +1438,37 @@ found_slot: rfi hash_page_out: -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - tophys(r5,r5,r6) - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ blr next_slot: .long 0 +#endif /* CONFIG_8xx */ + +#ifndef CONFIG_APUS +/* + * Copy routine used to copy the kernel to start at physical address 0 + * and flush and invalidate the caches as needed. + * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset + * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. + */ +copy_and_flush: + addi r5,r5,-4 + addi r6,r6,-4 +4: li r0,8 + mtctr r0 +3: addi r6,r6,4 /* copy a cache line */ + lwzx r0,r6,r4 + stwx r0,r6,r3 + bdnz 3b + dcbst r6,r3 /* write it to memory */ + sync + icbi r6,r3 /* flush the icache line */ + cmplw 0,r6,r5 + blt 4b + isync + addi r5,r5,4 + addi r6,r6,4 + blr +#endif #ifdef CONFIG_APUS /* On APUS the first 0x4000 bytes of the kernel will be mapped @@ -1072,6 +1484,7 @@ next_slot: */ start_here: +#ifndef CONFIG_8xx /* * Enable caches and 604-specific features if necessary. */ @@ -1108,6 +1521,7 @@ start_here: ori r11,r11,HID0_BTCD 5: mtspr HID0,r11 /* superscalar exec & br history tbl */ 4: +#endif /* CONFIG_8xx */ /* ptr to current */ lis r2,init_task_union@h ori r2,r2,init_task_union@l @@ -1140,6 +1554,9 @@ start_here: mr r6,r28 mr r7,r27 bl identify_machine +#ifdef CONFIG_MBX + bl set_mbx_memory +#endif bl MMU_init /* @@ -1147,8 +1564,19 @@ start_here: * for SDR1 (hash table pointer) and the segment registers * and change to using our exception vectors. */ +#ifndef CONFIG_8xx lis r6,_SDR1@ha lwz r6,_SDR1@l(r6) +#else + /* The right way to do this would be to track it down through + * init's TSS like the context switch code does, but this is + * easier......until someone changes init's static structures. + */ + lis r6, swapper_pg_dir@h + tophys(r6,r6,0) + ori r6, r6, swapper_pg_dir@l + mtspr M_TWB, r6 +#endif lis r4,2f@h ori r4,r4,2f@l tophys(r4,r4,r3) @@ -1158,8 +1586,10 @@ start_here: rfi /* Load up the kernel context */ 2: + SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ +#ifndef CONFIG_8xx mtspr SDR1,r6 li r0,16 /* load up segment register values */ mtctr r0 /* for context 0 */ @@ -1179,7 +1609,8 @@ start_here: LOAD_BAT(1,16,r3,r4,r5) LOAD_BAT(2,32,r3,r4,r5) LOAD_BAT(3,48,r3,r4,r5) - +#endif /* CONFIG_8xx */ + /* Set up for using our exception vectors */ /* ptr to phys current tss */ tophys(r4,r2,r4) @@ -1188,36 +1619,6 @@ start_here: li r3,0 mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ -/* On CHRP copy exception vectors down to 0 */ - lis r5,_stext@ha - addi r5,r5,_stext@l - addis r5,r5,-KERNELBASE@h - cmpwi 0,r5,0 - beq 77f /* vectors are already at 0 */ - li r3,0x1000 - mtctr r3 - li r4,-4 - addi r5,r5,-4 -74: lwzu r0,4(r5) - stwu r0,4(r4) - bdnz 74b - /* need to flush/invalidate caches too */ - li r3,0x4000/CACHE_LINE_SIZE - li r4,0 - mtctr r3 -73: dcbst 0,r4 - addi r4,r4,CACHE_LINE_SIZE - bdnz 73b - sync - li r4,0 - mtctr r3 -72: icbi 0,r4 - addi r4,r4,CACHE_LINE_SIZE - bdnz 72b - sync - isync -77: - /* Now turn on the MMU for real! */ li r4,MSR_KERNEL lis r3,start_kernel@h @@ -1227,29 +1628,6 @@ start_here: rfi /* enable MMU and jump to start_kernel */ - .globl reset_SDR1 -reset_SDR1: - lis r6,_SDR1@ha - lwz r6,_SDR1@l(r6) - mfmsr r5 - li r4,0 - ori r4,r4,MSR_EE|MSR_IR|MSR_DR - andc r3,r5,r4 - lis r4,2f@h - ori r4,r4,2f@l - tophys(r4,r4,r5) - mtspr SRR0,r4 - mtspr SRR1,r3 - rfi -2: /* load new SDR1 */ - tlbia - mtspr SDR1,r6 - /* turn the mmu back on */ - mflr r3 - mtspr SRR0,r3 - mtspr SRR1,r5 - rfi - /* * FP unavailable trap from kernel - print a message, but let * the task use FP in the kernel until it returns to user mode. @@ -1272,10 +1650,19 @@ KernelFP: * and save its floating-point registers in its thread_struct. * Enables the FPU for use in the kernel on return. */ +/* smp_giveup_fpu() takes an arg to tell it where to save the fpu + * regs since last_task_used_math can't be trusted (many many race + * conditions). -- Cort + */ + .globl smp_giveup_fpu +smp_giveup_fpu: + mr r4,r3 + b 12f .globl giveup_fpu giveup_fpu: lis r3,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) +12: mfmsr r5 ori r5,r5,MSR_FP SYNC @@ -1284,8 +1671,10 @@ giveup_fpu: cmpi 0,r4,0 beqlr- /* if no previous owner, done */ addi r4,r4,TSS /* want TSS of last_task_used_math */ +#ifndef __SMP__ li r5,0 stw r5,last_task_used_math@l(r3) +#endif /* __SMP__ */ SAVE_32FPRS(0, r4) mffs fr0 stfd fr0,TSS_FPSCR-4(r4) @@ -1445,6 +1834,18 @@ syscall_ret_2: * * The code which creates the new task context is in 'copy_thread' * in arch/ppc/kernel/process.c + * + * The MPC8xx has something that currently happens "automagically." + * Unshared user space address translations are subject to ASID (context) + * match. During each task switch, the ASID is incremented. We can + * guarantee (I hope :-) that no entries currently match this ASID + * because every task will cause at least a TLB entry to be loaded for + * the first instruction and data access, plus the kernel running will + * have displaced several more TLBs. The MMU contains 32 entries for + * each TLB, and there are 16 contexts, so we just need to make sure + * two pages get replaced for every context switch, which currently + * happens. There are other TLB management techniques that I will + * eventually implement, but this is the easiest for now. -- Dan */ _GLOBAL(_switch) stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1) @@ -1476,6 +1877,7 @@ _GLOBAL(_switch) SYNC lwz r1,KSP(r4) /* Load new stack pointer */ addi r2,r4,-TSS /* Update current */ +#ifndef CONFIG_8xx /* Set up segment registers for new task */ rlwinm r5,r5,4,8,27 /* VSID = context << 4 */ addis r5,r5,0x6000 /* Set Ks, Ku bits */ @@ -1486,9 +1888,32 @@ _GLOBAL(_switch) addi r5,r5,1 /* next VSID */ addis r3,r3,0x1000 /* address of next segment */ bdnz 3b +#else +/* On the MPC8xx, we place the physical address of the new task + * page directory loaded into the MMU base register, and set the + * ASID compare register with the new "context". + */ + mtspr M_CASID, r5 /* Update context */ + lwz r5,MM-TSS(r4) /* Get virtual address of mm */ + lwz r5,PGD(r5) /* get new->mm->pgd */ + tophys(r5, r5, 0) /* convert to phys addr */ + mtspr M_TWB, r5 /* Update MMU base address */ +#endif SYNC /* FALL THROUGH into int_return */ +#ifdef __SMP__ + /* drop scheduler_lock since we weren't called by schedule() */ + lwz r5,TSS_SMP_FORK_RET(r4) + cmpi 0,r5,0 + beq+ int_return + li r3,0 + lis r5,scheduler_lock@ha + stw r3,TSS_SMP_FORK_RET(r4) + stw r3,scheduler_lock@l+4(r5) /* owner_pc */ + stw r3,scheduler_lock@l+8(r5) /* owner_cpu */ + stw r3,scheduler_lock@l(r5) /* lock */ +#endif /* __SMP__ */ /* * Trap exit. @@ -1566,6 +1991,18 @@ int_return: SYNC rfi +#if 0/*def __SMP__*/ + .globl ret_from_smpfork +ret_from_smpfork: + /* drop scheduler_lock since schedule() called us */ + lis r4,scheduler_lock@ha + li r5,0 + stw r5,scheduler_lock@l+4(r4) /* owner_pc */ + stw r5,scheduler_lock@l+8(r4) /* owner_cpu */ + stw r5,scheduler_lock@l(r4) /* lock */ + b int_return +#endif /* __SMP__ */ + /* * Fake an interrupt from kernel mode. * This is used when enable_irq loses an interrupt. @@ -1686,6 +2123,7 @@ _GLOBAL(flush_page_to_ram) * Flush entries from the hash table with VSIDs in the range * given. */ +#ifndef CONFIG_8xx _GLOBAL(flush_hash_segments) #ifdef NO_RELOAD_HTAB /* @@ -1700,15 +2138,6 @@ _GLOBAL(flush_hash_segments) rlwnm. r0,r9,r0,0,0 bne 99f #endif /* NO_RELOAD_HTAB */ -#ifdef __SMP__ - lis r6,hash_table_lock@h - ori r6,r6,hash_table_lock@l -1011: lwarx r0,0,r6 - stwcx. r6,0,r6 - bne- 1011b - cmpi 0,r0,0 - bne 1011b -#endif /* __SMP__ */ rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */ oris r3,r3,0x8000 /* set V bit */ rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */ @@ -1730,12 +2159,6 @@ _GLOBAL(flush_hash_segments) stw r0,0(r5) /* invalidate entry */ 2: bdnz 1b /* continue with loop */ sync -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ 99: tlbia isync blr @@ -1753,15 +2176,6 @@ _GLOBAL(flush_hash_page) rlwnm. r0,r9,r0,0,0 bne 99f #endif /* NO_RELOAD_HTAB */ -#ifdef __SMP__ - lis r6,hash_table_lock@h - ori r6,r6,hash_table_lock@l -1011: lwarx r0,0,r6 - stwcx. r6,0,r6 - bne- 1011b - cmpi 0,r0,0 - bne 1011b -#endif /* __SMP__ */ rlwinm r3,r3,11,1,20 /* put context into vsid */ rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ oris r3,r3,0x8000 /* set V (valid) bit */ @@ -1794,22 +2208,17 @@ _GLOBAL(flush_hash_page) 3: li r0,0 stw r0,0(r7) /* invalidate entry */ 4: sync -#ifdef __SMP__ - lis r5,hash_table_lock@h - ori r5,r5,hash_table_lock@l - li r6,0 - stw r6,0(r5) -#endif /* __SMP__ */ 99: tlbie r4 /* in hw tlb too */ isync blr - +#endif /* CONFIG_8xx */ /* * This routine is just here to keep GCC happy - sigh... */ _GLOBAL(__main) blr +#ifndef CONFIG_8xx /* * On CHRP, the Run-Time Abstraction Services (RTAS) have to be * called with the MMU off. @@ -1819,9 +2228,9 @@ enter_rtas: stwu r1,-16(r1) mflr r0 stw r0,20(r1) - addis r3,r3,-KERNELBASE@h lis r4,rtas_data@ha lwz r4,rtas_data@l(r4) + addis r4,r4,-KERNELBASE@h lis r6,1f@ha /* physical return address for rtas */ addi r6,r6,1f@l addis r6,r6,-KERNELBASE@h @@ -1829,14 +2238,15 @@ enter_rtas: addis r7,r7,-KERNELBASE@h lis r8,rtas_entry@ha lwz r8,rtas_entry@l(r8) + addis r5,r8,-KERNELBASE@h mfmsr r9 stw r9,8(r1) - li r0,0 ori r0,r0,MSR_EE|MSR_SE|MSR_BE andc r0,r9,r0 andi. r9,r9,MSR_ME|MSR_RI sync /* disable interrupts so SRR0/1 */ mtmsr r0 /* don't get trashed */ + li r6,0 mtlr r6 mtspr SPRG2,r7 mtspr SRR0,r8 @@ -1850,23 +2260,26 @@ enter_rtas: mtspr SRR0,r8 mtspr SRR1,r9 rfi /* return to caller */ +#endif /* CONFIG_8xx */ +#ifdef CONFIG_8xx +/* This is called during an exec when new page tables are created. + * It maps to the SET_PAGE_DIR macro. I guess I should make it an + * inline function. + */ +_GLOBAL(set_page_dir) + addis r3,r3,-KERNELBASE@h /* convert to phys addr */ + mtspr M_TWB, r3 /* Update MMU base address */ + blr +#endif - .globl amhere -amhere: .long 0 - + #ifdef __SMP__ /* * Secondary processor begins executing here. */ .globl secondary_entry secondary_entry: - lis r0,amhere@h - ori r0,r0,amhere@l - addis r0,r0,-KERNELBASE@h - stw r0,0(r0) - sync - isync /* just like __start() with a few changes -- Cort */ mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ @@ -1938,16 +2351,6 @@ secondary_entry: ori r11,r11,HID0_BTCD 5: mtspr HID0,r11 /* superscalar exec & br history tbl */ 4: - /* get ptr to current */ - lis r2,current_set@h - ori r2,r2,current_set@l - /* assume we're second processor for now */ - lwz r2,4(r2) - /* stack */ - addi r1,r2,TASK_UNION_SIZE - li r0,0 - stwu r0,-STACK_FRAME_OVERHEAD(r1) - /* * init_MMU on the first processor has setup the variables * for us - all we need to do is load them -- Cort @@ -1969,6 +2372,18 @@ secondary_entry: rfi /* Load up the kernel context */ 2: + /* get ptr to current */ + lis r2,current_set@h + ori r2,r2,current_set@l + /* assume we're second processor for now */ + tophys(r2,r2,r10) + lwz r2,4(r2) + /* stack */ + addi r1,r2,TASK_UNION_SIZE + li r0,0 + tophys(r3,r1,r10) + stwu r0,-STACK_FRAME_OVERHEAD(r3) + SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ mtspr SDR1,r6 @@ -2025,6 +2440,31 @@ secondary_entry: /* should never return */ .long 0 #endif /* __SMP__ */ + +#ifdef CONFIG_MBX +/* Jump into the system reset for the MBX rom. + * We first disable the MMU, and then jump to the ROM reset address. + * + * This does not work, don't bother trying. There is no place in + * the ROM we can jump to cause a reset. We will have to program + * a watchdog of some type that we don't service to cause a processor + * reset. + */ + .globl MBX_gorom +MBX_gorom: + li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) + lis r4,2f@h + addis r4,r4,-KERNELBASE@h + ori r4,r4,2f@l + mtspr SRR0,r4 + mtspr SRR1,r3 + rfi +2: + lis r4, 0xfe000000@h + addi r4, r4, 0xfe000000@l + mtlr r4 + blr +#endif /* * We put a few things here that have to be page-aligned. diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index f47d6f3d6..21c6966e0 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.13 1998/01/06 06:44:55 cort Exp $ + * $Id: idle.c,v 1.35 1998/04/07 20:24:23 cort Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -13,6 +13,7 @@ */ #define __KERNEL_SYSCALLS__ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -31,41 +32,44 @@ #include <asm/smp_lock.h> #include <asm/processor.h> #include <asm/mmu.h> +#include <asm/cache.h> +#ifdef CONFIG_PMAC +#include <asm/mediabay.h> +#endif -int zero_paged(void *unused); -void inline power_save(void); +void zero_paged(void); +void power_save(void); void inline htab_reclaim(void); +unsigned long htab_reclaim_on = 0; +unsigned long zero_paged_on = 0; + int idled(void *unused) { int ret = -EPERM; - /* - * want one per cpu since it would be nice to have all - * processors who aren't doing anything - * zero-ing pages since this daemon is lock-free - * -- Cort - */ - /* kernel_thread(zero_paged, NULL, 0); */ - -#ifdef __SMP__ -printk("SMP %d: in idle. current = %s/%d\n", - current->processor,current->comm,current->pid); -#endif /* __SMP__ */ for (;;) { + __sti(); + /* endless loop with no priority at all */ current->priority = -100; current->counter = -100; + + check_pgt_cache(); - /* endless idle loop with no priority at all */ - /* htab_reclaim(); */ - schedule(); + if ( !need_resched && zero_paged_on ) zero_paged(); + if ( !need_resched && htab_reclaim_on ) htab_reclaim(); + + /* + * Only processor 1 may sleep now since processor 2 would + * never wake up. Need to add timer code for processor 2 + * then it can sleep. -- Cort + */ #ifndef __SMP__ - /* can't do this on smp since second processor - will never wake up -- Cort */ - /* power_save(); */ -#endif /* __SMP__ */ + if ( !need_resched ) power_save(); +#endif /* __SMP__ */ + schedule(); } ret = 0; return ret; @@ -76,13 +80,16 @@ printk("SMP %d: in idle. current = %s/%d\n", * Mark 'zombie' pte's in the hash table as invalid. * This improves performance for the hash table reload code * a bit since we don't consider unused pages as valid. - * I haven't done any rigorous performance analysis yet - * so it's still experimental and turned off here. * -- Cort */ +PTE *reclaim_ptr = 0; void inline htab_reclaim(void) { +#ifndef CONFIG_8xx +#if 0 PTE *ptr, *start; + static int dir = 1; +#endif struct task_struct *p; unsigned long valid = 0; extern PTE *Hash, *Hash_end; @@ -91,28 +98,33 @@ void inline htab_reclaim(void) /* if we don't have a htab */ if ( Hash_size == 0 ) return; - /*lock_dcache();*/ - + lock_dcache(1); + +#if 0 /* find a random place in the htab to start each time */ - start = &Hash[jiffies%(Hash_size/sizeof(ptr))]; - for ( ptr = start; ptr < Hash_end ; ptr++) + start = &Hash[jiffies%(Hash_size/sizeof(PTE))]; + /* go a different direction each time */ + dir *= -1; + for ( ptr = start; + !need_resched && (ptr != Hash_end) && (ptr != Hash); + ptr += dir) { - if ( ptr == start ) - return; - if ( ptr == Hash_end ) - ptr = Hash; - valid = 0; - if (!ptr->v) +#else + if ( !reclaim_ptr ) reclaim_ptr = Hash; + while ( !need_resched ) + { + reclaim_ptr++; + if ( reclaim_ptr == Hash_end ) reclaim_ptr = Hash; +#endif + if (!reclaim_ptr->v) continue; + valid = 0; for_each_task(p) { if ( need_resched ) - { - /*unlock_dcache();*/ - return; - } + goto out; /* if this vsid/context is in use */ - if ( (ptr->vsid >> 4) == p->mm->context ) + if ( (reclaim_ptr->vsid >> 4) == p->mm->context ) { valid = 1; break; @@ -121,19 +133,28 @@ void inline htab_reclaim(void) if ( valid ) continue; /* this pte isn't used */ - ptr->v = 0; + reclaim_ptr->v = 0; } - /*unlock_dcache();*/ +out: + if ( need_resched ) printk("need_resched: %x\n", need_resched); + unlock_dcache(); +#endif /* CONFIG_8xx */ } - + /* * Syscall entry into the idle task. -- Cort */ asmlinkage int sys_idle(void) { + extern int media_bay_task(void *); if(current->pid != 0) return -EPERM; +#ifdef CONFIG_PMAC + if (media_bay_present) + kernel_thread(media_bay_task, NULL, 0); +#endif + idled(NULL); return 0; /* should never execute this but it makes gcc happy -- Cort */ } @@ -157,10 +178,8 @@ unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */ unsigned long bytecount = 0; /* pointer into the currently being zero'd page */ unsigned long zerocount = 0; /* # currently pre-zero'd pages */ unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */ -unsigned long pageptr = 0; /* current page being zero'd */ unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */ unsigned long zeropage_calls = 0;/* # zero'd pages request that've been made */ -static struct wait_queue * page_zerod_wait = NULL; #define PAGE_THRESHOLD 96 /* how many pages to keep pre-zero'd */ /* @@ -189,7 +208,6 @@ unsigned long get_prezerod_page(void) */ atomic_inc((atomic_t *)&zeropage_hits); atomic_dec((atomic_t *)&zerocount); - wake_up(&page_zerod_wait); need_resched = 1; /* zero out the pointer to next in the page */ @@ -201,35 +219,18 @@ unsigned long get_prezerod_page(void) /* * Experimental stuff to zero out pages in the idle task - * to speed up get_free_pages() -- Cort - * Zero's out pages until we need to resched or - * we've reached the limit of zero'd pages. + * to speed up get_free_pages(). Zero's out pages until + * we've reached the limit of zero'd pages. We handle + * reschedule()'s in here so when we return we know we've + * zero'd all we need to for now. */ -int zero_paged(void *unused) +void zero_paged(void) { - extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); - pgd_t *dir; - pmd_t *pmd; + unsigned long pageptr = 0; /* current page being zero'd */ pte_t *pte; - - sprintf(current->comm, "zero_paged (idle)"); - /* current->blocked = ~0UL; */ -#ifdef __SMP__ - printk("Started zero_paged (cpu %d)\n", hard_smp_processor_id()); -#else - printk("Started zero_paged\n"); -#endif /* __SMP__ */ - - __sti(); - while ( 1 ) + while ( zerocount <= PAGE_THRESHOLD ) { - /* don't want to be pre-empted by swapper or power_save */ - current->priority = -98; - current->counter = -98; - /* we don't want to run until we have something to do */ - while ( zerocount >= PAGE_THRESHOLD ) - sleep_on(&page_zerod_wait); /* * Mark a page as reserved so we can mess with it * If we're interrupted we keep this page and our place in it @@ -237,7 +238,7 @@ int zero_paged(void *unused) */ pageptr = __get_free_pages(GFP_ATOMIC, 0); if ( !pageptr ) - goto retry; + return; if ( need_resched ) schedule(); @@ -245,20 +246,15 @@ int zero_paged(void *unused) /* * Make the page no cache so we don't blow our cache with 0's */ - dir = pgd_offset( init_task.mm, pageptr ); - if (dir) + pte = find_pte(init_task.mm, pageptr); + if ( !pte ) { - pmd = pmd_offset(dir, pageptr & PAGE_MASK); - if (pmd && pmd_present(*pmd)) - { - pte = pte_offset(pmd, pageptr & PAGE_MASK); - if (pte && pte_present(*pte)) - { - pte_uncache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); - } - } + printk("pte NULL in zero_paged()\n"); + return; } + + pte_uncache(*pte); + flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); /* * Important here to not take time away from real processes. @@ -308,35 +304,34 @@ int zero_paged(void *unused) */ atomic_inc((atomic_t *)&zerocount); atomic_inc((atomic_t *)&zerototal); -retry: - schedule(); } } -void inline power_save(void) +int powersave_mode = HID0_DOZE; + +void power_save(void) { unsigned long msr, hid0; - /* no powersaving modes on the 601 */ - if( (_get_PVR()>>16) == 1 ) + /* only sleep on the 603-family/750 processors */ + switch (_get_PVR() >> 16) { + case 3: /* 603 */ + case 6: /* 603e */ + case 7: /* 603ev */ + case 8: /* 750 */ + break; + default: return; + } - __sti(); - asm volatile( - /* clear powersaving modes and set nap mode */ - "mfspr %3,1008 \n\t" - "andc %3,%3,%4 \n\t" - "or %3,%3,%5 \n\t" - "mtspr 1008,%3 \n\t" - /* enter the mode */ - "mfmsr %0 \n\t" - "oris %0,%0,%2 \n\t" - "sync \n\t" - "mtmsr %0 \n\t" - "isync \n\t" - : "=&r" (msr) - : "0" (msr), "i" (MSR_POW>>16), - "r" (hid0), - "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), - "r" (HID0_NAP)); + save_flags(msr); + cli(); + if (!need_resched) { + asm("mfspr %0,1008" : "=r" (hid0) :); + hid0 &= ~(HID0_NAP | HID0_SLEEP | HID0_DOZE); + hid0 |= powersave_mode | HID0_DPM; + asm("mtspr 1008,%0" : : "r" (hid0)); + msr |= MSR_POW; + } + restore_flags(msr); } diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 4616d59a2..462805a65 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -14,6 +14,14 @@ * instead of just grabbing them. Thus setups with different IRQ numbers * shouldn't result in any weird surprises, and installing new handlers * should be easier. + * + * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the + * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit + * mask register (of which only 16 are defined), hence the weird shifting + * and compliment of the cached_irq_mask. I want to be able to stuff + * this right into the SIU SMASK register. + * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx + * to reduce code space and undefined function references. */ @@ -30,30 +38,41 @@ #include <linux/malloc.h> #include <linux/openpic.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/openpic.h> #include <asm/hydra.h> #include <asm/system.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/irq.h> #include <asm/bitops.h> #include <asm/gg2.h> +#include <asm/cache.h> +#ifdef CONFIG_8xx +#include <asm/8xx_immap.h> +#include <asm/mbx.h> +#endif #undef SHOW_IRQ unsigned lost_interrupts = 0; unsigned int local_irq_count[NR_CPUS]; -static struct irqaction irq_action[NR_IRQS]; +static struct irqaction *irq_action[NR_IRQS]; static int spurious_interrupts = 0; +#ifndef CONFIG_8xx +static unsigned int cached_irq_mask = 0xffffffff; +#else static unsigned int cached_irq_mask = 0xffffffff; +#endif static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } -spinlock_t irq_controller_lock; +/*spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;*/ #ifdef __SMP__ atomic_t __ppc_bh_counter = ATOMIC_INIT(0); #else int __ppc_bh_counter = 0; #endif +static volatile unsigned char *gg2_int_ack_special; +extern volatile unsigned long ipi_count; #define cached_21 (((char *)(&cached_irq_mask))[3]) #define cached_A1 (((char *)(&cached_irq_mask))[2]) @@ -61,9 +80,19 @@ int __ppc_bh_counter = 0; /* * These are set to the appropriate functions by init_IRQ() */ +#ifndef CONFIG_8xx void (*mask_and_ack_irq)(int irq_nr); void (*mask_irq)(unsigned int irq_nr); void (*unmask_irq)(unsigned int irq_nr); +#else /* CONFIG_8xx */ +/* init_IRQ() happens too late for the MBX because we initialize the + * CPM early and it calls request_irq() before we have these function + * pointers initialized. + */ +#define mask_and_ack_irq(irq) mbx_mask_irq(irq) +#define mask_irq(irq) mbx_mask_irq(irq) +#define unmask_irq(irq) mbx_unmask_irq(irq) +#endif /* CONFIG_8xx */ /* prep */ @@ -79,9 +108,46 @@ extern unsigned long route_pci_interrupts(void); #define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE)) + +/* nasty hack for shared irq's since we need to do kmalloc calls but + * can't very very early in the boot when we need to do a request irq. + * this needs to be removed. + * -- Cort + */ +static char cache_bitmask = 0; +static struct irqaction malloc_cache[4]; +extern int mem_init_done; + +void *irq_kmalloc(size_t size, int pri) +{ + unsigned int i; + if ( mem_init_done ) + return kmalloc(size,pri); + for ( i = 0; i <= 3 ; i++ ) + if ( ! ( cache_bitmask & (1<<i) ) ) + { + cache_bitmask |= (1<<i); + return (void *)(&malloc_cache[i]); + } + return 0; +} + +void irq_kfree(void *ptr) +{ + unsigned int i; + for ( i = 0 ; i <= 3 ; i++ ) + if ( ptr == &malloc_cache[i] ) + { + cache_bitmask &= ~(1<<i); + return; + } + kfree(ptr); +} + +#ifndef CONFIG_8xx void i8259_mask_and_ack_irq(int irq_nr) { - spin_lock(&irq_controller_lock); + /* spin_lock(&irq_controller_lock);*/ cached_irq_mask |= 1 << irq_nr; if (irq_nr > 7) { inb(0xA1); /* DUMMY */ @@ -96,20 +162,22 @@ void i8259_mask_and_ack_irq(int irq_nr) outb(0x60|irq_nr,0x20); /* specific eoi */ } - spin_unlock(&irq_controller_lock); + /* spin_unlock(&irq_controller_lock);*/ } void pmac_mask_and_ack_irq(int irq_nr) { unsigned long bit = 1UL << irq_nr; - spin_lock(&irq_controller_lock); + /* spin_lock(&irq_controller_lock);*/ cached_irq_mask |= bit; lost_interrupts &= ~bit; out_le32(IRQ_ACK, bit); out_le32(IRQ_ENABLE, ~cached_irq_mask); out_le32(IRQ_ACK, bit); - spin_unlock(&irq_controller_lock); + /* spin_unlock(&irq_controller_lock);*/ + /*if ( irq_controller_lock.lock ) + panic("irq controller lock still held in mask and ack\n");*/ } void chrp_mask_and_ack_irq(int irq_nr) @@ -188,44 +256,96 @@ static void chrp_unmask_irq(unsigned int irq_nr) else openpic_enable_irq(irq_to_openpic(irq_nr)); } +#else /* CONFIG_8xx */ +static void mbx_mask_irq(unsigned int irq_nr) +{ + cached_irq_mask &= ~(1 << (31-irq_nr)); + ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = + cached_irq_mask; +} + +static void mbx_unmask_irq(unsigned int irq_nr) +{ + cached_irq_mask |= (1 << (31-irq_nr)); + ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = + cached_irq_mask; +} +#endif /* CONFIG_8xx */ void disable_irq(unsigned int irq_nr) { - unsigned long flags; + /*unsigned long flags;*/ - spin_lock_irqsave(&irq_controller_lock, flags); + /* spin_lock_irqsave(&irq_controller_lock, flags);*/ mask_irq(irq_nr); - spin_unlock_irqrestore(&irq_controller_lock, flags); + /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ synchronize_irq(); } void enable_irq(unsigned int irq_nr) { - unsigned long flags; + /*unsigned long flags;*/ - spin_lock_irqsave(&irq_controller_lock, flags); + /* spin_lock_irqsave(&irq_controller_lock, flags);*/ unmask_irq(irq_nr); - spin_unlock_irqrestore(&irq_controller_lock, flags); + /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ } int get_irq_list(char *buf) { - int i, len = 0; + int i, len = 0, j; struct irqaction * action; + len += sprintf(buf+len, " "); + for (j=0; j<smp_num_cpus; j++) + len += sprintf(buf+len, "CPU%d ",j); + *(char *)(buf+len++) = '\n'; + for (i = 0 ; i < NR_IRQS ; i++) { - action = irq_action + i; + action = irq_action[i]; if (!action || !action->handler) continue; - len += sprintf(buf+len, "%2d: %10u %s", - i, kstat.interrupts[i], action->name); + len += sprintf(buf+len, "%3d: ", i); +#ifdef __SMP__ + for (j = 0; j < smp_num_cpus; j++) + len += sprintf(buf+len, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#else + len += sprintf(buf+len, "%10u ", kstat_irqs(i)); +#endif /* __SMP__ */ + switch( _machine ) + { + case _MACH_prep: + len += sprintf(buf+len, " 82c59 "); + break; + case _MACH_Pmac: + len += sprintf(buf+len, " PMAC-PIC "); + break; + case _MACH_chrp: + if ( is_8259_irq(i) ) + len += sprintf(buf+len, " 82c59 "); + else + len += sprintf(buf+len, " OpenPIC "); + break; + } + + len += sprintf(buf+len, " %s",action->name); for (action=action->next; action; action = action->next) { len += sprintf(buf+len, ", %s", action->name); } len += sprintf(buf+len, "\n"); } - len += sprintf(buf+len, "99: %10u spurious or short\n", - spurious_interrupts); +#ifdef __SMP__ + /* should this be per processor send/receive? */ + len += sprintf(buf+len, "IPI: %10lu\n", ipi_count); + for ( i = 0 ; i <= smp_num_cpus-1; i++ ) + len += sprintf(buf+len," "); + len += sprintf(buf+len, " interprocessor messages received\n"); +#endif + len += sprintf(buf+len, "BAD: %10u",spurious_interrupts); + for ( i = 0 ; i <= smp_num_cpus-1; i++ ) + len += sprintf(buf+len," "); + len += sprintf(buf+len, " spurious or short\n"); return len; } @@ -394,20 +514,31 @@ asmlinkage void do_IRQ(struct pt_regs *regs) int status; int openpic_eoi_done = 0; + /* save the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + unsigned dcache_locked; + + dcache_locked = unlock_dcache(); + hardirq_enter(cpu); +#ifndef CONFIG_8xx #ifdef __SMP__ if ( cpu != 0 ) - panic("cpu %d received interrupt", cpu); -#endif /* __SMP__ */ - - hardirq_enter(cpu); + { + if ( !lost_interrupts ) + { + extern smp_message_recv(void); + goto out; + + ipi_count++; + smp_message_recv(); + goto out; + } + /* could be here due to a do_fake_interrupt call but we don't + mess with the controller from the second cpu -- Cort */ + goto out; + } +#endif /* __SMP__ */ - /* - * I'll put this ugly mess of code into a function - * such as get_pending_irq() or some such clear thing - * so we don't have a switch in the irq code and - * the chrp code is merged a bit with the prep. - * -- Cort - */ switch ( _machine ) { case _MACH_Pmac: @@ -425,7 +556,7 @@ asmlinkage void do_IRQ(struct pt_regs *regs) * * This should go in the above mask/ack code soon. -- Cort */ - irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL; + irq = *gg2_int_ack_special; /* * Acknowledge as soon as possible to allow i8259 * interrupt nesting @@ -456,7 +587,6 @@ asmlinkage void do_IRQ(struct pt_regs *regs) } break; case _MACH_prep: -#if 1 outb(0x0C, 0x20); irq = inb(0x20) & 7; if (irq == 2) @@ -470,23 +600,6 @@ retry_cascade: irq = (irq&7) + 8; } bits = 1UL << irq; -#else - /* - * get the isr from the intr controller since - * the bit in the irr has been cleared - */ - outb(0x0a, 0x20); - bits = inb(0x20)&0xff; - /* handle cascade */ - if ( bits & 4 ) - { - bits &= ~4UL; - outb(0x0a, 0xA0); - bits |= inb(0xA0)<<8; - } - /* ignore masked irqs */ - bits &= ~cached_irq_mask; -#endif break; } @@ -494,43 +607,62 @@ retry_cascade: printk("Bogus interrupt from PC = %lx\n", regs->nip); goto out; } + +#else /* CONFIG_8xx */ + /* For MPC8xx, read the SIVEC register and shift the bits down + * to get the irq number. + */ + bits = ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_sivec; + irq = bits >> 26; +#endif /* CONFIG_8xx */ mask_and_ack_irq(irq); status = 0; - action = irq_action + irq; - kstat.interrupts[irq]++; - if (action->handler) { + action = irq_action[irq]; + kstat.irqs[cpu][irq]++; + if ( action && action->handler) { if (!(action->flags & SA_INTERRUPT)) __sti(); - status |= action->flags; - action->handler(irq, action->dev_id, regs); - /*if (status & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq);*/ - __cli(); /* in case the handler turned them on */ - spin_lock(&irq_controller_lock); + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + /*if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq);*/ + action = action->next; + } while ( action ); + __cli(); + /* spin_lock(&irq_controller_lock);*/ unmask_irq(irq); - spin_unlock(&irq_controller_lock); + /* spin_unlock(&irq_controller_lock);*/ } else { +#ifndef CONFIG_8xx if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */ +#endif spurious_interrupts++; disable_irq( irq ); } /* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */ +#ifndef CONFIG_8xx if ( is_prep && (irq > 7) ) goto retry_cascade; /* do_bottom_half is called if necessary from int_return in head.S */ out: if (_machine == _MACH_chrp && !openpic_eoi_done) openpic_eoi(0); +#endif /* CONFIG_8xx */ hardirq_exit(cpu); + + /* restore the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + lock_dcache(dcache_locked); } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { - struct irqaction * action; + struct irqaction *old, **p, *action; unsigned long flags; #ifdef SHOW_IRQ @@ -540,49 +672,58 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) if (irq >= NR_IRQS) return -EINVAL; - action = irq + irq_action; - if (action->handler) - return -EBUSY; + if (!handler) - return -EINVAL; + { + /* Free */ + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) + { + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + restore_flags(flags); + irq_kfree(action); + return 0; + } + return -ENOENT; + } + + action = (struct irqaction *) + irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; save_flags(flags); cli(); + action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->dev_id = dev_id; + action->next = NULL; enable_irq(irq); - restore_flags(flags); + p = irq_action + irq; + + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & action->flags & SA_SHIRQ)) + return -EBUSY; + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + } + *p = action; + + restore_flags(flags); return 0; } void free_irq(unsigned int irq, void *dev_id) { - struct irqaction * action = irq + irq_action; - unsigned long flags; - -#ifdef SHOW_IRQ - printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id); -#endif /* SHOW_IRQ */ - - if (irq >= NR_IRQS) { - printk("Trying to free IRQ%d\n",irq); - return; - } - if (!action->handler) { - printk("Trying to free free IRQ%d\n",irq); - return; - } - disable_irq(irq); - save_flags(flags); - cli(); - action->handler = NULL; - action->flags = 0; - action->mask = 0; - action->name = NULL; - action->dev_id = NULL; - restore_flags(flags); + request_irq(irq, NULL, 0, NULL, dev_id); } unsigned long probe_irq_on (void) @@ -595,6 +736,7 @@ int probe_irq_off (unsigned long irqs) return 0; } +#ifndef CONFIG_8xx __initfunc(static void i8259_init(void)) { /* init master interrupt controller */ @@ -616,11 +758,17 @@ __initfunc(static void i8259_init(void)) panic("Could not allocate cascade IRQ!"); enable_irq(2); /* Enable cascade interrupt */ } +#endif /* CONFIG_8xx */ +/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External + * interrupts can be either edge or level triggered, but there is no + * reason for us to change the EPPC-bug values (it would not work if we did). + */ __initfunc(void init_IRQ(void)) { extern void xmon_irq(int, void *, struct pt_regs *); +#ifndef CONFIG_8xx switch (_machine) { case _MACH_Pmac: @@ -637,7 +785,8 @@ __initfunc(void init_IRQ(void)) mask_and_ack_irq = chrp_mask_and_ack_irq; mask_irq = chrp_mask_irq; unmask_irq = chrp_unmask_irq; - ioremap(GG2_INT_ACK_SPECIAL, 1); + gg2_int_ack_special = (volatile unsigned char *) + ioremap(GG2_INT_ACK_SPECIAL, 1); openpic_init(); i8259_init(); #ifdef CONFIG_XMON @@ -653,7 +802,7 @@ __initfunc(void init_IRQ(void)) i8259_init(); route_pci_interrupts(); /* - * According to the Carolina spec from ibm irq's 0,1,2, and 8 + * According to the Carolina spec from ibm irqs 0,1,2, and 8 * must be edge triggered. Also, the pci intrs must be level * triggered and _only_ isa intrs can be level sensitive * which are 3-7,9-12,14-15. 13 is special - it can be level. @@ -686,5 +835,6 @@ __initfunc(void init_IRQ(void)) } break; - } + } +#endif /* CONFIG_8xx */ } diff --git a/arch/ppc/kernel/mbx_pci.c b/arch/ppc/kernel/mbx_pci.c new file mode 100644 index 000000000..30b7b1184 --- /dev/null +++ b/arch/ppc/kernel/mbx_pci.c @@ -0,0 +1,254 @@ +/* + * MBX pci routines. + * The MBX uses the QSpan PCI bridge. The config address register + * is located 0x500 from the base of the bridge control/status registers. + * The data register is located at 0x504. + * This is a two step operation. First, the address register is written, + * then the data register is read/written as required. + * I don't know what to do about interrupts (yet). + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/mbx.h> + + +/* + * This blows......The MBX uses the Tundra QSpan PCI bridge. When + * reading the configuration space, if something does not respond + * the bus times out and we get a machine check interrupt. So, the + * good ol' exception tables come to mind to trap it and return some + * value. + * + * On an error we just return a -1, since that is what the caller wants + * returned if nothing is present. I copied this from __get_user_asm, + * with the only difference of returning -1 instead of EFAULT. + * There is an associated hack in the machine check trap code. + * + * The QSPAN is also a big endian device, that is it makes the PCI + * look big endian to us. This presents a problem for the Linux PCI + * functions, which assume little endian. For example, we see the + * first 32-bit word like this: + * ------------------------ + * | Device ID | Vendor ID | + * ------------------------ + * If we read/write as a double word, that's OK. But in our world, + * when read as a word, device ID is at location 0, not location 2 as + * the little endian PCI would believe. We have to switch bits in + * the PCI addresses given to us to get the data to/from the correct + * byte lanes. + * + * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. + * It always forces the MS bit to zero. Therefore, dev_fn values + * greater than 128 are returned as "no device found" errors. + * + * The QSPAN can only perform long word (32-bit) configuration cycles. + * The "offset" must have the two LS bits set to zero. Read operations + * require we read the entire word and then sort out what should be + * returned. Write operations other than long word require that we + * read the long word, update the proper word or byte, then write the + * entire long word back. + * + * PCI Bridge hack. We assume (correctly) that bus 0 is the primary + * PCI bus from the QSPAN. If we are called with a bus number other + * than zero, we create a Type 1 configuration access that a downstream + * PCI bridge will interpret. + */ + +#define __get_mbx_pci_config(x, addr, op) \ + __asm__ __volatile__( \ + "1: "op" %0,0(%1)\n" \ + " eieio\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r"(x) : "r"(addr)) + +#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) +#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) + +#define mk_config_addr(bus, dev, offset) \ + (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) + +#define mk_config_type1(bus, dev, offset) \ + mk_config_addr(bus, dev, offset) | 1; + +int mbx_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz"); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *val = *cp; + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz"); + offset ^= 0x02; + + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *val = *sp; + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_mbx_pci_config(*val, QS_CONFIG_DATA, "lwz"); + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *cp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x02; + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *sp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *(unsigned int *)QS_CONFIG_DATA = val; + + return PCIBIOS_SUCCESSFUL; +} + +int mbx_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int num, devfn; + unsigned int x, vendev; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (devfn = 0; devfn < 32; devfn++) { + mbx_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); + if (x == vendev) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devfn<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int mbx_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int devnr, x, num; + + num = 0; + for (devnr = 0; devnr < 32; devnr++) { + mbx_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); + if ((x>>8) == class_code) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devnr<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c new file mode 100644 index 000000000..c028bb12d --- /dev/null +++ b/arch/ppc/kernel/mbx_setup.c @@ -0,0 +1,169 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/ide.h> +#include <asm/mbx.h> + +extern unsigned long loops_per_sec; + +unsigned long empty_zero_page[1024]; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +extern char saved_command_line[256]; + +extern unsigned long find_available_memory(void); +extern void mbx_cpm_reset(uint); + + +void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + + *p = 0; + *irq = 0; + + if (base != 0) /* Only map the first ATA flash drive */ + return; +#ifdef ATA_FLASH + base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200); + for (i = 0; i < 8; ++i) + *p++ = base++; + *p = ++base; /* Does not matter */ + if (irq) + *irq = 13; +#endif +} + +int +mbx_get_cpuinfo(char *buffer) +{ + int pvr = _get_PVR(); + int len; + char *model; + bd_t *bp; + extern RESIDUAL res; + + /* I know the MPC860 is 0x50. I don't have the book handy + * to check the others. + */ + if ((pvr>>16) == 0x50) + model = "MPC860"; + else + model = "unknown"; + +#ifdef __SMP__ +#define CD(X) (cpu_data[n].X) +#else +#define CD(X) (X) +#define CPUN 0 +#endif + bp = (bd_t *)&res; + + len = sprintf(buffer,"processor\t: %d\n" + "cpu\t\t: %s\n" + "revision\t: %d.%d\n" + "clock\t\t: %d MHz\n" + "bus clock\t: %d MHz\n", + CPUN, + model, + MAJOR(pvr), MINOR(pvr), + bp->bi_intfreq / 1000000, + bp->bi_busfreq / 1000000 + ); + + return len; +} + +__initfunc(void +mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) +{ + int cpm_page; + + cpm_page = *memory_start_p; + *memory_start_p += PAGE_SIZE; + + /* Reset the Communication Processor Module. + */ + mbx_cpm_reset(cpm_page); + +#ifdef notdef + ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ +#endif + +#ifdef CONFIG_BLK_DEV_RAM +#if 0 + ROOT_DEV = to_kdev_t(0x0200); /* floppy */ + rd_prompt = 1; + rd_doload = 1; + rd_image_start = 0; +#endif + /* initrd_start and size are setup by boot/head.S and kernel/head.S */ + if ( initrd_start ) + { + if (initrd_end > *memory_end_p) + { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + } +#endif + +#ifdef notdef + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +#endif +} + +void +abort(void) +{ +#ifdef CONFIG_XMON + extern void xmon(void *); + xmon(0); +#endif + machine_restart(NULL); +} diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 2c866eed8..6607e00bd 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -12,6 +12,7 @@ * */ +#include <linux/config.h> #include <linux/sys.h> #include <asm/unistd.h> #include <asm/errno.h> @@ -19,6 +20,7 @@ #include "ppc_asm.tmpl" #include "ppc_defs.h" +#ifndef CONFIG_8xx /* This instruction is not implemented on the PPC 601 or 603 */ #define tlbia \ li r4,128; \ @@ -27,7 +29,7 @@ 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b - +#endif .text /* @@ -323,6 +325,18 @@ _GLOBAL(_get_SP) mr r3,r1 /* Close enough */ blr +_GLOBAL(_get_THRM1) + mfspr r3,THRM1 + blr + +_GLOBAL(_set_THRM1) + mtspr THRM1,r3 + blr + +_GLOBAL(_get_L2CR) + mfspr r3,L2CR + blr + _GLOBAL(_get_PVR) mfspr r3,PVR blr @@ -348,33 +362,6 @@ cvt_df: stfs 0,0(r4) blr - -_GLOBAL(lock_dcache) - mfspr r3,PVR /* nop on 601 */ - rlwinm r3,r3,16,16,31 - cmpwi 0,r3,1 - beqlr- - mfspr r3,HID0 - ori r3,r3,HID0_DLOCK - mtspr HID0,r3 - sync - isync - blr - -_GLOBAL(unlock_dcache) - mfspr r3,PVR /* nop on 601 */ - rlwinm r3,r3,16,16,31 - cmpwi 0,r3,1 - beqlr- - mfspr r3,HID0 - li r4,HID0_DLOCK - andc r3,r3,r4 - mtspr HID0,r3 - sync - isync - blr - - /* * Create a kernel thread * __kernel_thread(flags, fn, arg) @@ -386,6 +373,16 @@ _GLOBAL(__kernel_thread) bnelr /* return if parent */ mtlr r4 /* fn addr in lr */ mr r3,r5 /* load arg and call fn */ +#if 0/*def __SMP__*/ + /* drop scheduler_lock since schedule() called us */ + lis r4,scheduler_lock@ha + li r5,0 + stw r5,scheduler_lock@l+4(r4) /* owner_pc */ + stw r5,scheduler_lock@l+8(r4) /* owner_cpu */ + stw r5,scheduler_lock@l(r4) + sync + isync +#endif /* __SMP__ */ blrl li r0,__NR_exit /* exit after child exits */ li r3,0 @@ -413,7 +410,9 @@ SYSCALL(execve) SYSCALL(open) SYSCALL(close) SYSCALL(waitpid) +SYSCALL(fork) SYSCALL(delete_module) +SYSCALL(_exit) /* Why isn't this a) automatic, b) written in 'C'? */ @@ -593,5 +592,7 @@ sys_call_table: .long sys_setresgid .long sys_getresgid /* 170 */ .long sys_prctl - .space (NR_syscalls-171)*4 + .long sys_xstat + .long sys_xmknod + .space (NR_syscalls-173)*4 diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c index 8db1763db..9de056504 100644 --- a/arch/ppc/kernel/mk_defs.c +++ b/arch/ppc/kernel/mk_defs.c @@ -19,6 +19,7 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> +#include <asm/io.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> @@ -29,9 +30,11 @@ void main(void) { + DEFINE(KERNELBASE, KERNELBASE); DEFINE(STATE, offsetof(struct task_struct, state)); DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task)); DEFINE(COUNTER, offsetof(struct task_struct, counter)); + DEFINE(PROCESSOR, offsetof(struct task_struct, processor)); DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); DEFINE(TSS, offsetof(struct task_struct, tss)); DEFINE(MM, offsetof(struct task_struct, mm)); @@ -45,6 +48,7 @@ main(void) DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0])); DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr)); + DEFINE(TSS_SMP_FORK_RET, offsetof(struct thread_struct, smp_fork_ret)); /* Interrupt register frame */ DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 7e39487d8..186165a46 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,24 +1,27 @@ /* - * $Id: pci.c,v 1.18 1997/10/29 03:35:07 cort Exp $ + * $Id: pci.c,v 1.24 1998/02/19 21:29:49 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> #include <linux/config.h> #include <linux/pci.h> +#include <linux/openpic.h> #include <asm/processor.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/pci-bridge.h> +#include <asm/irq.h> -#if !defined(CONFIG_MACH_SPECIFIC) +#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC) unsigned long isa_io_base; +#endif /* CONFIG_MACH_SPECIFIC || CONFIG_PMAC */ +#if !defined(CONFIG_MACH_SPECIFIC) unsigned long isa_mem_base; unsigned long pci_dram_offset; #endif /* CONFIG_MACH_SPECIFIC */ @@ -121,49 +124,6 @@ int pcibios_present(void) return 1; } -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -/* - * Given the class, find the n'th instance of that device - * in the system. - */ -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - - __initfunc(unsigned long pcibios_init(unsigned long mem_start,unsigned long mem_end)) { @@ -203,6 +163,70 @@ __initfunc(void __initfunc(unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) + { + extern route_pci_interrupts(void); + struct pci_dev *dev; + extern struct bridge_data **bridges; + extern unsigned char *Motherboard_map; + extern unsigned char *Motherboard_routes; + + /* + * FIXME: This is broken: We should not assign IRQ's to IRQless + * devices (look at PCI_INTERRUPT_PIN) and we also should + * honor the existence of multi-function devices where + * different functions have different interrupt pins. [mj] + */ + switch (_machine ) + { + case _MACH_prep: + route_pci_interrupts(); + for(dev=pci_devices; dev; dev=dev->next) + { + unsigned char d = PCI_SLOT(dev->devfn); + dev->irq = Motherboard_routes[Motherboard_map[d]]; + } + break; + case _MACH_chrp: + /* PCI interrupts are controlled by the OpenPIC */ + for(dev=pci_devices; dev; dev=dev->next) + if (dev->irq) + dev->irq = openpic_to_irq(dev->irq); + break; + case _MACH_Pmac: + for(dev=pci_devices; dev; dev=dev->next) + { + /* + * Open Firmware often doesn't initialize the, + * PCI_INTERRUPT_LINE config register properly, so we + * should find the device node and se if it has an + * AAPL,interrupts property. + */ + struct bridge_data *bp = bridges[dev->bus->number]; + struct device_node *node; + unsigned int *reg; + unsigned char pin; + + if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || + !pin) + continue; /* No interrupt generated -> no fixup */ + for (node = bp->node->child; node != 0; + node = node->sibling) { + reg = (unsigned int *) get_property(node, "reg", 0); + if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + dev->irq = node->intrs[0].line; + break; + } + } + break; + } return mem_start; } + +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index d0144a567..a71aede12 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -12,27 +12,18 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/bios32.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/prom.h> #include <asm/pci-bridge.h> -struct bridge_data { - volatile unsigned int *cfg_addr; - volatile unsigned char *cfg_data; - void *io_base; - int bus_number; - int max_bus; - struct bridge_data *next; - struct device_node *node; -}; - -static struct bridge_data **bridges, *bridge_list; +struct bridge_data **bridges, *bridge_list; static int max_bus; static void add_bridges(struct device_node *dev, unsigned long *mem_ptr); @@ -112,9 +103,11 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) int *bus_range; int len; struct bridge_data *bp; + struct reg_property *addr; for (; dev != NULL; dev = dev->next) { - if (dev->n_addrs < 1) { + addr = (struct reg_property *) get_property(dev, "reg", &len); + if (addr == NULL || len < sizeof(*addr)) { printk(KERN_WARNING "Can't use %s: no address\n", dev->full_name); continue; @@ -130,15 +123,18 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) else printk(KERN_INFO "PCI buses %d..%d", bus_range[0], bus_range[1]); - printk(" controlled by %s at %x\n", - dev->name, dev->addrs[0].address); + printk(" controlled by %s at %x\n", dev->name, addr->address); bp = (struct bridge_data *) *mem_ptr; *mem_ptr += sizeof(struct bridge_data); bp->cfg_addr = (volatile unsigned int *) - ioremap(dev->addrs[0].address + 0x800000, 0x1000); + ioremap(addr->address + 0x800000, 0x1000); bp->cfg_data = (volatile unsigned char *) - ioremap(dev->addrs[0].address + 0xc00000, 0x1000); - bp->io_base = (void *) ioremap(dev->addrs[0].address, 0x10000); + ioremap(addr->address + 0xc00000, 0x1000); + bp->io_base = (void *) ioremap(addr->address, 0x10000); +#ifdef CONFIG_PMAC + if (isa_io_base == 0) + isa_io_base = (unsigned long) bp->io_base; +#endif bp->bus_number = bus_range[0]; bp->max_bus = bus_range[1]; bp->next = bridge_list; @@ -180,7 +176,7 @@ int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, } int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val) + unsigned char offset, unsigned char *val) { struct bridge_data *bp; @@ -198,32 +194,11 @@ int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, } udelay(2); *val = in_8(bp->cfg_data + (offset & 3)); - - if (offset == PCI_INTERRUPT_LINE) { - /* - * Open Firmware often doesn't initialize this - * register properly, so we find the node and see - * if it has an AAPL,interrupts property. - */ - struct device_node *node; - unsigned int *reg; - - for (node = bp->node->child; node != 0; node = node->sibling) { - reg = (unsigned int *) get_property(node, "reg", 0); - if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn) - continue; - /* this is the node, see if it has interrupts */ - if (node->n_intrs > 0) - *val = node->intrs[0]; - break; - } - } - return PCIBIOS_SUCCESSFUL; } int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val) + unsigned char offset, unsigned short *val) { struct bridge_data *bp; @@ -245,7 +220,7 @@ int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val) + unsigned char offset, unsigned int *val) { struct bridge_data *bp; @@ -267,7 +242,7 @@ int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val) + unsigned char offset, unsigned char val) { struct bridge_data *bp; @@ -288,7 +263,7 @@ int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val) + unsigned char offset, unsigned short val) { struct bridge_data *bp; @@ -309,7 +284,7 @@ int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, } int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val) + unsigned char offset, unsigned int val) { struct bridge_data *bp; diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 4e37becd5..afcd4fd8b 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -47,23 +47,18 @@ #include <asm/ide.h> #include <asm/pci-bridge.h> #include <asm/adb.h> +#include <asm/mediabay.h> +#include <asm/ohare.h> +#include <asm/mediabay.h> #include "time.h" -/* - * A magic address and value to put into it on machines with the - * "ohare" I/O controller. This makes the IDE CD work on Starmaxes. - * Contributed by Harry Eaton. - */ -#define OMAGICPLACE ((volatile unsigned *) 0xf3000038) -#define OMAGICCONT 0xbeff7a - extern int root_mountflags; unsigned char drive_info; #define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ -static void gc_init(const char *, int); +static void ohare_init(void); void pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) @@ -91,27 +86,44 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) loops_per_sec = 50000000; } + /* this area has the CPU identification register + and some registers used by smp boards */ + ioremap(0xf8000000, 0x1000); + *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); - gc_init("gc", 0); - gc_init("ohare", 1); -#ifdef CONFIG_ABSTRACT_CONSOLE + ohare_init(); + +#ifdef CONFIG_FB /* Frame buffer device based console */ conswitchp = &fb_con; #endif } -static void gc_init(const char *name, int isohare) +static volatile u32 *feature_addr; + +static void ohare_init(void) { struct device_node *np; - for (np = find_devices(name); np != NULL; np = np->next) { - if (np->n_addrs > 0) - ioremap(np->addrs[0].address, np->addrs[0].size); - if (isohare) { - printk(KERN_INFO "Twiddling the magic ohare bits\n"); - out_le32(OMAGICPLACE, OMAGICCONT); - } + np = find_devices("ohare"); + if (np == 0) + return; + if (np->next != 0) + printk(KERN_WARNING "only using the first ohare\n"); + if (np->n_addrs == 0) { + printk(KERN_ERR "No addresses for %s\n", np->full_name); + return; + } + feature_addr = (volatile u32 *) + ioremap(np->addrs[0].address + OHARE_FEATURE_REG, 4); + + if (find_devices("via-pmu") == 0) { + printk(KERN_INFO "Twiddling the magic ohare bits\n"); + out_le32(feature_addr, STARMAX_FEATURES); + } else { + out_le32(feature_addr, in_le32(feature_addr) | PBOOK_FEATURES); + printk(KERN_DEBUG "feature reg = %x\n", in_le32(feature_addr)); } } @@ -125,10 +137,15 @@ kdev_t boot_dev; unsigned long powermac_init(unsigned long mem_start, unsigned long mem_end) { - pmac_nvram_init(); +#ifdef CONFIG_KGDB + extern void zs_kgdb_hook(int tty_num); + zs_kgdb_hook(0); +#endif adb_init(); + pmac_nvram_init(); if (_machine == _MACH_Pmac) { pmac_read_rtc_time(); + media_bay_init(); } #ifdef CONFIG_PMAC_CONSOLE pmac_find_display(); @@ -175,7 +192,7 @@ note_scsi_host(struct device_node *node, void *host) #include "../../../drivers/scsi/sd.h" #include "../../../drivers/scsi/hosts.h" -int sd_find_target(void *host, int tgt) +kdev_t sd_find_target(void *host, int tgt) { Scsi_Disk *dp; int i; @@ -190,7 +207,7 @@ int sd_find_target(void *host, int tgt) void find_boot_device(void) { - int dev; + kdev_t dev; if (kdev_t_to_nr(ROOT_DEV) != 0) return; @@ -201,7 +218,7 @@ void find_boot_device(void) dev = sd_find_target(boot_host, boot_target); if (dev == 0) return; - boot_dev = to_kdev_t(dev + boot_part); + boot_dev = MKDEV(MAJOR(dev), MINOR(dev) + boot_part); #endif /* XXX should cope with booting from IDE also */ } @@ -221,39 +238,92 @@ void note_bootable_part(kdev_t dev, int part) } } +#ifdef CONFIG_BLK_DEV_IDE +int pmac_ide_ports_known; +ide_ioreg_t pmac_ide_regbase[MAX_HWIFS]; +int pmac_ide_irq[MAX_HWIFS]; + void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) { - struct device_node *np; int i; - static struct device_node *atas; - static int atas_valid; *p = 0; - *irq = 0; - if (!atas_valid) { - atas = find_devices("ATA"); - atas_valid = 1; - } - for (i = (int)base, np = atas; i > 0 && np != NULL; --i, np = np->next) - ; - if (np == NULL) + if (base == 0) return; - if (np->n_addrs == 0) { - printk("ide: no addresses for device %s\n", np->full_name); + if (base == mb_cd_base && !check_media_bay(MB_CD)) { + mb_cd_index = -1; return; } - if (np->n_intrs == 0) { - printk("ide: no intrs for device %s, using 13\n", - np->full_name); - *irq = 13; - } else { - *irq = np->intrs[0]; - } - base = (unsigned long) ioremap(np->addrs[0].address, 0x200); for (i = 0; i < 8; ++i) *p++ = base + i * 0x10; *p = base + 0x160; + if (irq != NULL) { + *irq = 0; + for (i = 0; i < MAX_HWIFS; ++i) { + if (base == pmac_ide_regbase[i]) { + *irq = pmac_ide_irq[i]; + break; + } + } + } +} + +void pmac_ide_probe(void) +{ + struct device_node *np; + int i; + struct device_node *atas; + struct device_node *p, **pp, *removables, **rp; + + pp = &atas; + rp = &removables; + p = find_devices("ATA"); + if (p == NULL) + p = find_devices("IDE"); + /* Move removable devices such as the media-bay CDROM + on the PB3400 to the end of the list. */ + for (; p != NULL; p = p->next) { + if (p->parent && p->parent->name + && strcasecmp(p->parent->name, "media-bay") == 0) { + *rp = p; + rp = &p->next; + } else { + *pp = p; + pp = &p->next; + } + } + *rp = NULL; + *pp = removables; + + for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) { + if (np->n_addrs == 0) { + printk(KERN_WARNING "ide: no address for device %s\n", + np->full_name); + continue; + } + pmac_ide_regbase[i] = (unsigned long) + ioremap(np->addrs[0].address, 0x200); + if (np->n_intrs == 0) { + printk("ide: no intrs for device %s, using 13\n", + np->full_name); + pmac_ide_irq[i] = 13; + } else { + pmac_ide_irq[i] = np->intrs[0].line; + } + + if (np->parent && np->parent->name + && strcasecmp(np->parent->name, "media-bay") == 0) { + mb_cd_index = i; + mb_cd_base = pmac_ide_regbase[i]; + mb_cd_irq = pmac_ide_irq[i]; + } + + ++i; + } + + pmac_ide_ports_known = 1; } +#endif /* CONFIG_BLK_DEV_IDE */ int pmac_get_cpuinfo(char *buffer) diff --git a/arch/ppc/kernel/pmac_support.c b/arch/ppc/kernel/pmac_support.c index 17226f8ff..4ceaa0dc9 100644 --- a/arch/ppc/kernel/pmac_support.c +++ b/arch/ppc/kernel/pmac_support.c @@ -7,8 +7,11 @@ #include <linux/nvram.h> #include <asm/ptrace.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/system.h> #include <asm/prom.h> +#include <asm/adb.h> +#include <asm/pmu.h> /* * Read and write the non-volatile RAM on PowerMacs and CHRP machines. @@ -32,8 +35,7 @@ void pmac_nvram_init(void) } nvram_naddrs = dp->n_addrs; if (_machine == _MACH_chrp && nvram_naddrs == 1) { - /* XXX for now */ - nvram_data = ioremap(0xf70e0000, NVRAM_SIZE); + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_mult = 1; } else if (nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); @@ -41,6 +43,8 @@ void pmac_nvram_init(void) } else if (nvram_naddrs == 2) { nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + } else if (nvram_naddrs == 0 && adb_hardware == ADB_VIAPMU) { + nvram_naddrs = -1; } else { printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", nvram_naddrs); @@ -49,7 +53,16 @@ void pmac_nvram_init(void) unsigned char nvram_read_byte(int addr) { + struct adb_request req; + switch (nvram_naddrs) { + case -1: + if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + break; + while (!req.complete) + pmu_poll(); + return req.reply[1]; case 1: return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; case 2: @@ -62,7 +75,16 @@ unsigned char nvram_read_byte(int addr) void nvram_write_byte(unsigned char val, int addr) { + struct adb_request req; + switch (nvram_naddrs) { + case -1: + if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + break; + while (!req.complete) + pmu_poll(); + break; case 1: nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; break; diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c index 1a31ffa9a..3c976506e 100644 --- a/arch/ppc/kernel/pmac_time.c +++ b/arch/ppc/kernel/pmac_time.c @@ -15,8 +15,11 @@ #include <linux/mm.h> #include <asm/adb.h> #include <asm/cuda.h> +#include <asm/pmu.h> #include <asm/prom.h> #include <asm/system.h> +#include <asm/io.h> +#include <asm/pgtable.h> #include "time.h" @@ -44,7 +47,11 @@ /* Bits in IFR and IER */ #define T1_INT 0x40 /* Timer 1 interrupt */ -static int via_calibrate_decr(void) +/* + * Calibrate the decrementer register using VIA timer 1. + * This is used both on powermacs and CHRP machines. + */ +int via_calibrate_decr(void) { struct device_node *vias; volatile unsigned char *via; @@ -54,9 +61,12 @@ static int via_calibrate_decr(void) vias = find_devices("via-cuda"); if (vias == 0) vias = find_devices("via-pmu"); + if (vias == 0) + vias = find_devices("via"); if (vias == 0 || vias->n_addrs == 0) return 0; - via = (volatile unsigned char *) vias->addrs[0].address; + via = (volatile unsigned char *) + ioremap(vias->addrs[0].address, vias->addrs[0].size); /* set timer 1 for continuous interrupts */ out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); @@ -123,14 +133,30 @@ pmac_get_rtc_time(void) struct adb_request req; /* Get the time from the RTC */ - cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME); - while (!req.complete) - cuda_poll(); - if (req.reply_len != 7) - printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", - req.reply_len); - return (req.reply[3] << 24) + (req.reply[4] << 16) - + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; + switch (adb_hardware) { + case ADB_VIACUDA: + if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0) + return 0; + while (!req.complete) + cuda_poll(); + if (req.reply_len != 7) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + return (req.reply[3] << 24) + (req.reply[4] << 16) + + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; + case ADB_VIAPMU: + if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) + return 0; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 5) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + return (req.reply[1] << 24) + (req.reply[2] << 16) + + (req.reply[3] << 8) + req.reply[4] - RTC_OFFSET; + default: + return 0; + } } int pmac_set_rtc_time(unsigned long nowtime) diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c new file mode 100644 index 000000000..b7eab0fa1 --- /dev/null +++ b/arch/ppc/kernel/ppc-stub.c @@ -0,0 +1,705 @@ +/* $Id: ppc-stub.c,v 1.2 1998/04/11 17:29:03 geert Exp $ + * ppc-stub.c: KGDB support for the Linux kernel. + * + * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC + * some stuff borrowed from Paul Mackerras' xmon + * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) + * + * Modifications to run under Linux + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * This file originally came from the gdb sources, and the + * copyright notices have been retained below. + */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/system.h> +#include <asm/signal.h> +#include <asm/system.h> +#include <asm/kgdb.h> +#include <asm/pgtable.h> +#include <asm/ptrace.h> + +void breakinst(void); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active = 0; +static u_int fault_jmp_buf[100]; +static int kdebug; + +static const char hexchars[]="0123456789abcdef"; + +/* Place where we save old trap entries for restoration - sparc*/ +/* struct tt_entry kgdb_savettable[256]; */ +/* typedef void (*trapfunc_t)(void); */ + +#if 0 +/* Install an exception handler for kgdb */ +static void exceptionHandler(int tnum, unsigned int *tfunc) +{ + /* We are dorking with a live trap table, all irqs off */ +} +#endif + +int +kgdb_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} +void +kgdb_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + } else { + /* error condition */ + } + debugger_fault_handler = 0; + *buf = 0; + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int i; + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + for (i=0; i<count; i++) { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + *mem++ = ch; + } + flush_icache_range((int)mem, (int)mem+count); + } else { + /* error condition */ + } + debugger_fault_handler = 0; + return mem; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + } else { + /* error condition */ + } + debugger_fault_handler = 0; + + return (numChars); +} + +/* scan for the sequence $<data>#<checksum> */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $<packet info>#<checksum>. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +static void kgdb_flush_cache_all(void) +{ + flush_instruction_cache(); +} + +static inline int get_msr() +{ + int msr; + asm volatile("mfmsr %0" : "=r" (msr):); + return msr; +} + +static inline void set_msr(int msr) +{ + asm volatile("mfmsr %0" : : "r" (msr)); +} + +/* Set up exception handlers for tracing and breakpoints + * [could be called kgdb_init()] + */ +void set_debug_traps(void) +{ +#if 0 + unsigned char c; + + save_and_cli(flags); + + /* In case GDB is started before us, ack any packets (presumably + * "$?#xx") sitting there. + * + * I've found this code causes more problems than it solves, + * so that's why it's commented out. GDB seems to work fine + * now starting either before or after the kernel -bwb + */ + + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ +#endif + debugger = kgdb; + debugger_bpt = kgdb_bpt; + debugger_sstep = kgdb_sstep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; + + kgdb_interruptible(1); + initialized = 1; +} + +static void kgdb_fault_handler(struct pt_regs *regs) +{ + kgdb_longjmp((long*)fault_jmp_buf, 1); +} + +int kgdb_bpt(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +int kgdb_sstep(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +void kgdb(struct pt_regs *regs) +{ + handle_exception(regs); +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support iabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support dabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +/* Convert the SPARC hardware trap type code to a unix signal number. */ +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGILL }, /* reserved instruction or sumpin' */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGINT }, /* watch */ + { 0xe00, SIGFPE }, /* fp assist */ + { 0, 0} /* Must be last */ +}; + +static int computeSignal(unsigned int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * This function does all command processing for interfacing to gdb. + */ +static void +handle_exception (struct pt_regs *regs) +{ + int sigval; + int addr; + int length; + char *ptr; + unsigned int msr; + + if (debugger_fault_handler) { + debugger_fault_handler(regs); + panic("kgdb longjump failed!\n"); + } + if (kgdb_active) { + printk("interrupt while in kgdb, returning\n"); + return; + } + kgdb_active = 1; + + printk("kgdb: entering handle_exception; trap [0x%x]\n", + (unsigned int)regs->trap); + + kgdb_interruptible(0); + lock_kernel(); + msr = get_msr(); + set_msr(msr & ~MSR_EE); /* disable interrupts */ + + if (regs->nip == (unsigned long)breakinst) { + /* Skip over breakpoint trap insn */ + regs->nip += 4; + } + + /* reply to host that an exception has occurred */ + sigval = computeSignal(regs->trap); + ptr = remcomOutBuffer; + + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + *ptr++ = 0; + + putpacket(remcomOutBuffer); + + /* XXX We may want to add some features dealing with poking the + * XXX page tables, ... (look at sparc-stub.c for more info) + * XXX also required hacking to the gdb sources directly... + */ + + while (1) { + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; +#if 0 + case 'q': /* this screws up gdb for some reason...*/ + { + extern long _start, sdata, __bss_start; + + ptr = &remcomInBuffer[1]; + if (strncmp(ptr, "Offsets", 7) != 0) + break; + + ptr = remcomOutBuffer; + sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", + &_start, &sdata, &__bss_start); + break; + } +#endif + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; + + case 'g': /* return the value of the CPU registers. + * some of them are non-PowerPC names :( + * they are stored in gdb like: + * struct { + * u32 gpr[32]; + * f64 fpr[32]; + * u32 pc, ps, cnd, lr; (ps=msr) + * u32 cnt, xer, mq; + * } + */ + { + int i; + ptr = remcomOutBuffer; + /* General Purpose Regs */ + ptr = mem2hex((char *)regs, ptr, 32 * 4); + /* Floating Point Regs - FIXME */ + /*ptr = mem2hex((char *), ptr, 32 * 8);*/ + for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ + ptr[i] = '0'; + } + ptr += 32*8*2; + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = mem2hex((char *)®s->nip, ptr, 4); + ptr = mem2hex((char *)®s->msr, ptr, 4); + ptr = mem2hex((char *)®s->ccr, ptr, 4); + ptr = mem2hex((char *)®s->link, ptr, 4); + ptr = mem2hex((char *)®s->ctr, ptr, 4); + ptr = mem2hex((char *)®s->xer, ptr, 4); + } + break; + + case 'G': /* set the value of the CPU registers */ + { + ptr = &remcomInBuffer[1]; + + /* + * If the stack pointer has moved, you should pray. + * (cause only god can help you). + */ + + /* General Purpose Regs */ + hex2mem(ptr, (char *)regs, 32 * 4); + + /* Floating Point Regs - FIXME?? */ + /*ptr = hex2mem(ptr, ??, 32 * 8);*/ + ptr += 32*8*2; + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = hex2mem(ptr, (char *)®s->nip, 4); + ptr = hex2mem(ptr, (char *)®s->msr, 4); + ptr = hex2mem(ptr, (char *)®s->ccr, 4); + ptr = hex2mem(ptr, (char *)®s->link, 4); + ptr = hex2mem(ptr, (char *)®s->ctr, 4); + ptr = hex2mem(ptr, (char *)®s->xer, 4); + + strcpy(remcomOutBuffer,"OK"); + } + break; + case 'H': + /* dont do anything, yet, just acknowledge */ + hexToInt(&ptr, &addr); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, remcomOutBuffer,length)) + break; + strcpy (remcomOutBuffer, "E03"); + } else { + strcpy(remcomOutBuffer,"E01"); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length)) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E03"); + } + } else { + strcpy(remcomOutBuffer, "E02"); + } + break; + + + case 'k': /* kill the program, actually just continue */ + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + regs->nip = addr; + } + +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + set_msr(msr); + kgdb_interruptible(1); + unlock_kernel(); + kgdb_active = 0; + return; + + case 's': + kgdb_flush_cache_all(); + regs->msr |= MSR_SE; + set_msr(msr | MSR_SE); + unlock_kernel(); + kgdb_active = 0; + return; + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + } /* switch */ + if (remcomOutBuffer[0] && kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1) */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint(void) +{ + if (!initialized) { + printk("breakpoint() called b4 kgdb init\n"); + return; + } + + asm(" .globl breakinst + breakinst: trap + "); +} diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index 386bfe3ac..e1803a269 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -1,5 +1,5 @@ /* - * $Id: ppc_htab.c,v 1.16 1997/11/17 18:25:04 cort Exp $ + * $Id: ppc_htab.c,v 1.17 1998/03/14 07:52:49 cort Exp $ * * PowerPC hash table management proc entry. Will show information * about the current hash table and will allow changes to it. @@ -12,6 +12,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/proc_fs.h> @@ -88,6 +89,7 @@ struct inode_operations proc_ppc_htab_inode_operations = { #define PMC1 953 #define PMC2 954 +#ifndef CONFIG_8xx char *pmc1_lookup(unsigned long mmcr0) { switch ( mmcr0 & (0x7f<<7) ) @@ -123,7 +125,7 @@ char *pmc2_lookup(unsigned long mmcr0) return "unknown"; } } - +#endif /* CONFIG_8xx */ /* * print some useful info about the hash table. This function @@ -133,6 +135,7 @@ char *pmc2_lookup(unsigned long mmcr0) static ssize_t ppc_htab_read(struct file * file, char * buf, size_t count, loff_t *ppos) { +#ifndef CONFIG_8xx unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0; int n = 0, valid; unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0; @@ -249,6 +252,9 @@ return_string: copy_to_user(buf, buffer + *ppos, n); *ppos += n; return n; +#else /* CONFIG_8xx */ + return 0; +#endif /* CONFIG_8xx */ } /* @@ -257,6 +263,7 @@ return_string: static ssize_t ppc_htab_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) { +#ifndef CONFIG_8xx unsigned long tmp; if ( current->uid != 0 ) return -EACCES; @@ -493,6 +500,9 @@ static ssize_t ppc_htab_write(struct file * file, const char * buffer, reset_SDR1(); #endif return count; +#else /* CONFIG_8xx */ + return 0; +#endif /* CONFIG_8xx */ } @@ -512,4 +522,3 @@ ppc_htab_lseek(struct file * file, loff_t offset, int orig) return(-EINVAL); } } - diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 6ec6258ab..29df507d3 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -4,9 +4,7 @@ #include <linux/elfcore.h> #include <linux/sched.h> #include <linux/string.h> -#include <linux/bios32.h> #include <linux/interrupt.h> -#include <linux/pci.h> #include <asm/semaphore.h> #include <asm/processor.h> @@ -18,9 +16,11 @@ #include <asm/pgtable.h> #include <asm/adb.h> #include <asm/cuda.h> +#include <asm/pmu.h> #include <asm/prom.h> #include <asm/system.h> #include <asm/pci-bridge.h> +#include <asm/irq.h> extern void transfer_to_handler(void); extern void int_return(void); @@ -49,9 +49,13 @@ EXPORT_SYMBOL(sys_sigreturn); EXPORT_SYMBOL(lost_interrupts); EXPORT_SYMBOL(do_lost_interrupts); EXPORT_SYMBOL(__ppc_bh_counter); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); -#if !defined(CONFIG_MACH_SPECIFIC) +#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC) EXPORT_SYMBOL(isa_io_base); +#endif +#if !defined(CONFIG_MACH_SPECIFIC) EXPORT_SYMBOL(pci_dram_offset); #endif @@ -114,11 +118,15 @@ EXPORT_SYMBOL(outw); EXPORT_SYMBOL(outl); EXPORT_SYMBOL(outsl);*/ +EXPORT_SYMBOL(_insb); +EXPORT_SYMBOL(_outsb); EXPORT_SYMBOL(_insw); EXPORT_SYMBOL(_outsw); EXPORT_SYMBOL(_insl); EXPORT_SYMBOL(_outsl); EXPORT_SYMBOL(ioremap); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(start_thread); @@ -140,6 +148,10 @@ EXPORT_SYMBOL(adb_autopoll); EXPORT_SYMBOL(adb_register); EXPORT_SYMBOL(cuda_request); EXPORT_SYMBOL(cuda_send_request); +EXPORT_SYMBOL(cuda_poll); +EXPORT_SYMBOL(pmu_request); +EXPORT_SYMBOL(pmu_send_request); +EXPORT_SYMBOL(pmu_poll); EXPORT_SYMBOL(abort); EXPORT_SYMBOL(find_devices); EXPORT_SYMBOL(find_type_devices); @@ -148,7 +160,3 @@ EXPORT_SYMBOL(get_property); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); EXPORT_SYMBOL(note_scsi_host); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index 193ded4df..999406200 100644 --- a/arch/ppc/kernel/prep_pci.c +++ b/arch/ppc/kernel/prep_pci.c @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.12 1997/10/29 03:35:08 cort Exp $ + * $Id: prep_pci.c,v 1.16 1998/02/23 02:47:32 davem Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -8,7 +8,6 @@ */ #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/kernel.h> #include <linux/init.h> @@ -271,6 +270,12 @@ static char Nobis_pci_IRQ_routes[] = { #define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */ #define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */ +/* + * FIXME: This code incorrectly assumes there's only bus #0, breaking all + * PCI-to-PCI bridges. Also multi-function devices are not supported + * at all. [mj] + */ + int prep_pcibios_read_config_dword (unsigned char bus, unsigned char dev, unsigned char offset, unsigned int *val) @@ -319,16 +324,6 @@ prep_pcibios_read_config_byte (unsigned char bus, unsigned char _val; volatile unsigned char *ptr; dev >>= 3; - /* Note: the configuration registers don't always have this right! */ - if (offset == PCI_INTERRUPT_LINE) - { - *val = Motherboard_routes[Motherboard_map[dev]]; -/*printk("dev %d map %d route %d on board %d\n", - dev,Motherboard_map[dev], - Motherboard_routes[Motherboard_map[dev]], - *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/ - return PCIBIOS_SUCCESSFUL; - } if ((bus != 0) || (dev > MAX_DEVNR)) { *(unsigned long *)val = (unsigned long) 0xFFFFFFFF; @@ -406,7 +401,7 @@ __initfunc(unsigned long route_pci_interrupts(void)) int i; if ( _prep_type == _PREP_Motorola) - { + { switch (inb(0x800) & 0xF0) { case 0x10: /* MVME16xx */ @@ -430,7 +425,6 @@ __initfunc(unsigned long route_pci_interrupts(void)) break; case 0x40: /* PowerStack */ default: /* Can't hurt, can it? */ - Motherboard_map_name = "Blackhawk (Powerstack)"; Motherboard_map = Blackhawk_pci_IRQ_map; Motherboard_routes = Blackhawk_pci_IRQ_routes; @@ -474,3 +468,4 @@ __initfunc(unsigned long route_pci_interrupts(void)) *ibc_pcicon |= 0x20; return 0; } + diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index 0aee7cff4..5234435ca 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -80,8 +80,7 @@ prep_get_cpuinfo(char *buffer) { extern char *Motherboard_map_name; extern RESIDUAL res; - int i; - int len; + int len, i; #ifdef __SMP__ #define CD(X) (cpu_data[n].X) @@ -93,7 +92,7 @@ prep_get_cpuinfo(char *buffer) if ( res.ResidualLength == 0 ) return len; - + /* print info about SIMMs */ len += sprintf(buffer+len,"simms\t\t: "); for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ ) @@ -106,6 +105,7 @@ prep_get_cpuinfo(char *buffer) } len += sprintf(buffer+len,"\n"); +#if 0 /* TLB */ len += sprintf(buffer+len,"tlb\t\t:"); switch(res.VitalProductData.TLBAttrib) @@ -123,7 +123,6 @@ prep_get_cpuinfo(char *buffer) len += sprintf(buffer+len," not present\n"); break; } - /* L1 */ len += sprintf(buffer+len,"l1\t\t: "); switch(res.VitalProductData.CacheAttrib) @@ -144,6 +143,7 @@ prep_get_cpuinfo(char *buffer) len += sprintf(buffer+len,"not present\n"); break; } +#endif /* L2 */ if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */ @@ -201,7 +201,11 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) } } #endif - + /* make the serial port the console */ + /* strcat(cmd_line,"console=ttyS0,9600n8"); */ + /* use the normal console but send output to the serial port, too */ + /*strcat(cmd_line,"console=tty0 console=ttyS0,9600n8");*/ + sprintf(cmd_line,"%s console=tty0 console=ttyS0,9600n8", cmd_line); printk("Boot arguments: %s\n", cmd_line); #ifdef CONFIG_CS4232 @@ -256,9 +260,5 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) #ifdef CONFIG_VGA_CONSOLE conswitchp = &vga_con; #endif -#ifdef CONFIG_FB - /* Frame buffer device based console */ - conswitchp = &fb_con; -#endif #endif } diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c index 4c3a91f91..3536eab43 100644 --- a/arch/ppc/kernel/prep_time.c +++ b/arch/ppc/kernel/prep_time.c @@ -220,7 +220,7 @@ static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs) #ifdef CONFIG_HEARTBEAT /* use hard disk LED as a heartbeat instead -- much more useful for debugging -- Cort */ - switch(kstat.interrupts[0] % 101) + switch(kstat_irqs(0) % 101) { /* act like an actual heart beat -- ie thump-thump-pause... */ case 0: diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index 7ffaf58c0..1c993bdc1 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -1,4 +1,3 @@ - /* * linux/arch/ppc/kernel/process.c * @@ -77,8 +76,13 @@ struct task_struct *current_set[NR_CPUS] = {&init_task, }; int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if (last_task_used_math == current) giveup_fpu(); +#endif memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); return 1; } @@ -98,7 +102,7 @@ int check_stack(struct task_struct *tsk) printk("tss.magic bad: %08x\n", tsk->tss.magic); } #endif - + if ( !tsk ) printk("check_stack(): tsk bad tsk %p\n",tsk); @@ -157,17 +161,21 @@ switch_to(struct task_struct *prev, struct task_struct *new) #endif #ifdef SHOW_TASK_SWITCHES - printk("%s/%d -> %s/%d cpu %d\n", + printk("%s/%d -> %s/%d NIP %08lx cpu %d sfr %d lock %x\n", prev->comm,prev->pid, - new->comm,new->pid,new->processor); + new->comm,new->pid,new->tss.regs->nip,new->processor, + new->tss.smp_fork_ret,scheduler_lock.lock); #endif #ifdef __SMP__ - /* bad news if last_task_used_math changes processors right now -- Cort */ - if ( (last_task_used_math == new) && - (new->processor != new->last_processor) ) - panic("last_task_used_math switched processors"); + /* avoid complexity of lazy save/restore of fpu + * by just saving it every time we switch out -- Cort + */ + if ( prev->tss.regs->msr & MSR_FP ) + smp_giveup_fpu(prev); + /* be noisy about processor changes for debugging -- Cort */ - if ( new->last_processor != new->processor ) + if ( (new->last_processor != NO_PROC_ID) && + (new->last_processor != new->processor) ) printk("switch_to(): changing cpu's %d -> %d %s/%d\n", new->last_processor,new->processor, new->comm,new->pid); @@ -181,11 +189,6 @@ switch_to(struct task_struct *prev, struct task_struct *new) _enable_interrupts(s); } -asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) -{ - return 0; -} - void show_regs(struct pt_regs * regs) { int i; @@ -257,12 +260,11 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, ((unsigned long)p + sizeof(union task_union) - STACK_FRAME_OVERHEAD)) - 2; *childregs = *regs; - if ((childregs->msr & MSR_PR) == 0) childregs->gpr[2] = (unsigned long) p; /* `current' in new task */ childregs->gpr[3] = 0; /* Result from fork() */ p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; - p->tss.regs = childregs; + p->tss.regs = childregs; if (usp >= (unsigned long) regs) { /* Stack is in kernel space - must adjust */ childregs->gpr[1] = (unsigned long)(childregs + 1); @@ -271,18 +273,28 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, childregs->gpr[1] = usp; } p->tss.last_syscall = -1; - + /* * copy fpu info - assume lazy fpu switch now always * -- Cort */ +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if ( last_task_used_math == current ) giveup_fpu(); +#endif memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); p->tss.fpscr = current->tss.fpscr; childregs->msr &= ~MSR_FP; +#ifdef __SMP__ + if ( (p->pid != 0) || !(clone_flags & CLONE_PID) ) + p->tss.smp_fork_ret = 1; + p->last_processor = NO_PROC_ID; +#endif /* __SMP__ */ return 0; } @@ -337,20 +349,48 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) shove_aux_table(sp); } +asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + unsigned long clone_flags = p1; + int res; + lock_kernel(); + res = do_fork(clone_flags, regs->gpr[1], regs); + /* + * only parent returns here, child returns to either + * syscall_ret_1() or kernel_thread() + * -- Cort + */ +#ifdef __SMP__ + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* __SMP__ */ + unlock_kernel(); + return res; +} asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) { - int ret; + int res; lock_kernel(); - ret = do_fork(SIGCHLD, regs->gpr[1], regs); -#if 0/*def __SMP__*/ - if ( ret ) /* drop scheduler lock in child */ - scheduler_lock.lock = 0L; -#endif /* __SMP__ */ + res = do_fork(SIGCHLD, regs->gpr[1], regs); + /* only parent returns here */ +#ifdef __SMP__ + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* __SMP__ */ unlock_kernel(); - return ret; + return res; } asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, @@ -374,28 +414,6 @@ out: return error; } -asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, - struct pt_regs *regs) -{ - unsigned long clone_flags = p1; - int res; - - lock_kernel(); - res = do_fork(clone_flags, regs->gpr[1], regs); -#ifdef __SMP__ - /* When we clone the idle task we keep the same pid but - * the return value of 0 for both causes problems. - * -- Cort - */ - if ((current->pid == 0) && (current == &init_task)) - res = 1; - if ( 0 /*res*/ ) /* drop scheduler lock in child */ - scheduler_lock.lock = 0L; -#endif /* __SMP__ */ - unlock_kernel(); - return res; -} - void print_backtrace(unsigned long *sp) { diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index 121ffea73..0e656caa1 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -16,6 +16,8 @@ #include <asm/prom.h> #include <asm/page.h> #include <asm/processor.h> +#include <asm/irq.h> +#include <asm/io.h> /* * Properties whose value is longer than this get excluded from our @@ -50,6 +52,19 @@ struct pci_range { unsigned size_lo; }; +struct isa_reg_property { + unsigned space; + unsigned address; + unsigned size; +}; + +typedef unsigned long interpret_func(struct device_node *, unsigned long); +static interpret_func interpret_pci_props; +static interpret_func interpret_dbdma_props; +static interpret_func interpret_isa_props; +static interpret_func interpret_macio_props; +static interpret_func interpret_root_props; + char *prom_display_paths[FB_MAX] __initdata = { 0, }; unsigned int prom_num_displays = 0; @@ -60,19 +75,21 @@ extern char *klimit; char *bootpath = 0; char *bootdevice = 0; -unsigned int rtas_data = 0; -unsigned int rtas_entry = 0; +unsigned int rtas_data = 0; /* virtual pointer */ +unsigned int rtas_entry = 0; /* physical pointer */ +unsigned int rtas_size = 0; +char chunk[PAGE_SIZE*64]; static struct device_node *allnodes = 0; static void *call_prom(const char *service, int nargs, int nret, ...); -static void prom_print(const char *msg); + void prom_print(const char *msg); static void prom_exit(void); static unsigned long copy_device_tree(unsigned long, unsigned long); static unsigned long inspect_node(phandle, struct device_node *, unsigned long, unsigned long, struct device_node ***); static unsigned long finish_node(struct device_node *, unsigned long, - unsigned long); + interpret_func *); static unsigned long check_display(unsigned long); static int prom_next_node(phandle *); @@ -119,6 +136,18 @@ prom_exit() ; } +void +prom_enter(void) +{ + struct prom_args args; + unsigned long offset = reloc_offset(); + + args.service = RELOC("enter"); + args.nargs = 0; + args.nret = 0; + RELOC(prom)(&args); +} + static void * call_prom(const char *service, int nargs, int nret, ...) { @@ -140,7 +169,7 @@ call_prom(const char *service, int nargs, int nret, ...) return prom_args.args[nargs]; } -static void +void prom_print(const char *msg) { const char *p, *q; @@ -160,6 +189,11 @@ prom_print(const char *msg) } } + +#ifdef CONFIG_ALL_PPC +unsigned char OF_type[16], OF_model[16]; +#endif + /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. @@ -169,11 +203,14 @@ prom_init(int r3, int r4, prom_entry pp) { unsigned long mem; ihandle prom_rtas; - unsigned int rtas_size; unsigned long offset = reloc_offset(); int l; char *p, *d; + /* check if we're prep, return if we are */ + if ( *(unsigned long *)(0) == 0xdeadc0de ) + return; + /* First get a handle for the stdout device */ RELOC(prom) = pp; RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1, @@ -209,27 +246,57 @@ prom_init(int r3, int r4, prom_entry pp) prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); if (prom_rtas != (void *) -1) { - rtas_size = 0; + RELOC(rtas_size) = 0; call_prom(RELOC("getprop"), 4, 1, prom_rtas, - RELOC("rtas-size"), &rtas_size, sizeof(rtas_size)); + RELOC("rtas-size"), &RELOC(rtas_size), sizeof(rtas_size)); prom_print(RELOC("instantiating rtas...")); - if (rtas_size == 0) { + if (RELOC(rtas_size) == 0) { RELOC(rtas_data) = 0; } else { mem = (mem + 4095) & -4096; /* round to page bdry */ RELOC(rtas_data) = mem - KERNELBASE; - mem += rtas_size; + mem += RELOC(rtas_size); + } + prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas")); + RELOC(rtas_data) = ((ulong)chunk+4095)&-4096; + { + int i, nargs; + struct prom_args prom_args; + nargs = 3; + prom_args.service = RELOC("call-method"); + prom_args.nargs = nargs; + prom_args.nret = 2; + prom_args.args[0] = RELOC("instantiate-rtas"); + prom_args.args[1] = prom_rtas; + prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE); + RELOC(prom)(&prom_args); + if (prom_args.args[nargs] != 0) + i = 0; + else + i = (int)prom_args.args[nargs+1]; + RELOC(rtas_entry) = i; } - RELOC(rtas_entry) = (unsigned int) - call_prom(RELOC("instantiate-rtas"), 1, 1, - RELOC(rtas_data)); - if (RELOC(rtas_entry) == -1) + if ((RELOC(rtas_entry) == -1) || (RELOC(rtas_entry) == 0)) prom_print(RELOC(" failed\n")); else prom_print(RELOC(" done\n")); } RELOC(klimit) = (char *) (mem - offset); +#ifdef CONFIG_ALL_PPC + { + + ihandle prom_root; + + RELOC(prom_root) = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root), + RELOC("device_type"), RELOC(OF_type), + (void *) 16); + call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root), + RELOC("model"), RELOC(OF_model), + (void *) 16); + } +#endif } /* @@ -397,12 +464,18 @@ inspect_node(phandle node, struct device_node *dad, return mem_start; } +/* + * finish_device_tree is called once things are running normally + * (i.e. with text and data mapped to the address they were linked at). + * It traverses the device tree and fills in the name, type, + * {n_}addrs and {n_}intrs fields of each node. + */ void finish_device_tree(void) { unsigned long mem = (unsigned long) klimit; - mem = finish_node(allnodes, mem, 0UL); + mem = finish_node(allnodes, mem, NULL); printk(KERN_INFO "device tree used %lu bytes\n", mem - (unsigned long) allnodes); klimit = (char *) mem; @@ -410,23 +483,53 @@ finish_device_tree(void) static unsigned long finish_node(struct device_node *np, unsigned long mem_start, - unsigned long base_address) + interpret_func *ifunc) { - struct reg_property *rp; - struct pci_reg_property *pci_addrs; - struct address_range *adr; struct device_node *child; - int i, l; np->name = get_property(np, "name", 0); np->type = get_property(np, "device_type", 0); - /* get all the device addresses and interrupts */ - adr = (struct address_range *) mem_start; + /* get the device addresses and interrupts */ + if (ifunc != NULL) + mem_start = ifunc(np, mem_start); + + if (!strcmp(np->name, "device-tree")) + ifunc = interpret_root_props; + else if (np->type == 0) + ifunc = NULL; + else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) + ifunc = interpret_pci_props; + else if (!strcmp(np->type, "dbdma") + || (ifunc == interpret_dbdma_props + && (!strcmp(np->type, "escc") + || !strcmp(np->type, "media-bay")))) + ifunc = interpret_dbdma_props; + else if (!strcmp(np->type, "mac-io")) + ifunc = interpret_macio_props; + else if (!strcmp(np->type, "isa")) + ifunc = interpret_isa_props; + else + ifunc = NULL; + + for (child = np->child; child != NULL; child = child->sibling) + mem_start = finish_node(child, mem_start, ifunc); + + return mem_start; +} + +static unsigned long +interpret_pci_props(struct device_node *np, unsigned long mem_start) +{ + struct address_range *adr; + struct pci_reg_property *pci_addrs; + int i, l, *ip; + pci_addrs = (struct pci_reg_property *) get_property(np, "assigned-addresses", &l); - i = 0; - if (pci_addrs != 0) { + if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; while ((l -= sizeof(struct pci_reg_property)) >= 0) { /* XXX assumes PCI addresses mapped 1-1 to physical */ adr[i].space = pci_addrs[i].addr.a_hi; @@ -434,36 +537,194 @@ finish_node(struct device_node *np, unsigned long mem_start, adr[i].size = pci_addrs[i].size_lo; ++i; } - } else { - rp = (struct reg_property *) get_property(np, "reg", &l); - if (rp != 0) { - while ((l -= sizeof(struct reg_property)) >= 0) { - adr[i].space = 0; - adr[i].address = rp[i].address + base_address; - adr[i].size = rp[i].size; - ++i; - } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 0; + } + } + + return mem_start; +} + +static unsigned long +interpret_dbdma_props(struct device_node *np, unsigned long mem_start) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; } } - if (i > 0) { + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } np->addrs = adr; np->n_addrs = i; mem_start += i * sizeof(struct address_range); } - np->intrs = (int *) get_property(np, "AAPL,interrupts", &l); - if (np->intrs == 0) - np->intrs = (int *) get_property(np, "interrupts", &l); - if (np->intrs != 0) + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 0; + } + } - if (np->type != 0 && np->n_addrs > 0 - && (strcmp(np->type, "dbdma") == 0 - || strcmp(np->type, "mac-io") == 0)) - base_address = np->addrs[0].address; + return mem_start; +} - for (child = np->child; child != NULL; child = child->sibling) - mem_start = finish_node(child, mem_start, base_address); +static unsigned long +interpret_macio_props(struct device_node *np, unsigned long mem_start) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / (2 * sizeof(int)); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = openpic_to_irq(*ip++); + np->intrs[i].sense = *ip++; + } + } + + return mem_start; +} + +static unsigned long +interpret_isa_props(struct device_node *np, unsigned long mem_start) +{ + struct isa_reg_property *rp; + struct address_range *adr; + int i, l, *ip; + + rp = (struct isa_reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct isa_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = rp[i].space; + adr[i].address = rp[i].address + + (adr[i].space? 0: _ISA_MEM_BASE); + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / (2 * sizeof(int)); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = *ip++; + } + } + + return mem_start; +} + +static unsigned long +interpret_root_props(struct device_node *np, unsigned long mem_start) +{ + struct reg_property *rp; + struct address_range *adr; + int i, l, *ip; + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 0; + } + } return mem_start; } @@ -648,14 +909,14 @@ call_rtas(const char *service, int nargs, int nret, printk(KERN_ERR "No RTAS service called %s\n", service); return -1; } - u.words[0] = *tokp; + u.words[0] = __pa(*tokp); u.words[1] = nargs; u.words[2] = nret; va_start(list, outputs); for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); - enter_rtas(&u); + enter_rtas((void *)__pa(&u)); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index 4f6068c6d..ce2f35058 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -390,8 +390,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = get_reg(child, addr); } else if (addr >= PT_FPR0 && addr <= PT_FPSCR) { +#ifdef __SMP__ + if (child->tss.regs->msr & MSR_FP ) + smp_giveup_fpu(child); +#else + /* only current can be last task to use math on SMP */ if (last_task_used_math == child) giveup_fpu(); +#endif tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; } else @@ -423,8 +429,13 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; } if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { +#ifndef __SMP__ + if (child->tss.regs->msr & MSR_FP ) + smp_giveup_fpu(child); +#else if (last_task_used_math == child) giveup_fpu(); +#endif ((long *)child->tss.fpr)[addr - PT_FPR0] = data; ret = 0; goto out; diff --git a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c index fdf62e921..b5516d0e5 100644 --- a/arch/ppc/kernel/residual.c +++ b/arch/ppc/kernel/residual.c @@ -1,5 +1,5 @@ /* - * $Id: residual.c,v 1.5 1997/10/30 21:25:19 cort Exp $ + * $Id: residual.c,v 1.7 1998/03/08 05:49:20 davem Exp $ * * Code to deal with the PReP residual data. * diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 86407e273..bdf05af76 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.48 1998/01/01 10:04:44 paulus Exp $ + * $Id: setup.c,v 1.68 1998/04/07 08:20:33 geert Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -13,10 +13,26 @@ #include <asm/adb.h> #include <asm/cuda.h> +#include <asm/pmu.h> #include <asm/residual.h> #include <asm/io.h> #include <asm/ide.h> #include <asm/prom.h> +#include <asm/processor.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif +/* ifdef APUS specific stuff until the merge is completed. -jskov */ +#ifdef CONFIG_APUS +#include <asm/bootinfo.h> +#include <asm/setup.h> +#include <asm/amigappc.h> +extern unsigned long m68k_machtype; +extern void amiga_reset (void); +extern struct mem_info m68k_ramdisk; +extern int m68k_parse_bootinfo(const struct bi_record *); +extern char _end[]; +#endif extern char cmd_line[512]; char saved_command_line[256]; @@ -26,13 +42,13 @@ unsigned char aux_device_present; unsigned long ISA_DMA_THRESHOLD; unsigned long DMA_MODE_READ, DMA_MODE_WRITE; int _machine; +/* if we have openfirmware */ +unsigned long have_of; #endif /* ! CONFIG_MACH_SPECIFIC */ /* copy of the residual data */ RESIDUAL res; int _prep_type; -/* if we have openfirmware */ -unsigned long have_of; /* * Perhaps we can put the pmac screen_info[] here @@ -40,6 +56,7 @@ unsigned long have_of; * Until we get multiple-console support in here * that is. -- Cort */ +#ifndef CONFIG_MBX #if !defined(CONFIG_PMAC_CONSOLE) struct screen_info screen_info = { 0, 25, /* orig-x, orig-y */ @@ -65,29 +82,66 @@ void pmac_find_display(void) } #endif +#else /* CONFIG_MBX */ + +/* We need this to satisfy some external references until we can + * strip the kernel down. + */ +struct screen_info screen_info = { + 0, 25, /* orig-x, orig-y */ + { 0, 0 }, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25, /* orig-video-lines */ + 0, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; +#endif /* CONFIG_MBX */ + /* cmd is ignored for now... */ void machine_restart(char *cmd) { struct adb_request req; unsigned long flags; unsigned long i = 10000; -#if 0 +#if 0 int err; #endif switch(_machine) { case _MACH_Pmac: - cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); - for (;;) - cuda_poll(); + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; + case ADB_VIAPMU: + pmu_request(&req, NULL, 1, PMU_RESET); + for (;;) + pmu_poll(); + break; + default: + } break; + case _MACH_chrp: #if 0 /* RTAS doesn't seem to work on Longtrail. For now, do it the same way as the PReP. */ - err = call_rtas("system-reboot", 0, 1, NULL); + /*err = call_rtas("system-reboot", 0, 1, NULL); printk("RTAS system-reboot returned %d\n", err); - for (;;); + for (;;);*/ + + { + extern unsigned int rtas_entry, rtas_data, rtas_size; + unsigned long status, value; + printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n", + rtas_entry,rtas_data,rtas_size); + } #endif case _MACH_prep: _disable_interrupts(); @@ -104,6 +158,23 @@ void machine_restart(char *cmd) while ( i != 0 ) i++; panic("restart failed\n"); break; + case _MACH_apus: + cli(); + /* APUS:FIXME: Reset the system. Apparently there's + * more magic to it than this!?!? + */ +#if 0 + APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); + APUS_WRITE(APUS_REG_RESET, + REGRESET_PPCRESET|REGRESET_M68KRESET| + REGRESET_AMIGARESET|REGRESET_AUXRESET| + REGRESET_SCSIRESET); +#endif + printk("\n**************************************\n"); + printk("*** You can make a hard reset now! ***\n"); + printk("**************************************\n"); + for(;;); + break; } } @@ -116,9 +187,23 @@ void machine_power_off(void) switch (_machine) { case _MACH_Pmac: - cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); - for (;;) - cuda_poll(); + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_POWERDOWN); + for (;;) + cuda_poll(); + break; + case ADB_VIAPMU: + pmu_request(&req, NULL, 5, PMU_SHUTDOWN, + 'M', 'A', 'T', 'T'); + for (;;) + pmu_poll(); + break; + default: + } + break; + case _MACH_chrp: #if 0 /* RTAS doesn't seem to work on Longtrail. For now, do it the same way as the PReP. */ @@ -126,9 +211,19 @@ void machine_power_off(void) printk("RTAS system-reboot returned %d\n", err); for (;;); #endif + case _MACH_prep: machine_restart(NULL); +#ifdef CONFIG_APUS + case _MACH_apus: +#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) + apm_set_power_state(APM_STATE_OFF); + for (;;); +#endif +#endif } + for (;;) + ; } void machine_halt(void) @@ -141,18 +236,90 @@ void machine_halt(void) machine_power_off(); /* for now */ #endif } - else /* prep or chrp */ + else /* prep, chrp or apus */ machine_restart(NULL); } +#ifdef CONFIG_BLK_DEV_IDE void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) { - if ( _machine == _MACH_Pmac ) + switch (_machine) { + case _MACH_Pmac: pmac_ide_init_hwif_ports(p,base,irq); - else /* prep or chrp */ + break; + case _MACH_chrp: + chrp_ide_init_hwif_ports(p,base,irq); + break; + case _MACH_prep: prep_ide_init_hwif_ports(p,base,irq); + break; + } +} +#endif + +unsigned long cpu_temp(void) +{ + unsigned long i, temp, thrm1, dir; + int sanity; + /* + * setup thrm3 - need to give TAU at least 20us + * to do the compare so assume a 300MHz clock. + * We need 300*20 ticks then. + * -- Cort + */ + asm("mtspr 1020, %1\n\t" + "mtspr 1021, %1\n\t" + "mtspr 1022, %0\n\t":: + "r" ( ((300*20)<<18) | THRM3_E), "r" (0) ); + +#if 0 + for ( i = 127 ; i >= 0 ; i-- ) + { + asm("mtspr 1020, %0\n\t":: + "r" (THRM1_TID|THRM1_V|(i<<2)) ); + /* check value */ + while ( !( thrm1 & THRM1_TIV) ) + asm("mfspr %0, 1020 \n\t": "=r" (thrm1) ); + if ( thrm1 & THRM1_TIN ) + { + printk("tin set: %x tiv %x\n", thrm1,thrm1&THRM1_TIV); + goto out; + } + + } +#endif +#if 0 + i = 32; /* increment */ + dir = 1; /* direction we're checking 0=up 1=down */ + temp = 64; /* threshold checking against */ + while ( i ) + { + _set_THRM1((1<<29) | THRM1_V | (temp<<2) ); + printk("checking %d in dir %d thrm set to %x/%x\n", temp,dir, + ( (1<<29) | THRM1_V | (temp<<2)),_get_THRM1()); + /* check value */ + sanity = 0x0fffffff; + while ( (!( thrm1 & THRM1_TIV)) && (sanity--) ) + thrm1 = _get_THRM1(); + /*asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );*/ + if ( ! sanity || sanity==0xffffffff ) printk("no sanity\n"); + /* temp is not in that direction */ + if ( !(thrm1 & THRM1_TIN) ) + { + printk("not in that dir thrm1 %x\n",thrm1); + if ( dir == 0 ) dir = 1; + else dir = 0; + } + if ( dir ) temp -= i; + else temp += i; + i /= 2; + } + asm("mtspr 1020, %0\n\t" + "mtspr 1022, %0\n\t" ::"r" (0) ); +#endif + return temp; } int get_cpuinfo(char *buffer) @@ -160,9 +327,11 @@ int get_cpuinfo(char *buffer) extern int pmac_get_cpuinfo(char *); extern int chrp_get_cpuinfo(char *); extern int prep_get_cpuinfo(char *); + extern int apus_get_cpuinfo(char *); unsigned long len = 0; unsigned long bogosum = 0; unsigned long i; + unsigned long cr; #ifdef __SMP__ extern unsigned long cpu_present_map; extern struct cpuinfo_PPC cpu_data[NR_CPUS]; @@ -202,7 +371,18 @@ int get_cpuinfo(char *buffer) len += sprintf(len+buffer, "603ev\n"); break; case 8: - len += sprintf(len+buffer, "750 (Arthur)\n"); + len += sprintf(len+buffer,"750\n"); + cr = _get_L2CR(); + len += sprintf(len+buffer,"L2CR\t\t: %lx\n",cr); + if ( cr & (0x1<<1)) cr = 256; + else if ( cr & (0x2<<1)) cr = 512; + else if ( cr & (0x3<<1)) cr = 1024; + else cr = 0; + len += sprintf(len+buffer,"on-chip l2\t: " + "%ld KB (%s)\n", + cr,(_get_L2CR()&1) ? "on" : "off"); + len += sprintf(len+buffer,"temperature \t: %lu C\n", + cpu_temp()); break; case 9: len += sprintf(len+buffer, "604e\n"); @@ -216,6 +396,7 @@ int get_cpuinfo(char *buffer) break; } + /* * Assume here that all clock rates are the same in a * smp system. -- Cort @@ -290,6 +471,11 @@ int get_cpuinfo(char *buffer) case _MACH_chrp: len += chrp_get_cpuinfo(buffer+len); break; +#ifdef CONFIG_APUS + case _MACH_apus: + len += apus_get_cpuinfo(buffer+len); + break; +#endif } return len; } @@ -302,43 +488,63 @@ __initfunc(unsigned long identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7)) { - extern unsigned long initrd_start, initrd_end; extern setup_pci_ptrs(void); - unsigned long boot_sdr1; - ihandle prom_root; - unsigned char type[16], model[16]; - - asm("mfspr %0,25\n\t" :"=r" (boot_sdr1)); +#ifndef CONFIG_MBX8xx - /* - * if we have a sdr1 then we have openfirmware - * and can ask it what machine we are (chrp/pmac/prep). - * otherwise we're definitely prep. -- Cort - */ - if ( !boot_sdr1 ) +#ifdef CONFIG_APUS + if ( r3 == 0x61707573 ) { - /* we know for certain we're prep if no OF */ + /* Parse bootinfo. The bootinfo is located right after + the kernel bss */ + m68k_parse_bootinfo((const struct bi_record *)&_end); + have_of = 0; - /* make a copy of residual data */ - if ( r3 ) - memcpy((void *)&res,(void *)(r3+KERNELBASE), - sizeof(RESIDUAL)); + +#ifdef CONFIG_BLK_DEV_INITRD + /* Take care of initrd if we have one. Use data from + bootinfo to avoid the need to initialize PPC + registers when kernel is booted via a PPC reset. */ + if ( m68k_ramdisk.addr ) { + initrd_start = (unsigned long) __va(m68k_ramdisk.addr); + initrd_end = (unsigned long) + __va(m68k_ramdisk.size + m68k_ramdisk.addr); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + return 0; + } +#endif + #ifndef CONFIG_MACH_SPECIFIC + /* prep boot loader tells us if we're prep or not */ + if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) ) + { _machine = _MACH_prep; -#endif /* CONFIG_MACH_SPECIFIC */ + have_of = 0; + } else + { + /* need to ask OF if we're chrp or pmac */ + extern unsigned char OF_type[16], OF_model[16]; + prom_print(OF_type); + prom_print(OF_model); + if ( !strncmp("chrp", OF_type,4) ) + { + _machine = _MACH_chrp; + } + else + { + /*if ( !strncmp("Power Macintosh", type,15) )*/ + _machine = _MACH_Pmac; + } + _machine = _MACH_Pmac; + } - else +#endif /* CONFIG_MACH_SPECIFIC */ + + if ( have_of ) { - /* - * init prom here, then ask the openfirmware - * what machine we are (prep/chrp/pmac). We don't use - * OF on prep just yet. -- Cort - */ -#ifndef CONFIG_PREP /* don't use OF on prep yet */ - have_of = 1; /* prom_init has already been called from __start */ finish_device_tree(); - /* * If we were booted via quik, r3 points to the physical * address of the command-line parameters. @@ -356,7 +562,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, } else { struct device_node *chosen; char *p; - + #ifdef CONFIG_BLK_DEV_INITRD if (r3 - KERNELBASE < 0x800000 && r4 != 0 && r4 != 0xdeadbeef) { @@ -365,7 +571,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); } #endif - chosen = find_path_device("/chosen"); + chosen = find_devices("chosen"); if (chosen != NULL) { p = get_property(chosen, "bootargs", NULL); if (p != NULL) @@ -373,47 +579,18 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, } } cmd_line[sizeof(cmd_line) - 1] = 0; -#endif /* CONFIG_PREP */ - -#ifndef CONFIG_MACH_SPECIFIC -#if 0 - prom_root = call_prom("finddevice", 1, 1, "/"); - call_prom("getprop", 4, 1, prom_root, "device_type", &type, - (void *) sizeof(type)); - call_prom("getprop", 4, 1, prom_root, "model", &type, - (void *) sizeof(model)); - if ( !strncmp("chrp", type,4) ) - { - _machine = _MACH_chrp; - } - else - { - /*if ( !strncmp("Power Macintosh", type,15) )*/ - _machine = _MACH_Pmac; - } -#else - -#ifdef CONFIG_CHRP - _machine = _MACH_chrp; -#endif /* CONFIG_CHRP */ -#ifdef CONFIG_PMAC - _machine = _MACH_Pmac; -#endif /* CONFIG_PMAC */ -#ifdef CONFIG_PREP - _machine = _MACH_Prep; -#endif /* CONFIG_PREP */ -#endif /* #if */ -#endif /* CONFIG_MACH_SPECIFIC */ } +#ifdef CONFIG_PCI /* so that pmac/chrp can use pci to find its console -- Cort */ setup_pci_ptrs(); - +#endif + switch (_machine) { case _MACH_Pmac: #if !defined(CONFIG_MACH_SPECIFIC) - isa_io_base = PMAC_ISA_IO_BASE; + /* isa_io_base gets set in pmac_find_bridges */ isa_mem_base = PMAC_ISA_MEM_BASE; pci_dram_offset = PMAC_PCI_DRAM_OFFSET; ISA_DMA_THRESHOLD = ~0L; @@ -422,6 +599,10 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, #endif /* ! CONFIG_MACH_SPECIFIC */ break; case _MACH_prep: + /* make a copy of residual data */ + if ( r3 ) + memcpy((void *)&res,(void *)(r3+KERNELBASE), + sizeof(RESIDUAL)); #if !defined(CONFIG_MACH_SPECIFIC) isa_io_base = PREP_ISA_IO_BASE; isa_mem_base = PREP_ISA_MEM_BASE; @@ -434,13 +615,12 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, if ( res.ResidualLength != 0 ) { if ( !strncmp(res.VitalProductData.PrintableModel,"IBM",3) ) - _prep_type = 0x00; + _prep_type = _PREP_IBM; else - _prep_type = 0x01; + _prep_type = _PREP_Motorola; } else /* assume motorola if no residual (netboot?) */ _prep_type = _PREP_Motorola; - #ifdef CONFIG_BLK_DEV_RAM /* take care of initrd if we have one */ if ( r4 ) @@ -457,7 +637,14 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, } break; case _MACH_chrp: - /* LongTrail */ +#ifdef CONFIG_BLK_DEV_RAM + /* take care of initrd if we have one */ + if ( r3 ) + { + initrd_start = r3 + KERNELBASE; + initrd_end = r3+ r4 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_RAM */ #if !defined(CONFIG_MACH_SPECIFIC) isa_io_base = CHRP_ISA_IO_BASE; isa_mem_base = CHRP_ISA_MEM_BASE; @@ -470,13 +657,33 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, default: printk("Unknown machine type in identify_machine!\n"); } - return 0; -} +#else /* CONFIG_MBX8xx */ + extern setup_pci_ptrs(void); -__initfunc(unsigned long -bios32_init(unsigned long memory_start, unsigned long memory_end)) -{ - return memory_start; + if ( r3 ) + memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + + setup_pci_ptrs(); + +#ifdef CONFIG_BLK_DEV_RAM + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_RAM */ + /* take care of cmd line */ + if ( r6 ) + { + + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + +#endif /* CONFIG_MBX */ + + return 0; } __initfunc(void setup_arch(char **cmdline_p, @@ -485,12 +692,18 @@ __initfunc(void setup_arch(char **cmdline_p, extern void pmac_setup_arch(unsigned long *, unsigned long *); extern void chrp_setup_arch(unsigned long *, unsigned long *); extern void prep_setup_arch(unsigned long *, unsigned long *); + extern void apus_setup_arch(char **, unsigned long *, unsigned long *); extern int panic_timeout; extern char _etext[], _edata[]; extern char *klimit; extern unsigned long find_available_memory(void); extern unsigned long *end_of_DRAM; +#ifdef CONFIG_XMON + extern void xmon_map_scc(void); + xmon_map_scc(); +#endif /* CONFIG_XMON */ + /* reboot on panic */ panic_timeout = 180; @@ -516,6 +729,12 @@ __initfunc(void setup_arch(char **cmdline_p, case _MACH_chrp: chrp_setup_arch(memory_start_p, memory_end_p); break; +#ifdef CONFIG_APUS + case _MACH_apus: + m68k_machtype = MACH_AMIGA; + apus_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; +#endif default: printk("Unknown machine %d in setup_arch()\n", _machine); } diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index ae6d3c5aa..1c977bb51 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -201,8 +201,13 @@ int sys_sigreturn(struct pt_regs *regs) if (sc == (struct sigcontext_struct *)(sigctx.regs)) { /* Last stacked signal - restore registers */ sr = (struct sigregs *) sigctx.regs; +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if (last_task_used_math == current) giveup_fpu(); +#endif if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) goto badframe; @@ -249,8 +254,13 @@ setup_frame(struct pt_regs *regs, struct sigregs *frame, if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) goto badframe; - if (last_task_used_math == current) - giveup_fpu(); +#ifdef __SMP__ + if ( regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else + if (last_task_used_math == current) + giveup_fpu(); +#endif if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) || __copy_to_user(&frame->fp_regs, current->tss.fpr, ELF_NFPREG * sizeof(double)) diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index 33d3a23b4..217e695f8 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -1,5 +1,5 @@ /* - * $Id: smp.c,v 1.8 1998/01/06 06:44:57 cort Exp $ + * $Id: smp.c,v 1.22 1998/04/10 01:53:34 cort Exp $ * * Smp support for ppc. * @@ -30,22 +30,27 @@ #include <asm/init.h> #include <asm/io.h> +#include "time.h" + int smp_threads_ready = 0; volatile int smp_commenced = 0; int smp_num_cpus = 1; unsigned long cpu_present_map = 0; volatile int cpu_number_map[NR_CPUS]; volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; +volatile int __cpu_logical_map[NR_CPUS]; static unsigned char boot_cpu_id = 0; struct cpuinfo_PPC cpu_data[NR_CPUS]; -struct klock_info klock_info = { KLOCK_CLEAR, 0 }; +struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 }; volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */ -volatile unsigned long hash_table_lock; +volatile unsigned long ipi_count; + +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; int start_secondary(void *); -extern void init_IRQ(void); extern int cpu_idle(void *unused); void smp_boot_cpus(void) @@ -56,49 +61,75 @@ void smp_boot_cpus(void) struct task_struct *p; printk("Entering SMP Mode...\n"); + + for (i = 0; i < NR_CPUS; i++) { + cpu_number_map[i] = -1; + prof_counter[i] = 1; + prof_multiplier[i] = 1; + } + cpu_present_map = 0; for(i=0; i < NR_CPUS; i++) - cpu_number_map[i] = -1; + __cpu_logical_map[i] = -1; smp_store_cpu_info(boot_cpu_id); active_kernel_processor = boot_cpu_id; current->processor = boot_cpu_id; - cpu_present_map |= 1; + cpu_number_map[boot_cpu_id] = 0; + __cpu_logical_map[0] = boot_cpu_id; + + if ( _machine != _MACH_Pmac ) + { + printk("SMP not supported on this machine.\n"); + return; + } + /* assume a 2nd processor for now */ cpu_present_map |= (1 << 1); smp_num_cpus = 2; - cpu_number_map[boot_cpu_id] = 0; /* create a process for second processor */ kernel_thread(start_secondary, NULL, CLONE_PID); - cpu_number_map[1] = 1; p = task[1]; if ( !p ) panic("No idle task for secondary processor\n"); p->processor = 1; current_set[1] = p; + /* need to flush here since secondary bat's aren't setup */ + dcbf((volatile unsigned long *)¤t_set[1]); + /* setup entry point of secondary processor */ *(volatile unsigned long *)(0xf2800000) = (unsigned long)secondary_entry-KERNELBASE; - /* interrupt secondary to begin executing code */ eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0; + /* interrupt secondary to begin executing code */ + *(volatile unsigned long *)(0xf80000c0) = 0L; eieio(); /* wait to see if the secondary made a callin (is actually up) */ - for ( timeout = 0; timeout < 1500 ; timeout++ ) + for ( timeout = 0; timeout < 15000 ; timeout++ ) + { + if(cpu_callin_map[1]) + break; udelay(100); + } if(cpu_callin_map[1]) { cpu_number_map[1] = 1; + __cpu_logical_map[i] = 1; printk("Processor 1 found.\n"); + +#if 0 /* this sync's the decr's */ + set_dec(decrementer_count); +#endif + /* interrupt secondary to sync the time bases */ + smp_message_pass(1,0xf0f0, 0, 0); + /* interrupt secondary to begin executing code */ + /**(volatile unsigned long *)(0xf80000c0) = 0L; + eieio();*/ } else { smp_num_cpus--; printk("Processor %d is stuck.\n", 1); } -{ - extern unsigned long amhere; - printk("amhere: %x\n", amhere); -} } void smp_commence(void) @@ -128,16 +159,15 @@ int start_secondary(void *unused) void smp_callin(void) { printk("SMP %d: smp_callin()\n",current->processor); - /*calibrate_delay();*/ smp_store_cpu_info(1); - - /* assume we're just the secondary processor for now */ - cpu_callin_map[1] = 1; - dcbf(&cpu_callin_map[1]); + set_dec(decrementer_count); current->mm->mmap->vm_page_prot = PAGE_SHARED; current->mm->mmap->vm_start = PAGE_OFFSET; current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; + + /* assume we're just the secondary processor for now */ + cpu_callin_map[1] = 1; while(!smp_commenced) barrier(); @@ -149,21 +179,120 @@ void smp_setup(char *str, int *ints) printk("SMP %d: smp_setup()\n",current->processor); } -void smp_message_pass(int target, int msg, unsigned long data, int wait) +void smp_local_timer_interrupt(struct pt_regs * regs) { - printk("SMP %d: sending smp message\n",current->processor); -#if 0 - if ( smp_processor_id() == 0 ) + int cpu = smp_processor_id(); + extern void update_one_process(struct task_struct *,unsigned long, + unsigned long,unsigned long,int); + + if (!--prof_counter[cpu]) { + int user=0,system=0; + struct task_struct * p = current; + + /* + * After doing the above, we need to make like + * a normal interrupt - otherwise timer interrupts + * ignore the global interrupt lock, which is the + * WrongThing (tm) to do. + */ + + if (user_mode(regs)) + user=1; + else + system=1; + + if (p->pid) { + update_one_process(p, 1, user, system, cpu); + + p->counter -= 1; + if (p->counter < 0) { + p->counter = 0; + need_resched = 1; + } + if (p->priority < DEF_PRIORITY) { + kstat.cpu_nice += user; + kstat.per_cpu_nice[cpu] += user; + } else { + kstat.cpu_user += user; + kstat.per_cpu_user[cpu] += user; + } + + kstat.cpu_system += system; + kstat.per_cpu_system[cpu] += system; + + } + prof_counter[cpu]=prof_multiplier[cpu]; + } +} + +/* + * Dirty hack to get smp message passing working. + * Right now it only works for stop cpu's but will be setup + * later for more general message passing. + * + * As it is now, if we're sending two message as the same time + * we have race conditions. I avoided doing locks here since + * all that works right now is the stop cpu message. + * + * -- Cort + */ +int smp_message[NR_CPUS]; +void smp_message_recv(void) +{ + int msg = smp_message[smp_processor_id()]; + + printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg); + + /* make sure msg is for us */ + if ( msg == -1 ) return; +printk("recv after msg check\n"); + switch( msg ) { - /* interrupt secondary */ - *(volatile unsigned long *)(0xf80000c0) = 0; + case MSG_STOP_CPU: + __cli(); + while (1) ; + break; + case 0xf0f0: /* syncing time bases - just return */ + break; + default: + printk("SMP %d: smp_message_recv(): unknown msg %d\n", + smp_processor_id(), msg); + break; } - else + /* reset message */ + smp_message[smp_processor_id()] = -1; +} + +spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; +void smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + printk("SMP %d: sending smp message\n", current->processor); + + spin_lock(&mesg_pass_lock); + if ( _machine != _MACH_Pmac ) + return; + +#define OTHER (~smp_processor_id() & 1) + + switch( target ) { - /* interrupt primary */ - *(volatile unsigned long *)(0xf3019000); + case MSG_ALL: + smp_message[smp_processor_id()] = msg; + /* fall through */ + case MSG_ALL_BUT_SELF: + smp_message[OTHER] = msg; + break; + default: + smp_message[target] = msg; + break; } -#endif + /* interrupt secondary processor */ + /**(volatile unsigned long *)(0xf80000c0) = 0xffffffff; + eieio();*/ + *(volatile unsigned long *)(0xf80000c0) = 0; + /* interrupt primary */ + /**(volatile unsigned long *)(0xf3019000);*/ + spin_unlock(&mesg_pass_lock); } int setup_profiling_timer(unsigned int multiplier) diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c new file mode 100644 index 000000000..dedf24806 --- /dev/null +++ b/arch/ppc/kernel/softemu8xx.c @@ -0,0 +1,96 @@ +/* + * Software emulation of some PPC instructions for the 8xx core. + * + * Copyright (C) 1998 Dan Malek (dmalek@jlc.net) + * + * Software floating emuation for the MPC8xx processor. I did this mostly + * because it was easier than trying to get the libraries compiled for + * software floating point. The goal is still to get the libraries done, + * but I lost patience and needed some hacks to at least get init and + * shells running. The first problem is the setjmp/longjmp that save + * and restore the floating point registers. + * + * For this emulation, our working registers are found on the register + * save area. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> + +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> + +/* Eventually we may need a look-up table, but this works for now. +*/ +#define LFD 50 +#define LFDU 51 +#define STFD 54 +#define STFDU 55 + +/* + * We return 0 on success, 1 on unimplemented instruction, and EFAULT + * if a load/store faulted. + */ +int +Soft_emulate_8xx(struct pt_regs *regs) +{ + uint inst, instword; + uint flreg, idxreg, disp; + uint retval; + uint *ea, *ip; + + retval = 0; + + instword = *((uint *)regs->nip); + inst = instword >> 26; + + flreg = (instword >> 21) & 0x1f; + idxreg = (instword >> 16) & 0x1f; + disp = instword & 0xffff; + + ea = (uint *)(regs->gpr[idxreg] + disp); + ip = (uint *)¤t->tss.fpr[flreg]; + + if (inst == LFD) { + if (copy_from_user(ip, ea, sizeof(double))) + retval = EFAULT; + } + else if (inst == LFDU) { + + if (copy_from_user(ip, ea, sizeof(double))) + retval = EFAULT; + else + regs->gpr[idxreg] = (uint)ea; + } + else if (inst == STFD) { + + if (copy_to_user(ea, ip, sizeof(double))) + retval = EFAULT; + } + else if (inst == STFDU) { + + if (copy_to_user(ea, ip, sizeof(double))) + retval = EFAULT; + else + regs->gpr[idxreg] = (uint)ea; + } + else { + retval = 1; + } + + if (retval == 0) + regs->nip += 4; + return(retval); +} diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index 319db15de..5a1063d4e 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -1,11 +1,25 @@ /* - * $Id: time.c,v 1.17 1997/12/28 22:47:21 paulus Exp $ + * $Id: time.c,v 1.28 1998/04/07 18:49:49 cort Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge * Paul Mackerras' version and mine for PReP and Pmac. + * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). + * + * Since the MPC8xx has a programmable interrupt timer, I decided to + * use that rather than the decrementer. Two reasons: 1.) the clock + * frequency is low, causing 2.) a long wait in the timer interrupt + * while ((d = get_dec()) == dval) + * loop. The MPC8xx can be driven from a variety of input clocks, + * so a number of assumptions have been made here because the kernel + * parameter HZ is a constant. We assume (correctly, today :-) that + * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal. + * This is then divided by 4, providing a 8192 Hz clock into the PIT. + * Since it is not possible to get a nice 100 Hz clock out of this, without + * creating a software PLL, I have set HZ to 128. -- Dan */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -22,12 +36,21 @@ #include <asm/io.h> #include <asm/processor.h> #include <asm/nvram.h> +#include <asm/cache.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif +#ifdef CONFIG_8xx +#include <asm/8xx_immap.h> +#endif #include "time.h" /* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */ int (*set_rtc_time)(unsigned long); +void smp_local_timer_interrupt(struct pt_regs *); + /* keep track of when we need to update the rtc */ unsigned long last_rtc_update = 0; @@ -48,6 +71,14 @@ unsigned count_period_den; /* count_period_num / count_period_den us */ void timer_interrupt(struct pt_regs * regs) { int dval, d; + unsigned long cpu = smp_processor_id(); + /* save the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + unsigned dcache_locked = unlock_dcache(); + +if ( smp_processor_id() ) printk("SMP 1: timer intr\n"); + hardirq_enter(cpu); + while ((dval = get_dec()) < 0) { /* * Wait for the decrementer to change, then jump @@ -57,17 +88,49 @@ void timer_interrupt(struct pt_regs * regs) while ((d = get_dec()) == dval) ; set_dec(d + decrementer_count); - do_timer(regs); - /* - * update the rtc when needed - */ - if ( xtime.tv_sec > last_rtc_update + 660 ) - if (set_rtc_time(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + if ( !smp_processor_id() ) + { + do_timer(regs); + /* + * update the rtc when needed + */ + if ( xtime.tv_sec > last_rtc_update + 660 ) + if (set_rtc_time(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } } +#ifdef __SMP__ + smp_local_timer_interrupt(regs); +#endif + + hardirq_exit(cpu); + /* restore the HID0 in case dcache was off - see idle.c + * this hack should leave for a better solution -- Cort */ + lock_dcache(dcache_locked); +} + +#ifdef CONFIG_MBX +/* A place holder for time base interrupts, if they are ever enabled. +*/ +void timebase_interrupt(int irq, void * dev, struct pt_regs * regs) +{ +} + +/* The RTC on the MPC8xx is an internal register. + * We want to protect this during power down, so we need to unlock, + * modify, and re-lock. + */ +static int +mbx_set_rtc_time(unsigned long time) +{ + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY; + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc = time; + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY; + return(0); } +#endif /* CONFIG_MBX */ /* * This version of gettimeofday has microsecond resolution. @@ -108,6 +171,7 @@ void do_settimeofday(struct timeval *tv) void time_init(void) { +#ifndef CONFIG_MBX if ((_get_PVR() >> 16) == 1) { /* 601 processor: dec counts down by 128 every 128ns */ decrementer_count = DECREMENTER_COUNT_601; @@ -119,9 +183,10 @@ time_init(void) case _MACH_Pmac: /* can't call pmac_get_rtc_time() yet, because via-cuda isn't initialized yet. */ - if ((_get_PVR() >> 16) != 1) + if ( (_get_PVR() >> 16) != 1 && (!smp_processor_id()) ) pmac_calibrate_decr(); - set_rtc_time = pmac_set_rtc_time; + if ( !smp_processor_id() ) + set_rtc_time = pmac_set_rtc_time; break; case _MACH_chrp: chrp_time_init(); @@ -135,18 +200,63 @@ time_init(void) prep_calibrate_decr(); set_rtc_time = prep_set_rtc_time; break; +/* ifdef APUS specific stuff until the merge is completed. -jskov */ +#ifdef CONFIG_APUS + case _MACH_apus: + { + xtime.tv_sec = apus_get_rtc_time(); + apus_calibrate_decr(); + set_rtc_time = apus_set_rtc_time; + break; } +#endif + } + xtime.tv_usec = 0; + set_dec(decrementer_count); +#else + mbx_calibrate_decr(); + set_rtc_time = mbx_set_rtc_time; + + /* First, unlock all of the registers we are going to modify. + * To protect them from corruption during power down, registers + * that are maintained by keep alive power are "locked". To + * modify these registers we have to write the key value to + * the key location associated with the register. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; + ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY; + + + /* Disable the RTC one second and alarm interrupts. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtcsc &= + ~(RTCSC_SIE | RTCSC_ALE); + + /* Enabling the decrementer also enables the timebase interrupts + * (or from the other point of view, to get decrementer interrupts + * we have to enable the timebase). The decrementer interrupt + * is wired into the vector table, nothing to do here for that. + */ + ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_tbscr = + ((mk_int_int_mask(DEC_INTERRUPT) << 8) | + (TBSCR_TBF | TBSCR_TBE)); + if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0) + panic("Could not allocate timer IRQ!"); + + /* Get time from the RTC. + */ + xtime.tv_sec = ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc; xtime.tv_usec = 0; - /* - * mark the rtc/on-chip timer as in sync +#endif /* CONFIG_MBX */ + + /* mark the rtc/on-chip timer as in sync * so we don't update right away */ last_rtc_update = xtime.tv_sec; - - set_dec(decrementer_count); } +#ifndef CONFIG_MBX /* * Uses the on-board timer to calibrate the on-chip decrementer register * for prep systems. On the pmac the OF tells us what the frequency is @@ -158,6 +268,27 @@ volatile int *done_ptr = &calibrate_done; void prep_calibrate_decr(void) { unsigned long flags; + + /* the Powerstack II's have trouble with the timer so + * we use a default value -- Cort + */ + if ( (_prep_type == _PREP_Motorola) && + ((inb(0x800) & 0xF0) & 0x40) ) + { + unsigned long freq, divisor; + static unsigned long t2 = 0; + + t2 = 998700000/60; + freq = t2 * 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", + freq, divisor,t2>>20); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; + return; + } + save_flags(flags); @@ -181,7 +312,7 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs) { unsigned long freq, divisor; static unsigned long t1 = 0, t2 = 0; - + if ( !t1 ) t1 = get_dec(); else if (!t2) @@ -189,11 +320,6 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs) t2 = get_dec(); t2 = t1-t2; /* decr's in 1/HZ */ t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ -if ( (t2>>20) > 100 ) -{ - printk("Decrementer frequency too high: %luMHz. Using 15MHz.\n",t2>>20); - t2 = 998700000/60; -} freq = t2 * 60; /* try to make freq/1e6 an integer */ divisor = 60; printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", @@ -205,6 +331,32 @@ if ( (t2>>20) > 100 ) } } +#else /* CONFIG_MBX */ + +/* The decrementer counts at the system (internal) clock frequency divided by + * sixteen, or external oscillator divided by four. Currently, we only + * support the MBX, which is system clock divided by sixteen. + */ +void mbx_calibrate_decr(void) +{ + int freq, fp, divisor; + + if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0) + printk("WARNING: Wrong decrementer source clock.\n"); + + /* The manual says the frequency is in Hz, but it is really + * as MHz. The value 'fp' is the number of decrementer ticks + * per second. + */ + /*fp = (mbx_board_info.bi_intfreq * 1000000) / 16;*/ + freq = fp*60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} +#endif /* CONFIG_MBX */ /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h index 538ac7bb1..64a07250c 100644 --- a/arch/ppc/kernel/time.h +++ b/arch/ppc/kernel/time.h @@ -1,5 +1,5 @@ /* - * $Id: time.h,v 1.7 1997/12/28 22:47:24 paulus Exp $ + * $Id: time.h,v 1.10 1998/04/01 07:46:03 geert Exp $ * Common time prototypes and such for all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -12,6 +12,7 @@ void prep_calibrate_decr_handler(int, void *,struct pt_regs *); void prep_calibrate_decr(void); void pmac_calibrate_decr(void); +extern void apus_calibrate_decr(void); extern unsigned decrementer_count; extern unsigned count_period_num; extern unsigned count_period_den; @@ -24,12 +25,16 @@ extern unsigned long last_rtc_update; unsigned long prep_get_rtc_time(void); unsigned long pmac_get_rtc_time(void); unsigned long chrp_get_rtc_time(void); +unsigned long apus_get_rtc_time(void); int prep_set_rtc_time(unsigned long nowtime); int pmac_set_rtc_time(unsigned long nowtime); int chrp_set_rtc_time(unsigned long nowtime); +int apus_set_rtc_time(unsigned long nowtime); void pmac_read_rtc_time(void); void chrp_calibrate_decr(void); void chrp_time_init(void); +int via_calibrate_decr(void); +void mbx_calibrate_decr(void); /* Accessor functions for the decrementer register. */ static __inline__ unsigned int get_dec(void) diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 7199dd5c1..c5c90b527 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -39,13 +39,31 @@ extern int fix_alignment(struct pt_regs *); extern void bad_page_fault(struct pt_regs *, unsigned long); #ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); extern int xmon_bpt(struct pt_regs *regs); extern int xmon_sstep(struct pt_regs *regs); -extern void xmon(struct pt_regs *regs); extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); extern void (*xmon_fault_handler)(struct pt_regs *regs); #endif +#ifdef CONFIG_XMON +void (*debugger)(struct pt_regs *regs) = xmon; +int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt; +int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep; +int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match; +int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match; +void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#ifdef CONFIG_KGDB +void (*debugger)(struct pt_regs *regs); +int (*debugger_bpt)(struct pt_regs *regs); +int (*debugger_sstep)(struct pt_regs *regs); +int (*debugger_iabr_match)(struct pt_regs *regs); +int (*debugger_dabr_match)(struct pt_regs *regs); +void (*debugger_fault_handler)(struct pt_regs *regs); +#endif +#endif /* * Trap & Exception support */ @@ -61,8 +79,8 @@ _exception(int signr, struct pt_regs *regs) if (!user_mode(regs)) { show_regs(regs); -#ifdef CONFIG_XMON - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); #endif print_backtrace((unsigned long *)regs->gpr[1]); panic("Exception in kernel pc %lx signal %d",regs->nip,signr); @@ -75,9 +93,9 @@ MachineCheckException(struct pt_regs *regs) { if ( !user_mode(regs) ) { -#ifdef CONFIG_XMON - if (xmon_fault_handler) { - xmon_fault_handler(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler) { + debugger_fault_handler(regs); return; } #endif @@ -103,8 +121,8 @@ MachineCheckException(struct pt_regs *regs) printk("Unknown values in msr\n"); } show_regs(regs); -#ifdef CONFIG_XMON - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); #endif print_backtrace((unsigned long *)regs->gpr[1]); panic("machine check"); @@ -123,8 +141,8 @@ UnknownException(struct pt_regs *regs) void InstructionBreakpoint(struct pt_regs *regs) { -#ifdef CONFIG_XMON - if (xmon_iabr_match(regs)) +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_iabr_match(regs)) return; #endif _exception(SIGTRAP, regs); @@ -144,8 +162,8 @@ ProgramCheckException(struct pt_regs *regs) _exception(SIGFPE, regs); } else if (regs->msr & 0x20000) { /* trap exception */ -#ifdef CONFIG_XMON - if (xmon_bpt(regs)) +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_bpt(regs)) return; #endif _exception(SIGTRAP, regs); @@ -158,8 +176,8 @@ void SingleStepException(struct pt_regs *regs) { regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ -#ifdef CONFIG_XMON - if (xmon_sstep(regs)) +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_sstep(regs)) return; #endif _exception(SIGTRAP, regs); @@ -170,8 +188,13 @@ AlignmentException(struct pt_regs *regs) { int fixed; +#ifdef __SMP__ + if (regs->msr & MSR_FP ) + smp_giveup_fpu(current); +#else if (last_task_used_math == current) giveup_fpu(); +#endif fixed = fix_alignment(regs); if (fixed == 1) { regs->nip += 4; /* skip over emulated instruction */ @@ -190,8 +213,8 @@ StackOverflow(struct pt_regs *regs) { printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", current, regs->gpr[1]); -#ifdef CONFIG_XMON - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); #endif show_regs(regs); print_backtrace((unsigned long *)regs->gpr[1]); @@ -205,3 +228,41 @@ trace_syscall(struct pt_regs *regs) current, current->pid, regs->nip, regs->link, regs->gpr[0], regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); } + +#ifdef CONFIG_8xx + +void +SoftwareEmulation(struct pt_regs *regs) +{ + int errcode; + extern int Soft_emulate_8xx (struct pt_regs *regs); + + if (user_mode(regs)) { +#if 0 + printk("(user mode)\n"); + _exception(SIGTRAP, regs); +#else + if (errcode = Soft_emulate_8xx(regs)) { +printk("Software Emulation 0x%x: 0x%x ", + regs->nip, *((uint *)regs->nip)); +print_8xx_pte(current->mm, regs->nip); + if (errcode == EFAULT) + _exception(SIGBUS, regs); + else + _exception(SIGILL, regs); + } +#endif + } + else { + printk("(kernel mode)\n"); + panic("Kernel Mode Software Emulation"); + } +} +#endif + +void +TAUException(struct pt_regs *regs) +{ + printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); +} diff --git a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c index 5e2ceb889..55cc665d7 100644 --- a/arch/ppc/lib/locks.c +++ b/arch/ppc/lib/locks.c @@ -1,5 +1,5 @@ /* - * $Id: locks.c,v 1.7 1998/01/06 06:44:59 cort Exp $ + * $Id: locks.c,v 1.17 1998/03/26 22:19:38 cort Exp $ * * Locks for smp ppc * @@ -9,53 +9,78 @@ #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/delay.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/spinlock.h> +#include <asm/io.h> #define DEBUG_LOCKS 1 #undef INIT_STUCK -#define INIT_STUCK 10000 - -#undef STUCK -#define STUCK \ -if(!--stuck) { printk("spin_lock(%p) CPU#%d nip %08lx\n", lock, cpu, nip); stuck = INIT_STUCK; } +#define INIT_STUCK 1000000 void _spin_lock(spinlock_t *lock) { - unsigned long val, nip = (unsigned long)__builtin_return_address(0); int cpu = smp_processor_id(); +#ifdef DEBUG_LOCKS int stuck = INIT_STUCK; - -again: +#endif /* DEBUG_LOCKS */ /* try expensive atomic load/store to get lock */ - __asm__ __volatile__( - "10: \n\t" - "lwarx %0,0,%1 \n\t" - "stwcx. %2,0,%1 \n\t" - "bne- 10b \n\t" - : "=r" (val) - : "r" (&(lock->lock)), "r" ( (cpu&3)|(nip&~3L) )); - if(val) { + while((unsigned long )xchg_u32((void *)&lock->lock,0xffffffff)) { /* try cheap load until it's free */ while(lock->lock) { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("_spin_lock(%p) CPU#%d NIP %p" + " holder: cpu %ld pc %08lX\n", + lock, cpu, __builtin_return_address(0), + lock->owner_cpu,lock->owner_pc); + stuck = INIT_STUCK; + /* steal the lock */ + /*xchg_u32((void *)&lock->lock,0);*/ + } +#endif /* DEBUG_LOCKS */ barrier(); } - goto again; } + lock->owner_pc = (unsigned long)__builtin_return_address(0); + lock->owner_cpu = cpu; +} + +int spin_trylock(spinlock_t *lock) +{ + unsigned long result; + + result = (unsigned long )xchg_u32((void *)&lock->lock,0xffffffff); + if ( !result ) + { + lock->owner_cpu = smp_processor_id(); + lock->owner_pc = (unsigned long)__builtin_return_address(0); + } + return (result == 0); } + + void _spin_unlock(spinlock_t *lp) { +#ifdef DEBUG_LOCKS + if ( !lp->lock ) + panic("_spin_unlock(%p): no lock cpu %d %s/%d\n", lp, + smp_processor_id(),current->comm,current->pid); + if ( lp->owner_cpu != smp_processor_id() ) + panic("_spin_unlock(%p): cpu %d trying clear of cpu %d pc %lx val %lx\n", + lp, smp_processor_id(), (int)lp->owner_cpu, + lp->owner_pc,lp->lock); +#endif /* DEBUG_LOCKS */ + lp->owner_pc = lp->owner_cpu = 0; + eieio(); lp->lock = 0; + eieio(); } -#undef STUCK -#define STUCK \ -if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; } - /* * Just like x86, implement read-write locks as a 32-bit counter * with the high bit (sign) being the "write" bit. @@ -63,8 +88,10 @@ if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; } */ void _read_lock(rwlock_t *rw) { +#ifdef DEBUG_LOCKS unsigned long stuck = INIT_STUCK; int cpu = smp_processor_id(); +#endif /* DEBUG_LOCKS */ again: /* get our read lock in there */ @@ -76,7 +103,13 @@ again: /* wait for the write lock to go away */ while ((signed long)((rw)->lock) < 0) { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("_read_lock(%p) CPU#%d\n", rw, cpu); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ } /* try to get the read lock again */ goto again; @@ -87,33 +120,34 @@ void _read_unlock(rwlock_t *rw) { #ifdef DEBUG_LOCKS if ( rw->lock == 0 ) - { - if ( current) printk("_read_unlock(): %s/%d (nip %08lX) lock %lx", - current->comm,current->pid,current->tss.regs->nip, + current->comm,current->pid,current->tss.regs->nip, rw->lock); - else - printk("no current\n"); - } #endif /* DEBUG_LOCKS */ atomic_dec((atomic_t *) &(rw)->lock); } -#undef STUCK -#define STUCK \ -if(!--stuck) { printk("write_lock(%p) CPU#%d lock %lx)\n", rw, cpu,rw->lock); stuck = INIT_STUCK; } - void _write_lock(rwlock_t *rw) { +#ifdef DEBUG_LOCKS unsigned long stuck = INIT_STUCK; int cpu = smp_processor_id(); +#endif /* DEBUG_LOCKS */ again: if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */ { while ( (rw)->lock & (1<<31) ) /* wait for write lock */ { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("write_lock(%p) CPU#%d lock %lx)\n", + rw, cpu,rw->lock); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ + barrier(); } goto again; } @@ -124,7 +158,15 @@ again: clear_bit(31,&(rw)->lock); while ( (rw)->lock & ~(1<<31) ) { - STUCK; +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("write_lock(%p) 2 CPU#%d lock %lx)\n", + rw, cpu,rw->lock); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ + barrier(); } goto again; } @@ -134,14 +176,9 @@ void _write_unlock(rwlock_t *rw) { #ifdef DEBUG_LOCKS if ( !(rw->lock & (1<<31)) ) - { - if ( current) printk("_write_lock(): %s/%d (nip %08lX) lock %lx", current->comm,current->pid,current->tss.regs->nip, rw->lock); - else - printk("no current\n"); - } #endif /* DEBUG_LOCKS */ clear_bit(31,&(rw)->lock); } @@ -149,6 +186,8 @@ void _write_unlock(rwlock_t *rw) void __lock_kernel(struct task_struct *task) { #ifdef DEBUG_LOCKS + unsigned long stuck = INIT_STUCK; + if ( (signed long)(task->lock_depth) < 0 ) { printk("__lock_kernel(): %s/%d (nip %08lX) lock depth %x\n", @@ -156,20 +195,40 @@ void __lock_kernel(struct task_struct *task) task->lock_depth); } #endif /* DEBUG_LOCKS */ + + if ( atomic_inc_return((atomic_t *) &task->lock_depth) != 1 ) + return; /* mine! */ - if ( atomic_inc_return((atomic_t *) &task->lock_depth) == 1 ) - klock_info.akp = smp_processor_id(); + while ( xchg_u32( (void *)&klock_info.kernel_flag, KLOCK_HELD) ) + { + /* try cheap load until it's free */ + while(klock_info.kernel_flag) { +#ifdef DEBUG_LOCKS + if(!--stuck) + { + printk("_lock_kernel() CPU#%d NIP %p\n", + smp_processor_id(), + __builtin_return_address(0)); + stuck = INIT_STUCK; + } +#endif /* DEBUG_LOCKS */ + barrier(); + } + } + + klock_info.akp = smp_processor_id(); /* my kernel mode! mine!!! */ } - + void __unlock_kernel(struct task_struct *task) { #ifdef DEBUG_LOCKS - if ( task->lock_depth == 0 ) + if ( (task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD) ) { - printk("__unlock_kernel(): %s/%d (nip %08lX) lock depth %x\n", - task->comm,task->pid,task->tss.regs->nip, - task->lock_depth); + printk("__unlock_kernel(): %s/%d (nip %08lX) " + "lock depth %x flags %lx\n", + task->comm,task->pid,task->tss.regs->nip, + task->lock_depth, klock_info.kernel_flag); klock_info.akp = NO_PROC_ID; klock_info.kernel_flag = 0; return; @@ -177,8 +236,8 @@ void __unlock_kernel(struct task_struct *task) #endif /* DEBUG_LOCKS */ if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) ) { - klock_info.akp = NO_PROC_ID; - klock_info.kernel_flag = 0; + klock_info.akp = NO_PROC_ID; + klock_info.kernel_flag = KLOCK_CLEAR; } } @@ -192,4 +251,3 @@ void reacquire_kernel_lock(struct task_struct *task, int cpu,int depth) __sti(); } } - diff --git a/arch/ppc/mbx_defconfig b/arch/ppc/mbx_defconfig new file mode 100644 index 000000000..e498b5249 --- /dev/null +++ b/arch/ppc/mbx_defconfig @@ -0,0 +1,196 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +# CONFIG_6xx is not set +CONFIG_8xx=y +# CONFIG_PMAC is not set +# CONFIG_PREP is not set +# CONFIG_CHRP is not set +# CONFIG_ALL_PPC is not set +CONFIG_MBX=y +CONFIG_MACH_SPECIFIC=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +# CONFIG_MODULES is not set +CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y +CONFIG_NET=y +# CONFIG_SYSCTL is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +# CONFIG_PMAC_CONSOLE is not set +# CONFIG_MAC_KEYBOARD is not set +# CONFIG_MAC_FLOPPY is not set +# CONFIG_PROC_DEVICETREE is not set +# CONFIG_XMON is not set +CONFIG_VGA_CONSOLE=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_IDE is not set +# CONFIG_BLK_DEV_HD_ONLY is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_RARP is not set +CONFIG_IP_NOSR=y +# CONFIG_SKB_LARGE is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_EISA is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_WAN_DRIVERS is not set +# CONFIG_LAPBETHER is not set +# CONFIG_X25_ASY is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set +# CONFIG_CDROM is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_PROC_FS is not set +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set +# CONFIG_SMB_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_NLS is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index 8eaa49e92..9d0914979 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -35,11 +35,11 @@ #include <asm/system.h> #include <asm/uaccess.h> -#ifdef CONFIG_XMON -extern void xmon(struct pt_regs *); -extern void (*xmon_fault_handler)(struct pt_regs *); -extern int xmon_dabr_match(struct pt_regs *); -int xmon_kernel_faults = 0; +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +extern void (*debugger)(struct pt_regs *); +extern void (*debugger_fault_handler)(struct pt_regs *); +extern int (*debugger_dabr_match)(struct pt_regs *); +int debugger_kernel_faults = 0; #endif unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */ @@ -72,14 +72,14 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, (regs->trap == 0x400)?"instr":"data" );*/ -#ifdef CONFIG_XMON - if (xmon_fault_handler && regs->trap == 0x300) { - xmon_fault_handler(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler && regs->trap == 0x300) { + debugger_fault_handler(regs); return; } if (error_code & 0x00400000) { /* DABR match */ - if (xmon_dabr_match(regs)) + if (debugger_dabr_match(regs)) return; } #endif @@ -90,9 +90,9 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, printk("page fault in interrupt handler, addr=%lx\n", address); show_regs(regs); -#ifdef CONFIG_XMON - if (xmon_kernel_faults) - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); #endif } } @@ -112,11 +112,22 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, goto bad_area; good_area: +#ifdef CONFIG_6xx if (error_code & 0x95700000) /* an error such as lwarx to I/O controller space, address matching DABR, eciwx, etc. */ +#endif /* CONFIG_6xx */ +#ifdef CONFIG_8xx + /* The MPC8xx seems to always set 0x80000000, which is + * "undefined". Of those that can be set, this is the only + * one which seems bad. + */ + if (error_code & 0x10000000) + /* Guarded storage error. */ +#endif /* CONFIG_8xx */ goto bad_area; + /* a write */ if (error_code & 0x02000000) { if (!(vma->vm_flags & VM_WRITE)) @@ -190,14 +201,47 @@ bad_page_fault(struct pt_regs *regs, unsigned long address) /* kernel has accessed a bad area */ show_regs(regs); print_backtrace( (unsigned long *)regs->gpr[1] ); -#ifdef CONFIG_XMON - if (xmon_kernel_faults) - xmon(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); #endif panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", regs->nip,regs->link,address,current->comm,current->pid); } +/* + * I need a va to pte function for the MPC8xx so I can set the cache + * attributes on individual pages used by the Communication Processor + * Module. + */ +pte_t *va_to_pte(struct task_struct *tsk, unsigned long address) +{ + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + + dir = pgd_offset(tsk->mm, address & PAGE_MASK); + if (dir) + { + pmd = pmd_offset(dir, address & PAGE_MASK); + if (pmd && pmd_present(*pmd)) + { + pte = pte_offset(pmd, address & PAGE_MASK); + if (pte && pte_present(*pte)) + { + return(pte); + } + } else + { + return (0); + } + } else + { + return (0); + } + return (0); +} + unsigned long va_to_phys(unsigned long address) { pgd_t *dir; @@ -226,6 +270,57 @@ unsigned long va_to_phys(unsigned long address) return (0); } +void +print_8xx_pte(struct mm_struct *mm, unsigned long addr) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + + printk(" pte @ 0x%8lx: ", addr); + pgd = pgd_offset(mm, addr & PAGE_MASK); + if (pgd) { + pmd = pmd_offset(pgd, addr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) { + pte = pte_offset(pmd, addr & PAGE_MASK); + if (pte) { + printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n", + (long)pgd, (long)pte, (long)pte_val(*pte)); + } + else { + printk("no pte\n"); + } + } + else { + printk("no pmd\n"); + } + } + else { + printk("no pgd\n"); + } +} + +int +get_8xx_pte(struct mm_struct *mm, unsigned long addr) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + int retval = 0; + + pgd = pgd_offset(mm, addr & PAGE_MASK); + if (pgd) { + pmd = pmd_offset(pgd, addr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) { + pte = pte_offset(pmd, addr & PAGE_MASK); + if (pte) { + retval = (int)pte_val(*pte); + } + } + } + return(retval); +} + #if 0 /* * Misc debugging functions. Please leave them here. -- Cort diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index 273076d15..5967e29e6 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -7,6 +7,7 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -31,6 +32,7 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/stddef.h> +#include <linux/vmalloc.h> #include <asm/prom.h> #include <asm/io.h> #include <asm/mmu_context.h> @@ -40,12 +42,27 @@ #ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> /* for initrd_* */ #endif +#ifdef CONFIG_8xx +#include <asm/8xx_immap.h> +#endif +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif -int prom_trashed; -int next_mmu_context; +#ifndef CONFIG_8xx unsigned long _SDR1; PTE *Hash, *Hash_end; unsigned long Hash_size, Hash_mask; +#endif /* CONFIG_8xx */ + +/* ifdef APUS specific stuff until the merge is completed. -jskov */ +#ifdef CONFIG_APUS +#include <asm/setup.h> +#include <asm/amigahw.h> +#endif + +int prom_trashed; +int next_mmu_context; unsigned long *end_of_DRAM; int mem_init_done; extern pgd_t swapper_pg_dir[]; @@ -55,10 +72,16 @@ extern char __init_begin, __init_end; extern RESIDUAL res; char *klimit = _end; struct device_node *memory_node; +unsigned long ioremap_base; +unsigned long ioremap_bot; +#ifndef __SMP__ +struct pgtable_cache_struct quicklists; +#endif -void *find_mem_piece(unsigned, unsigned); -static void mapin_ram(void); +#ifndef CONFIG_8xx static void hash_init(void); +#endif /* CONFIG_8xx */ +static void mapin_ram(void); static void *MMU_get_page(void); void map_page(struct task_struct *, unsigned long va, unsigned long pa, int flags); @@ -68,6 +91,38 @@ extern unsigned long *find_end_of_memory(void); extern struct task_struct *current_set[NR_CPUS]; +#ifdef CONFIG_MBX +/* This is a big hack that may not yet work correctly. + * The MBX8xx boards have a single DIMM socket for additional memory. + * Although it appears you can set magical locations in the serial + * EEPROM to get EPPC-Bug to configure this memory, there are no tools + * (i.e. commands) to make this easy. If you screw up, you will most + * likely end up with a board that will not boot until you find a + * way to program the EEPROM correctly. I decided to simply program + * the memory controller here to add the additional memory. + * The reason this may not work correctly is that depending upon the + * on-board and DIMM memory size, there may be holes in the physical + * address space. This is the case for me, I have a 4 MB local memory + * and a 32 MB DIMM. + * The DIMM is 64 bits wide, and we see it as two banks of 32 bit + * memory. The holes are caused by the requirement to map the + * memory on a natural alignment, that is a 16 MB bank must begin on + * a 16 MB boundary. The DIMM_SIZE below represents the size of the + * bank, which is the total size divided by two. + * Although I may not have all of this working, the intention is to + * mark all of the page maps in the "hole" as reserved, and adjust + * num_physpages accordingly. In the current implementation, this + * seems to work, but there are some assumptions about contiguous + * memory. The correct solution is to modify the memory allocators + * to know about holes, but that will have to wait for another day. + * + * define DIMM_8xx to enable this feature. + * define DIMM_SIZE to reflect the bank size (DIMM size divided by two). + */ +/*#define DIMM_8xx 1 */ +#define DIMM_SIZE (16 * 1024 * 1024) +#endif /* CONFIG_MBX */ + /* * this tells the system to map all of ram with the segregs * (i.e. page tables) instead of the bats. @@ -77,6 +132,34 @@ extern struct task_struct *current_set[NR_CPUS]; /* optimization for 603 to load the tlb directly from the linux table */ #define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */ +void __bad_pte(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + clear_page((unsigned long)pte); + pmd_val(*pmd) = (unsigned long)pte; + return pte + offset; + } + pmd_val(*pmd) = (unsigned long)BAD_PAGETABLE; + return NULL; + } + free_page((unsigned long)pte); + if (pmd_bad(*pmd)) { + __bad_pte(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -121,65 +204,32 @@ struct mem_pieces phys_mem; struct mem_pieces phys_avail; struct mem_pieces prom_mem; -static void get_mem_prop(char *, struct mem_pieces *); -static void sort_mem_pieces(struct mem_pieces *); -static void coalesce_mem_pieces(struct mem_pieces *); -static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); +void *find_mem_piece(unsigned, unsigned); static void print_mem_pieces(struct mem_pieces *); -static void -sort_mem_pieces(struct mem_pieces *mp) -{ - unsigned long a, s; - int i, j; - - for (i = 1; i < mp->n_regions; ++i) { - a = mp->regions[i].address; - s = mp->regions[i].size; - for (j = i - 1; j >= 0; --j) { - if (a >= mp->regions[j].address) - break; - mp->regions[j+1] = mp->regions[j]; - } - mp->regions[j+1].address = a; - mp->regions[j+1].size = s; - } -} - -static void -coalesce_mem_pieces(struct mem_pieces *mp) +/* + * Scan a region for a piece of a given size with the required alignment. + */ +void * +find_mem_piece(unsigned size, unsigned align) { - unsigned long a, e; - int i, j, d; + int i; + unsigned a, e; + struct mem_pieces *mp = &phys_avail; - d = 0; - for (i = 0; i < mp->n_regions; i = j) { + for (i = 0; i < mp->n_regions; ++i) { a = mp->regions[i].address; e = a + mp->regions[i].size; - for (j = i + 1; j < mp->n_regions - && mp->regions[j].address <= e; ++j) - e = mp->regions[j].address + mp->regions[j].size; - mp->regions[d].address = a; - mp->regions[d].size = e - a; - ++d; + a = (a + align - 1) & -align; + if (a + size <= e) { + remove_mem_piece(mp, a, size, 1); + return __va(a); + } } - mp->n_regions = d; -} - -/* - * Add some memory to an array of pieces - */ -static void -append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size) -{ - struct reg_property *rp; - - if (mp->n_regions >= MAX_MEM_REGIONS) - return; - rp = &mp->regions[mp->n_regions++]; - rp->address = start; - rp->size = size; + printk("Couldn't find %u bytes at %u alignment\n", size, align); + abort(); + return NULL; } /* @@ -252,33 +302,73 @@ print_mem_pieces(struct mem_pieces *mp) printk("\n"); } -/* - * Scan a region for a piece of a given size with the required alignment. - */ -void * -find_mem_piece(unsigned size, unsigned align) + + +#ifndef CONFIG_8xx +static void hash_init(void); +static void get_mem_prop(char *, struct mem_pieces *); +static void sort_mem_pieces(struct mem_pieces *); +static void coalesce_mem_pieces(struct mem_pieces *); +static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); + +static void +sort_mem_pieces(struct mem_pieces *mp) { - int i; - unsigned a, e; - struct mem_pieces *mp = &phys_avail; + unsigned long a, s; + int i, j; - for (i = 0; i < mp->n_regions; ++i) { + for (i = 1; i < mp->n_regions; ++i) { a = mp->regions[i].address; - e = a + mp->regions[i].size; - a = (a + align - 1) & -align; - if (a + size <= e) { - remove_mem_piece(mp, a, size, 1); - return __va(a); + s = mp->regions[i].size; + for (j = i - 1; j >= 0; --j) { + if (a >= mp->regions[j].address) + break; + mp->regions[j+1] = mp->regions[j]; } + mp->regions[j+1].address = a; + mp->regions[j+1].size = s; } - printk("Couldn't find %u bytes at %u alignment\n", size, align); - abort(); - return NULL; +} + +static void +coalesce_mem_pieces(struct mem_pieces *mp) +{ + unsigned long a, e; + int i, j, d; + + d = 0; + for (i = 0; i < mp->n_regions; i = j) { + a = mp->regions[i].address; + e = a + mp->regions[i].size; + for (j = i + 1; j < mp->n_regions + && mp->regions[j].address <= e; ++j) + e = mp->regions[j].address + mp->regions[j].size; + mp->regions[d].address = a; + mp->regions[d].size = e - a; + ++d; + } + mp->n_regions = d; +} + +/* + * Add some memory to an array of pieces + */ +static void +append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size) +{ + struct reg_property *rp; + + if (mp->n_regions >= MAX_MEM_REGIONS) + return; + rp = &mp->regions[mp->n_regions++]; + rp->address = start; + rp->size = size; } /* * Read in a property describing some pieces of memory. */ + static void get_mem_prop(char *name, struct mem_pieces *mp) { @@ -365,6 +455,41 @@ unsigned long *pmac_find_end_of_memory(void) return __va(total); } +#endif /* CONFIG_8xx */ + +#ifdef CONFIG_APUS +#define HARDWARE_MAPPED_SIZE (512*1024) +unsigned long *apus_find_end_of_memory(void) +{ + unsigned long kstart, ksize; + + /* Add the chunk that ADOS does not see. Removed again below. */ + m68k_memory[0].size += HARDWARE_MAPPED_SIZE; + + append_mem_piece(&phys_mem, m68k_memory[0].addr, m68k_memory[0].size); + + phys_avail = phys_mem; + kstart = __pa(_stext); + ksize = PAGE_ALIGN(klimit - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 1); + + /* Remove the upper HARDWARE_MAPPED_SIZE bytes where the address + * range 0xfff00000-0xfffx0000 is mapped to. + * We do it this way to ensure that the memory registered in the + * system has a power-of-two size. + */ + remove_mem_piece(&phys_avail, + (m68k_memory[0].addr + m68k_memory[0].size + - HARDWARE_MAPPED_SIZE), + HARDWARE_MAPPED_SIZE, 1); + + /* FIXME:APUS: Only handles one block of memory! Problem is + * that the VTOP/PTOV code in head.S would be a mess if it had + * to handle more than one block. + */ + return __va(m68k_memory[0].addr + m68k_memory[0].size); +} +#endif /* * Find some memory for setup_arch to return. @@ -381,6 +506,16 @@ unsigned long find_available_memory(void) unsigned long start, end; free = 0; + if (_machine == _MACH_mbx) { + /* Return the first, not the last region, because we + * may not yet have properly initialized the additonal + * memory DIMM. + */ + a = PAGE_ALIGN(phys_avail.regions[0].address); + avail_start = (unsigned long) __va(a); + return avail_start; + } + for (i = 0; i < phys_avail.n_regions - 1; ++i) { start = phys_avail.regions[i].address; end = start + phys_avail.regions[i].size; @@ -396,7 +531,7 @@ unsigned long find_available_memory(void) void show_mem(void) { int i,free = 0,total = 0,reserved = 0; - int shared = 0; + int shared = 0, cached = 0; struct task_struct *p; printk("Mem-info:\n"); @@ -407,6 +542,8 @@ void show_mem(void) total++; if (PageReserved(mem_map+i)) reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; else if (!atomic_read(&mem_map[i].count)) free++; else @@ -416,6 +553,8 @@ void show_mem(void) printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%d pages in page table cache\n",(int)pgtable_cache_size); show_buffers(); #ifdef CONFIG_NET show_net_buffers(); @@ -487,6 +626,7 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) int codepages = 0; int datapages = 0; int initpages = 0; + extern unsigned int rtas_data, rtas_size; end_mem &= PAGE_MASK; high_memory = (void *) end_mem; @@ -496,6 +636,7 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) /* mark usable pages in the mem_map[] */ start_mem = PAGE_ALIGN(start_mem); +#ifndef CONFIG_8xx remove_mem_piece(&phys_avail, __pa(avail_start), start_mem - avail_start, 1); @@ -520,7 +661,52 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); } prom_trashed = 1; - +#else /* CONFIG_8xx */ + /* When we get here, all of the page maps have been set up and + * Linux thinks we have contiguous memory. Since the MBX can + * have memory holes, we need to compensate for that here. + * The memory holes are currently pages marked reserved (all + * pages right now are marked reserved). + * All of the memory allocated by the kernel up to this point + * had to come from region 0. + */ + + /* First, unreserve all memory from the page following start_mem + * to the end of region 0. + */ + for (addr = start_mem + PAGE_SIZE ; + addr < (ulong) __va(phys_mem.regions[0].size); + addr += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + } + + /* Now add any additional regions to the system. + */ + for (i = 1; i < phys_avail.n_regions; ++i) { + a = (unsigned long) __va(phys_avail.regions[i].address); + lim = a + phys_avail.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + } + phys_avail.n_regions = 0; /* Nothing available, kernel owns */ + /* Count up the size of the holes. We look for the space + * between the end of one region and the start of the next. + */ + lim = 0; + for (i = 0; i < phys_mem.n_regions-1; ++i) { + a = (unsigned long) phys_mem.regions[i].address; + a += phys_mem.regions[i].size; + lim += phys_mem.regions[i+1].address - a; + } + + /* It appears that num_physpages is only used for quota checking, + * when pages are locked down. We subtract the size of the holes + * from it now. + */ + num_physpages -= lim/PAGE_SIZE; +#endif /* CONFIG_8xx */ + for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { if (PageReserved(mem_map + MAP_NR(addr))) { if (addr < (ulong) etext) @@ -537,7 +723,12 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) if (!initrd_start || addr < (initrd_start & PAGE_MASK) || addr >= initrd_end) #endif /* CONFIG_BLK_DEV_INITRD */ - free_page(addr); +#ifndef CONFIG_8xx + if ( !rtas_data || + addr < (rtas_data & PAGE_MASK) || + addr >= (rtas_data+rtas_size)) +#endif /* CONFIG_8xx */ + free_page(addr); } printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n", @@ -594,6 +785,7 @@ void si_meminfo(struct sysinfo *val) return; } +#ifndef CONFIG_8xx union ubat { /* BAT register values to be loaded */ BAT bat; P601_BAT bat_601; @@ -692,7 +884,7 @@ unsigned long *prep_find_end_of_memory(void) return (__va(total)); } - +#endif /* CONFIG_8xx */ /* * Map in all of physical memory starting at KERNELBASE. @@ -702,8 +894,9 @@ unsigned long *prep_find_end_of_memory(void) static void mapin_ram() { int i; - unsigned long tot, bl, done; unsigned long v, p, s, f; +#ifndef CONFIG_8xx + unsigned long tot, mem_base, bl, done; #ifndef MAP_RAM_WITH_SEGREGS /* Set up BAT2 and if necessary BAT3 to cover RAM. */ @@ -711,15 +904,17 @@ static void mapin_ram() for (bl = 128<<10; bl < 256<<20; bl <<= 1) if (bl * 2 > tot) break; - setbat(2, KERNELBASE, 0, bl, RAM_PAGE); - done = __pa(bat_addrs[2].limit) + 1; + + mem_base = __pa(KERNELBASE); + setbat(2, KERNELBASE, mem_base, bl, RAM_PAGE); + done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1; if (done < tot) { /* use BAT3 to cover a bit more */ tot -= done; for (bl = 128<<10; bl < 256<<20; bl <<= 1) if (bl * 2 > tot) break; - setbat(3, KERNELBASE+done, done, bl, RAM_PAGE); + setbat(3, KERNELBASE+done, mem_base+done, bl, RAM_PAGE); } #endif @@ -734,6 +929,27 @@ static void mapin_ram() /* On the powerpc, no user access forces R/W kernel access */ f |= _PAGE_USER; +#else /* CONFIG_8xx */ + for (i = 0; i < phys_mem.n_regions; ++i) { + v = (ulong)__va(phys_mem.regions[i].address); + p = phys_mem.regions[i].address; + for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { + /* On the MPC8xx, we want the page shared so we + * don't get ASID compares on kernel space. + */ + f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED; + + /* I don't really need the rest of this code, but + * I grabbed it because I think the line: + * f |= _PAGE_USER + * is incorrect. It needs to be set to bits we + * don't define to cause a kernel read-only. On + * the MPC8xx, the PAGE_DIRTY takes care of that + * for us (along with the RW software state). + */ + if ((char *) v < _stext || (char *) v >= etext) + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; +#endif /* CONFIG_8xx */ map_page(&init_task, v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; @@ -741,6 +957,7 @@ static void mapin_ram() } } +#ifndef CONFIG_8xx /* * Initialize the hash table and patch the instructions in head.S. */ @@ -820,7 +1037,7 @@ static void hash_init(void) Hash_end = 0; } - +#endif /* CONFIG_8xx */ /* * Do very early mm setup such as finding the size of memory @@ -832,13 +1049,19 @@ static void hash_init(void) void MMU_init(void) { +#ifndef CONFIG_8xx if (have_of) end_of_DRAM = pmac_find_end_of_memory(); +#ifdef CONFIG_APUS + else if (_machine == _MACH_apus ) + end_of_DRAM = apus_find_end_of_memory(); +#endif else /* prep */ end_of_DRAM = prep_find_end_of_memory(); hash_init(); _SDR1 = __pa(Hash) | (Hash_mask >> 10); + ioremap_base = 0xf8000000; /* Map in all of RAM starting at KERNELBASE */ mapin_ram(); @@ -856,16 +1079,40 @@ MMU_init(void) IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0)); break; case _MACH_chrp: - setbat(0, 0xc0000000, 0xc0000000, 0x10000000, IO_PAGE); - setbat(1, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE); - setbat(3, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); + setbat(0, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE); break; case _MACH_Pmac: setbat(0, 0xf3000000, 0xf3000000, 0x100000, IO_PAGE); - /* this is used to cover registers used by smp boards -- Cort */ - setbat(3, 0xf8000000, 0xf8000000, 0x100000, IO_PAGE); + ioremap_base = 0xf0000000; break; +#ifdef CONFIG_APUS + case _MACH_apus: + /* Map Cyberstorm PPC registers. */ + /* FIXME:APUS: Performance penalty here. Restrict it + * to the Cyberstorm registers. + */ + setbat(0, 0xfff00000, 0xfff00000, 0x00080000, IO_PAGE); + /* Map chip and ZorroII memory */ + setbat(1, zTwoBase, 0x00000000, 0x01000000, IO_PAGE); + break; +#endif } + ioremap_bot = ioremap_base; +#else /* CONFIG_8xx */ + + /* Map in all of RAM starting at KERNELBASE */ + mapin_ram(); + + /* Now map in some of the I/O space that is generically needed + * or shared with multiple devices. + * All of this fits into the same 4Mbyte region, so it only + * requires one page table page. + */ + ioremap(NVRAM_ADDR, NVRAM_SIZE); + ioremap(MBX_CSR_ADDR, MBX_CSR_SIZE); + ioremap(MBX_IMAP_ADDR, MBX_IMAP_SIZE); + ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE); +#endif /* CONFIG_8xx */ } static void * @@ -887,27 +1134,98 @@ MMU_get_page() void * ioremap(unsigned long addr, unsigned long size) { - unsigned long p, end = addr + size; + return __ioremap(addr, size, _PAGE_NO_CACHE); +} + +void * +__ioremap(unsigned long addr, unsigned long size, unsigned long flags) +{ + unsigned long p, v, i; + + /* + * Choose an address to map it to. + * Once the vmalloc system is running, we use it. + * Before then, we map addresses >= ioremap_base + * virt == phys; for addresses below this we use + * space going down from ioremap_base (ioremap_bot + * records where we're up to). + * + * We should also look out for a frame buffer and + * map it with a free BAT register, if there is one. + */ + p = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - p; + if (size == 0) + return NULL; - for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE) - map_page(&init_task, p, p, - pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); - return (void *) addr; + if (mem_init_done) { + struct vm_struct *area; + area = get_vm_area(size); + if (area == 0) + return NULL; + v = VMALLOC_VMADDR(area->addr); + } else { + if (p >= ioremap_base) + v = p; + else + v = (ioremap_bot -= size); + } + + flags |= pgprot_val(PAGE_KERNEL); + if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU)) + flags |= _PAGE_GUARDED; + for (i = 0; i < size; i += PAGE_SIZE) + map_page(&init_task, v+i, p+i, flags); + + return (void *) (v + (addr & ~PAGE_MASK)); } -void iounmap(unsigned long *addr) +void iounmap(void *addr) { /* XXX todo */ } +unsigned long iopa(unsigned long addr) +{ + unsigned long idx; + pmd_t *pd; + pte_t *pg; +#ifndef CONFIG_8xx + int b; +#endif + idx = addr & ~PAGE_MASK; + addr = addr & PAGE_MASK; + +#ifndef CONFIG_8xx + /* Check the BATs */ + for (b = 0; b < 4; ++b) + if (addr >= bat_addrs[b].start && addr <= bat_addrs[b].limit) + return bat_addrs[b].phys | idx; +#endif /* CONFIG_8xx */ + /* Do we have a page table? */ + if (init_task.mm->pgd == NULL) + return 0; + + /* Use upper 10 bits of addr to index the first level map */ + pd = (pmd_t *) (init_task.mm->pgd + (addr >> PGDIR_SHIFT)); + if (pmd_none(*pd)) + return 0; + + /* Use middle 10 bits of addr to index the second-level map */ + pg = pte_offset(pd, addr); + return (pte_val(*pg) & PAGE_MASK) | idx; +} + void map_page(struct task_struct *tsk, unsigned long va, unsigned long pa, int flags) { pmd_t *pd; pte_t *pg; +#ifndef CONFIG_8xx int b; - +#endif + if (tsk->mm->pgd == NULL) { /* Allocate upper level page map */ tsk->mm->pgd = (pgd_t *) MMU_get_page(); @@ -915,6 +1233,7 @@ map_page(struct task_struct *tsk, unsigned long va, /* Use upper 10 bits of VA to index the first level map */ pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT)); if (pmd_none(*pd)) { +#ifndef CONFIG_8xx /* * Need to allocate second-level table, but first * check whether this address is already mapped by @@ -927,14 +1246,16 @@ map_page(struct task_struct *tsk, unsigned long va, return; } } +#endif /* CONFIG_8xx */ pg = (pte_t *) MMU_get_page(); pmd_val(*pd) = (unsigned long) pg; } /* Use middle 10 bits of VA to index the second-level map */ pg = pte_offset(pd, va); set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); - /*flush_hash_page(va >> 28, va);*/ +#ifndef CONFIG_8xx flush_hash_page(0, va); +#endif } /* @@ -957,11 +1278,14 @@ map_page(struct task_struct *tsk, unsigned long va, void local_flush_tlb_all(void) { +#ifndef CONFIG_8xx memset(Hash, 0, Hash_size); _tlbia(); +#else + asm volatile ("tlbia" : : ); +#endif } - /* * Flush all the (user) entries for the address space described * by mm. We can't rely on mm->mmap describing all the entries @@ -970,33 +1294,43 @@ local_flush_tlb_all(void) void local_flush_tlb_mm(struct mm_struct *mm) { +#ifndef CONFIG_8xx mm->context = NO_CONTEXT; if (mm == current->mm) { get_mmu_context(current); /* done by get_mmu_context() now -- Cort */ /*set_context(current->mm->context);*/ } +#else + asm volatile ("tlbia" : : ); +#endif } void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { +#ifndef CONFIG_8xx if (vmaddr < TASK_SIZE) flush_hash_page(vma->vm_mm->context, vmaddr); else flush_hash_page(0, vmaddr); +#else + asm volatile ("tlbia" : : ); +#endif } -/* for each page addr in the range, call MMU_invalidate_page() - if the range is very large and the hash table is small it might be faster to - do a search of the hash table and just invalidate pages that are in the range - but that's for study later. - -- Cort - */ +/* + * for each page addr in the range, call MMU_invalidate_page() + * if the range is very large and the hash table is small it might be + * faster to do a search of the hash table and just invalidate pages + * that are in the range but that's for study later. + * -- Cort + */ void local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { +#ifndef CONFIG_8xx start &= PAGE_MASK; if (end - start > 20 * PAGE_SIZE) @@ -1009,6 +1343,9 @@ local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long e { flush_hash_page(mm->context, start); } +#else + asm volatile ("tlbia" : : ); +#endif } /* @@ -1020,6 +1357,7 @@ local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long e void mmu_context_overflow(void) { +#ifndef CONFIG_8xx struct task_struct *tsk; printk(KERN_DEBUG "mmu_context_overflow\n"); @@ -1034,6 +1372,12 @@ mmu_context_overflow(void) /* make sure current always has a context */ current->mm->context = MUNGE_CONTEXT(++next_mmu_context); set_context(current->mm->context); +#else + /* We set the value to -1 because it is pre-incremented before + * before use. + */ + next_mmu_context = -1; +#endif } #if 0 @@ -1090,3 +1434,69 @@ void local_flush_cache_range(struct mm_struct *mm, unsigned long start, } #endif +#ifdef CONFIG_MBX +/* + * This is a big hack right now, but it may turn into something real + * someday. + * + * For the MBX860 (at this time anyway), there is nothing to initialize + * associated the the PROM. Rather than include all of the prom.c + * functions in the image just to get prom_init, all we really need right + * now is the initialization of the physical memory region. + */ +void +set_mbx_memory(void) +{ + unsigned long kstart, ksize; + bd_t *binfo; +#ifdef DIMM_8xx + volatile memctl8xx_t *mcp; +#endif + + binfo = (bd_t *)&res; + + /* The MBX can have up to three memory regions, the on-board + * DRAM plus two more banks of DIMM socket memory. The DIMM is + * 64 bits, seen from the processor as two 32 bit banks. + * The on-board DRAM is reflected in the board information + * structure, and is either 4 Mbytes or 16 Mbytes. + * I think there is a way to program the serial EEPROM information + * so EPPC-Bug will initialize this memory, but I have not + * done that and it may not be a wise thing to do. If you + * remove the DIMM without reprogramming the EEPROM, bad things + * could happen since EPPC-Bug tries to use the upper 128K of + * memory. + */ + phys_mem.n_regions = 1; + phys_mem.regions[0].address = 0; + phys_mem.regions[0].size = binfo->bi_memsize; + end_of_DRAM = __va(binfo->bi_memsize); + +#ifdef DIMM_8xx + /* This is a big hack. It assumes my 32 Mbyte DIMM in a 40 MHz + * MPC860. Don't do this (or change this) if you are running + * something else. + */ + mcp = (memctl8xx_t *)(&(((immap_t *)MBX_IMAP_ADDR)->im_memctl)); + + mcp->memc_or2 = (~(DIMM_SIZE-1) | 0x00000400); + mcp->memc_br2 = DIMM_SIZE | 0x00000081; + mcp->memc_or3 = (~((2*DIMM_SIZE)-1) | 0x00000400); + mcp->memc_br3 = 2*DIMM_SIZE | 0x00000081; + + + phys_mem.regions[phys_mem.n_regions].address = DIMM_SIZE; + phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE; + phys_mem.regions[phys_mem.n_regions].address = 2 * DIMM_SIZE; + phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE; + + end_of_DRAM = __va(3 * DIMM_SIZE); +#endif + + phys_avail = phys_mem; + + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(_end - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 0); +} +#endif diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig index 529b33138..8cf7eb8a6 100644 --- a/arch/ppc/pmac_defconfig +++ b/arch/ppc/pmac_defconfig @@ -1,5 +1,5 @@ # -# Automatically generated by make menuconfig: don't edit +# Automatically generated make config: don't edit # # @@ -7,11 +7,12 @@ # CONFIG_PPC=y CONFIG_NATIVE=y +CONFIG_PPC6XX=y +# CONFIG_PPC8XX is not set CONFIG_MACH_SPECIFIC=y CONFIG_PMAC=y # CONFIG_PREP is not set # CONFIG_CHRP is not set -CONFIG_COMMON=y # # General setup @@ -19,8 +20,9 @@ CONFIG_COMMON=y CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y # CONFIG_MODVERSIONS is not set -CONFIG_KMOD=y +CONFIG_KERNELD=y CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -28,14 +30,20 @@ CONFIG_SYSVIPC=y CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y CONFIG_BINFMT_MISC=m -CONFIG_BINFMT_JAVA=m +# CONFIG_BINFMT_JAVA is not set +# CONFIG_ABSTRACT_CONSOLE is not set CONFIG_PMAC_CONSOLE=y CONFIG_MAC_KEYBOARD=y CONFIG_MAC_FLOPPY=y +CONFIG_MACMOUSE=y CONFIG_PROC_DEVICETREE=y # CONFIG_XMON is not set +CONFIG_CONTROL_VIDEO=y +CONFIG_PLATINUM_VIDEO=y +CONFIG_VALKYRIE_VIDEO=y CONFIG_ATY_VIDEO=y CONFIG_IMSTT_VIDEO=y +CONFIG_CHIPS_VIDEO=y # # Plug and Play support @@ -47,6 +55,10 @@ CONFIG_IMSTT_VIDEO=y # # CONFIG_BLK_DEV_FD is not set CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y @@ -55,14 +67,20 @@ CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_RZ1000 is not set -# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_BLK_DEV_IDEPCI is not set # CONFIG_IDE_CHIPSETS is not set + +# +# Additional Block Devices +# CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_EZ is not set +CONFIG_PARIDE_PARPORT=m +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # @@ -77,6 +95,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set CONFIG_NET_ALIAS=y +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -85,37 +104,56 @@ CONFIG_IP_MULTICAST=y # CONFIG_IP_ACCT is not set # CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set -CONFIG_NET_IPIP=m +# CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=y # CONFIG_SYN_COOKIES is not set + +# +# (it is safe to leave these untouched) +# CONFIG_INET_RARP=y CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set + +# +# +# # CONFIG_IPX is not set # CONFIG_ATALK is not set -# CONFIG_AX25 is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set # CONFIG_LLC is not set # CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set # CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE is not set # # SCSI support # CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -124,7 +162,12 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set -# CONFIG_SCSI_AIC7XXX is not set +CONFIG_SCSI_AIC7XXX=m +# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set +# CONFIG_OVERRIDE_CMDS is not set +# CONFIG_AIC7XXX_PAGE_ENABLE is not set +CONFIG_AIC7XXX_PROC_STATS=y +CONFIG_AIC7XXX_RESET_DELAY=15 # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set @@ -134,12 +177,16 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set # CONFIG_SCSI_PPA is not set # CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_SEAGATE is not set @@ -158,27 +205,47 @@ CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set -# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y CONFIG_MACE=y -CONFIG_DEC_ELCP=m # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set -# CONFIG_NET_EISA is not set +CONFIG_NET_EISA=y +# CONFIG_PCNET32 is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=m +CONFIG_DEC_ELCP=m +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set # CONFIG_PLIP is not set CONFIG_PPP=m -# CONFIG_NET_RADIO is not set + +# +# CCP compressors for PPP are only built as modules. +# # CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_SHAPER is not set # +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# # ISDN subsystem # # CONFIG_ISDN is not set @@ -187,28 +254,68 @@ CONFIG_PPP=m # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set +CONFIG_CDROM=y # # Filesystems # # CONFIG_QUOTA is not set -CONFIG_MINIX_FS=m +# CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y CONFIG_ISO9660_FS=y -# CONFIG_NLS is not set +# CONFIG_JOLIET is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_NFSD is not set +CONFIG_NFSD=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=m # CONFIG_ROMFS_FS is not set CONFIG_AUTOFS_FS=y # CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set CONFIG_MAC_PARTITION=y +CONFIG_NLS=y + +# +# Native Language Support +# +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set # # Character devices @@ -217,20 +324,25 @@ CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_SOFTCURSOR is not set CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_PRINTER is not set # CONFIG_MOUSE is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set -# CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set # CONFIG_VIDEO_DEV is not set -# CONFIG_VIDEO_BT848 is not set CONFIG_NVRAM=y # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig index 147fa3005..a323e652e 100644 --- a/arch/ppc/prep_defconfig +++ b/arch/ppc/prep_defconfig @@ -6,12 +6,14 @@ # Platform support # CONFIG_PPC=y -CONFIG_NATIVE=y -CONFIG_MACH_SPECIFIC=y +CONFIG_6xx=y +# CONFIG_8xx is not set # CONFIG_PMAC is not set CONFIG_PREP=y # CONFIG_CHRP is not set -CONFIG_COMMON=y +# CONFIG_ALL_PPC is not set +# CONFIG_MBX is not set +CONFIG_MACH_SPECIFIC=y # # General setup @@ -19,20 +21,25 @@ CONFIG_COMMON=y CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y CONFIG_MODVERSIONS=y -CONFIG_KMOD=y +CONFIG_KERNELD=y CONFIG_PCI=y +# CONFIG_PCI_QUIRKS is not set # CONFIG_PCI_OPTIMIZE is not set +CONFIG_PCI_OLD_PROC=y CONFIG_NET=y -# CONFIG_SYSCTL is not set +CONFIG_SYSCTL=y CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +# CONFIG_ABSTRACT_CONSOLE is not set # CONFIG_PMAC_CONSOLE is not set # CONFIG_MAC_KEYBOARD is not set # CONFIG_MAC_FLOPPY is not set +# CONFIG_MACMOUSE is not set # CONFIG_PROC_DEVICETREE is not set # CONFIG_XMON is not set CONFIG_VGA_CONSOLE=y @@ -55,14 +62,16 @@ CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_RZ1000 is not set -# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_BLK_DEV_IDEPCI is not set # CONFIG_IDE_CHIPSETS is not set -# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_EZ is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # @@ -77,32 +86,34 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set # CONFIG_IP_PNP is not set -# CONFIG_IP_ACCT is not set -# CONFIG_IP_MASQUERADE is not set +CONFIG_IP_ACCT=y # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_ALIAS is not set -# CONFIG_SYN_COOKIES is not set +CONFIG_SYN_COOKIES=y CONFIG_INET_RARP=y # CONFIG_IP_NOSR is not set CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set -# CONFIG_AX25 is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set # CONFIG_LLC is not set # CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set # CONFIG_NET_SCHED is not set +# CONFIG_NET_PROFILE is not set # # SCSI support @@ -115,6 +126,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_CHR_DEV_SG is not set # CONFIG_SCSI_MULTI_LUN is not set # CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -133,6 +145,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set @@ -143,8 +156,10 @@ CONFIG_SCSI_NCR53C8XX_IOMAPPED=y CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 CONFIG_SCSI_NCR53C8XX_SYNC=5 # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set -# CONFIG_SCSI_PPA is not set # CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_SEAGATE is not set @@ -152,6 +167,7 @@ CONFIG_SCSI_NCR53C8XX_SYNC=5 # CONFIG_SCSI_T128 is not set # CONFIG_SCSI_U14_34F is not set # CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_DEBUG is not set # CONFIG_SCSI_MESH is not set # CONFIG_SCSI_MAC53C94 is not set @@ -162,12 +178,13 @@ CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set -# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_3COM is not set CONFIG_LANCE=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y CONFIG_PCNET32=y @@ -184,14 +201,18 @@ CONFIG_DE4X5=y # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set -# CONFIG_PLIP is not set -CONFIG_PPP=m -# CONFIG_NET_RADIO is not set +CONFIG_PPP=y # CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_SHAPER is not set # +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# # ISDN subsystem # # CONFIG_ISDN is not set @@ -200,6 +221,7 @@ CONFIG_PPP=m # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set +CONFIG_CDROM=y # # Filesystems @@ -208,29 +230,59 @@ CONFIG_PPP=m # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set -# CONFIG_FAT_FS is not set -# CONFIG_MSDOS_FS is not set +CONFIG_JOLIET=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set -# CONFIG_VFAT_FS is not set +CONFIG_VFAT_FS=y CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_NFSD is not set +CONFIG_NFSD=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_ADFS_FS is not set # CONFIG_MAC_PARTITION is not set +CONFIG_NLS=y # # Native Language Support # -# CONFIG_NLS is not set +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set # # Character devices @@ -239,14 +291,9 @@ CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_SOFTCURSOR is not set CONFIG_SERIAL=y -CONFIG_SERIAL_EXTENDED=y -# CONFIG_SERIAL_MANY_PORTS is not set -# CONFIG_SERIAL_SHARE_IRQ is not set -# CONFIG_SERIAL_MULTIPORT is not set -# CONFIG_HUB6 is not set CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_PRINTER is not set CONFIG_MOUSE=y # CONFIG_ATIXL_BUSMOUSE is not set # CONFIG_BUSMOUSE is not set @@ -256,14 +303,18 @@ CONFIG_PSMOUSE=y # CONFIG_PC110_PAD is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set -# CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set # CONFIG_VIDEO_DEV is not set -# CONFIG_VIDEO_BT848 is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound |