From 56231056ea784f1cec6450f649b1adaed1f56366 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 6 Sep 2007 16:27:37 +0000 Subject: strip the kernel version suffix from target directories, except for brcm-2.4 (the -2.4 will be included in the board name here). CONFIG_LINUX__ becomes CONFIG_TARGET_, same for profiles. SVN-Revision: 8653 --- .../files/drivers/mtd/maps/bcm963xx-flash.c | 277 ++++++ .../brcm63xx/files/drivers/serial/bcm63xx_cons.c | 1041 ++++++++++++++++++++ 2 files changed, 1318 insertions(+) create mode 100644 target/linux/brcm63xx/files/drivers/mtd/maps/bcm963xx-flash.c create mode 100644 target/linux/brcm63xx/files/drivers/serial/bcm63xx_cons.c (limited to 'target/linux/brcm63xx/files/drivers') diff --git a/target/linux/brcm63xx/files/drivers/mtd/maps/bcm963xx-flash.c b/target/linux/brcm63xx/files/drivers/mtd/maps/bcm963xx-flash.c new file mode 100644 index 0000000000..c4c4526b49 --- /dev/null +++ b/target/linux/brcm63xx/files/drivers/mtd/maps/bcm963xx-flash.c @@ -0,0 +1,277 @@ +/* + * $Id$ + * Copyright (C) 2006 Florian Fainelli + * Mike Albon + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 Mike Albon "); diff --git a/target/linux/brcm63xx/files/drivers/serial/bcm63xx_cons.c b/target/linux/brcm63xx/files/drivers/serial/bcm63xx_cons.c new file mode 100644 index 0000000000..b026aa10ab --- /dev/null +++ b/target/linux/brcm63xx/files/drivers/serial/bcm63xx_cons.c @@ -0,0 +1,1041 @@ +/* +<: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 +#include +#include +#include +#include +#include +#include + +/* for definition of struct console */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include <6348_map_part.h> +#include <6348_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 ktermios 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 ktermios *serial_termios[BCM_NUM_UARTS]; +static struct ktermios *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 ktermios *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 + enable_brcm_irq(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 ktermios *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; + enable_brcm_irq(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); +#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->name = "ttyS"; + serial_driver->major = TTY_MAJOR; + serial_driver->minor_start = 64; + serial_driver->num = 1; + 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 < 1; 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); -- cgit v1.2.3