diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
commit | db7d4daea91e105e3859cf461d7e53b9b77454b2 (patch) | |
tree | 9bb65b95440af09e8aca63abe56970dd3360cc57 /drivers/sbus/char/zs.c | |
parent | 9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff) |
Merge with Linux 2.2.8.
Diffstat (limited to 'drivers/sbus/char/zs.c')
-rw-r--r-- | drivers/sbus/char/zs.c | 178 |
1 files changed, 118 insertions, 60 deletions
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c index 67aa3c574..9195f5a0e 100644 --- a/drivers/sbus/char/zs.c +++ b/drivers/sbus/char/zs.c @@ -1,4 +1,4 @@ -/* $Id: zs.c,v 1.32 1998/11/08 11:15:29 davem Exp $ +/* $Id: zs.c,v 1.41 1999/04/16 16:22:27 jj Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -53,6 +53,22 @@ static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */ #define KEYBOARD_LINE 0x2 #define MOUSE_LINE 0x3 +/* On 32-bit sparcs we need to delay after register accesses + * to accomodate sun4 systems, but we do not need to flush writes. + * On 64-bit sparc we only need to flush single writes to ensure + * completion. + */ +#ifndef __sparc_v9__ +#define ZSDELAY() udelay(5) +#define ZSDELAY_LONG() udelay(20) +#define ZS_WSYNC(channel) do { } while(0) +#else +#define ZSDELAY() +#define ZSDELAY_LONG() +#define ZS_WSYNC(channel) \ + ((void) *((volatile unsigned char *)(&((channel)->control)))) +#endif + struct sun_zslayout **zs_chips; struct sun_zschannel **zs_channels; struct sun_zschannel *zs_mousechan; @@ -200,9 +216,9 @@ static inline unsigned char read_zsreg(struct sun_zschannel *channel, unsigned char retval; channel->control = reg; - udelay(5); + ZSDELAY(); retval = channel->control; - udelay(5); + ZSDELAY(); return retval; } @@ -210,9 +226,9 @@ static inline void write_zsreg(struct sun_zschannel *channel, unsigned char reg, unsigned char value) { channel->control = reg; - udelay(5); + ZSDELAY(); channel->control = value; - udelay(5); + ZSDELAY(); } static inline void load_zsregs(struct sun_serial *info, unsigned char *regs) @@ -239,7 +255,7 @@ static inline void load_zsregs(struct sun_serial *info, unsigned char *regs) write_zsreg(channel, R9, CHRA); else write_zsreg(channel, R9, CHRB); - udelay(20); /* wait for some old sun4's */ + ZSDELAY_LONG(); write_zsreg(channel, R4, regs[R4]); write_zsreg(channel, R3, regs[R3] & ~RxENAB); write_zsreg(channel, R5, regs[R5] & ~TxENAB); @@ -267,10 +283,16 @@ static inline void zs_put_char(struct sun_zschannel *channel, char ch) { int loops = ZS_PUT_CHAR_MAX_DELAY; + /* Do not change this to use ZSDELAY as this is + * a timed polling loop and on sparc64 ZSDELAY + * is a nop. -DaveM + */ while((channel->control & Tx_BUF_EMP) == 0 && --loops) udelay(5); + channel->data = ch; - udelay(5); + ZSDELAY(); + ZS_WSYNC(channel); } /* Sets or clears DTR/RTS on the requested line */ @@ -420,7 +442,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs do { ch = (info->zs_channel->data) & info->parity_mask; - udelay(5); + ZSDELAY(); /* If this is the console keyboard, we need to handle * L1-A's here. @@ -482,7 +504,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs /* Check if we have another character... */ stat = info->zs_channel->control; - udelay(5); + ZSDELAY(); if (!(stat & Rx_CH_AV)) break; @@ -507,7 +529,8 @@ static _INLINE_ void transmit_chars(struct sun_serial *info) if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) { /* That's peculiar... */ info->zs_channel->control = RES_Tx_P; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); return; } @@ -521,7 +544,8 @@ static _INLINE_ void transmit_chars(struct sun_serial *info) if(info->xmit_cnt <= 0) { info->zs_channel->control = RES_Tx_P; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); } } @@ -531,10 +555,11 @@ static _INLINE_ void status_handle(struct sun_serial *info) /* Get status from Read Register 0 */ status = info->zs_channel->control; - udelay(5); + ZSDELAY(); /* Clear status condition... */ info->zs_channel->control = RES_EXT_INT; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); #if 0 if(status & DCD) { @@ -571,7 +596,7 @@ static _INLINE_ void special_receive(struct sun_serial *info) stat = read_zsreg(info->zs_channel, R1); if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) { ch = info->zs_channel->data; - udelay(5); + ZSDELAY(); } if (!tty) @@ -592,7 +617,8 @@ done: queue_task(&tty->flip.tqueue, &tq_timer); clear: info->zs_channel->control = ERR_RES; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); } @@ -772,9 +798,12 @@ static int startup(struct sun_serial * info) * Clear the interrupt registers. */ info->zs_channel->control = ERR_RES; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); + info->zs_channel->control = RES_H_IUS; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); /* * Now, initialize the Zilog @@ -798,9 +827,12 @@ static int startup(struct sun_serial * info) * And clear the interrupt registers again for luck. */ info->zs_channel->control = ERR_RES; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); + info->zs_channel->control = RES_H_IUS; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); @@ -1006,6 +1038,7 @@ void putDebugChar(char kgdb_char) while((chan->control & Tx_BUF_EMP)==0) udelay(5); chan->data = kgdb_char; + ZS_WSYNC(chan); } char getDebugChar(void) @@ -1013,7 +1046,7 @@ char getDebugChar(void) struct sun_zschannel *chan = zs_kgdbchan; while((chan->control & Rx_CH_AV)==0) - barrier(); + udelay(5); return chan->data; } @@ -1254,6 +1287,9 @@ static int set_serial_info(struct sun_serial * info, goto check_and_exit; } + if(new_serial.baud_base < 9600) + return -EINVAL; + if (info->count > 1) return -EBUSY; @@ -1291,6 +1327,7 @@ static int get_lsr_info(struct sun_serial * info, unsigned int *value) cli(); status = info->zs_channel->control; + ZSDELAY(); sti(); put_user_ret(status,value, -EFAULT); return 0; @@ -1303,6 +1340,7 @@ static int get_modem_info(struct sun_serial * info, unsigned int *value) cli(); status = info->zs_channel->control; + ZSDELAY(); sti(); result = ((info->curregs[5] & RTS) ? TIOCM_RTS : 0) | ((info->curregs[5] & DTR) ? TIOCM_DTR : 0) @@ -1806,7 +1844,7 @@ int zs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - char *revision = "$Revision: 1.32 $"; + char *revision = "$Revision: 1.41 $"; char *version, *p; version = strchr(revision, ' '); @@ -1853,8 +1891,8 @@ get_zs(int chip)) int len = prom_getproperty(zsnode, "address", (void *) vaddr, sizeof(vaddr)); - if(len == -1) { - struct linux_sbus *sbus; + if(len == -1 || central_bus != NULL) { + struct linux_sbus *sbus = NULL; struct linux_sbus_device *sdev = NULL; /* "address" property is not guarenteed, @@ -1862,20 +1900,40 @@ get_zs(int chip)) * anyways by our clever TLB miss handling * scheme, so don't fail here. -DaveM */ - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { - if (sdev->prom_node == zsnode) - goto found; + if (central_bus == NULL) { + for_each_sbus(sbus) { + for_each_sbusdev(sdev, sbus) { + if (sdev->prom_node == zsnode) + goto found; + } } } found: - if (sdev == NULL) + if (sdev == NULL && central_bus == NULL) prom_halt(); - prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev); - mapped_addr = (unsigned long) - sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, - PAGE_SIZE, "Zilog Registers", - sdev->reg_addrs[0].which_io, 0x0); + if (central_bus == NULL) { + prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev); + mapped_addr = (unsigned long) + sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + PAGE_SIZE, "Zilog Registers", + sdev->reg_addrs[0].which_io, 0x0); + } else { + struct linux_prom_registers zsregs[1]; + int err; + + err = prom_getproperty(zsnode, "reg", + (char *)&zsregs[0], + sizeof(zsregs)); + if (err == -1) { + prom_printf("ZS: Cannot map Zilog regs.\n"); + prom_halt(); + } + prom_apply_fhc_ranges(central_bus->child, &zsregs[0], 1); + prom_apply_central_ranges(central_bus, &zsregs[0], 1); + mapped_addr = (unsigned long) + __va((((unsigned long)zsregs[0].which_io)<<32) | + (((unsigned long)zsregs[0].phys_addr))); + } } else if(len % sizeof(unsigned int)) { prom_printf("WHOOPS: proplen for %s " "was %d, need multiple of " @@ -1954,25 +2012,27 @@ get_zs(int chip)) /* Can use the prom for other machine types */ zsnode = prom_getchild(prom_root_node); if (sparc_cpu_model == sun4d) { - int board, node; + int node; + int no = 0; tmpnode = zsnode; + zsnode = 0; + bbnode = 0; while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) { - board = prom_getintdefault (tmpnode, "board#", -1); - if (board == (chip >> 1)) { - node = prom_getchild(tmpnode); - if (node && (node = prom_searchsiblings(node, "bootbus"))) { + bbnode = prom_getchild(tmpnode); + if (bbnode && (bbnode = prom_searchsiblings(bbnode, "bootbus"))) { + if (no == (chip >> 1)) { cpunode = tmpnode; - bbnode = node; - zsnode = prom_getchild(node); + zsnode = prom_getchild(bbnode); chipid = (chip & 1); break; } + no++; } tmpnode = prom_getsibling(tmpnode); } if (!tmpnode) - panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1); + panic ("get_zs: couldn't find %dth bootbus\n", chip >> 1); } else { tmpnode = prom_searchsiblings(zsnode, "obio"); if(tmpnode) @@ -2066,13 +2126,12 @@ __initfunc(int zs_probe (unsigned long *memory_start)) node = prom_getchild(prom_root_node); if (sparc_cpu_model == sun4d) { - node = prom_searchsiblings(node, "boards"); - if (!node) - panic ("Cannot find out count of boards"); - else - node = prom_getchild(node); - while (node && (node = prom_searchsiblings(node, "bif"))) { - NUM_SERIAL += 2; + int bbnode; + + while (node && (node = prom_searchsiblings(node, "cpu-unit"))) { + bbnode = prom_getchild(node); + if (bbnode && prom_searchsiblings(bbnode, "bootbus")) + NUM_SERIAL += 2; node = prom_getsibling(node); } goto no_probe; @@ -2082,7 +2141,7 @@ __initfunc(int zs_probe (unsigned long *memory_start)) int central_node; /* Central bus zilogs must be checked for first, - * since Enterprise boxes have SBUS as well. + * since Enterprise boxes might have SBUSes as well. */ central_node = prom_finddevice("/central"); if(central_node != 0 && central_node != -1) @@ -2219,11 +2278,6 @@ __initfunc(int zs_init(void)) return 0; #endif -#ifdef CONFIG_PCI - if (pci_present()) - return 0; -#endif - /* Setup base handler, and timer table. */ init_bh(SERIAL_BH, do_serial_bh); timer_table[RS_TIMER].fn = zs_timer; @@ -2290,13 +2344,21 @@ __initfunc(int zs_init(void)) /* Initialize Softinfo */ zs_prepare(); + /* Grab IRQ line before poking the chips so we do + * not lose any interrupts. + */ + if (request_irq(zilog_irq, zs_interrupt, + (SA_INTERRUPT | SA_STATIC_ALLOC), + "Zilog8530", zs_chain)) + panic("Unable to attach zs intr\n"); + /* Initialize Hardware */ for(channel = 0; channel < NUM_CHANNELS; channel++) { /* Hardware reset each chip */ if (!(channel & 1)) { write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES); - udelay(20); /* wait for some old sun4's */ + ZSDELAY_LONG(); dummy = read_zsreg(zs_soft[channel].zs_channel, R0); } @@ -2462,10 +2524,6 @@ __initfunc(int zs_init(void)) printk(" is a Zilog8530\n"); } - if (request_irq(zilog_irq, zs_interrupt, - (SA_INTERRUPT | SA_STATIC_ALLOC), - "Zilog8530", zs_chain)) - panic("Unable to attach zs intr\n"); restore_flags(flags); keyboard_zsinit(kbd_put_char); @@ -2549,7 +2607,7 @@ static void zs_fair_output(struct sun_serial *info) /* Last character is being transmitted now (hopefully). */ info->zs_channel->control = RES_Tx_P; - udelay(5); + ZSDELAY(); restore_flags(flags); return; |