diff options
Diffstat (limited to 'arch/ppc/8xx_io')
-rw-r--r-- | arch/ppc/8xx_io/Makefile | 8 | ||||
-rw-r--r-- | arch/ppc/8xx_io/commproc.c | 45 | ||||
-rw-r--r-- | arch/ppc/8xx_io/commproc.h | 87 | ||||
-rw-r--r-- | arch/ppc/8xx_io/enet.c | 2 | ||||
-rw-r--r-- | arch/ppc/8xx_io/fec.c | 985 | ||||
-rw-r--r-- | arch/ppc/8xx_io/uart.c | 290 |
6 files changed, 1314 insertions, 103 deletions
diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile index b33919a5d..0da0a492d 100644 --- a/arch/ppc/8xx_io/Makefile +++ b/arch/ppc/8xx_io/Makefile @@ -8,6 +8,12 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := 8xx_io.a -O_OBJS = commproc.o uart.o enet.o +O_OBJS = commproc.o uart.o +ifdef CONFIG_MBX +O_OBJS += enet.o +endif +ifdef CONFIG_FADS +O_OBJS += fec.o +endif include $(TOPDIR)/Rules.make diff --git a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c index 00bbbe72d..b1ea955ed 100644 --- a/arch/ppc/8xx_io/commproc.c +++ b/arch/ppc/8xx_io/commproc.c @@ -21,6 +21,7 @@ * applications that require more DP ram, we can expand the boundaries * but then we have to be careful of any downloaded microcode. */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -29,7 +30,12 @@ #include <linux/mm.h> #include <linux/interrupt.h> #include <asm/irq.h> +#ifdef CONFIG_MBX #include <asm/mbx.h> +#endif +#ifdef CONFIG_FADS +#include <asm/fads.h> +#endif #include <asm/page.h> #include <asm/pgtable.h> #include <asm/8xx_immap.h> @@ -52,13 +58,13 @@ 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) +m8xx_cpm_reset(uint host_page_addr) { volatile immap_t *imp; volatile cpm8xx_t *commproc; pte_t *pte; - imp = (immap_t *)MBX_IMAP_ADDR; + imp = (immap_t *)IMAP_ADDR; commproc = (cpm8xx_t *)&imp->im_cpm; #ifdef notdef @@ -78,6 +84,10 @@ mbx_cpm_reset(uint host_page_addr) #endif /* Set SDMA Bus Request priority 5. + * On 860T, this also enables FEC priority 6. I am not sure + * this is what we realy want for some applications, but the + * manual recommends it. + * Bit 25, FAM can also be set to use FEC aggressive mode (860T). */ imp->im_siu_conf.sc_sdcr = 1; @@ -99,10 +109,10 @@ mbx_cpm_reset(uint host_page_addr) /* Initialize the CPM interrupt controller. */ - ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr = + ((immap_t *)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; + ((immap_t *)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) @@ -111,7 +121,7 @@ mbx_cpm_reset(uint host_page_addr) /* 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; + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN; } /* CPM interrupt controller interrupt. @@ -124,19 +134,19 @@ cpm_interrupt(int irq, void * dev, struct pt_regs * regs) /* 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; + ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr = 1; + vec = ((volatile immap_t *)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); + ((immap_t *)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); + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr |= (1 << vec); } @@ -160,7 +170,7 @@ cpm_install_handler(int vec, void (*handler)(void *), void *dev_id) (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); + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec); } /* Allocate some memory from the dual ported ram. We may want to @@ -168,7 +178,7 @@ cpm_install_handler(int vec, void (*handler)(void *), void *dev_id) * citizen. */ uint -mbx_cpm_dpalloc(uint size) +m8xx_cpm_dpalloc(uint size) { uint retloc; @@ -185,7 +195,7 @@ mbx_cpm_dpalloc(uint size) * UART "fifos" and the like. */ uint -mbx_cpm_hostalloc(uint size) +m8xx_cpm_hostalloc(uint size) { uint retloc; @@ -201,13 +211,13 @@ mbx_cpm_hostalloc(uint size) /* 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....... + * This assumes the baudrate is 16x oversampled by the uart. */ -#define BRG_INT_CLK (40000000/16) +#define BRG_INT_CLK (((bd_t *)res)->bi_intfreq * 1000000) +#define BRG_UART_CLK (BRG_INT_CLK/16) void -mbx_cpm_setbrg(uint brg, uint rate) +m8xx_cpm_setbrg(uint brg, uint rate) { volatile uint *bp; @@ -215,5 +225,6 @@ mbx_cpm_setbrg(uint brg, uint rate) */ bp = (uint *)&cpmp->cp_brgc1; bp += brg; - *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN; + *bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN; } + diff --git a/arch/ppc/8xx_io/commproc.h b/arch/ppc/8xx_io/commproc.h index 38046a31c..50140ab26 100644 --- a/arch/ppc/8xx_io/commproc.h +++ b/arch/ppc/8xx_io/commproc.h @@ -62,9 +62,9 @@ * 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); +uint m8xx_cpm_dpalloc(uint size); +uint m8xx_cpm_hostalloc(uint size); +void m8xx_cpm_setbrg(uint brg, uint rate); /* Buffer descriptors used by many of the CPM protocols. */ @@ -87,8 +87,16 @@ typedef struct cpm_buf_desc { #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. +/* Parameter RAM offsets. +*/ +#define PROFF_SCC1 ((uint)0x0000) +#define PROFF_SCC2 ((uint)0x0100) +#define PROFF_SCC3 ((uint)0x0200) +#define PROFF_SMC1 ((uint)0x0280) +#define PROFF_SCC4 ((uint)0x0300) +#define PROFF_SMC2 ((uint)0x0380) + +/* Define enough so I can at least use the serial port as a UART. */ typedef struct smc_uart { ushort smc_rbase; /* Rx Buffer descriptor base address */ @@ -114,9 +122,6 @@ typedef struct smc_uart { 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 */ @@ -139,7 +144,7 @@ typedef struct smc_uart { /* SMC Event and Mask register. */ #define SMCM_TXE ((unsigned char)0x10) -#define SMCM_BSY ((unsigned char)0x14) +#define SMCM_BSY ((unsigned char)0x04) #define SMCM_TX ((unsigned char)0x02) #define SMCM_RX ((unsigned char)0x01) @@ -238,6 +243,13 @@ typedef struct smc_uart { #define SCC_TODR_TOD ((ushort)0x8000) +/* SCC Event and Mask register. +*/ +#define SCCM_TXE ((unsigned char)0x10) +#define SCCM_BSY ((unsigned char)0x04) +#define SCCM_TX ((unsigned char)0x02) +#define SCCM_RX ((unsigned char)0x01) + typedef struct scc_param { ushort scc_rbase; /* Rx Buffer descriptor base address */ ushort scc_tbase; /* Tx Buffer descriptor base address */ @@ -317,8 +329,6 @@ typedef struct scc_enet { 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 @@ -397,6 +407,37 @@ typedef struct scc_enet { #define BD_ENET_TX_CSL ((ushort)0x0001) #define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ +/* SCC as UART +*/ +typedef struct scc_uart { + sccp_t scc_genscc; + uint scc_res1; /* Reserved */ + uint scc_res2; /* Reserved */ + ushort scc_maxidl; /* Maximum idle chars */ + ushort scc_idlc; /* temp idle counter */ + ushort scc_brkcr; /* Break count register */ + ushort scc_parec; /* receive parity error counter */ + ushort scc_frmec; /* receive framing error counter */ + ushort scc_nosec; /* receive noise counter */ + ushort scc_brkec; /* receive break condition counter */ + ushort scc_brkln; /* last received break length */ + ushort scc_uaddr1; /* UART address character 1 */ + ushort scc_uaddr2; /* UART address character 2 */ + ushort scc_rtemp; /* Temp storage */ + ushort scc_toseq; /* Transmit out of sequence char */ + ushort scc_char1; /* control character 1 */ + ushort scc_char2; /* control character 2 */ + ushort scc_char3; /* control character 3 */ + ushort scc_char4; /* control character 4 */ + ushort scc_char5; /* control character 5 */ + ushort scc_char6; /* control character 6 */ + ushort scc_char7; /* control character 7 */ + ushort scc_char8; /* control character 8 */ + ushort scc_rccm; /* receive control character mask */ + ushort scc_rccr; /* receive control character register */ + ushort scc_rlbc; /* receive last break character */ +} scc_uart_t; + /* SCC Event and Mask registers when it is used as a UART. */ #define UART_SCCM_GLR ((ushort)0x1000) @@ -411,6 +452,30 @@ typedef struct scc_enet { #define UART_SCCM_TX ((ushort)0x0002) #define UART_SCCM_RX ((ushort)0x0001) +/* The SCC PMSR when used as a UART. +*/ +#define SCU_PMSR_FLC ((ushort)0x8000) +#define SCU_PMSR_SL ((ushort)0x4000) +#define SCU_PMSR_CL ((ushort)0x3000) +#define SCU_PMSR_UM ((ushort)0x0c00) +#define SCU_PMSR_FRZ ((ushort)0x0200) +#define SCU_PMSR_RZS ((ushort)0x0100) +#define SCU_PMSR_SYN ((ushort)0x0080) +#define SCU_PMSR_DRT ((ushort)0x0040) +#define SCU_PMSR_PEN ((ushort)0x0010) +#define SCU_PMSR_RPM ((ushort)0x000c) +#define SCU_PMSR_REVP ((ushort)0x0008) +#define SCU_PMSR_TPM ((ushort)0x0003) +#define SCU_PMSR_TEVP ((ushort)0x0003) + +/* CPM Transparent mode SCC. + */ +typedef struct scc_trans { + sccp_t st_genscc; + uint st_cpres; /* Preset CRC */ + uint st_cmask; /* Constant mask for CRC */ +} scc_trans_t; + /* 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 diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c index 0b5f0452c..8c3401d6f 100644 --- a/arch/ppc/8xx_io/enet.c +++ b/arch/ppc/8xx_io/enet.c @@ -96,7 +96,7 @@ */ /* 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 + * pool. The code may assume these are power of two, so it is best * to keep them that size. * We don't need to allocate pages for the transmitter. We just use * the skbuffer directly. diff --git a/arch/ppc/8xx_io/fec.c b/arch/ppc/8xx_io/fec.c new file mode 100644 index 000000000..2b797a498 --- /dev/null +++ b/arch/ppc/8xx_io/fec.c @@ -0,0 +1,985 @@ +/* + * Fast Ethernet Controller (FECC) driver for Motorola MPC8xx. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * This version of the driver is specific to the FADS implementation, + * since the board contains control registers external to the processor + * for the control of the LevelOne LXT970 transceiver. The MPC860T manual + * describes connections using the internal parallel port I/O, which + * is basically all of Port D. + * + * 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/init.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <asm/8xx_immap.h> +#include <asm/pgtable.h> +#include <asm/fads.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/uaccess.h> +#include "commproc.h" + +/* 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. + */ +#if 1 +#define FEC_ENET_RX_PAGES 4 +#define FEC_ENET_RX_FRSIZE 2048 +#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) +#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) +#define TX_RING_SIZE 8 /* Must be power of two */ +#define TX_RING_MOD_MASK 7 /* for this to work */ +#else +#define FEC_ENET_RX_PAGES 16 +#define FEC_ENET_RX_FRSIZE 2048 +#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) +#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) +#define TX_RING_SIZE 16 /* Must be power of two */ +#define TX_RING_MOD_MASK 15 /* for this to work */ +#endif + +/* Interrupt events/masks. +*/ +#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ +#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ +#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ +#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ +#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ +#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ +#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ +#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ +#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ +#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ + +/* The FEC 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 FEC 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 fec_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 fec_enet_open(struct device *dev); +static int fec_enet_start_xmit(struct sk_buff *skb, struct device *dev); +static int fec_enet_rx(struct device *dev); +static void fec_enet_mii(struct device *dev); +static void fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs); +static int fec_enet_close(struct device *dev); +static struct net_device_stats *fec_enet_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + +static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 }; + +/* MII processing. We keep this as simple as possible. Requests are + * placed on the list (if there is room). When the request is finished + * by the MII, an optional function may be called. + */ +typedef struct mii_list { + uint mii_regval; + void (*mii_func)(uint val); + struct mii_list *mii_next; +} mii_list_t; + +#define NMII 10 +mii_list_t mii_cmds[NMII]; +mii_list_t *mii_free; +mii_list_t *mii_head; +mii_list_t *mii_tail; + +static int mii_queue(int request, void (*func)(int)); + +/* Make MII read/write commands for the FEC. +*/ +#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) +#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \ + (VAL & 0xffff)) + +static int +fec_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 +fec_enet_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct fec_enet_private *fep = (struct fec_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); + fep->stats.tx_errors++; +#ifndef final_version + { + int i; + cbd_t *bdp; + printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n", + fep->cur_tx, fep->tx_full ? " (full)" : "", + fep->cur_rx); + bdp = fep->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 = fep->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*)&fep->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 = fep->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); + fep->lock = 0; + return 1; + } +#endif + + /* Clear all of the status flags. + */ + bdp->cbd_sc &= ~BD_ENET_TX_STATS; + + /* Set buffer length and buffer pointer. + */ + bdp->cbd_bufaddr = __pa(skb->data); + bdp->cbd_datlen = skb->len; + + /* Save skb pointer. + */ + fep->tx_skbuff[fep->skb_cur] = skb; + + fep->stats.tx_bytes += skb->len; + fep->skb_cur = (fep->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. + */ + save_flags(flags); + cli(); + + bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); + + dev->trans_start = jiffies; + (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_x_des_active = 0x01000000; + + /* If this was the last BD in the ring, start at the beginning again. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = fep->tx_bd_base; + else + bdp++; + + fep->lock = 0; + if (bdp->cbd_sc & BD_ENET_TX_READY) + fep->tx_full = 1; + else + dev->tbusy=0; + restore_flags(flags); + + fep->cur_tx = (cbd_t *)bdp; + + return 0; +} + +/* The interrupt handler. + * This is called from the MPC core interrupt. + */ +static void +fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct device *dev = dev_id; + struct fec_enet_private *fep; + volatile cbd_t *bdp; + volatile fec_t *ep; + uint int_events; + int c=0; + + fep = (struct fec_enet_private *)dev->priv; + ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); + 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. + */ + while ((int_events = ep->fec_ievent) != 0) { + ep->fec_ievent = int_events; + if ((int_events & + (FEC_ENET_HBERR | FEC_ENET_BABR | + FEC_ENET_BABT | FEC_ENET_EBERR)) != 0) + printk("FEC ERROR %x\n", int_events); + + /* Handle receive event in its own function. + */ + if (int_events & (FEC_ENET_RXF | FEC_ENET_RXB)) + fec_enet_rx(dev_id); + + /* Transmit OK, or non-fatal error. Update the buffer descriptors. + * FEC handles all errors, we just discover them as part of the + * transmit process. + */ + if (int_events & (FEC_ENET_TXF | FEC_ENET_TXB)) { + bdp = fep->dirty_tx; + while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) { +#if 1 + if (bdp==fep->cur_tx) + break; +#endif + if (++c>1) {/*we go here when an it has been lost*/}; + + + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + fep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + fep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + fep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + fep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + fep->stats.tx_carrier_errors++; + + fep->stats.tx_errors++; + + fep->stats.tx_packets++; + +#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) + fep->stats.collisions++; + + /* Free the sk buffer associated with this last transmit. + */ + dev_kfree_skb(fep->tx_skbuff[fep->skb_dirty]/*, FREE_WRITE*/); + fep->skb_dirty = (fep->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 = fep->tx_bd_base; + else + bdp++; + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (fep->tx_full && dev->tbusy) { + fep->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + fep->dirty_tx = (cbd_t *)bdp; +#if 0 + if (bdp==fep->cur_tx) + break; +#endif + }/*while (bdp->cbd_sc&BD_ENET_TX_READY)==0*/ + } /* if tx events */ + + if (int_events & FEC_ENET_MII) + fec_enet_mii(dev_id); + + } /* while any 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 +fec_enet_rx(struct device *dev) +{ + struct fec_enet_private *fep; + volatile cbd_t *bdp; + struct sk_buff *skb; + ushort pkt_len; + volatile fec_t *ep; + + fep = (struct fec_enet_private *)dev->priv; + ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = fep->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, + * the last indicator should be set. + */ + if ((bdp->cbd_sc & BD_ENET_RX_LAST) == 0) + printk("FEC ENET: rcv is not +last\n"); +#endif + + /* Frame too long or too short. + */ + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) + fep->stats.rx_length_errors++; + if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ + fep->stats.rx_frame_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ + fep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ + fep->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) { + fep->stats.rx_frame_errors++; + } + else { + + /* Process the incoming frame. + */ + fep->stats.rx_packets++; + pkt_len = bdp->cbd_datlen; + fep->stats.rx_bytes += pkt_len; + + /* This does 16 byte alignment, exactly what we need. + */ + skb = dev_alloc_skb(pkt_len); + + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + fep->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 = fep->rx_bd_base; + else + bdp++; + +#if 1 + /* Doing this here will keep the FEC running while we process + * incoming frames. On a heavily loaded network, we should be + * able to keep up at the expense of system resources. + */ + ep->fec_r_des_active = 0x01000000; +#endif + } + fep->cur_rx = (cbd_t *)bdp; + +#if 0 + /* Doing this here will allow us to process all frames in the + * ring before the FEC is allowed to put more there. On a heavily + * loaded network, some frames may be lost. Unfortunately, this + * increases the interrupt overhead since we can potentially work + * our way back to the interrupt return only to come right back + * here. + */ + ep->fec_r_des_active = 0x01000000; +#endif + + return 0; +} + +static void +fec_enet_mii(struct device *dev) +{ + struct fec_enet_private *fep; + volatile fec_t *ep; + mii_list_t *mip; + uint mii_reg; + + fep = (struct fec_enet_private *)dev->priv; + ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); + mii_reg = ep->fec_mii_data; + + if ((mip = mii_head) == NULL) { + printk("MII and no head!\n"); + return; + } + + if (mip->mii_func != NULL) + (*(mip->mii_func))(mii_reg); + + mii_head = mip->mii_next; + mip->mii_next = mii_free; + mii_free = mip; + + if ((mip = mii_head) != NULL) + ep->fec_mii_data = mip->mii_regval; +} + +static int +mii_queue(int regval, void (*func)(int)) +{ + unsigned long flags; + mii_list_t *mip; + int retval; + + retval = 0; + + save_flags(flags); + cli(); + + if ((mip = mii_free) != NULL) { + mii_free = mip->mii_next; + mip->mii_regval = regval; + mip->mii_func = func; + mip->mii_next = NULL; + if (mii_head) { + mii_tail->mii_next = mip; + mii_tail = mip; + } + else { + mii_head = mii_tail = mip; + (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_mii_data = regval; + } + } + else { + retval = 1; + } + + restore_flags(flags); + + return(retval); +} + +static void +mii_status(uint mii_reg) +{ + if (((mii_reg >> 18) & 0x1f) == 1) { + /* status register. + */ + printk("fec: "); + if (mii_reg & 0x0004) + printk("link up"); + else + printk("link down"); + + if (mii_reg & 0x0010) + printk(",remote fault"); + if (mii_reg & 0x0020) + printk(",auto complete"); + printk("\n"); + } + if (((mii_reg >> 18) & 0x1f) == 0x14) { + /* Extended chip status register. + */ + printk("fec: "); + if (mii_reg & 0x0800) + printk("100 Mbps"); + else + printk("10 Mbps"); + + if (mii_reg & 0x1000) + printk(", Full-Duplex\n"); + else + printk(", Half-Duplex\n"); + } +} + +static void +mii_startup_cmds(void) +{ + + /* Read status registers to clear any pending interrupt. + */ + mii_queue(mk_mii_read(1), mii_status); + mii_queue(mk_mii_read(18), mii_status); + + /* Read extended chip status register. + */ + mii_queue(mk_mii_read(0x14), mii_status); + + /* Enable Link status change interrupts. + mii_queue(mk_mii_write(0x11, 0x0002), NULL); + */ +} + +/* This supports the mii_link interrupt below. + * We should get called three times. Once for register 1, once for + * register 18, and once for register 20. + */ +static uint mii_saved_reg1; + +static void +mii_relink(uint mii_reg) +{ + if (((mii_reg >> 18) & 0x1f) == 1) { + /* Just save the status register and get out. + */ + mii_saved_reg1 = mii_reg; + return; + } + if (((mii_reg >> 18) & 0x1f) == 18) { + /* Not much here, but has to be read to clear the + * interrupt condition. + */ + if ((mii_reg & 0x8000) == 0) + printk("fec: re-link and no IRQ?\n"); + if ((mii_reg & 0x4000) == 0) + printk("fec: no PHY power?\n"); + } + if (((mii_reg >> 18) & 0x1f) == 20) { + /* Extended chip status register. + * OK, now we have it all, so figure out what is going on. + */ + printk("fec: "); + if (mii_saved_reg1 & 0x0004) + printk("link up"); + else + printk("link down"); + + if (mii_saved_reg1 & 0x0010) + printk(", remote fault"); + if (mii_saved_reg1 & 0x0020) + printk(", auto complete"); + + if (mii_reg & 0x0800) + printk(", 100 Mbps"); + else + printk(", 10 Mbps"); + + if (mii_reg & 0x1000) + printk(", Full-Duplex\n"); + else + printk(", Half-Duplex\n"); + } +} + +/* This interrupt occurs when the LTX970 detects a link change. +*/ +static void +mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct device *dev = dev_id; + struct fec_enet_private *fep; + volatile fec_t *ep; + + fep = (struct fec_enet_private *)dev->priv; + ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); + + /* We need to sequentially read registers 1 and 18 to clear + * the interrupt. We don't need to do that here because this + * is an edge triggered interrupt that has already been acknowledged + * by the top level handler. We also read the extended status + * register 20. We just queue the commands and let them happen + * as part of the "normal" processing. + */ + mii_queue(mk_mii_read(1), mii_relink); + mii_queue(mk_mii_read(18), mii_relink); + mii_queue(mk_mii_read(20), mii_relink); +} + +static int +fec_enet_close(struct device *dev) +{ + /* Don't know what to do yet. + */ + + return 0; +} + +static struct net_device_stats *fec_enet_get_stats(struct device *dev) +{ + struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv; + + return &fep->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 fec_enet_private *fep; + struct dev_mc_list *dmi; + u_char *mcptr, *tdptr; + volatile fec_t *ep; + int i, j; + + fep = (struct fec_enet_private *)dev->priv; + ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); + + if (dev->flags&IFF_PROMISC) { + + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + ep->fec_r_cntrl |= 0x0008; + } else { + + ep->fec_r_cntrl &= ~0x0008; + + if (dev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's. + */ + ep->fec_hash_table_high = 0xffffffff; + ep->fec_hash_table_low = 0xffffffff; + } +#if 0 + 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; + /* this delay is necessary here -- Cort */ + udelay(10); + while (cpmp->cp_cpcr & CPM_CR_FLG); + } + } +#endif + } +} + +/* Initialize the FECC Ethernet on 860T. + */ +__initfunc(int m8xx_enet_init(void)) +{ + struct device *dev; + struct fec_enet_private *fep; + int i, j; + unsigned char *eap; + unsigned long mem_addr; + pte_t *pte; + volatile cbd_t *bdp; + cbd_t *cbd_base; + volatile immap_t *immap; + volatile fec_t *fecp; + unsigned char rtc_save_cfg, rtc_val; + + immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ + + /* Allocate some private information. + */ + fep = (struct fec_enet_private *)kmalloc(sizeof(*fep), GFP_KERNEL); + __clear_user(fep,sizeof(*fep)); + + /* Create an Ethernet device instance. + */ + dev = init_etherdev(0, 0); + + fecp = &(immap->im_cpm.cp_fec); + + /* Whack a reset. We should wait for this. + */ + fecp->fec_ecntrl = 1; + udelay(10); + + /* Enable interrupts we wish to service. + */ + fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB | + FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); + + /* Clear any outstanding interrupt. + */ + fecp->fec_ievent = 0xffc0; + + fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; + + /* Set station address. + */ + fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1]; + fecp->fec_addr_high = my_enet_addr[2]; + + eap = (unsigned char *)&my_enet_addr[0]; + for (i=0; i<6; i++) + dev->dev_addr[i] = *eap++; + + /* Reset all multicast. + */ + fecp->fec_hash_table_high = 0; + fecp->fec_hash_table_low = 0; + + /* Set maximum receive buffer size. + */ + fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; + fecp->fec_r_hash = PKT_MAXBUF_SIZE; + + /* Allocate memory for buffer descriptors. + */ + if (((RX_RING_SIZE + TX_RING_SIZE) * sizeof(cbd_t)) > PAGE_SIZE) { + printk("FECC init error. Need more space.\n"); + printk("FECC initialization failed.\n"); + return 1; + } + mem_addr = __get_free_page(GFP_KERNEL); + cbd_base = (cbd_t *)mem_addr; + + /* Make it uncached. + */ + pte = va_to_pte(&init_task, (int)mem_addr); + pte_val(*pte) |= _PAGE_NO_CACHE; + flush_tlb_page(current->mm->mmap, mem_addr); + + /* Set receive and transmit descriptor base. + */ + fecp->fec_r_des_start = __pa(mem_addr); + fep->rx_bd_base = cbd_base; + fecp->fec_x_des_start = __pa((unsigned long)(cbd_base + RX_RING_SIZE)); + fep->tx_bd_base = cbd_base + RX_RING_SIZE; + + fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; + fep->cur_rx = fep->rx_bd_base; + + fep->skb_cur = fep->skb_dirty = 0; + + /* Initialize the receive buffer descriptors. + */ + bdp = fep->rx_bd_base; + for (i=0; i<FEC_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<FEC_ENET_RX_FRPPG; j++) { + bdp->cbd_sc = BD_ENET_RX_EMPTY; + bdp->cbd_bufaddr = __pa(mem_addr); + mem_addr += FEC_ENET_RX_FRSIZE; + bdp++; + } + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* ...and the same for transmmit. + */ + bdp = fep->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; + + /* Enable MII mode, half-duplex until we know better.. + */ + fecp->fec_r_cntrl = 0x0c; + fecp->fec_x_cntrl = 0x00; + + /* Enable big endian and don't care about SDMA FC. + */ + fecp->fec_fun_code = 0x78000000; + + /* Set MII speed (50 MHz core). + */ + fecp->fec_mii_speed = 0x14; + + /* Configure all of port D for MII. + */ + immap->im_ioport.iop_pdpar = 0x1fff; + immap->im_ioport.iop_pddir = 0x1c58; + + /* Install our interrupt handlers. The 860T FADS board uses + * IRQ2 for the MII interrupt. + */ + if (request_irq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0) + panic("Could not allocate FEC IRQ!"); + if (request_irq(SIU_IRQ2, mii_link_interrupt, 0, "mii", dev) != 0) + panic("Could not allocate MII IRQ!"); + + dev->base_addr = (unsigned long)fecp; + dev->priv = fep; + dev->name = "fec"; + + /* The FEC Ethernet specific entries in the device structure. */ + dev->open = fec_enet_open; + dev->hard_start_xmit = fec_enet_start_xmit; + dev->stop = fec_enet_close; + dev->get_stats = fec_enet_get_stats; + dev->set_multicast_list = set_multicast_list; + + /* And last, enable the transmit and receive processing. + */ + fecp->fec_ecntrl = 2; + fecp->fec_r_des_active = 0x01000000; + + printk("FEC ENET Version 0.1, "); + for (i=0; i<5; i++) + printk("%02x:", dev->dev_addr[i]); + printk("%02x\n", dev->dev_addr[5]); + + for (i=0; i<NMII-1; i++) + mii_cmds[i].mii_next = &mii_cmds[i+1]; + mii_free = mii_cmds; + + mii_startup_cmds(); + + return 0; +} + diff --git a/arch/ppc/8xx_io/uart.c b/arch/ppc/8xx_io/uart.c index 9d859a46b..fdbd0c796 100644 --- a/arch/ppc/8xx_io/uart.c +++ b/arch/ppc/8xx_io/uart.c @@ -37,7 +37,14 @@ #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> +#include <asm/8xx_immap.h> +#ifdef CONFIG_MBX +#include <asm/mbx.h> +#endif +#ifdef CONFIG_FADS +#include <asm/fads.h> +#endif #include "commproc.h" #ifdef CONFIG_SERIAL_CONSOLE @@ -53,7 +60,7 @@ #define TX_WAKEUP ASYNC_SHARE_IRQ static char *serial_name = "CPM UART driver"; -static char *serial_version = "0.01"; +static char *serial_version = "0.02"; static DECLARE_TASK_QUEUE(tq_serial); @@ -96,10 +103,16 @@ static int serial_console_setup(struct console *co, char *options); #define smc_scc_num hub6 #define SCC_NUM_BASE 2 +/* The index into the CPM registers for the first SCC in the table. +*/ +#define SCC_IDX_BASE 1 + 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 */ + { 0, 0, PROFF_SCC2, CPMVEC_SCC2, 0, 2 }, /* SCC2 ttyS2 */ + { 0, 0, PROFF_SCC3, CPMVEC_SCC3, 0, 3 }, /* SCC3 ttyS3 */ }; #define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) @@ -212,7 +225,7 @@ static void rs_8xx_stop(struct tty_struct *tty) smcp->smc_smcm &= ~SMCM_TX; } else { - sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp = &cpmp->cp_scc[idx - SCC_IDX_BASE]; sccp->scc_sccm &= ~UART_SCCM_TX; } restore_flags(flags); @@ -235,7 +248,7 @@ static void rs_8xx_start(struct tty_struct *tty) smcp->smc_smcm |= SMCM_TX; } else { - sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp = &cpmp->cp_scc[idx - SCC_IDX_BASE]; sccp->scc_sccm |= UART_SCCM_TX; } restore_flags(flags); @@ -507,26 +520,33 @@ static void rs_8xx_interrupt(void *dev_id) int idx; ser_info_t *info; volatile smc_t *smcp; + volatile scc_t *sccp; info = (ser_info_t *)dev_id; if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) { smcp = &cpmp->cp_smc[idx]; + events = smcp->smc_smce; + if (events & SMCM_RX) + receive_chars(info); + if (events & SMCM_TX) + transmit_chars(info); + smcp->smc_smce = events; } else { - panic("SCC UART Interrupt....not ready"); + sccp = &cpmp->cp_scc[idx - SCC_IDX_BASE]; + events = sccp->scc_scce; + if (events & SCCM_RX) + receive_chars(info); + if (events & SCCM_TX) + transmit_chars(info); + sccp->scc_scce = events; } - 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 @@ -610,6 +630,7 @@ static int startup(ser_info_t *info) volatile smc_t *smcp; volatile scc_t *sccp; volatile smc_uart_t *up; + volatile scc_uart_t *scup; save_flags(flags); cli(); @@ -662,13 +683,28 @@ static int startup(ser_info_t *info) * are coming. */ up = (smc_uart_t *)&cpmp->cp_dparam[state->port]; +#if 0 up->smc_mrblr = 1; /* receive buffer length */ up->smc_maxidl = 0; /* wait forever for next char */ +#else + up->smc_mrblr = RX_BUF_SIZE; + up->smc_maxidl = RX_BUF_SIZE; +#endif up->smc_brkcr = 1; /* number of break chars */ } else { - sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; - sccp->scc_sccm |= UART_SCCM_RX; + sccp = &cpmp->cp_scc[idx - SCC_IDX_BASE]; + scup = (scc_uart_t *)&cpmp->cp_dparam[state->port]; +#if 0 + scup->scc_genscc.scc_mrblr = 1; /* receive buffer length */ + scup->scc_maxidl = 0; /* wait forever for next char */ +#else + scup->scc_genscc.scc_mrblr = RX_BUF_SIZE; + scup->scc_maxidl = RX_BUF_SIZE; +#endif + + sccp->scc_sccm |= (UART_SCCM_TX | UART_SCCM_RX); + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); } info->flags |= ASYNC_INITIALIZED; @@ -719,10 +755,10 @@ static void shutdown(ser_info_t * info) smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); } else { - sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; - sccp->scc_sccm &= ~UART_SCCM_RX; + sccp = &cpmp->cp_scc[idx - SCC_IDX_BASE]; + sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); } - if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); @@ -738,8 +774,8 @@ static void shutdown(ser_info_t * info) static void change_speed(ser_info_t *info) { int baud_rate; - unsigned cflag, cval, prev_mode; - int i, bits, idx; + unsigned cflag, cval, scval, prev_mode; + int i, bits, sbits, idx; unsigned long flags; volatile smc_t *smcp; volatile scc_t *sccp; @@ -754,6 +790,7 @@ static void change_speed(ser_info_t *info) * The value 'bits' counts this for us. */ cval = 0; + scval = 0; /* byte size and parity */ switch (cflag & CSIZE) { @@ -764,16 +801,22 @@ static void change_speed(ser_info_t *info) /* Never happens, but GCC is too dumb to figure it out */ default: bits = 8; break; } + sbits = bits - 5; + if (cflag & CSTOPB) { cval |= SMCMR_SL; /* Two stops */ + scval |= SCU_PMSR_SL; bits++; } if (cflag & PARENB) { cval |= SMCMR_PEN; + scval |= SCU_PMSR_PEN; bits++; } - if (!(cflag & PARODD)) + if (!(cflag & PARODD)) { cval |= SMCMR_PM_EVEN; + scval |= (SCU_PMSR_REVP | SCU_PMSR_TEVP); + } /* Determine divisor based on baud rate */ i = cflag & CBAUD; @@ -859,11 +902,11 @@ static void change_speed(ser_info_t *info) smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN)); } else { - sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; - sccp->scc_sccm &= ~UART_SCCM_RX; + sccp = &cpmp->cp_scc[idx - SCC_IDX_BASE]; + sccp->scc_pmsr = (sbits << 12) | scval; } - mbx_cpm_setbrg(info->state->smc_scc_num, baud_rate); + m8xx_cpm_setbrg(info->state->smc_scc_num, baud_rate); restore_flags(flags); } @@ -1271,12 +1314,11 @@ static void end_break(ser_info_t *info) 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(); + schedule_timeout(duration); end_break(info); #ifdef SERIAL_DEBUG_SEND_BREAK printk("done jiffies=%lu\n", jiffies); @@ -1568,8 +1610,9 @@ static void rs_8xx_close(struct tty_struct *tty, struct file * filp) smcp->smc_smcmr &= ~SMCMR_REN; } else { - sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE]; + sccp = &cpmp->cp_scc[idx - SCC_IDX_BASE]; sccp->scc_sccm &= ~UART_SCCM_RX; + sccp->scc_gsmrl &= ~SCC_GSMRL_ENR; } /* * Before we drop DTR, make sure the UART transmitter @@ -1589,8 +1632,7 @@ static void rs_8xx_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } @@ -1647,8 +1689,7 @@ static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout) #endif current->state = TASK_INTERRUPTIBLE; /* current->counter = 0; make us low-priority */ - current->timeout = jiffies + char_time; - schedule(); + schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && ((orig_jiffies + timeout) < jiffies)) @@ -2218,12 +2259,15 @@ __initfunc(int rs_8xx_init(void)) struct serial_state * state; ser_info_t *info; uint mem_addr, dp_addr; - int i, j; + int i, j, idx; ushort chan; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile smc_t *sp; volatile smc_uart_t *up; + volatile scc_t *scp; + volatile scc_uart_t *sup; + volatile immap_t *immap; init_bh(SERIAL_BH, do_serial_bh); #if 0 @@ -2289,6 +2333,7 @@ __initfunc(int rs_8xx_init(void)) panic("Couldn't register callout driver\n"); cp = cpmp; /* Get pointer to Communication Processor */ + immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ /* Configure SMCs Tx/Rx instead of port B parallel I/O. */ @@ -2296,10 +2341,41 @@ __initfunc(int rs_8xx_init(void)) cp->cp_pbdir &= ~0x00000cc0; cp->cp_pbodr &= ~0x00000cc0; + /* Configure SCC2 and SCC3 instead of port A parallel I/O. + */ +#ifndef CONFIG_MBX + /* The "standard" configuration through the 860. + */ + immap->im_ioport.iop_papar |= 0x003c; + immap->im_ioport.iop_padir &= ~0x003c; + immap->im_ioport.iop_paodr &= ~0x003c; +#else + /* On the MBX, SCC3 is through Port D. + */ + immap->im_ioport.iop_papar |= 0x000c; /* SCC2 on port A */ + immap->im_ioport.iop_padir &= ~0x000c; + immap->im_ioport.iop_paodr &= ~0x000c; + + immap->im_ioport.iop_pdpar |= 0x0030; /* SCC3 on port D */ +#endif + + /* Since we don't yet do modem control, connect the port C pins + * as general purpose I/O. This will assert CTS and CD for the + * SCC ports. + */ + immap->im_ioport.iop_pcdir |= 0x03c6; + immap->im_ioport.iop_pcpar &= ~0x03c6; + /* Wire BRG1 to SMC1 and BRG2 to SMC2. */ cp->cp_simode = 0x10000000; + /* Connect SCC2 and SCC3 to NMSI. Connect BRG3 to SCC2 and + * BRG4 to SCC3. + */ + cp->cp_sicr &= ~0x00ffff00; + cp->cp_sicr |= 0x001b1200; + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { state->magic = SSTATE_MAGIC; state->line = i; @@ -2340,28 +2416,21 @@ __initfunc(int rs_8xx_init(void)) 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); + dp_addr = m8xx_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); + mem_addr = m8xx_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++) { @@ -2373,18 +2442,28 @@ __initfunc(int rs_8xx_init(void)) 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); + if ((idx = state->smc_scc_num) < SCC_NUM_BASE) { + sp = &cp->cp_smc[idx]; + up = (smc_uart_t *)&cp->cp_dparam[state->port]; + up->smc_rbase = dp_addr; + } + else { + scp = &cp->cp_scc[idx - SCC_IDX_BASE]; + sup = (scc_uart_t *)&cp->cp_dparam[state->port]; + sup->scc_genscc.scc_rbase = dp_addr; + } + + dp_addr = m8xx_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); + mem_addr = m8xx_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++) { @@ -2396,39 +2475,104 @@ __initfunc(int rs_8xx_init(void)) 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; + if (idx < SCC_NUM_BASE) { + up->smc_tbase = dp_addr; - /* 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 */ + /* Set up the uart parameters in the + * parameter ram. + */ + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; - /* 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, + /* Set this to 1 for now, so we get single + * character interrupts. Using idle charater + * time requires some additional tuning. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + up->smc_brkcr = 1; + + /* 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); + 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 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; + /* Disable all interrupts and clear all pending + * events. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + } + else { + sup->scc_genscc.scc_tbase = dp_addr; + + /* Set up the uart parameters in the + * parameter ram. + */ + sup->scc_genscc.scc_rfcr = SMC_EB; + sup->scc_genscc.scc_tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single + * character interrupts. Using idle charater + * time requires some additional tuning. + */ + sup->scc_genscc.scc_mrblr = 1; + sup->scc_maxidl = 0; + sup->scc_brkcr = 1; + sup->scc_parec = 0; + sup->scc_frmec = 0; + sup->scc_nosec = 0; + sup->scc_brkec = 0; + sup->scc_uaddr1 = 0; + sup->scc_uaddr2 = 0; + sup->scc_toseq = 0; + sup->scc_char1 = 0x8000; + sup->scc_char2 = 0x8000; + sup->scc_char3 = 0x8000; + sup->scc_char4 = 0x8000; + sup->scc_char5 = 0x8000; + sup->scc_char6 = 0x8000; + sup->scc_char7 = 0x8000; + sup->scc_char8 = 0x8000; + sup->scc_rccm = 0xc0ff; + + /* Send the CPM an initialize command. + */ + if (state->smc_scc_num == 2) + chan = CPM_CR_CH_SCC2; + else + chan = CPM_CR_CH_SCC3; + + 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. + */ + scp->scc_gsmrh = 0; + scp->scc_gsmrl = + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + /* Disable all interrupts and clear all pending + * events. + */ + scp->scc_sccm = 0; + scp->scc_scce = 0xffff; + scp->scc_dsr = 0x7e7e; + scp->scc_pmsr = 0x3000; + } /* Install interrupt handler. */ @@ -2436,7 +2580,7 @@ __initfunc(int rs_8xx_init(void)) /* Set up the baud rate generator. */ - mbx_cpm_setbrg(state->smc_scc_num, 9600); + m8xx_cpm_setbrg(state->smc_scc_num, 9600); /* If the port is the console, enable Rx and Tx. */ @@ -2479,11 +2623,11 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) /* Allocate space for two buffer descriptors in the DP ram. */ - dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * 2); + dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 2); /* Allocate space for two 2 byte FIFOs in the host memory. */ - mem_addr = mbx_cpm_hostalloc(4); + mem_addr = m8xx_cpm_hostalloc(4); /* Set the physical address of the host memory buffers in * the buffer descriptors. @@ -2526,7 +2670,7 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) /* Set up the baud rate generator. */ - mbx_cpm_setbrg(ser->smc_scc_num, 9600); + m8xx_cpm_setbrg(ser->smc_scc_num, 9600); /* And finally, enable Rx and Tx. */ |