📄 l1.c
字号:
/* $Id: l1.c,v 1.1.1.1 2004/02/04 12:55:33 laputa Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc. * Copyright (C) 2000 by Colin Ngam *//* In general, this file is organized in a hierarchy from lower-level * to higher-level layers, as follows: * * UART routines * Bedrock/L1 "PPP-like" protocol implementation * System controller "message" interface (allows multiplexing * of various kinds of requests and responses with * console I/O) * Console interfaces (there are two): * (1) "elscuart", used in the IP35prom and (maybe) some * debugging situations elsewhere, and * (2) "l1_cons", the glue that allows the L1 to act * as the system console for the stdio libraries * * Routines making use of the system controller "message"-style interface * can be found in l1_command.c. Their names are leftover from early SN0, * when the "module system controller" (msc) was known as the "entry level * system controller" (elsc). The names and signatures of those functions * remain unchanged in order to keep the SN0 -> SN1 system controller * changes fairly localized. */#include <linux/types.h>#include <linux/config.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <asm/sn/sgi.h>#include <asm/sn/iograph.h>#include <asm/sn/invent.h>#include <asm/sn/hcl.h>#include <asm/sn/hcl_util.h>#include <asm/sn/labelcl.h>#include <asm/sn/eeprom.h>#include <asm/sn/ksys/i2c.h>#include <asm/sn/router.h>#include <asm/sn/module.h>#include <asm/sn/ksys/l1.h>#include <asm/sn/nodepda.h>#include <asm/sn/clksupport.h>#include <asm/sn/sn1/uart16550.h>/* * Delete this when atomic_clear is part of atomic.h. */static __inline__ intatomic_clear (int i, atomic_t *v){ __s32 old, new; do { old = atomic_read(v); new = old & ~i; } while (ia64_cmpxchg("acq", v, old, new, sizeof(atomic_t)) != old); return new;}#if defined(EEPROM_DEBUG)#define db_printf(x) printk x#else#define db_printf(x)#endif// From irix/kern/sys/SN/SN1/bdrkhspecregs.h#define HSPEC_UART_0 0x00000080 /* UART Registers *//********************************************************************* * Hardware-level (UART) driver routines. *//* macros for reading/writing registers */#define LD(x) (*(volatile uint64_t *)(x))#define SD(x, v) (LD(x) = (uint64_t) (v))/* location of uart receive/xmit data register */#define L1_UART_BASE(n) ((ulong)REMOTE_HSPEC_ADDR((n), HSPEC_UART_0))#define LOCAL_HUB LOCAL_HUB_ADDR#define LOCK_HUB REMOTE_HUB_ADDR#define ADDR_L1_REG(n, r) \ (L1_UART_BASE(n) | ( (r) << 3 ))#define READ_L1_UART_REG(n, r) \ ( LD(ADDR_L1_REG((n), (r))) )#define WRITE_L1_UART_REG(n, r, v) \ ( SD(ADDR_L1_REG((n), (r)), (v)) )/* UART-related #defines */#define UART_BAUD_RATE 57600#define UART_FIFO_DEPTH 0xf0#define UART_DELAY_SPAN 10#define UART_PUTC_TIMEOUT 50000#define UART_INIT_TIMEOUT 100000/* error codes */#define UART_SUCCESS 0#define UART_TIMEOUT (-1)#define UART_LINK (-2)#define UART_NO_CHAR (-3)#define UART_VECTOR (-4)#ifdef BRINGUP#define UART_DELAY(x) { int i; i = x * 1000; while (--i); }#else#define UART_DELAY(x) us_delay(x)#endif/* * Some macros for handling Endian-ness */#ifdef LITTLE_ENDIAN#define COPY_INT_TO_BUFFER(_b, _i, _n) \ { \ _b[_i++] = (_n >> 24) & 0xff; \ _b[_i++] = (_n >> 16) & 0xff; \ _b[_i++] = (_n >> 8) & 0xff; \ _b[_i++] = _n & 0xff; \ }#define COPY_BUFFER_TO_INT(_b, _i, _n) \ { \ _n = (_b[_i++] << 24) & 0xff; \ _n |= (_b[_i++] << 16) & 0xff; \ _n |= (_b[_i++] << 8) & 0xff; \ _n |= _b[_i++] & 0xff; \ }#define COPY_BUFFER_TO_BUFFER(_b, _i, _bn) \ { \ char *_xyz = (char *)_bn; \ _xyz[3] = _b[_i++]; \ _xyz[2] = _b[_i++]; \ _xyz[1] = _b[_i++]; \ _xyz[0] = _b[_i++]; \ }#else /* BIG_ENDIAN */extern char *bcopy(const char * src, char * dest, int count);#define COPY_INT_TO_BUFFER(_b, _i, _n) \ { \ bcopy((char *)&_n, _b, sizeof(_n)); \ _i += sizeof(_n); \ }#define COPY_BUFFER_TO_INT(_b, _i, _n) \ { \ bcopy(&_b[_i], &_n, sizeof(_n)); \ _i += sizeof(_n); \ }#define COPY_BUFFER_TO_BUFFER(_b, _i, _bn) \ { \ bcopy(&(_b[_i]), _bn, sizeof(int)); \ _i += sizeof(int); \ }#endif /* LITTLE_ENDIAN */void kmem_free(void *where, int size);#define BCOPY(x,y,z) memcpy(y,x,z)/* * Console locking defines and functions. * */#ifdef BRINGUP#define FORCE_CONSOLE_NASID#endif#define HUB_LOCK 16#define PRIMARY_LOCK_TIMEOUT 10000000#define HUB_LOCK_REG(n) LOCK_HUB(n, MD_PERF_CNT0)#define SET_BITS(reg, bits) SD(reg, LD(reg) | (bits))#define CLR_BITS(reg, bits) SD(reg, LD(reg) & ~(bits))#define TST_BITS(reg, bits) ((LD(reg) & (bits)) != 0)#define HUB_TEST_AND_SET(n) LD(LOCK_HUB(n,LB_SCRATCH_REG3_RZ))#define HUB_CLEAR(n) SD(LOCK_HUB(n,LB_SCRATCH_REG3),0)#define RTC_TIME_MAX ((rtc_time_t) ~0ULL)/* * primary_lock * * Allows CPU's 0-3 to mutually exclude the hub from one another by * obtaining a blocking lock. Does nothing if only one CPU is active. * * This lock should be held just long enough to set or clear a global * lock bit. After a relatively short timeout period, this routine * figures something is wrong, and steals the lock. It does not set * any other CPU to "dead". */inline voidprimary_lock(nasid_t nasid){ rtc_time_t expire; expire = rtc_time() + PRIMARY_LOCK_TIMEOUT; while (HUB_TEST_AND_SET(nasid)) { if (rtc_time() > expire) { HUB_CLEAR(nasid); } }}/* * primary_unlock (internal) * * Counterpart to primary_lock */inline voidprimary_unlock(nasid_t nasid){ HUB_CLEAR(nasid);}/* * hub_unlock * * Counterpart to hub_lock_timeout and hub_lock */inline voidhub_unlock(nasid_t nasid, int level){ uint64_t mask = 1ULL << level; primary_lock(nasid); CLR_BITS(HUB_LOCK_REG(nasid), mask); primary_unlock(nasid);}/* * hub_lock_timeout * * Uses primary_lock to implement multiple lock levels. * * There are 20 lock levels from 0 to 19 (limited by the number of bits * in HUB_LOCK_REG). To prevent deadlock, multiple locks should be * obtained in order of increasingly higher level, and released in the * reverse order. * * A timeout value of 0 may be used for no timeout. * * Returns 0 if successful, -1 if lock times out. */inline inthub_lock_timeout(nasid_t nasid, int level, rtc_time_t timeout){ uint64_t mask = 1ULL << level; rtc_time_t expire = (timeout ? rtc_time() + timeout : RTC_TIME_MAX); int done = 0; while (! done) { while (TST_BITS(HUB_LOCK_REG(nasid), mask)) { if (rtc_time() > expire) return -1; } primary_lock(nasid); if (! TST_BITS(HUB_LOCK_REG(nasid), mask)) { SET_BITS(HUB_LOCK_REG(nasid), mask); done = 1; } primary_unlock(nasid); } return 0;}#define LOCK_TIMEOUT (0x1500000 * 1) /* 0x1500000 is ~30 sec */inline voidlock_console(nasid_t nasid){ int ret; ret = hub_lock_timeout(nasid, HUB_LOCK, (rtc_time_t)LOCK_TIMEOUT); if ( ret != 0 ) { /* timeout */ hub_unlock(nasid, HUB_LOCK); /* If the 2nd lock fails, just pile ahead.... */ hub_lock_timeout(nasid, HUB_LOCK, (rtc_time_t)LOCK_TIMEOUT); }}inline voidunlock_console(nasid_t nasid){ hub_unlock(nasid, HUB_LOCK);}int get_L1_baud(void){ return UART_BAUD_RATE;}/* uart driver functions */static voiduart_delay( rtc_time_t delay_span ){ UART_DELAY( delay_span );}#define UART_PUTC_READY(n) ( (READ_L1_UART_REG((n), REG_LSR) & LSR_XHRE) && (READ_L1_UART_REG((n), REG_MSR) & MSR_CTS) )static intuart_putc( l1sc_t *sc ) {#ifdef BRINGUP /* need a delay to avoid dropping chars */ UART_DELAY(57);#endif#ifdef FORCE_CONSOLE_NASID /* We need this for the console write path _elscuart_flush() -> brl1_send() */ sc->nasid = 0;#endif WRITE_L1_UART_REG( sc->nasid, REG_DAT, sc->send[sc->sent] ); return UART_SUCCESS;}static intuart_getc( l1sc_t *sc ){ u_char lsr_reg = 0; nasid_t nasid = sc->nasid;#ifdef FORCE_CONSOLE_NASID nasid = sc->nasid = 0;#endif if( (lsr_reg = READ_L1_UART_REG( nasid, REG_LSR )) & (LSR_RCA | LSR_PARERR | LSR_FRMERR) ) { if( lsr_reg & LSR_RCA ) return( (u_char)READ_L1_UART_REG( nasid, REG_DAT ) ); else if( lsr_reg & (LSR_PARERR | LSR_FRMERR) ) { return UART_LINK; } } return UART_NO_CHAR;}#define PROM_SER_CLK_SPEED 12000000#define PROM_SER_DIVISOR(x) (PROM_SER_CLK_SPEED / ((x) * 16))static voiduart_init( l1sc_t *sc, int baud ){ rtc_time_t expire; int clkdiv; nasid_t nasid; clkdiv = PROM_SER_DIVISOR(baud); expire = rtc_time() + UART_INIT_TIMEOUT; nasid = sc->nasid; /* make sure the transmit FIFO is empty */ while( !(READ_L1_UART_REG( nasid, REG_LSR ) & LSR_XSRE) ) { uart_delay( UART_DELAY_SPAN ); if( rtc_time() > expire ) { break; } } if ( sc->uart == BRL1_LOCALUART ) lock_console(nasid); WRITE_L1_UART_REG( nasid, REG_LCR, LCR_DLAB ); uart_delay( UART_DELAY_SPAN ); WRITE_L1_UART_REG( nasid, REG_DLH, (clkdiv >> 8) & 0xff ); uart_delay( UART_DELAY_SPAN ); WRITE_L1_UART_REG( nasid, REG_DLL, clkdiv & 0xff ); uart_delay( UART_DELAY_SPAN ); /* set operating parameters and set DLAB to 0 */ WRITE_L1_UART_REG( nasid, REG_LCR, LCR_BITS8 | LCR_STOP1 ); uart_delay( UART_DELAY_SPAN ); WRITE_L1_UART_REG( nasid, REG_MCR, MCR_RTS | MCR_AFE ); uart_delay( UART_DELAY_SPAN ); /* disable interrupts */ WRITE_L1_UART_REG( nasid, REG_ICR, 0x0 ); uart_delay( UART_DELAY_SPAN ); /* enable FIFO mode and reset both FIFOs */ WRITE_L1_UART_REG( nasid, REG_FCR, FCR_FIFOEN ); uart_delay( UART_DELAY_SPAN ); WRITE_L1_UART_REG( nasid, REG_FCR, FCR_FIFOEN | FCR_RxFIFO | FCR_TxFIFO ); if ( sc->uart == BRL1_LOCALUART ) unlock_console(nasid);}/* This requires the console lock */static voiduart_intr_enable( l1sc_t *sc, u_char mask ){ u_char lcr_reg, icr_reg; nasid_t nasid = sc->nasid; /* make sure that the DLAB bit in the LCR register is 0 */ lcr_reg = READ_L1_UART_REG( nasid, REG_LCR ); lcr_reg &= ~(LCR_DLAB); WRITE_L1_UART_REG( nasid, REG_LCR, lcr_reg ); /* enable indicated interrupts */ icr_reg = READ_L1_UART_REG( nasid, REG_ICR ); icr_reg |= mask; WRITE_L1_UART_REG( nasid, REG_ICR, icr_reg /*(ICR_RIEN | ICR_TIEN)*/ );}/* This requires the console lock */static voiduart_intr_disable( l1sc_t *sc, u_char mask ){ u_char lcr_reg, icr_reg; nasid_t nasid = sc->nasid; /* make sure that the DLAB bit in the LCR register is 0 */ lcr_reg = READ_L1_UART_REG( nasid, REG_LCR ); lcr_reg &= ~(LCR_DLAB); WRITE_L1_UART_REG( nasid, REG_LCR, lcr_reg ); /* enable indicated interrupts */ icr_reg = READ_L1_UART_REG( nasid, REG_ICR ); icr_reg &= mask; WRITE_L1_UART_REG( nasid, REG_ICR, icr_reg /*(ICR_RIEN | ICR_TIEN)*/ );}#define uart_enable_xmit_intr(sc) \ uart_intr_enable((sc), ICR_TIEN)#define uart_disable_xmit_intr(sc) \ uart_intr_disable((sc), ~(ICR_TIEN))#define uart_enable_recv_intr(sc) \ uart_intr_enable((sc), ICR_RIEN)#define uart_disable_recv_intr(sc) \ uart_intr_disable((sc), ~(ICR_RIEN))/********************************************************************* * Routines for accessing a remote (router) UART */#define READ_RTR_L1_UART_REG(p, n, r, v) \ { \ if( vector_read_node( (p), (n), 0, \ RR_JBUS1(r), (v) ) ) { \ return UART_VECTOR; \ } \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -