⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cyclades.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
    /* value =>    00    01   02    03    04 */    /* divide by    8    32   128   512  2048 */    0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,  0x03,  0x02,    0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};static char baud_bpr_25[] = {  /* 25 MHz baud rate period table */    0x00,  0xf5,  0xa3,  0x6f,  0x5c,  0x51,  0xf5,  0xa3,  0x51,  0xa3,    0x6d,  0x51,  0xa3,  0x51,  0xa3,  0x51,  0x36,  0x29,  0x1b,  0x15};static char baud_co_60[] = {  /* 60 MHz clock option table (CD1400 J) */    /* value =>    00    01   02    03    04 */    /* divide by    8    32   128   512  2048 */    0x00,  0x00,  0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,    0x03,  0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,    0x00};static char baud_bpr_60[] = {  /* 60 MHz baud rate period table (CD1400 J) */    0x00,  0x82,  0x21,  0xff,  0xdb,  0xc3,  0x92,  0x62,  0xc3,  0x62,    0x41,  0xc3,  0x62,  0xc3,  0x62,  0xc3,  0x82,  0x62,  0x41,  0x32,    0x21};static char baud_cor3[] = {  /* receive threshold */    0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,    0x0a,  0x0a,  0x0a,  0x09,  0x09,  0x08,  0x08,  0x08,  0x08,  0x07,    0x07};/* * The Cyclades driver implements HW flow control as any serial driver. * The cyclades_port structure member rflow and the vector rflow_thr  * allows us to take advantage of a special feature in the CD1400 to avoid  * data loss even when the system interrupt latency is too high. These flags  * are to be used only with very special applications. Setting these flags  * requires the use of a special cable (DTR and RTS reversed). In the new  * CD1400-based boards (rev. 6.00 or later), there is no need for special  * cables. */static char rflow_thr[] = {  /* rflow threshold */    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,    0x00,  0x00,  0x00,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,    0x0a};/*  The Cyclom-Ye has placed the sequential chips in non-sequential *  address order.  This look-up table overcomes that problem. */static int cy_chip_offset [] =    { 0x0000,      0x0400,      0x0800,      0x0C00,      0x0200,      0x0600,      0x0A00,      0x0E00    };/* PCI related definitions */static unsigned short	cy_pci_nboard = 0;static unsigned short	cy_isa_nboard = 0;static unsigned short	cy_nboard = 0;static unsigned short	cy_pci_dev_id[] = {			    PCI_DEVICE_ID_CYCLOM_Y_Lo,	/* PCI < 1Mb */			    PCI_DEVICE_ID_CYCLOM_Y_Hi,	/* PCI > 1Mb */			    PCI_DEVICE_ID_CYCLOM_4Y_Lo,	/* 4Y PCI < 1Mb */			    PCI_DEVICE_ID_CYCLOM_4Y_Hi,	/* 4Y PCI > 1Mb */			    PCI_DEVICE_ID_CYCLOM_8Y_Lo,	/* 8Y PCI < 1Mb */			    PCI_DEVICE_ID_CYCLOM_8Y_Hi,	/* 8Y PCI > 1Mb */			    PCI_DEVICE_ID_CYCLOM_Z_Lo,	/* Z PCI < 1Mb */			    PCI_DEVICE_ID_CYCLOM_Z_Hi,	/* Z PCI > 1Mb */			    0				/* end of table */			};static void cy_start(struct tty_struct *);static void set_line_char(struct cyclades_port *);static void cy_probe(int, void *, struct pt_regs *);static void cyz_poll(unsigned long);#ifdef CYCLOM_SHOW_STATUSstatic void show_status(int);#endifstatic int cyclades_get_proc_info(char *, char **, off_t , int , int *, void *);/* The Cyclades-Z polling cycle is defined by this variable */static long cyz_polling_cycle = CZ_DEF_POLL;static int cyz_timeron = 0;static struct timer_listcyz_timerlist = {    NULL, NULL, 0, 0, cyz_poll};/**************************************************error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));copy_to_user  (to, from, count);***************************************************************error = verify_area(VERIFY_READ,  (void *) arg, sizeof(unsigned long *));copy_from_user(to, from, count);**************************************************/static inline intserial_paranoia_check(struct cyclades_port *info,                        kdev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK    static const char *badmagic =        "cyc Warning: bad magic number for serial struct (%s) in %s\n";    static const char *badinfo =        "cyc Warning: null cyclades_port for (%s) in %s\n";    static const char *badrange =        "cyc Warning: cyclades_port out of range for (%s) in %s\n";    if (!info) {        printk(badinfo, kdevname(device), routine);        return 1;    }    if( (long)info < (long)(&cy_port[0])    || (long)(&cy_port[NR_PORTS]) < (long)info ){        printk(badrange, kdevname(device), routine);        return 1;    }    if (info->magic != CYCLADES_MAGIC) {        printk(badmagic, kdevname(device), routine);        return 1;    }#endif        return 0;} /* serial_paranoia_check *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver * (also known as the "bottom half").  This can be called any * number of times for any channel without harm. */static inline voidcy_sched_event(struct cyclades_port *info, int event){    info->event |= 1 << event; /* remember what kind of event and who */    queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */    mark_bh(CYCLADES_BH);                       /* then trigger event */} /* cy_sched_event *//* * 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 * cy#/_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 cy_sched_event(), and they get done here. * * This is done through one level of indirection--the task queue. * When a hardware interrupt service routine wants service by the * driver's bottom half, it enqueues the appropriate tq_struct (one * per port) to the tq_cyclades work queue and sets a request flag * via mark_bh for processing that queue.  When the time is right, * do_cyclades_bh is called (because of the mark_bh) and it requests * that the work queue be processed. * * Although this may seem unwieldy, it gives the system a way to * pass an argument (in this case the pointer to the cyclades_port * structure) to the bottom half of the driver.  Previous kernels * had to poll every port to see if that port needed servicing. */static voiddo_cyclades_bh(void){    run_task_queue(&tq_cyclades);} /* do_cyclades_bh */static voiddo_softint(void *private_){  struct cyclades_port *info = (struct cyclades_port *) private_;  struct tty_struct    *tty;    tty = info->tty;    if (!tty)        return;    if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) {        tty_hangup(info->tty);        wake_up_interruptible(&info->open_wait);        info->flags &= ~(ASYNC_NORMAL_ACTIVE|                             ASYNC_CALLOUT_ACTIVE);    }    if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {        wake_up_interruptible(&info->open_wait);    }    if (test_and_clear_bit(Cy_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);    }#ifdef Z_WAKE    if (test_and_clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) {        wake_up_interruptible(&info->shutdown_wait);    }#endif} /* do_softint *//***********************************************************//********* Start of block of Cyclom-Y specific code ********//* This routine waits up to 1000 micro-seconds for the previous   command to the Cirrus chip to complete and then issues the   new command.  An error is returned if the previous command   didn't finish within the time limit. */static intcyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index){  unsigned long flags;  volatile int  i;    save_flags(flags); cli();        /* Check to see that the previous command has completed */        for(i = 0 ; i < 100 ; i++){            if (cy_readb(base_addr+(CyCCR<<index)) == 0){                break;            }            udelay(10L);        }        /* if the CCR never cleared, the previous command            didn't finish within the "reasonable time" */        if ( i == 100 ) {            restore_flags(flags);            return (-1);        }        /* Issue the new command */        cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);    restore_flags(flags);    return(0);} /* cyy_issue_cmd */static int probe_ready;/* * Grab all interrupts in preparation for doing an automatic irq * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a * mask of irq's which were grabbed and should therefore be freed * using free_all_interrupts(). */static intgrab_all_interrupts(int dontgrab){  int irq_lines = 0;  int i, mask;        for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {        if (!(mask & dontgrab)        && !request_irq(i, cy_probe,	             SA_INTERRUPT, "serial probe", NULL)) {            irq_lines |= mask;        }    }    return irq_lines;} /* grab_all_interrupts *//* * Release all interrupts grabbed by grab_all_interrupts */static voidfree_all_interrupts(int irq_lines){  int i;        for (i = 0; i < 16; i++) {        if (irq_lines & (1 << i)) {            free_irq(i,NULL);	}    }} /* free_all_interrupts *//* * This routine returns a bitfield of "wild interrupts".  Basically, * any unclaimed interrupts which is flapping around. */static intcheck_wild_interrupts(void){  int   i, mask;  int   wild_interrupts = 0;  int   irq_lines;  unsigned long timeout;  unsigned long flags;            /*Turn on interrupts (they may be off) */    save_flags(flags); sti();        irq_lines = grab_all_interrupts(0);               /*         * Delay for 0.1 seconds -- we use a busy loop since this may          * occur during the bootup sequence         */        timeout = jiffies+(HZ/10);        while (time_after_eq(timeout, jiffies))            ;                cy_triggered = 0;       /* Reset after letting things settle */        timeout = jiffies+(HZ/10);        while (time_after_eq(timeout, jiffies))                ;                for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {            if ((cy_triggered & (1 << i)) &&                (irq_lines & (1 << i))) {                    wild_interrupts |= mask;            }        }        free_all_interrupts(irq_lines);    restore_flags(flags);    return wild_interrupts;} /* check_wild_interrupts *//* * This routine is called by do_auto_irq(); it attempts to determine * which interrupt a serial port is configured to use.  It is not * fool-proof, but it works a large part of the time. */static intget_auto_irq(volatile ucchar *address){  unsigned long   	timeout;  volatile ucchar 	*base_addr;  int           	index;  unsigned long		flags;    index = 0;  /* IRQ probing is only for ISA */    base_addr = address;    intr_base_addr = address;            /*     * Enable interrupts and see who answers     */    cy_irq_triggered = 0;    save_flags(flags); cli();        cy_writeb((u_long)base_addr+(CyCAR<<index), 0);        cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);        cy_writeb((u_long)base_addr+(CySRER<<index),              cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);        probe_ready = 1;    restore_flags(flags);        timeout = jiffies+(HZ/50);    while (time_after_eq(timeout, jiffies)) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -