📄 aurora.c
字号:
/* $Id: aurora.c,v 1.19 2002/01/08 16:00:16 davem Exp $ * linux/drivers/sbus/char/aurora.c -- Aurora multiport driver * * Copyright (c) 1999 by Oliver Aldulea (oli at bv dot ro) * * This code is based on the RISCom/8 multiport serial driver written * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial * driver, written by Linus Torvalds, Theodore T'so and others. * The Aurora multiport programming info was obtained mainly from the * Cirrus Logic CD180 documentation (available on the web), and by * doing heavy tests on the board. Many thanks to Eddie C. Dost for the * help on the sbus interface. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Revision 1.0 * * This is the first public release. * * Most of the information you need is in the aurora.h file. Please * read that file before reading this one. * * Several parts of the code do not have comments yet. * * n.b. The board can support 115.2 bit rates, but only on a few * ports. The total badwidth of one chip (ports 0-7 or 8-15) is equal * to OSC_FREQ div 16. In case of my board, each chip can take 6 * channels of 115.2 kbaud. This information is not well-tested. * * Fixed to use tty_get_baud_rate(). * Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 */#include <linux/module.h>#include <linux/errno.h>#include <linux/sched.h>#ifdef AURORA_INT_DEBUG#include <linux/timer.h>#endif#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/bitops.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/oplib.h>#include <asm/system.h>#include <asm/kdebug.h>#include <asm/sbus.h>#include <asm/uaccess.h>#include "aurora.h"#include "cd180.h"unsigned char irqs[4] = { 0, 0, 0, 0};#ifdef AURORA_INT_DEBUGint irqhit=0;#endifstatic struct tty_driver *aurora_driver;static struct Aurora_board aurora_board[AURORA_NBOARD] = { {0,},};static struct Aurora_port aurora_port[AURORA_TNPORTS] = { { 0, },};/* no longer used. static struct Aurora_board * IRQ_to_board[16] = { NULL, } ;*/static unsigned char * tmp_buf = NULL;static DECLARE_MUTEX(tmp_buf_sem);DECLARE_TASK_QUEUE(tq_aurora);static inline int aurora_paranoia_check(struct Aurora_port const * port, char *name, const char *routine){#ifdef AURORA_PARANOIA_CHECK static const char *badmagic = KERN_DEBUG "aurora: Warning: bad aurora port magic number for device %s in %s\n"; static const char *badinfo = KERN_DEBUG "aurora: Warning: null aurora port for device %s in %s\n"; if (!port) { printk(badinfo, name, routine); return 1; } if (port->magic != AURORA_MAGIC) { printk(badmagic, name, routine); return 1; }#endif return 0;}/* * * Service functions for aurora driver. * *//* Get board number from pointer */static inline int board_No (struct Aurora_board const * bp){ return bp - aurora_board;}/* Get port number from pointer */static inline int port_No (struct Aurora_port const * port){ return AURORA_PORT(port - aurora_port); }/* Get pointer to board from pointer to port */static inline struct Aurora_board * port_Board(struct Aurora_port const * port){ return &aurora_board[AURORA_BOARD(port - aurora_port)];}/* Wait for Channel Command Register ready */static inline void aurora_wait_CCR(struct aurora_reg128 * r){ unsigned long delay;#ifdef AURORA_DEBUGprintk("aurora_wait_CCR\n");#endif /* FIXME: need something more descriptive than 100000 :) */ for (delay = 100000; delay; delay--) if (!sbus_readb(&r->r[CD180_CCR])) return; printk(KERN_DEBUG "aurora: Timeout waiting for CCR.\n");}/* * aurora probe functions. *//* Must be called with enabled interrupts */static inline void aurora_long_delay(unsigned long delay){ unsigned long i;#ifdef AURORA_DEBUG printk("aurora_long_delay: start\n");#endif for (i = jiffies + delay; time_before(jiffies, i); ) ;#ifdef AURORA_DEBUG printk("aurora_long_delay: end\n");#endif}/* Reset and setup CD180 chip */static int aurora_init_CD180(struct Aurora_board * bp, int chip){ unsigned long flags; int id; #ifdef AURORA_DEBUG printk("aurora_init_CD180: start %d:%d\n", board_No(bp), chip);#endif save_flags(flags); cli(); sbus_writeb(0, &bp->r[chip]->r[CD180_CAR]); sbus_writeb(0, &bp->r[chip]->r[CD180_GSVR]); /* Wait for CCR ready */ aurora_wait_CCR(bp->r[chip]); /* Reset CD180 chip */ sbus_writeb(CCR_HARDRESET, &bp->r[chip]->r[CD180_CCR]); udelay(1); sti(); id=1000; while((--id) && (sbus_readb(&bp->r[chip]->r[CD180_GSVR])!=0xff))udelay(100); if(!id) { printk(KERN_ERR "aurora%d: Chip %d failed init.\n", board_No(bp), chip); restore_flags(flags); return(-1); } cli(); sbus_writeb((board_No(bp)<<5)|((chip+1)<<3), &bp->r[chip]->r[CD180_GSVR]); /* Set ID for this chip */ sbus_writeb(0x80|bp->ACK_MINT, &bp->r[chip]->r[CD180_MSMR]); /* Prio for modem intr */ sbus_writeb(0x80|bp->ACK_TINT, &bp->r[chip]->r[CD180_TSMR]); /* Prio for transmitter intr */ sbus_writeb(0x80|bp->ACK_RINT, &bp->r[chip]->r[CD180_RSMR]); /* Prio for receiver intr */ /* Setting up prescaler. We need 4 tick per 1 ms */ sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) >> 8, &bp->r[chip]->r[CD180_PPRH]); sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) & 0xff, &bp->r[chip]->r[CD180_PPRL]); sbus_writeb(SRCR_AUTOPRI|SRCR_GLOBPRI, &bp->r[chip]->r[CD180_SRCR]); id = sbus_readb(&bp->r[chip]->r[CD180_GFRCR]); printk(KERN_INFO "aurora%d: Chip %d id %02x: ", board_No(bp), chip,id); if(sbus_readb(&bp->r[chip]->r[CD180_SRCR]) & 128) { switch (id) { case 0x82:printk("CL-CD1864 rev A\n");break; case 0x83:printk("CL-CD1865 rev A\n");break; case 0x84:printk("CL-CD1865 rev B\n");break; case 0x85:printk("CL-CD1865 rev C\n");break; default:printk("Unknown.\n"); }; } else { switch (id) { case 0x81:printk("CL-CD180 rev B\n");break; case 0x82:printk("CL-CD180 rev C\n");break; default:printk("Unknown.\n"); }; } restore_flags(flags);#ifdef AURORA_DEBUG printk("aurora_init_CD180: end\n");#endif return 0;}static int valid_irq(unsigned char irq){int i;for(i=0;i<TYPE_1_IRQS;i++) if (type_1_irq[i]==irq) return 1;return 0;}static irqreturn_t aurora_interrupt(int irq, void * dev_id, struct pt_regs * regs);/* Main probing routine, also sets irq. */static int aurora_probe(void){ struct sbus_bus *sbus; struct sbus_dev *sdev; int grrr; char buf[30]; int bn = 0; struct Aurora_board *bp; for_each_sbus(sbus) { for_each_sbusdev(sdev, sbus) {/* printk("Try: %x %s\n",sdev,sdev->prom_name);*/ if (!strcmp(sdev->prom_name, "sio16")) {#ifdef AURORA_DEBUG printk(KERN_INFO "aurora: sio16 at %p\n",sdev);#endif if((sdev->reg_addrs[0].reg_size!=1) && (sdev->reg_addrs[1].reg_size!=128) && (sdev->reg_addrs[2].reg_size!=128) && (sdev->reg_addrs[3].reg_size!=4)) { printk(KERN_ERR "aurora%d: registers' sizes " "do not match.\n", bn); break; } bp = &aurora_board[bn]; bp->r0 = (struct aurora_reg1 *) sbus_ioremap(&sdev->resource[0], 0, sdev->reg_addrs[0].reg_size, "sio16"); if (bp->r0 == NULL) { printk(KERN_ERR "aurora%d: can't map " "reg_addrs[0]\n", bn); break; }#ifdef AURORA_DEBUG printk("Map reg 0: %p\n", bp->r0);#endif bp->r[0] = (struct aurora_reg128 *) sbus_ioremap(&sdev->resource[1], 0, sdev->reg_addrs[1].reg_size, "sio16"); if (bp->r[0] == NULL) { printk(KERN_ERR "aurora%d: can't map " "reg_addrs[1]\n", bn); break; }#ifdef AURORA_DEBUG printk("Map reg 1: %p\n", bp->r[0]);#endif bp->r[1] = (struct aurora_reg128 *) sbus_ioremap(&sdev->resource[2], 0, sdev->reg_addrs[2].reg_size, "sio16"); if (bp->r[1] == NULL) { printk(KERN_ERR "aurora%d: can't map " "reg_addrs[2]\n", bn); break; }#ifdef AURORA_DEBUG printk("Map reg 2: %p\n", bp->r[1]);#endif bp->r3 = (struct aurora_reg4 *) sbus_ioremap(&sdev->resource[3], 0, sdev->reg_addrs[3].reg_size, "sio16"); if (bp->r3 == NULL) { printk(KERN_ERR "aurora%d: can't map " "reg_addrs[3]\n", bn); break; }#ifdef AURORA_DEBUG printk("Map reg 3: %p\n", bp->r3);#endif /* Variables setup */ bp->flags = 0;#ifdef AURORA_DEBUG grrr=prom_getint(sdev->prom_node,"intr"); printk("intr pri %d\n", grrr);#endif if ((bp->irq=irqs[bn]) && valid_irq(bp->irq) && !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { free_irq(bp->irq|0x30, bp); } else if ((bp->irq=prom_getint(sdev->prom_node, "bintr")) && valid_irq(bp->irq) && !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { free_irq(bp->irq|0x30, bp); } else if ((bp->irq=prom_getint(sdev->prom_node, "intr")) && valid_irq(bp->irq) && !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { free_irq(bp->irq|0x30, bp); } else for(grrr=0;grrr<TYPE_1_IRQS;grrr++) { if ((bp->irq=type_1_irq[grrr])&&!request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) { free_irq(bp->irq|0x30, bp); break; } else { printk(KERN_ERR "aurora%d: Could not get an irq for this board !!!\n",bn); bp->flags=0xff; } } if(bp->flags==0xff)break; printk(KERN_INFO "aurora%d: irq %d\n",bn,bp->irq&0x0f); buf[0]=0; grrr=prom_getproperty(sdev->prom_node,"dtr_rts",buf,sizeof(buf)); if(!strcmp(buf,"swapped")){ printk(KERN_INFO "aurora%d: Swapped DTR and RTS\n",bn); bp->DTR=MSVR_RTS; bp->RTS=MSVR_DTR; bp->MSVDTR=CD180_MSVRTS; bp->MSVRTS=CD180_MSVDTR; bp->flags|=AURORA_BOARD_DTR_FLOW_OK; }else{ #ifdef AURORA_FORCE_DTR_FLOW printk(KERN_INFO "aurora%d: Forcing swapped DTR-RTS\n",bn); bp->DTR=MSVR_RTS; bp->RTS=MSVR_DTR; bp->MSVDTR=CD180_MSVRTS; bp->MSVRTS=CD180_MSVDTR; bp->flags|=AURORA_BOARD_DTR_FLOW_OK; #else printk(KERN_INFO "aurora%d: Normal DTR and RTS\n",bn); bp->DTR=MSVR_DTR; bp->RTS=MSVR_RTS; bp->MSVDTR=CD180_MSVDTR; bp->MSVRTS=CD180_MSVRTS; #endif } bp->oscfreq=prom_getint(sdev->prom_node,"clk")*100; printk(KERN_INFO "aurora%d: Oscillator: %d Hz\n",bn,bp->oscfreq); grrr=prom_getproperty(sdev->prom_node,"chip",buf,sizeof(buf)); printk(KERN_INFO "aurora%d: Chips: %s\n",bn,buf); grrr=prom_getproperty(sdev->prom_node,"manu",buf,sizeof(buf)); printk(KERN_INFO "aurora%d: Manufacturer: %s\n",bn,buf); grrr=prom_getproperty(sdev->prom_node,"model",buf,sizeof(buf)); printk(KERN_INFO "aurora%d: Model: %s\n",bn,buf); grrr=prom_getproperty(sdev->prom_node,"rev",buf,sizeof(buf)); printk(KERN_INFO "aurora%d: Revision: %s\n",bn,buf); grrr=prom_getproperty(sdev->prom_node,"mode",buf,sizeof(buf)); printk(KERN_INFO "aurora%d: Mode: %s\n",bn,buf); #ifdef MODULE bp->count=0; #endif bp->flags = AURORA_BOARD_PRESENT; /* hardware ack */ bp->ACK_MINT=1; bp->ACK_TINT=2; bp->ACK_RINT=3; bn++; } } } return bn;}static void aurora_release_io_range(struct Aurora_board *bp){ sbus_iounmap((unsigned long)bp->r0, 1); sbus_iounmap((unsigned long)bp->r[0], 128); sbus_iounmap((unsigned long)bp->r[1], 128); sbus_iounmap((unsigned long)bp->r3, 4);}static inline void aurora_mark_event(struct Aurora_port * port, int event){#ifdef AURORA_DEBUG printk("aurora_mark_event: start\n");#endif set_bit(event, &port->event); queue_task(&port->tqueue, &tq_aurora); mark_bh(AURORA_BH);#ifdef AURORA_DEBUG printk("aurora_mark_event: end\n");#endif}static __inline__ struct Aurora_port * aurora_get_port(struct Aurora_board const * bp, int chip, unsigned char const *what){ unsigned char channel; struct Aurora_port * port; channel = ((chip << 3) | ((sbus_readb(&bp->r[chip]->r[CD180_GSCR]) & GSCR_CHAN) >> GSCR_CHAN_OFF)); port = &aurora_port[board_No(bp) * AURORA_NPORT * AURORA_NCD180 + channel]; if (port->flags & ASYNC_INITIALIZED) return port; printk(KERN_DEBUG "aurora%d: %s interrupt from invalid port %d\n", board_No(bp), what, channel); return NULL;}static void aurora_receive_exc(struct Aurora_board const * bp, int chip){ struct Aurora_port *port; struct tty_struct *tty; unsigned char status; unsigned char ch; if (!(port = aurora_get_port(bp, chip, "Receive_x"))) return; tty = port->tty; if (tty->flip.count >= TTY_FLIPBUF_SIZE) {#ifdef AURORA_INTNORM printk("aurora%d: port %d: Working around flip buffer overflow.\n", board_No(bp), port_No(port));#endif return; } #ifdef AURORA_REPORT_OVERRUN status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]); if (status & RCSR_OE) { port->overrun++;#if 1 printk("aurora%d: port %d: Overrun. Total %ld overruns.\n", board_No(bp), port_No(port), port->overrun);#endif } status &= port->mark_mask;#else status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]) & port->mark_mask;#endif ch = sbus_readb(&bp->r[chip]->r[CD180_RDR]); if (!status) return; if (status & RCSR_TOUT) {/* printk("aurora%d: port %d: Receiver timeout. Hardware problems ?\n", board_No(bp), port_No(port));*/ return; } else if (status & RCSR_BREAK) { printk(KERN_DEBUG "aurora%d: port %d: Handling break...\n", board_No(bp), port_No(port)); *tty->flip.flag_buf_ptr++ = TTY_BREAK; if (port->flags & ASYNC_SAK) do_SAK(tty); } else if (status & RCSR_PE) *tty->flip.flag_buf_ptr++ = TTY_PARITY; else if (status & RCSR_FE) *tty->flip.flag_buf_ptr++ = TTY_FRAME; else if (status & RCSR_OE) *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; else *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; tty->flip.count++; queue_task(&tty->flip.tqueue, &tq_timer);}static void aurora_receive(struct Aurora_board const * bp, int chip){ struct Aurora_port *port; struct tty_struct *tty; unsigned char count,cnt; if (!(port = aurora_get_port(bp, chip, "Receive"))) return; tty = port->tty; count = sbus_readb(&bp->r[chip]->r[CD180_RDCR]);#ifdef AURORA_REPORT_FIFO port->hits[count > 8 ? 9 : count]++;#endif while (count--) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) {#ifdef AURORA_INTNORM printk("aurora%d: port %d: Working around flip buffer overflow.\n", board_No(bp), port_No(port));#endif break; } cnt = sbus_readb(&bp->r[chip]->r[CD180_RDR]); *tty->flip.char_buf_ptr++ = cnt; *tty->flip.flag_buf_ptr++ = 0; tty->flip.count++; } queue_task(&tty->flip.tqueue, &tq_timer);}static void aurora_transmit(struct Aurora_board const * bp, int chip){ struct Aurora_port *port; struct tty_struct *tty; unsigned char count; if (!(port = aurora_get_port(bp, chip, "Transmit"))) return; tty = port->tty; if (port->SRER & SRER_TXEMPTY) { /* FIFO drained */ sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); udelay(1); port->SRER &= ~SRER_TXEMPTY; sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); return; } if ((port->xmit_cnt <= 0 && !port->break_length) || tty->stopped || tty->hw_stopped) { sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); udelay(1); port->SRER &= ~SRER_TXRDY; sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); return; } if (port->break_length) { if (port->break_length > 0) { if (port->COR2 & COR2_ETC) { sbus_writeb(CD180_C_ESC, &bp->r[chip]->r[CD180_TDR]); sbus_writeb(CD180_C_SBRK, &bp->r[chip]->r[CD180_TDR]); port->COR2 &= ~COR2_ETC; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -