diff options
Diffstat (limited to 'target/linux/brcm63xx-2.6/files/drivers')
| -rw-r--r-- | target/linux/brcm63xx-2.6/files/drivers/mtd/maps/bcm963xx-flash.c | 277 | ||||
| -rw-r--r-- | target/linux/brcm63xx-2.6/files/drivers/serial/bcm63xx_cons.c | 1049 | 
2 files changed, 1326 insertions, 0 deletions
diff --git a/target/linux/brcm63xx-2.6/files/drivers/mtd/maps/bcm963xx-flash.c b/target/linux/brcm63xx-2.6/files/drivers/mtd/maps/bcm963xx-flash.c new file mode 100644 index 000000000..c4c4526b4 --- /dev/null +++ b/target/linux/brcm63xx-2.6/files/drivers/mtd/maps/bcm963xx-flash.c @@ -0,0 +1,277 @@ +/* + * $Id$ + * Copyright (C) 2006  Florian Fainelli <florian@openwrt.org> + * 			Mike Albon <malbon@openwrt.org> + * Copyright (C) $Date$  $Author$ + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +/* This is the BCM963xx flash map driver, in its actual state it only supports BCM96348 devices + * this driver is able to manage both bootloader we found on these boards : CFE and RedBoot + * + * RedBoot : + *  - this bootloader allows us to parse partitions and therefore deduce the MTD partition table + * + * CFE : + *   - CFE partitionning can be detected as for BCM947xx devices  + * + */ + +#include <asm/io.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/vmalloc.h> +#include <board.h> + +#define WINDOW_ADDR 0x1FC00000         /* Real address of the flash */ +#define WINDOW_SIZE 0x400000           /* Size of flash */ +#define BUSWIDTH 2                     /* Buswidth */ +#define EXTENDED_SIZE 0xBFC00000       /* Extended flash address */ +#define IMAGE_LEN 10                   /* Length of Length Field */ +#define ADDRESS_LEN 12                 /* Length of Address field */ +#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) + +extern int boot_loader_type;           /* For RedBoot / CFE detection */ +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts, unsigned long fis_origin); +static struct mtd_partition *parsed_parts; + +static void __exit bcm963xx_mtd_cleanup(void); + +static struct mtd_info *bcm963xx_mtd_info; + +static struct map_info bcm963xx_map = { +       .name = "bcm963xx", +       .size = WINDOW_SIZE, +       .bankwidth = BUSWIDTH, +       .phys = WINDOW_ADDR, +}; + + +int parse_cfe_partitions( struct mtd_info *master, struct mtd_partition **pparts) +{ +       int nrparts = 2, curpart = 0; // CFE and NVRAM always present. +       struct bcm963xx_cfe_map { +       unsigned char tagVersion[4];                            // Version of the image tag +       unsigned char sig_1[20];                                // Company Line 1 +       unsigned char sig_2[14];                                // Company Line 2 +       unsigned char chipid[6];                                        // Chip this image is for +       unsigned char boardid[16];                              // Board name +       unsigned char bigEndian[2];                             // Map endianness -- 1 BE 0 LE +       unsigned char totalLength[IMAGE_LEN];           //Total length of image +       unsigned char cfeAddress[ADDRESS_LEN];  // Address in memory of CFE +       unsigned char cfeLength[IMAGE_LEN];             // Size of CFE +       unsigned char rootAddress[ADDRESS_LEN];         // Address in memory of rootfs +       unsigned char rootLength[IMAGE_LEN];            // Size of rootfs +       unsigned char kernelAddress[ADDRESS_LEN];       // Address in memory of kernel +       unsigned char kernelLength[IMAGE_LEN];  // Size of kernel +       unsigned char dualImage[2];                             // Unused at present +       unsigned char inactiveFlag[2];                  // Unused at present +       unsigned char reserved1[74];                            // Reserved area not in use +       unsigned char imageCRC[4];                              // CRC32 of images +       unsigned char reserved2[16];                            // Unused at present +       unsigned char headerCRC[4];                             // CRC32 of header excluding tagVersion +       unsigned char reserved3[16];                            // Unused at present +       } *buf; +       struct mtd_partition *parts; +       int ret; +       size_t retlen; +       unsigned int rootfsaddr, kerneladdr, spareaddr; +       unsigned int rootfslen, kernellen, sparelen, totallen; +       int namelen = 0; +       int i; +       // Allocate memory for buffer +       buf = vmalloc(sizeof(struct bcm963xx_cfe_map)); + +       if (!buf) +               return -ENOMEM; + +       // Get the tag +       ret = master->read(master,master->erasesize,sizeof(struct bcm963xx_cfe_map), &retlen, (void *)buf); +       if (retlen != sizeof(struct bcm963xx_cfe_map)){ +               vfree(buf); +               return -EIO; +       }; +       printk("bcm963xx: CFE boot tag found with version %s and board type %s.\n",buf->tagVersion,buf->boardid); +       // Get the values and calculate +       sscanf(buf->rootAddress,"%u", &rootfsaddr); +       rootfsaddr = rootfsaddr - EXTENDED_SIZE; +       sscanf(buf->rootLength, "%u", &rootfslen); +       sscanf(buf->kernelAddress, "%u", &kerneladdr); +       kerneladdr = kerneladdr - EXTENDED_SIZE; +       sscanf(buf->kernelLength, "%u", &kernellen); +       sscanf(buf->totalLength, "%u", &totallen); +       spareaddr = ROUNDUP(totallen,master->erasesize) + master->erasesize; +       sparelen = master->size - spareaddr - master->erasesize; +       // Determine number of partitions +       namelen = 8; +       if (rootfslen > 0){ +               nrparts++; +               namelen =+ 6; +       }; +       if (kernellen > 0){ +               nrparts++; +               namelen =+ 6; +       }; +       if (sparelen > 0){ +               nrparts++; +               namelen =+ 6; +       }; +       // Ask kernel for more memory. +       parts = kmalloc(sizeof(*parts)*nrparts+10*nrparts, GFP_KERNEL); +       if (!parts){ +               vfree(buf); +               return -ENOMEM; +       }; +       memset(parts,0,sizeof(*parts)*nrparts+10*nrparts); +       // Start building partition list +       parts[curpart].name = "CFE"; +       parts[curpart].offset = 0; +       parts[curpart].size = master->erasesize; +       curpart++; +       if (kernellen > 0){ +               parts[curpart].name = "Kernel"; +               parts[curpart].offset = kerneladdr; +               parts[curpart].size = kernellen; +               curpart++; +       }; +       if (rootfslen > 0){ +               parts[curpart].name = "Rootfs"; +               parts[curpart].offset = rootfsaddr; +               parts[curpart].size = rootfslen; +               curpart++; +       }; +       if (sparelen > 0){ +               parts[curpart].name = "OpenWrt"; +               parts[curpart].offset = spareaddr; +               parts[curpart].size = sparelen; +               curpart++; +       }; +       parts[curpart].name = "NVRAM"; +       parts[curpart].offset = master->size - master->erasesize; +       parts[curpart].size = master->erasesize; +       for (i = 0; i < nrparts; i++) { +          printk("bcm963xx: Partition %d is %s offset %x and length %x\n", i, parts[i].name, parts[i].offset, parts[i].size); +       } +       *pparts = parts; +       vfree(buf); +       return nrparts; +}; + +static struct mtd_partition bcm963xx_parts[] = { +        { name: "bootloader",  size: 0,        offset: 0,      mask_flags: MTD_WRITEABLE }, +        { name: "rootfs",              size: 0,        offset: 0}, +        { name: "jffs2",        size: 5 * 0x10000,      offset: 57*0x10000} +}; + +static int bcm963xx_parts_size = sizeof(bcm963xx_parts) / sizeof(bcm963xx_parts[0]); + +static int bcm963xx_detect_cfe(struct mtd_info *master) +{ +       int idoffset = 0x4e0; +       static char idstring[8] = "CFE1CFE1"; +       char buf[8]; +       int ret; +       size_t retlen; + +       ret = master->read(master, idoffset, 8, &retlen, (void *)buf); +       printk("bcm963xx: Read Signature value of %s\n", buf); +       return strcmp(idstring,buf); +} + +static int __init bcm963xx_mtd_init(void) +{ +       printk("bcm963xx: 0x%08x at 0x%08x\n", WINDOW_SIZE, WINDOW_ADDR); +       bcm963xx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + +       if (!bcm963xx_map.virt) { +               printk("bcm963xx: Failed to ioremap\n"); +               return -EIO; +       } + +       simple_map_init(&bcm963xx_map); + +       bcm963xx_mtd_info = do_map_probe("cfi_probe", &bcm963xx_map); + +       if (bcm963xx_mtd_info) { +               bcm963xx_mtd_info->owner = THIS_MODULE; + +               //if (boot_loader_type == BOOT_CFE) +               if (bcm963xx_detect_cfe(bcm963xx_mtd_info) == 0) +               { +                       int parsed_nr_parts = 0; +                       char * part_type; +                       printk("bcm963xx: CFE bootloader detected\n"); +                       //add_mtd_device(bcm963xx_mtd_info); +                       //add_mtd_partitions(bcm963xx_mtd_info, bcm963xx_parts, bcm963xx_parts_size); +                       if (parsed_nr_parts == 0) { +                               int ret = parse_cfe_partitions(bcm963xx_mtd_info, &parsed_parts); +                               if (ret > 0) { +                                       part_type = "CFE"; +                                       parsed_nr_parts = ret; +                               } +                       } +                       add_mtd_partitions(bcm963xx_mtd_info, parsed_parts, parsed_nr_parts); +                       return 0; +               } +               else +               { +                       int parsed_nr_parts = 0; +                       char * part_type; + +                       if (bcm963xx_mtd_info->size > 0x00400000) { +                               printk("Support for extended flash memory size : 0x%08X ; ONLY 64MBIT SUPPORT\n", bcm963xx_mtd_info->size); +                               bcm963xx_map.virt = (unsigned long)(EXTENDED_SIZE); +                       } + +#ifdef CONFIG_MTD_REDBOOT_PARTS +                       if (parsed_nr_parts == 0) { +                               int ret = parse_redboot_partitions(bcm963xx_mtd_info, &parsed_parts, 0); +                               if (ret > 0) { +                                       part_type = "RedBoot"; +                                       parsed_nr_parts = ret; +                               } +                       } +#endif +                       add_mtd_partitions(bcm963xx_mtd_info, parsed_parts, parsed_nr_parts); + +                       return 0; +               } +       } +       iounmap(bcm963xx_map.virt); +       return -ENXIO; +} + +static void __exit bcm963xx_mtd_cleanup(void) +{ +       if (bcm963xx_mtd_info) { +               del_mtd_partitions(bcm963xx_mtd_info); +               map_destroy(bcm963xx_mtd_info); +       } + +       if (bcm963xx_map.virt) { +               iounmap(bcm963xx_map.virt); +               bcm963xx_map.virt = 0; +       } +} + +module_init(bcm963xx_mtd_init); +module_exit(bcm963xx_mtd_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org> Mike Albon <malbon@openwrt.org>"); diff --git a/target/linux/brcm63xx-2.6/files/drivers/serial/bcm63xx_cons.c b/target/linux/brcm63xx-2.6/files/drivers/serial/bcm63xx_cons.c new file mode 100644 index 000000000..282e7d61a --- /dev/null +++ b/target/linux/brcm63xx-2.6/files/drivers/serial/bcm63xx_cons.c @@ -0,0 +1,1049 @@ +/* +<:copyright-gpl  + Copyright 2002 Broadcom Corp. All Rights Reserved.  +  + This program is free software; you can distribute it and/or modify it  + under the terms of the GNU General Public License (Version 2) as  + published by the Free Software Foundation.  +  + This program is distributed in the hope 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.,  + 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.  +:> +*/ + +/* Description: Serial port driver for the BCM963XX. */ + +#define CARDNAME    "bcm963xx_serial driver" +#define VERSION     "2.0" +#define VER_STR     CARDNAME " v" VERSION "\n" + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h>  +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +/* for definition of struct console */ +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serialP.h> +#include <asm/uaccess.h> + +#include <bcmtypes.h> +#include <board.h> +#include <bcm_map_part.h> +#include <bcm_intr.h> + +static DEFINE_SPINLOCK(bcm963xx_serial_lock); + +extern void _putc(char); +extern void _puts(const char *); + +typedef struct bcm_serial { +    volatile Uart *         port; +    int                     type; +    int                     flags;  +    int                     irq; +    int                     baud_base; +    int                     blocked_open; +    unsigned short          close_delay; +    unsigned short          closing_wait; +    unsigned short          line;                /* port/line number */ +    unsigned short          cflags;              /* line configuration flag */ +    unsigned short          x_char;              /* xon/xoff character */ +    unsigned short          read_status_mask;    /* mask for read condition */ +    unsigned short          ignore_status_mask;  /* mask for ignore condition */ +    unsigned long           event;               /* mask used in BH */ +    int                     xmit_head;           /* Position of the head */ +    int                     xmit_tail;           /* Position of the tail */ +    int                     xmit_cnt;            /* Count of the chars in the buffer */ +    int                     count;               /* indicates how many times it has been opened */ +    int                     magic; + +    struct async_icount     icount;              /* keep track of things ... */ +    struct tty_struct       *tty;                /* tty associated */     +    struct termios          normal_termios; + +    wait_queue_head_t       open_wait; +    wait_queue_head_t       close_wait; + +    long                    session;             /* Session of opening process */ +    long                    pgrp;                /* pgrp of opening process */ + +    unsigned char           is_initialized; +} Context; + + +/*---------------------------------------------------------------------*/ +/* Define bits in the Interrupt Enable register                        */ +/*---------------------------------------------------------------------*/ +/* Enable receive interrupt              */ +#define RXINT   (RXFIFONE|RXOVFERR) + +/* Enable transmit interrupt             */ +#define TXINT    (TXFIFOEMT|TXUNDERR|TXOVFERR)  + +/* Enable receiver line status interrupt */ +#define LSINT    (RXBRK|RXPARERR|RXFRAMERR) + +#define BCM_NUM_UARTS                   1 + +#define BD_BCM63XX_TIMER_CLOCK_INPUT    (FPERIPH) + + +static struct bcm_serial multi[BCM_NUM_UARTS]; +static struct bcm_serial *lines[BCM_NUM_UARTS]; +static struct tty_driver *serial_driver; +static struct termios *serial_termios[BCM_NUM_UARTS]; +static struct termios *serial_termios_locked[BCM_NUM_UARTS]; + + +static void bcm_stop (struct tty_struct *tty); +static void bcm_start (struct tty_struct *tty); +static inline void receive_chars (struct bcm_serial * info); +static int startup (struct bcm_serial *info); +static void shutdown (struct bcm_serial * info); +static void change_speed( volatile Uart *pUart, tcflag_t cFlag ); +static void bcm63xx_cons_flush_chars (struct tty_struct *tty); +static int bcm63xx_cons_write (struct tty_struct *tty,  +    const unsigned char *buf, int count); +static int bcm63xx_cons_write_room (struct tty_struct *tty); +static int bcm_chars_in_buffer (struct tty_struct *tty); +static void bcm_flush_buffer (struct tty_struct *tty); +static void bcm_throttle (struct tty_struct *tty); +static void bcm_unthrottle (struct tty_struct *tty); +static void bcm_send_xchar (struct tty_struct *tty, char ch); +static int get_serial_info(struct bcm_serial *info, struct serial_struct *retinfo); +static int set_serial_info (struct bcm_serial *info, struct serial_struct *new_info); +static int get_lsr_info (struct bcm_serial *info, unsigned int *value); +static void send_break (struct bcm_serial *info, int duration); +static int bcm_ioctl (struct tty_struct * tty, struct file * file, +    unsigned int cmd, unsigned long arg); +static void bcm_set_termios (struct tty_struct *tty, struct termios *old_termios); +static void bcm63xx_cons_close (struct tty_struct *tty, struct file *filp); +static void bcm_hangup (struct tty_struct *tty); +static int block_til_ready (struct tty_struct *tty, struct file *filp, struct bcm_serial *info); +static int bcm63xx_cons_open (struct tty_struct * tty, struct file * filp); +static int __init bcm63xx_serialinit(void); + + +/* + * ------------------------------------------------------------ + * rs_stop () and rs_start () + * + * These routines are called before setting or resetting  + * tty->stopped. They enable or disable transmitter interrupts,  + * as necessary. + * ------------------------------------------------------------ + */ +static void bcm_stop (struct tty_struct *tty) +{ +}   + +static void bcm_start (struct tty_struct *tty) +{ +    _puts(CARDNAME " Start\n"); +}   + +/* + * ------------------------------------------------------------ + * receive_char () + * + * This routine deals with inputs from any lines. + * ------------------------------------------------------------ + */ +static inline void receive_chars (struct bcm_serial * info) +{ +    struct tty_struct *tty = 0; +    struct async_icount * icount; +    int ignore = 0; +    unsigned short status, tmp; +    UCHAR ch = 0; +    while ((status = info->port->intStatus) & RXINT) +    { +		char flag_char = TTY_NORMAL; + +        if (status & RXFIFONE) +            ch = info->port->Data;  // Read the character +        tty = info->tty;                  /* now tty points to the proper dev */ +        icount = &info->icount; +        if (! tty) +            break; +        if (!tty_buffer_request_room(tty, 1)) +            break; +        icount->rx++; +        if (status & RXBRK) +        { +            flag_char = TTY_BREAK; +            icount->brk++; +        } +        // keep track of the statistics +        if (status & (RXFRAMERR | RXPARERR | RXOVFERR)) +        { +            if (status & RXPARERR)                /* parity error */ +                icount->parity++; +            else +                if (status & RXFRAMERR)           /* frame error */ +                    icount->frame++; +            if (status & RXOVFERR) +            { +                // Overflow. Reset the RX FIFO +                info->port->fifoctl |= RSTRXFIFOS; +                icount->overrun++; +            } +            // check to see if we should ignore the character +            // and mask off conditions that should be ignored +            if (status & info->ignore_status_mask) +            { +                if (++ignore > 100 ) +                    break; +                goto ignore_char; +            } +            // Mask off the error conditions we want to ignore +            tmp = status & info->read_status_mask; +            if (tmp & RXPARERR) +            { +                flag_char = TTY_PARITY; +            } +            else +                if (tmp & RXFRAMERR) +                { +                    flag_char = TTY_FRAME; +                } +            if (tmp & RXOVFERR) +            { +	        tty_insert_flip_char(tty, ch, flag_char); +		ch = 0; +		flag_char = TTY_OVERRUN; +		if (!tty_buffer_request_room(tty, 1)) +		  break; +            } +        } +	tty_insert_flip_char(tty, ch, flag_char); +    } +ignore_char:; +	tty_flip_buffer_push(tty); +	tty_schedule_flip(tty); + +} + + +/* + * ------------------------------------------------------------ + * bcm_interrupt () + * + * this is the main interrupt routine for the chip. + * It deals with the multiple ports. + * ------------------------------------------------------------ + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static irqreturn_t bcm_interrupt (int irq, void * dev) +#else +static void bcm_interrupt (int irq, void * dev, struct pt_regs * regs) +#endif +{ +    struct bcm_serial * info = lines[0]; +    UINT16  intStat; + +    /* get pending interrupt flags from UART  */ + +    /* Mask with only the serial interrupts that are enabled */ +    intStat = info->port->intStatus & info->port->intMask; +    while (intStat) +    { +        if (intStat & RXINT) +            receive_chars (info);           +        else +            if (intStat & TXINT) +                info->port->intStatus = TXINT; +            else /* don't know what it was, so let's mask it */ +                info->port->intMask &= ~intStat; + +        intStat = info->port->intStatus & info->port->intMask; +    } + +    // Clear the interrupt +    BcmHalInterruptEnable (INTERRUPT_ID_UART); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +    return IRQ_HANDLED; +#endif     +} + +/* + * ------------------------------------------------------------------- + * startup () + * + * various initialization tasks + * -------------------------------------------------------------------  + */ +static int startup (struct bcm_serial *info) +{ +    // Port is already started... +    return 0; +} + +/*  + * ------------------------------------------------------------------- + * shutdown () + * + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + * -------------------------------------------------------------------  + */ +static void shutdown (struct bcm_serial * info) +{ +    unsigned long flags; +    if (!info->is_initialized) +        return; + +    spin_lock_irqsave(&bcm963xx_serial_lock, flags); + +    info->port->control &= ~(BRGEN|TXEN|RXEN); +    if (info->tty) +        set_bit (TTY_IO_ERROR, &info->tty->flags); +    info->is_initialized = 0; + +    spin_unlock_irqrestore(&bcm963xx_serial_lock, flags); +} +/*  + * ------------------------------------------------------------------- + * change_speed () + * + * Set the baud rate, character size, parity and stop bits. + * -------------------------------------------------------------------  + */ +static void change_speed( volatile Uart *pUart, tcflag_t cFlag ) +{ +    unsigned long ulFlags, ulBaud, ulClockFreqHz, ulTmp; +	 +    spin_lock_irqsave(&bcm963xx_serial_lock, ulFlags); +    switch( cFlag & (CBAUD | CBAUDEX) ) +    { +    case B115200: +        ulBaud = 115200; +        break; +    case B57600: +        ulBaud = 57600; +        break; +    case B38400: +        ulBaud = 38400; +        break; +    case B19200: +        ulBaud = 19200; +        break; +    case B9600: +        ulBaud = 9600; +        break; +    case B4800: +        ulBaud = 4800; +        break; +    case B2400: +        ulBaud = 2400; +        break; +    case B1800: +        ulBaud = 1800; +        break; +    case B1200: +        ulBaud = 1200; +        break; +    case B600: +        ulBaud = 600; +        break; +    case B300: +        ulBaud = 300; +        break; +    case B200: +        ulBaud = 200; +        break; +    case B150: +        ulBaud = 150; +        break; +    case B134: +        ulBaud = 134; +        break; +    case B110: +        ulBaud = 110; +        break; +    case B75: +        ulBaud = 75; +        break; +    case B50: +        ulBaud = 50; +        break; +    default: +        ulBaud = 115200; +        break; +    } + +    /* Calculate buad rate.  */ +    ulClockFreqHz = BD_BCM63XX_TIMER_CLOCK_INPUT; +    ulTmp = (ulClockFreqHz / ulBaud) / 16; +    if( ulTmp & 0x01 ) +        ulTmp /= 2; /* Rounding up, so sub is already accounted for */ +    else +        ulTmp = (ulTmp / 2) - 1; /* Rounding down so we must sub 1 */ +    pUart->baudword = ulTmp; + +    /* Set character size, stop bits and parity.  */ +    switch( cFlag & CSIZE ) +    { +    case CS5: +        ulTmp = BITS5SYM; /* select transmit 5 bit data size */ +        break; +    case CS6: +        ulTmp = BITS6SYM; /* select transmit 6 bit data size */ +        break; +    case CS7: +        ulTmp = BITS7SYM; /* select transmit 7 bit data size */ +        break; +    default: +        ulTmp = BITS8SYM; /* select transmit 8 bit data size */ +        break; +    } +    if( cFlag & CSTOPB ) +        ulTmp |= TWOSTOP;         /* select 2 stop bits */ +    else +        ulTmp |= ONESTOP;         /* select one stop bit */ + +    /* Write these values into the config reg.  */ +    pUart->config = ulTmp; +    pUart->control &= ~(RXPARITYEN | TXPARITYEN | RXPARITYEVEN | TXPARITYEVEN); +    switch( cFlag & (PARENB | PARODD) ) +    { +    case PARENB|PARODD: +        pUart->control |= RXPARITYEN | TXPARITYEN; +        break; +    case PARENB: +        pUart->control |= RXPARITYEN | TXPARITYEN | RXPARITYEVEN | TXPARITYEVEN; +        break; +    default: +        pUart->control |= 0; +        break; +    } + +    /* Reset and flush uart */ +    pUart->fifoctl = RSTTXFIFOS | RSTRXFIFOS; +    spin_unlock_irqrestore(&bcm963xx_serial_lock, ulFlags); +} + + +/*  + * ------------------------------------------------------------------- + * bcm_flush_char () + * + * Nothing to flush.  Polled I/O is used. + * -------------------------------------------------------------------  + */ +static void bcm63xx_cons_flush_chars (struct tty_struct *tty) +{ +} + + +/*  + * ------------------------------------------------------------------- + * bcm63xx_cons_write () + * + * Main output routine using polled I/O. + * -------------------------------------------------------------------  + */ +static int bcm63xx_cons_write (struct tty_struct *tty,  +    const unsigned char *buf, int count) +{ +    int c; + +    for (c = 0; c < count; c++) +        _putc(buf[c]); +    return count; +} + +/*  + * ------------------------------------------------------------------- + * bcm63xx_cons_write_room () + * + * Compute the amount of space available for writing. + * -------------------------------------------------------------------  + */ +static int bcm63xx_cons_write_room (struct tty_struct *tty) +{ +    /* Pick a number.  Any number.  Polled I/O is used. */ +    return 1024; +} + +/*  + * ------------------------------------------------------------------- + * bcm_chars_in_buffer () + * + * compute the amount of char left to be transmitted + * -------------------------------------------------------------------  + */ +static int bcm_chars_in_buffer (struct tty_struct *tty) +{ +    return 0; +} + +/*  + * ------------------------------------------------------------------- + * bcm_flush_buffer () + * + * Empty the output buffer + * -------------------------------------------------------------------  + */ +static void bcm_flush_buffer (struct tty_struct *tty) +{ +	tty_wakeup(tty); +} + +/* + * ------------------------------------------------------------ + * bcm_throttle () and bcm_unthrottle () + *  + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled (or not). + * ------------------------------------------------------------ + */ +static void bcm_throttle (struct tty_struct *tty) +{ +    struct bcm_serial *info = (struct bcm_serial *)tty->driver_data;   +    if (I_IXOFF(tty)) +        info->x_char = STOP_CHAR(tty); +} + +static void bcm_unthrottle (struct tty_struct *tty) +{ +    struct bcm_serial *info = (struct bcm_serial *)tty->driver_data;   +    if (I_IXOFF(tty)) +    { +        if (info->x_char) +            info->x_char = 0; +        else +            info->x_char = START_CHAR(tty); +    } +} + +static void bcm_send_xchar (struct tty_struct *tty, char ch) +{ +    struct bcm_serial *info = (struct bcm_serial *)tty->driver_data; +    info->x_char = ch; +    if (ch) +        bcm_start (info->tty); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl () and friends + * ------------------------------------------------------------ + */ +static int get_serial_info(struct bcm_serial *info, struct serial_struct *retinfo) +{ +    struct serial_struct tmp; + +    if (!retinfo) +        return -EFAULT; + +    memset (&tmp, 0, sizeof(tmp)); +    tmp.type            = info->type; +    tmp.line            = info->line; +    tmp.port            = (int) info->port; +    tmp.irq             = info->irq; +    tmp.flags           = 0; +    tmp.baud_base       = info->baud_base; +    tmp.close_delay     = info->close_delay; +    tmp.closing_wait    = info->closing_wait; + +    return copy_to_user (retinfo, &tmp, sizeof(*retinfo)); +} + +static int set_serial_info (struct bcm_serial *info, struct serial_struct *new_info) +{ +    struct serial_struct new_serial; +    struct bcm_serial old_info; +    int retval = 0; + +    if (!new_info) +        return -EFAULT; + +    copy_from_user (&new_serial, new_info, sizeof(new_serial)); +    old_info = *info; + +    if (!capable(CAP_SYS_ADMIN)) +        return -EPERM; + + +    if (info->count > 1) +        return -EBUSY; + +    /* OK, past this point, all the error checking has been done. +     * At this point, we start making changes..... +     */ +    info->baud_base = new_serial.baud_base; +    info->type = new_serial.type; +    info->close_delay = new_serial.close_delay; +    info->closing_wait = new_serial.closing_wait; +    retval = startup (info); +    return retval; +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + *          is emptied.  On bus types like RS485, the transmitter must + *          release the bus after transmitting. This must be done when + *          the transmit shift register is empty, not be done when the + *          transmit holding register is empty.  This functionality + *          allows an RS485 driver to be written in user space.  + */ +static int get_lsr_info (struct bcm_serial *info, unsigned int *value) +{ +    return( 0 ); +} + +/* + * This routine sends a break character out the serial port. + */ +static void send_break (struct bcm_serial *info, int duration) +{ +    unsigned long flags; + +    if (!info->port) +        return; + +    current->state = TASK_INTERRUPTIBLE; + +    /*save_flags (flags); +    cli();*/ +    spin_lock_irqsave(&bcm963xx_serial_lock, flags); + +    info->port->control |= XMITBREAK; +    schedule_timeout(duration); +    info->port->control &= ~XMITBREAK; + +    spin_unlock_irqrestore(&bcm963xx_serial_lock, flags); +    //restore_flags (flags); +} + +static int bcm_ioctl (struct tty_struct * tty, struct file * file, +    unsigned int cmd, unsigned long arg) +{ +    int error; +    struct bcm_serial * info = (struct bcm_serial *)tty->driver_data; +    int retval; + +    if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +        (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  && +        (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) +    { +        if (tty->flags & (1 << TTY_IO_ERROR)) +            return -EIO; +    } +    switch (cmd)  +    { + +    case TCSBRK:    /* SVID version: non-zero arg --> no break */ +        retval = tty_check_change (tty); +        if (retval) +            return retval; +        tty_wait_until_sent (tty, 0); +        if (!arg) +            send_break (info, HZ/4); /* 1/4 second */ +        return 0; + +    case TCSBRKP:   /* support for POSIX tcsendbreak() */ +        retval = tty_check_change (tty); +        if (retval) +            return retval; +        tty_wait_until_sent (tty, 0); +        send_break (info, arg ? arg*(HZ/10) : HZ/4); +        return 0; + +    case TIOCGSOFTCAR: +        error = access_ok (VERIFY_WRITE, (void *)arg, sizeof(long)); +        if (!error) +            return -EFAULT; +	else +	{ +	    put_user (C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); +	    return 0; +	} + +    case TIOCSSOFTCAR: +        error = get_user (arg, (unsigned long *)arg); +        if (error) +            return error; +        tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); +        return 0; + +    case TIOCGSERIAL: +        error = access_ok (VERIFY_WRITE, (void *)arg, sizeof(struct serial_struct)); +        if (!error) +            return -EFAULT; +	else +	    return get_serial_info (info, (struct serial_struct *)arg); + +    case TIOCSSERIAL: +        return set_serial_info (info, (struct serial_struct *) arg); + +    case TIOCSERGETLSR: /* Get line status register */ +        error = access_ok (VERIFY_WRITE, (void *)arg, sizeof(unsigned int)); +        if (!error) +            return -EFAULT; +        else +            return get_lsr_info (info, (unsigned int *)arg); + +    case TIOCSERGSTRUCT: +        error = access_ok (VERIFY_WRITE, (void *)arg, sizeof(struct bcm_serial)); +        if (!error) +            return -EFAULT; +	else +	{ +	    copy_to_user((struct bcm_serial *)arg, info, sizeof(struct bcm_serial)); +	    return 0; +	} + +    default: +        return -ENOIOCTLCMD; +    } +    return 0; +} + +static void bcm_set_termios (struct tty_struct *tty, struct termios *old_termios) +{ +    struct bcm_serial *info = (struct bcm_serial *)tty->driver_data; + +    if( tty->termios->c_cflag != old_termios->c_cflag ) +        change_speed (info->port, tty->termios->c_cflag); +} + +/* + * ------------------------------------------------------------ + * bcm63xx_cons_close() + *  + * This routine is called when the serial port gets closed.  First, we + * wait for the last remaining data to be sent.  Then, we turn off + * the transmit enable and receive enable flags. + * ------------------------------------------------------------ + */ +static void bcm63xx_cons_close (struct tty_struct *tty, struct file *filp) +{ +    struct bcm_serial * info = (struct bcm_serial *)tty->driver_data; +    unsigned long flags; + +    if (!info) +        return; + +    /*save_flags (flags);  +    cli();*/ +    spin_lock_irqsave(&bcm963xx_serial_lock, flags); + +    if (tty_hung_up_p (filp)) +    { +        spin_unlock_irqrestore(&bcm963xx_serial_lock, flags); +        //restore_flags (flags); +        return; +    } + +    if ((tty->count == 1) && (info->count != 1)) +    { + +        /* Uh, oh.  tty->count is 1, which means that the tty +         * structure will be freed.  Info->count should always +         * be one in these conditions.  If it's greater than +         * one, we've got real problems, since it means the +         * serial port won't be shutdown. +         */ +        printk("bcm63xx_cons_close: bad serial port count; tty->count is 1, " +            "info->count is %d\n", info->count); +        info->count = 1; +    } + +    if (--info->count < 0) +    { +        printk("ds_close: bad serial port count for ttys%d: %d\n", +        info->line, info->count); +        info->count = 0; +    } + +    if (info->count) +    { +        //restore_flags (flags); +	spin_unlock_irqrestore(&bcm963xx_serial_lock, flags); +        return; +    } + +    /* Now we wait for the transmit buffer to clear; and we notify  +     * the line discipline to only process XON/XOFF characters. +     */ +    tty->closing = 1; + +    /* At this point we stop accepting input.  To do this, we +     * disable the receive line status interrupts. +     */ +    shutdown (info); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +    if (tty->driver->flush_buffer) +        tty->driver->flush_buffer (tty); +#else     +    if (tty->driver.flush_buffer) +        tty->driver.flush_buffer (tty); +#endif +    if (tty->ldisc.flush_buffer) +        tty->ldisc.flush_buffer (tty); + +    tty->closing = 0; +    info->event = 0; +    info->tty = 0; +    if (tty->ldisc.num != tty_ldisc_get(N_TTY)->num) +    { +        if (tty->ldisc.close) +            (tty->ldisc.close)(tty); +        tty->ldisc = *tty_ldisc_get(N_TTY); +        tty->termios->c_line = N_TTY; +        if (tty->ldisc.open) +            (tty->ldisc.open)(tty); +    } +    if (info->blocked_open) +    { +        if (info->close_delay) +        { +            current->state = TASK_INTERRUPTIBLE; +            schedule_timeout(info->close_delay); +        } +        wake_up_interruptible (&info->open_wait); +    } +    wake_up_interruptible (&info->close_wait); + +    //restore_flags (flags); +    spin_unlock_irqrestore(&bcm963xx_serial_lock, flags); +} + +/* + * bcm_hangup () --- called by tty_hangup() when a hangup is signaled. + */ +static void bcm_hangup (struct tty_struct *tty) +{ + +    struct bcm_serial *info = (struct bcm_serial *)tty->driver_data; + +    shutdown (info); +    info->event = 0; +    info->count = 0; +    info->tty = 0; +    wake_up_interruptible (&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready (struct tty_struct *tty, struct file *filp, +    struct bcm_serial *info) +{ +    return 0; +}        + +/* + * This routine is called whenever a serial port is opened.  It + * enables interrupts for a serial port. It also performs the  + * serial-specific initialization for the tty structure. + */ +static int bcm63xx_cons_open (struct tty_struct * tty, struct file * filp) +{ +    struct bcm_serial *info; +    int retval, line; + +    // Make sure we're only opening on of the ports we support +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +    line = MINOR(tty->driver->cdev.dev) - tty->driver->minor_start; +#else +    line = MINOR(tty->device) - tty->driver.minor_start; +#endif     + +    if ((line < 0) || (line >= BCM_NUM_UARTS)) +        return -ENODEV; + +    info = lines[line]; + +    tty->low_latency=1; +    info->port->intMask  = 0;     /* Clear any pending interrupts */ +    info->port->intMask  = RXINT; /* Enable RX */ + +    info->count++; +    tty->driver_data = info; +    info->tty = tty; +    BcmHalInterruptEnable (INTERRUPT_ID_UART); + +    // Start up serial port +    retval = startup (info); +    if (retval) +        return retval; + +    retval = block_til_ready (tty, filp, info); +    if (retval) +        return retval; + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +    info->pgrp = process_group(current); +    info->session = current->signal->session; +#else +    info->session = current->session;     +    info->pgrp = current->pgrp; +#endif     + +    return 0; +} + + +static struct tty_operations rs_ops = { +	.open = bcm63xx_cons_open, +	.close = bcm63xx_cons_close, +	.write = bcm63xx_cons_write, +	.flush_chars = bcm63xx_cons_flush_chars, +	.write_room = bcm63xx_cons_write_room, +	.chars_in_buffer = bcm_chars_in_buffer, +	.flush_buffer = bcm_flush_buffer, +	.ioctl = bcm_ioctl, +	.throttle = bcm_throttle, +	.unthrottle = bcm_unthrottle, +	.send_xchar = bcm_send_xchar, +	.set_termios = bcm_set_termios, +	.stop = bcm_stop, +	.start = bcm_start, +	.hangup = bcm_hangup, +}; + +/* -------------------------------------------------------------------------- +    Name: bcm63xx_serialinit + Purpose: Initialize our BCM63xx serial driver +-------------------------------------------------------------------------- */ +static int __init bcm63xx_serialinit(void) +{ +    int i, flags; +    struct bcm_serial * info; + +    // Print the driver version information +    printk(VER_STR); +    serial_driver = alloc_tty_driver(BCM_NUM_UARTS); +    if (!serial_driver) +	return -ENOMEM; + +	serial_driver->owner 	    = THIS_MODULE; +//	serial_driver->devfs_name 	    = "tts/"; +//    serial_driver.magic             = TTY_DRIVER_MAGIC; +    serial_driver->name              = "ttyS"; +    serial_driver->major             = TTY_MAJOR; +    serial_driver->minor_start       = 64; +//    serial_driver.num               = BCM_NUM_UARTS; +    serial_driver->type              = TTY_DRIVER_TYPE_SERIAL; +    serial_driver->subtype           = SERIAL_TYPE_NORMAL; +    serial_driver->init_termios      = tty_std_termios; +    serial_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; +    serial_driver->flags             = TTY_DRIVER_REAL_RAW; + +    serial_driver->termios           = serial_termios; +    serial_driver->termios_locked    = serial_termios_locked; +     +    tty_set_operations(serial_driver, &rs_ops); + +    if (tty_register_driver (serial_driver)) +        panic("Couldn't register serial driver\n"); + +    //save_flags(flags); cli(); +    spin_lock_irqsave(&bcm963xx_serial_lock, flags); +     +    for (i = 0; i < BCM_NUM_UARTS; i++) +    { +        info = &multi[i];  +        lines[i] = info; +        info->magic = SERIAL_MAGIC; +        info->port                  = (Uart *) ((char *)UART_BASE + (i * 0x20)); +        info->tty                   = 0; +        info->irq                   = (2 - i) + 8; +        info->line                  = i; +        info->close_delay           = 50; +        info->closing_wait          = 3000; +        info->x_char                = 0; +        info->event                 = 0; +        info->count                 = 0; +        info->blocked_open          = 0;	 +        info->normal_termios        = serial_driver->init_termios; +        init_waitqueue_head(&info->open_wait);  +        init_waitqueue_head(&info->close_wait);  + +        /* If we are pointing to address zero then punt - not correctly +         * set up in setup.c to handle this.  +         */ +        if (! info->port) +            return 0; +        BcmHalMapInterrupt(bcm_interrupt, 0, INTERRUPT_ID_UART); +    } + +    /* order matters here... the trick is that flags +     * is updated... in request_irq - to immediatedly obliterate +     * it is unwise.  +     */ +    spin_unlock_irqrestore(&bcm963xx_serial_lock, flags); +    return 0; +} + +module_init(bcm63xx_serialinit); + +/* -------------------------------------------------------------------------- +    Name: bcm_console_print + Purpose: bcm_console_print is registered for printk. +          The console_lock must be held when we get here. +-------------------------------------------------------------------------- */ +static void bcm_console_print (struct console * cons, const char * str, +    unsigned int count) +{ +    unsigned int i; +    //_puts(str); +    for(i=0; i<count; i++, str++) +    { +        _putc(*str); +        if (*str == 10) +        { +            _putc(13); +        } +    } +} + +static struct tty_driver * bcm_console_device(struct console * c, int *index) +{ +    *index = c->index; +    return serial_driver; +} + +static int __init bcm_console_setup(struct console * co, char * options) +{ +    return 0; +} + +static struct console bcm_sercons = { +    .name	= "ttyS", +    .write	= bcm_console_print, +    .device	= bcm_console_device, +    .setup	= bcm_console_setup, +    .flags	= CON_PRINTBUFFER, +    .index	= -1, +}; + +static int __init bcm63xx_console_init(void) +{ +    register_console(&bcm_sercons); +    return 0; +} + +console_initcall(bcm63xx_console_init);  | 
